Add ReDroid (Android in Container) LXC setup scripts

- redroid-lxc-setup.sh: Interactive wizard for ReDroid deployment
  - Docker installation, ADB/scrcpy setup
  - Automatic architecture detection (ARM64/x86_64)
  - LXC binder device configuration
  - Multi-instance support with docker-compose
  - NDK translation for ARM apps on x86 hosts
- proxmox-host-config.sh: Proxmox host configuration helper
  - Binder kernel module loading
  - LXC config snippets for device passthrough
- VSCode tasks for ReDroid management:
  - Setup wizard, auto-setup, start/stop/status
  - Screen mirroring (scrcpy), ADB shell
  - APK installation, live logs
  - Binder device diagnostics

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-03-26 16:05:11 +01:00
parent ba6b5da03f
commit 5340ebce83
3 changed files with 719 additions and 0 deletions

113
.vscode/tasks.json vendored
View File

@ -211,6 +211,106 @@
"group": "build",
"presentation": { "reveal": "always" },
"problemMatcher": []
},
//
// GROUPE : REDROID (Android in Container)
//
{
"label": "🤖 ReDroid: Setup Wizard (interactif)",
"type": "shell",
"command": "sudo bash ${workspaceFolder}/redroid/redroid-lxc-setup.sh",
"group": { "kind": "build", "isDefault": false },
"presentation": {
"reveal": "always",
"panel": "new",
"title": "ReDroid LXC Wizard",
"echo": true
},
"problemMatcher": []
},
{
"label": "🤖 ReDroid: Auto-setup Android 12 x64",
"type": "shell",
"command": "sudo bash ${workspaceFolder}/redroid/redroid-lxc-setup.sh --auto --android-ver 12",
"group": "build",
"presentation": {
"reveal": "always",
"panel": "new",
"title": "ReDroid Auto-Setup"
},
"problemMatcher": []
},
{
"label": "▶ ReDroid: Start",
"type": "shell",
"command": "sudo redroid-ctl start",
"group": "build",
"presentation": { "reveal": "always", "panel": "shared", "title": "ReDroid" },
"problemMatcher": []
},
{
"label": "⏹ ReDroid: Stop",
"type": "shell",
"command": "sudo redroid-ctl stop",
"presentation": { "reveal": "always", "panel": "shared", "title": "ReDroid" },
"problemMatcher": []
},
{
"label": "📊 ReDroid: Status",
"type": "shell",
"command": "sudo redroid-ctl status",
"presentation": { "reveal": "always", "panel": "shared", "title": "ReDroid Status" },
"problemMatcher": []
},
{
"label": "📱 ReDroid: Ouvrir écran (scrcpy inst.0)",
"type": "shell",
"command": "redroid-ctl screen 0",
"presentation": { "reveal": "silent", "panel": "shared" },
"problemMatcher": []
},
{
"label": "🐚 ReDroid: ADB Shell (inst.0)",
"type": "shell",
"command": "redroid-ctl shell 0",
"presentation": { "reveal": "always", "panel": "new", "title": "ADB Shell" },
"problemMatcher": []
},
{
"label": "📦 ReDroid: Installer APK...",
"type": "shell",
"command": "redroid-ctl install ${input:apkPath} ${input:instanceIdx}",
"presentation": { "reveal": "always", "panel": "shared", "title": "APK Install" },
"problemMatcher": []
},
{
"label": "📋 ReDroid: Logs live (inst.0)",
"type": "shell",
"command": "sudo redroid-ctl logs 0",
"presentation": { "reveal": "always", "panel": "new", "title": "ReDroid Logs" },
"problemMatcher": []
},
{
"label": "🔍 ReDroid: Vérifier binder devices",
"type": "shell",
"command": "ls -la /dev/binder /dev/hwbinder /dev/vndbinder 2>/dev/null || (ls /dev/binderfs/ 2>/dev/null && echo 'binderfs OK') || echo '⚠ Binder absent — configurer hôte LXC'",
"presentation": { "reveal": "always", "panel": "shared", "title": "Diagnostic Binder" },
"problemMatcher": []
},
{
"label": "🖥 Proxmox Host: Charger modules binder",
"type": "shell",
"command": "bash -c 'echo \"=== À exécuter sur l hôte Proxmox ===\"; echo \"apt install linux-modules-extra-$(uname -r)\"; echo \"modprobe binder_linux devices=binder,hwbinder,vndbinder\"; echo \"modprobe ashmem_linux\"'",
"presentation": { "reveal": "always", "panel": "shared", "title": "Host Config Help" },
"problemMatcher": []
},
{
"label": "🔁 ReDroid: Démarrer 3 instances",
"type": "shell",
"command": "sudo bash ${workspaceFolder}/redroid/redroid-lxc-setup.sh --auto --instances 3 --port 5555",
"presentation": { "reveal": "always", "panel": "new", "title": "ReDroid Multi" },
"problemMatcher": []
}
],
@ -264,6 +364,19 @@
"description": "Port dev (80XX)",
"options": ["001", "002", "003", "004", "005", "006", "007", "008"],
"default": "001"
},
{
"id": "apkPath",
"type": "promptString",
"description": "Chemin complet vers l'APK",
"default": "/tmp/app.apk"
},
{
"id": "instanceIdx",
"type": "pickString",
"description": "Instance ReDroid cible",
"options": ["0", "1", "2", "3"],
"default": "0"
}
]
}

61
redroid/proxmox-host-config.sh Executable file
View File

@ -0,0 +1,61 @@
# ══════════════════════════════════════════════════════════════════
# CONFIG HÔTE PROXMOX — Passthrough binder vers LXC ReDroid
# À appliquer sur le NODE Proxmox (hôte physique)
# AVANT de lancer le wizard dans le LXC
# ══════════════════════════════════════════════════════════════════
# ── 1. Charger les modules sur l'HÔTE ────────────────────────────
apt install linux-modules-extra-$(uname -r)
modprobe binder_linux devices="binder,hwbinder,vndbinder"
modprobe ashmem_linux
# Persistance au reboot (hôte)
cat >> /etc/modules << 'EOF'
binder_linux
ashmem_linux
EOF
cat >> /etc/modprobe.d/binder.conf << 'EOF'
options binder_linux devices=binder,hwbinder,vndbinder
EOF
# ── 2. Config LXC dans Proxmox (/etc/pve/lxc/<CTID>.conf) ────────
# Remplacer <CTID> par l'ID de ton conteneur (ex: 200)
cat >> /etc/pve/lxc/<CTID>.conf << 'EOF'
# ReDroid — Android container
# Nesting + privileges
features: nesting=1
lxc.apparmor.profile: unconfined
# Binder device passthrough
lxc.cgroup2.devices.allow: c 10:* rwm
lxc.mount.entry: /dev/binder dev/binder none bind,optional,create=file 0 0
lxc.mount.entry: /dev/hwbinder dev/hwbinder none bind,optional,create=file 0 0
lxc.mount.entry: /dev/vndbinder dev/vndbinder none bind,optional,create=file 0 0
# Ashmem (Android 11 et inférieur)
lxc.mount.entry: /dev/ashmem dev/ashmem none bind,optional,create=file 0 0
EOF
# ── 3. Redémarrer le LXC ─────────────────────────────────────────
pct stop <CTID> && pct start <CTID>
# ── 4. Vérifier dans le LXC ──────────────────────────────────────
# pct enter <CTID>
# ls -la /dev/binder /dev/hwbinder /dev/vndbinder
# ══════════════════════════════════════════════════════════════════
# Notes architecture
# ══════════════════════════════════════════════════════════════════
#
# ESPRESSObin / MOCHAbin (Marvell Armada 3720, ARM64)
# → Image : redroid/redroid:12.0.0_64only-latest (arm64 tag)
# → Pas de NDK bridge nécessaire (natif ARM)
#
# x86_64 host
# → Image : redroid/redroid:12.0.0_64only-latest (amd64 tag)
# → NDK bridge libndk_translation activé pour apps ARM
#
# ══════════════════════════════════════════════════════════════════

545
redroid/redroid-lxc-setup.sh Executable file
View File

@ -0,0 +1,545 @@
#!/usr/bin/env bash
# ╔══════════════════════════════════════════════════════════════════╗
# ║ ReDroid LXC Setup Wizard — CyberMind SecuBox-Deb ║
# ║ Android-in-Container on Debian · ARM64 / x86_64 ║
# ╚══════════════════════════════════════════════════════════════════╝
# Usage: bash redroid-lxc-setup.sh [--auto] [--android-ver 12]
set -euo pipefail
# ─── Palette ──────────────────────────────────────────────────────
RED='\033[0;31m'; YEL='\033[0;33m'; GRN='\033[0;32m'
CYN='\033[0;36m'; BLU='\033[0;34m'; MAG='\033[0;35m'
WHT='\033[1;37m'; DIM='\033[2m'; RST='\033[0m'
BOLD='\033[1m'
# ─── Helpers ──────────────────────────────────────────────────────
banner() {
echo -e "${CYN}"
cat << 'EOF'
██████╗ ███████╗██████╗ ██████╗ ██████╗ ██╗██████╗
██╔══██╗██╔════╝██╔══██╗██╔══██╗██╔═══██╗██║██╔══██╗
██████╔╝█████╗ ██║ ██║██████╔╝██║ ██║██║██║ ██║
██╔══██╗██╔══╝ ██║ ██║██╔══██╗██║ ██║██║██║ ██║
██║ ██║███████╗██████╔╝██║ ██║╚██████╔╝██║██████╔╝
╚═╝ ╚═╝╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝╚═════╝
EOF
echo -e "${DIM} LXC Android Container Wizard · CyberMind SecuBox-Deb${RST}"
echo -e "${DIM} ────────────────────────────────────────────────────${RST}\n"
}
step() { echo -e "\n${BLU}${RST} ${BOLD}${1}${RST}"; }
ok() { echo -e " ${GRN}${RST} ${1}"; }
warn() { echo -e " ${YEL}${RST} ${1}"; }
err() { echo -e " ${RED}${RST} ${1}" >&2; }
ask() { echo -en " ${MAG}?${RST} ${1} "; }
confirm() {
ask "${1} [y/N]"
read -r ans
[[ "${ans,,}" == "y" || "${ans,,}" == "yes" ]]
}
require_root() {
if [[ $EUID -ne 0 ]]; then
err "Ce script doit être exécuté en root (ou via sudo)"
exit 1
fi
}
# ─── Détection architecture ───────────────────────────────────────
detect_arch() {
ARCH=$(uname -m)
case "$ARCH" in
aarch64|arm64) HOST_ARCH="arm64" ;;
x86_64) HOST_ARCH="amd64" ;;
*)
err "Architecture non supportée: $ARCH"
exit 1 ;;
esac
ok "Architecture hôte détectée : ${BOLD}${HOST_ARCH}${RST} (${ARCH})"
}
# ─── Vérification si dans LXC ─────────────────────────────────────
detect_lxc() {
if grep -q "lxc" /proc/1/environ 2>/dev/null || \
[[ -f /run/container_type ]] && grep -q "lxc" /run/container_type 2>/dev/null || \
systemd-detect-virt --quiet --container 2>/dev/null | grep -q "lxc"; then
IN_LXC=true
ok "Environnement LXC détecté"
else
IN_LXC=false
warn "Pas dans un LXC — vérification des paramètres nécessaire"
fi
}
# ─── Vérification prérequis LXC (config côté host Proxmox/LXC) ───
check_lxc_config() {
step "Vérification de la configuration LXC"
# Binder devices
local missing_devs=()
for dev in binder hwbinder vndbinder; do
if [[ -e "/dev/${dev}" ]] || [[ -e "/dev/binderfs/${dev}" ]]; then
ok "/dev/${dev} disponible"
else
missing_devs+=("$dev")
warn "/dev/${dev} ABSENT"
fi
done
if [[ ${#missing_devs[@]} -gt 0 ]]; then
echo -e "\n ${YEL}Modules binder manquants. Sur l'hôte Proxmox/LXC, exécuter :${RST}"
echo -e " ${DIM}apt install linux-modules-extra-\$(uname -r)${RST}"
echo -e " ${DIM}modprobe binder_linux devices=\"binder,hwbinder,vndbinder\"${RST}"
echo -e " ${DIM}modprobe ashmem_linux${RST}"
echo ""
echo -e " ${YEL}Ajouter dans /etc/pve/lxc/<CTID>.conf (Proxmox) :${RST}"
cat << 'LXCCONF'
lxc.cgroup2.devices.allow: c 10:* rwm
lxc.mount.entry: /dev/binder dev/binder none bind,optional,create=file 0 0
lxc.mount.entry: /dev/hwbinder dev/hwbinder none bind,optional,create=file 0 0
lxc.mount.entry: /dev/vndbinder dev/vndbinder none bind,optional,create=file 0 0
LXCCONF
echo ""
if ! confirm "Continuer quand même ?"; then
exit 0
fi
fi
# Nesting / privileged
if [[ -f /proc/sys/kernel/dmesg_restrict ]]; then
ok "Kernel capabilities accessibles"
fi
}
# ─── Installation Docker ──────────────────────────────────────────
install_docker() {
step "Installation Docker"
if command -v docker &>/dev/null; then
ok "Docker déjà installé : $(docker --version)"
return
fi
warn "Docker absent — installation en cours..."
apt-get update -qq
apt-get install -y -qq ca-certificates curl gnupg lsb-release
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update -qq
apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-compose-plugin
ok "Docker installé : $(docker --version)"
}
# ─── Installation ADB + scrcpy ───────────────────────────────────
install_adb_scrcpy() {
step "Installation ADB & scrcpy"
if ! command -v adb &>/dev/null; then
apt-get install -y -qq android-tools-adb
ok "ADB installé"
else
ok "ADB déjà présent : $(adb --version | head -1)"
fi
if ! command -v scrcpy &>/dev/null; then
apt-get install -y -qq scrcpy 2>/dev/null || {
warn "scrcpy non dispo via apt — installation snap..."
snap install scrcpy 2>/dev/null || warn "scrcpy non installé (headless OK)"
}
else
ok "scrcpy déjà présent : $(scrcpy --version 2>&1 | head -1)"
fi
}
# ─── Charger modules binder ───────────────────────────────────────
load_binder_modules() {
step "Chargement des modules kernel binder"
# Vérifier si dans LXC — si oui, les modules doivent exister côté hôte
if [[ "$IN_LXC" == "true" ]]; then
warn "Dans LXC : les modules doivent être chargés sur l'hôte, pas ici"
warn "Si /dev/binder est absent, configurer l'hôte Proxmox d'abord"
return
fi
for mod in binder_linux ashmem_linux; do
if lsmod | grep -q "${mod%_linux}"; then
ok "Module $mod chargé"
else
if modinfo "$mod" &>/dev/null; then
modprobe "$mod" ${mod == "binder_linux" ? 'devices="binder,hwbinder,vndbinder"' : ''} 2>/dev/null && ok "Module $mod chargé" || warn "Impossible de charger $mod"
else
# Essayer via linux-modules-extra
apt-get install -y -qq "linux-modules-extra-$(uname -r)" 2>/dev/null || true
modprobe "$mod" 2>/dev/null && ok "Module $mod chargé" || warn "$mod non disponible"
fi
fi
done
# Persistance au reboot
if ! grep -q "binder_linux" /etc/modules 2>/dev/null; then
echo "binder_linux" >> /etc/modules
echo "ashmem_linux" >> /etc/modules
ok "Modules ajoutés à /etc/modules (persistance)"
fi
}
# ─── Configuration réseau binderfs dans LXC ──────────────────────
setup_binderfs_lxc() {
if [[ "$IN_LXC" == "true" ]] && [[ ! -e "/dev/binder" ]]; then
step "Montage binderfs dans LXC"
mkdir -p /dev/binderfs
if ! mount -t binder binder /dev/binderfs 2>/dev/null; then
err "Impossible de monter binderfs — vérifier la config LXC hôte"
return 1
fi
ok "binderfs monté sur /dev/binderfs"
# Créer des symlinks /dev/binder → /dev/binderfs/binder
for dev in binder hwbinder vndbinder; do
ln -sf "/dev/binderfs/${dev}" "/dev/${dev}" 2>/dev/null || true
done
ok "Symlinks /dev/binder* créés"
fi
}
# ─── Choix de la version Android ─────────────────────────────────
choose_android_version() {
step "Sélection de la version Android"
local versions=("9" "10" "11" "12" "13" "15")
local tags=(
"redroid/redroid:9.0.0-latest"
"redroid/redroid:10.0.0-latest"
"redroid/redroid:11.0.0-latest"
"redroid/redroid:12.0.0_64only-latest"
"redroid/redroid:13.0.0-latest"
"redroid/redroid:15.0.0_64only-latest"
)
local recs=(
"stable, apps legacy"
"stable"
"recommandé + NDK ARM"
"recommandé 64-bit"
"stable récent"
"latest"
)
echo ""
for i in "${!versions[@]}"; do
local marker=""
[[ "${versions[$i]}" == "12" ]] && marker=" ${GRN}${RST}"
echo -e " ${CYN}[${i}]${RST} Android ${BOLD}${versions[$i]}${RST} ${DIM}${recs[$i]}${RST}${marker}"
done
echo ""
if [[ "${AUTO_MODE:-false}" == "true" ]]; then
ANDROID_IDX=3 # Android 12 par défaut
else
ask "Votre choix [0-5, défaut=3] :"
read -r choice
ANDROID_IDX=${choice:-3}
[[ "$ANDROID_IDX" =~ ^[0-5]$ ]] || ANDROID_IDX=3
fi
ANDROID_VER="${versions[$ANDROID_IDX]}"
DOCKER_IMAGE="${tags[$ANDROID_IDX]}"
ok "Sélectionné : Android ${BOLD}${ANDROID_VER}${RST}${DIM}${DOCKER_IMAGE}${RST}"
}
# ─── Paramètres réseau / ports ────────────────────────────────────
choose_ports() {
step "Configuration des ports"
if [[ "${AUTO_MODE:-false}" == "true" ]]; then
ADB_PORT=5555
SCRCPY_PORT=5556
else
ask "Port ADB [défaut: 5555] :"
read -r p; ADB_PORT=${p:-5555}
ask "Nombre d'instances à démarrer [défaut: 1] :"
read -r n; INSTANCE_COUNT=${n:-1}
fi
INSTANCE_COUNT=${INSTANCE_COUNT:-1}
ok "Port ADB de base : ${BOLD}${ADB_PORT}${RST} ×${INSTANCE_COUNT} instance(s)"
}
# ─── Options ARM translator ───────────────────────────────────────
choose_arm_translation() {
if [[ "$HOST_ARCH" == "amd64" ]]; then
step "Traduction ARM → x86 (NDK bridge)"
echo -e " ${DIM}Nécessaire pour faire tourner des APKs ARM sur hôte x86_64${RST}\n"
if [[ "${AUTO_MODE:-false}" == "true" ]]; then
USE_NDK=true
else
confirm "Activer libndk_translation (ARM apps sur x86) ?" && USE_NDK=true || USE_NDK=false
fi
[[ "$USE_NDK" == "true" ]] && ok "NDK ARM bridge activé" || warn "NDK désactivé (apps x86 seulement)"
else
USE_NDK=false
ok "ARM64 natif — traduction inutile"
fi
}
# ─── Génération docker-compose.yml ───────────────────────────────
generate_compose() {
step "Génération de docker-compose.yml"
local datadir="${REDROID_DATADIR:-/opt/redroid/data}"
mkdir -p "$datadir"
# Construire les binder volume mounts selon contexte
local binder_vols=""
if [[ "$IN_LXC" == "true" ]] && [[ -d "/dev/binderfs" ]]; then
binder_vols=' - /dev/binderfs/binder:/dev/binder
- /dev/binderfs/hwbinder:/dev/hwbinder
- /dev/binderfs/vndbinder:/dev/vndbinder'
fi
# NDK extra props
local ndk_props=""
if [[ "${USE_NDK:-false}" == "true" ]]; then
ndk_props=' - ro.product.cpu.abilist=x86_64,arm64-v8a,x86,armeabi-v7a,armeabi
- ro.product.cpu.abilist64=x86_64,arm64-v8a
- ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi
- ro.dalvik.vm.isa.arm=x86
- ro.dalvik.vm.isa.arm64=x86_64
- ro.enable.native.bridge.exec=1
- ro.dalvik.vm.native.bridge=libndk_translation.so
- ro.ndk_translation.version=0.2.2'
fi
# Générer N services
local services=""
for (( i=0; i<INSTANCE_COUNT; i++ )); do
local port=$(( ADB_PORT + i ))
local name="redroid-${i}"
local vol="${datadir}/instance-${i}"
mkdir -p "$vol"
services+="
${name}:
image: ${DOCKER_IMAGE}
container_name: ${name}
restart: unless-stopped
tty: true
stdin_open: true
privileged: true
ports:
- \"127.0.0.1:${port}:5555\"
volumes:
- ${vol}:/data"
if [[ -n "$binder_vols" ]]; then
services+="
${binder_vols}"
fi
services+="
command:
- androidboot.redroid_width=1080
- androidboot.redroid_height=1920
- androidboot.redroid_dpi=480
- androidboot.redroid_fps=60
- androidboot.redroid_gpu_mode=guest"
if [[ -n "$ndk_props" ]]; then
services+="
${ndk_props}"
fi
services+="
"
done
cat > /opt/redroid/docker-compose.yml << YAML
# ══════════════════════════════════════════════════════════════════
# ReDroid — Android in LXC Container
# CyberMind SecuBox-Deb · Generated $(date '+%Y-%m-%d %H:%M')
# Host arch : ${HOST_ARCH} Android : ${ANDROID_VER}
# Instances : ${INSTANCE_COUNT} ADB base port : ${ADB_PORT}
# ══════════════════════════════════════════════════════════════════
services:
${services}
YAML
ok "Fichier généré : /opt/redroid/docker-compose.yml"
}
# ─── Génération script de gestion ────────────────────────────────
generate_manager() {
cat > /usr/local/bin/redroid-ctl << 'MGMT'
#!/usr/bin/env bash
# redroid-ctl — gestion des instances ReDroid
# Usage: redroid-ctl {start|stop|restart|status|connect|screen|shell|install} [instance]
COMPOSE_FILE="/opt/redroid/docker-compose.yml"
ADB_BASE=5555
_instances() {
docker compose -f "$COMPOSE_FILE" ps --format json 2>/dev/null \
| python3 -c "import sys,json; [print(c['Name']) for c in json.load(sys.stdin)]" 2>/dev/null \
|| docker compose -f "$COMPOSE_FILE" ps --services 2>/dev/null
}
_adb_connect_all() {
local i=0
for name in $(_instances); do
local port=$(( ADB_BASE + i ))
adb connect "127.0.0.1:${port}" 2>/dev/null && \
echo " ✓ ADB connecté : ${name} → 127.0.0.1:${port}"
(( i++ ))
done
}
case "${1:-help}" in
start)
echo "▶ Démarrage ReDroid..."
cd /opt/redroid
docker compose up -d
sleep 5
_adb_connect_all
;;
stop)
echo "▶ Arrêt ReDroid..."
cd /opt/redroid && docker compose down
adb disconnect 2>/dev/null || true
;;
restart)
$0 stop; sleep 2; $0 start
;;
status)
echo "══ Instances ReDroid ══"
cd /opt/redroid && docker compose ps
echo ""
echo "══ Devices ADB ══"
adb devices
;;
connect)
# Reconnect ADB
adb disconnect 2>/dev/null || true
sleep 1
_adb_connect_all
;;
screen)
# Lancer scrcpy sur instance N (défaut: 0)
local IDX="${2:-0}"
local PORT=$(( ADB_BASE + IDX ))
scrcpy -s "127.0.0.1:${PORT}" --window-title "ReDroid-${IDX}" &
;;
shell)
local IDX="${2:-0}"
local PORT=$(( ADB_BASE + IDX ))
adb -s "127.0.0.1:${PORT}" shell
;;
install)
# Installer un APK sur instance N
# Usage: redroid-ctl install /path/to/app.apk [instance_idx]
local APK="${2:?Usage: redroid-ctl install <apk> [idx]}"
local IDX="${3:-0}"
local PORT=$(( ADB_BASE + IDX ))
adb -s "127.0.0.1:${PORT}" install -r "$APK"
;;
logs)
local IDX="${2:-0}"
local NAME="redroid-${IDX}"
docker logs -f "$NAME"
;;
help|*)
echo "Usage: redroid-ctl {start|stop|restart|status|connect|screen [N]|shell [N]|install <apk> [N]|logs [N]}"
;;
esac
MGMT
chmod +x /usr/local/bin/redroid-ctl
ok "Manager installé : /usr/local/bin/redroid-ctl"
}
# ─── Résumé final et démarrage ────────────────────────────────────
finalize() {
step "Démarrage des conteneurs"
cd /opt/redroid
if confirm "Démarrer les instances maintenant ?"; then
docker compose pull
docker compose up -d
echo -e "\n ${YEL}Attente boot Android (~15s)...${RST}"
sleep 15
# Connexion ADB
for (( i=0; i<INSTANCE_COUNT; i++ )); do
local port=$(( ADB_PORT + i ))
adb connect "127.0.0.1:${port}" && ok "ADB connecté sur 127.0.0.1:${port}" \
|| warn "ADB: tentative manuelle recommandée"
done
fi
echo -e "\n${CYN}╔══════════════════════════════════════════════════╗${RST}"
echo -e "${CYN}${RST} ${BOLD}ReDroid LXC · Déploiement terminé${RST} ${CYN}${RST}"
echo -e "${CYN}╚══════════════════════════════════════════════════╝${RST}"
echo ""
echo -e " ${GRN}Commandes disponibles :${RST}"
echo -e " ${CYN}redroid-ctl start${RST} — démarrer"
echo -e " ${CYN}redroid-ctl status${RST} — état + ADB devices"
echo -e " ${CYN}redroid-ctl screen [N]${RST} — affichage scrcpy"
echo -e " ${CYN}redroid-ctl shell [N]${RST} — shell adb"
echo -e " ${CYN}redroid-ctl install app.apk${RST}— installer un APK"
echo -e " ${CYN}redroid-ctl logs [N]${RST} — logs temps réel"
echo ""
echo -e " ${DIM}Compose : /opt/redroid/docker-compose.yml${RST}"
echo -e " ${DIM}Data : /opt/redroid/data/${RST}"
echo ""
}
# ─── Main ─────────────────────────────────────────────────────────
main() {
AUTO_MODE=false
ANDROID_VER_OVERRIDE=""
while [[ $# -gt 0 ]]; do
case "$1" in
--auto) AUTO_MODE=true ;;
--android-ver) ANDROID_VER_OVERRIDE="$2"; shift ;;
--instances) INSTANCE_COUNT="$2"; shift ;;
--port) ADB_PORT="$2"; shift ;;
--help|-h)
echo "Usage: $0 [--auto] [--android-ver {9|10|11|12|13|15}] [--instances N] [--port PORT]"
exit 0 ;;
esac
shift
done
banner
require_root
detect_arch
detect_lxc
check_lxc_config
install_docker
install_adb_scrcpy
load_binder_modules
setup_binderfs_lxc
choose_android_version
choose_ports
choose_arm_translation
generate_compose
generate_manager
finalize
}
main "$@"