diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 684ab790..64a7f25a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -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" } ] } diff --git a/redroid/proxmox-host-config.sh b/redroid/proxmox-host-config.sh new file mode 100755 index 00000000..84920c75 --- /dev/null +++ b/redroid/proxmox-host-config.sh @@ -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/.conf) ──────── +# Remplacer par l'ID de ton conteneur (ex: 200) + +cat >> /etc/pve/lxc/.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 && pct start + +# ── 4. Vérifier dans le LXC ────────────────────────────────────── +# pct enter +# 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 +# +# ══════════════════════════════════════════════════════════════════ diff --git a/redroid/redroid-lxc-setup.sh b/redroid/redroid-lxc-setup.sh new file mode 100755 index 00000000..b4e1c269 --- /dev/null +++ b/redroid/redroid-lxc-setup.sh @@ -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/.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 /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 [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 [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