diff --git a/android/0_termux-setup_v2.sh b/android/0_termux-setup_v2.sh old mode 100644 new mode 100755 index a2d0228..0ed8f6a --- a/android/0_termux-setup_v2.sh +++ b/android/0_termux-setup_v2.sh @@ -1,13 +1,19 @@ #!/data/data/com.termux/files/usr/bin/bash set -euo pipefail -# 0_termux-setupv2.sh -# - Termux bootstrap (packages, wakelock) -# - proot-distro + Debian bootstrap -# - ADB wireless pair/connect via Termux:API notifications (no Shizuku) -# - Optional PPK / phantom-process tweaks (best-effort) +# ----------------------------------------------------------------------------- +# GENERATED FILE: do not edit directly. +# Source modules: termux-setup/*.sh + manifest.sh +# Rebuild: (cd termux-setup && bash build_bundle.sh) +# ----------------------------------------------------------------------------- + + +# ---- BEGIN 00_lib_common.sh ---- +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh RED="\033[31m"; YEL="\033[33m"; GRN="\033[32m"; BLU="\033[34m"; RST="\033[0m"; BOLD="\033[1m" + log() { printf "${BLU}[iiab]${RST} %s\n" "$*"; } ok() { printf "${GRN}[iiab]${RST} %s\n" "$*"; } warn() { printf "${YEL}[iiab] WARNING:${RST} %s\n" "$*" >&2; } @@ -17,6 +23,13 @@ have() { command -v "$1" >/dev/null 2>&1; } need() { have "$1" || return 1; } die() { echo "[!] $*" >&2; exit 1; } +# ---- END 00_lib_common.sh ---- + + +# ---- BEGIN 10_mod_logging.sh ---- +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + # ------------------------- # Logging # ------------------------- @@ -57,9 +70,11 @@ setup_logging() { fi # Header (best-effort) + local started + started="$(date -Is 2>/dev/null || date 2>/dev/null || echo "?")" { echo "=== iiab termux setup v2 log ===" - echo "Started: $(date -Is)" + echo "Started: $started" echo "Script: $0" echo "Args: ${*:-}" echo "Android SDK=${ANDROID_SDK:-?} Release=${ANDROID_REL:-?}" @@ -89,80 +104,17 @@ setup_logging() { fi } -# ------------------------- -# Defaults -# ------------------------- -STATE_DIR="${HOME}/.iiab-android" -ADB_STATE_DIR="${STATE_DIR}/adbw_pair" -LOG_DIR="${STATE_DIR}/logs" -mkdir -p "$STATE_DIR" "$ADB_STATE_DIR" +# ---- END 10_mod_logging.sh ---- -HOST="127.0.0.1" -CONNECT_PORT="" -TIMEOUT_SECS=180 -NOTIF_BASE_ID=9400 -NOTIF_SEQ=0 -LAST_NOTIF_ID="" -CLEANUP_OFFLINE=1 -DEBUG=0 -RESET_DEBIAN=0 -ONLY_CONNECT=0 - -CHECK_NO_ADB=0 -CHECK_SDK="" -CHECK_MON="" -CHECK_PPK="" - -# Modes are mutually exclusive (baseline is default) -MODE="baseline" # baseline|with-adb|adb-only|connect-only|ppk-only|check|all -MODE_SET=0 +# ---- BEGIN 20_mod_termux_base.sh ---- +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh # Termux apt options (avoid conffile prompts) TERMUX_APT_OPTS=( "-y" "-o" "Dpkg::Options::=--force-confdef" "-o" "Dpkg::Options::=--force-confold" ) termux_apt() { apt-get "${TERMUX_APT_OPTS[@]}" "$@"; } -usage() { - cat <<'EOF' -Usage: - ./0_termux-setupv2.sh - -> Termux baseline + Debian bootstrap (idempotent). No ADB prompts. - - ./0_termux-setupv2.sh --with-adb - -> Termux baseline + Debian bootstrap + ADB pair/connect if needed (skips if already connected). - - ./0_termux-setupv2.sh --adb-only - -> Only ADB pair/connect if needed (no Debian; skips if already connected). - - ./0_termux-setupv2.sh --connect-only [CONNECT_PORT] - -> Connect-only (no pairing). Use this after the device was already paired before. - - ./0_termux-setupv2.sh --ppk-only - -> Set PPK only: max_phantom_processes=256 (requires ADB already connected). - Android 14-16 usually achieve this via "Disable child process restrictions" in Developer Options. - - ./0_termux-setupv2.sh --check - -> Check readiness: developer options flag (if readable), - (Android 14+) "Disable child process restrictions" proxy flag, and (Android 12-13) PPK effective value. - - ./0_termux-setupv2.sh --all - -> baseline + Debian + ADB pair/connect if needed + (Android 12-13 only) apply --ppk + run --check. - - Optional: - --connect-port 41313 (5 digits) Skip CONNECT PORT prompt - --timeout 180 Seconds to wait per prompt - --reset-debian Reset (reinstall) Debian in proot-distro - --no-log Disable logging - --log-file /path/file Write logs to a specific file - --debug Extra logs - -Notes: -- ADB prompts require: `pkg install termux-api` + Termux:API app installed + notification permission. -- Wireless debugging must be enabled. -- This script never uses adb root. -EOF -} - # ------------------------- # Android info # ------------------------- @@ -193,7 +145,6 @@ release_wakelock() { ok "Wakelock released (termux-wake-unlock)." fi } -trap 'cleanup_notif >/dev/null 2>&1 || true; release_wakelock >/dev/null 2>&1 || true' EXIT INT TERM # ------------------------- # One-time repo selector @@ -209,7 +160,8 @@ step_termux_repo_select_once() { if [[ -r /dev/tty ]]; then printf "\n${YEL}[iiab] One-time setup:${RST} Select a nearby Termux repository mirror for faster downloads.\n" >&2 local ans="Y" - if ! read -r -p "[iiab] Launch termux-change-repo now? [Y/n]: " ans < /dev/tty; then + printf "[iiab] Launch termux-change-repo now? [Y/n]: " > /dev/tty + if ! read -r ans < /dev/tty; then warn "No interactive TTY available; skipping mirror selection (run script directly to be prompted)." return 0 fi @@ -262,7 +214,9 @@ step_termux_base() { termux-api \ || true - if have proot-distro && have adb && have termux-notification; then + if have proot-distro && \ + have adb && have termux-notification && \ + have termux-dialog; then ok "Termux baseline ready." date > "$stamp" else @@ -270,6 +224,13 @@ step_termux_base() { fi } +# ---- END 20_mod_termux_base.sh ---- + + +# ---- BEGIN 30_mod_debian.sh ---- +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + # ------------------------- # Debian bootstrap # ------------------------- @@ -324,22 +285,47 @@ step_debian_bootstrap_default() { fi log "Installing minimal tools inside Debian (noninteractive)..." + if ! debian_exists; then + warn_red "Debian is not available in proot-distro (install may have failed). Rerun later." + return 0 + fi + local rc=0 + set +e proot-distro login debian -- bash -lc ' set -e export DEBIAN_FRONTEND=noninteractive apt-get update apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold \ install ca-certificates curl coreutils - ' || true - - ok "Debian bootstrap complete." + ' + rc=$? + set -e + if [[ $rc -eq 0 ]]; then + ok "Debian bootstrap complete." + else + warn_red "Debian bootstrap incomplete (inner apt-get failed, rc=$rc)." + warn "You can retry later with: proot-distro login debian" + fi } +# ---- END 30_mod_debian.sh ---- + + +# ---- BEGIN 40_mod_termux_api.sh ---- +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + # ------------------------- +# Termux:API notifications + prompts +# ------------------------- +NOTIF_BASE_ID=9400 +NOTIF_SEQ=0 +LAST_NOTIF_ID="" + # Termux:API sanity check (notifications) -# ------------------------- termux_api_ready() { have termux-notification || return 1 + have termux-dialog || return 1 # Quick probe: some Samsung setups fail until Termux:API app is installed/allowed. # We try a harmless notification and remove it. @@ -377,6 +363,7 @@ notify_ask_one() { local key="$1" title="$2" content="$3" local out="$ADB_STATE_DIR/$key.txt" rm -f "$out" + local action # Fresh notification each time + sound (use a new ID so Android plays sound each time) local nid @@ -387,6 +374,15 @@ notify_ask_one() { if have termux-notification-remove; then termux-notification-remove "$nid" >/dev/null 2>&1 || true fi + + # termux-notification does not provide a reply input; use termux-dialog to capture text. + if ! have termux-dialog; then + warn "termux-dialog not available. Install: pkg install termux-api + Termux:API app." + return 1 + fi + + action="sh -lc 'termux-dialog -i -t \"${title}\" -h \"${content}\" | tr -d \"\\n\" | sed -n \"s/.*\\\"text\\\"[[:space:]]*:[[:space:]]*\\\"\\([^\\\"]*\\)\\\".*/\\1/p\" > \"${out}\"'" + termux-notification \ --id "$nid" \ --ongoing \ @@ -395,14 +391,13 @@ notify_ask_one() { --content "$content" \ --sound \ --button1 "Answer" \ - --button1-action "sh -lc 'printf \"%s\" \"\$REPLY\" > \"$out\"'" \ + --button1-action "$action" \ || return 1 local start now start="$(date +%s)" while true; do - if [[ -s "$out" ]]; then - # Remove the prompt notification as soon as we got the answer + if [[ -f "$out" ]]; then if have termux-notification-remove; then termux-notification-remove "$nid" >/dev/null 2>&1 || true fi @@ -411,7 +406,6 @@ notify_ask_one() { fi now="$(date +%s)" if (( now - start >= TIMEOUT_SECS )); then - # Remove the prompt notification on timeout too if have termux-notification-remove; then termux-notification-remove "$nid" >/dev/null 2>&1 || true fi @@ -450,6 +444,16 @@ ask_code_6digits() { done } +# ---- END 40_mod_termux_api.sh ---- + + +# ---- BEGIN 50_mod_adb.sh ---- +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + +# ------------------------- +# ADB wireless pair/connect wizard +# ------------------------- cleanup_offline_loopback() { local keep_serial="$1" # e.g. 127.0.0.1:41313 local serial state rest @@ -462,9 +466,6 @@ cleanup_offline_loopback() { done < <(adb devices 2>/dev/null | tail -n +2 | sed '/^[[:space:]]*$/d') } -# ------------------------- -# ADB wireless pair/connect wizard -# ------------------------- adb_pair_connect() { need adb || die "Missing adb. Install: pkg install android-tools" @@ -552,7 +553,10 @@ adb_device_state() { # Return first loopback serial in "device" state (e.g. 127.0.0.1:41313) adb_any_loopback_device() { - adb devices 2>/dev/null | awk -v h="$HOST" 'NR>1 && $2=="device" && index($1, h":")==1 {print $1; exit}' + adb devices 2>/dev/null | awk -v h="$HOST" ' + NR>1 && $2=="device" && index($1, h":")==1 {print $1; found=1; exit} + END { exit (found ? 0 : 1) } + ' } # Pick the loopback serial we will operate on: @@ -628,6 +632,16 @@ adb_loopback_serial_or_die() { echo "$s" } +# ---- END 50_mod_adb.sh ---- + + +# ---- BEGIN 60_mod_ppk_checks.sh ---- +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + +# PPK / phantom-process checks and tuning via ADB (best-effort) +# Moved out of 99_main.sh to keep it as an orchestrator. + # ------------------------- # PPK / phantom-process tuning (best-effort) # ------------------------- @@ -763,6 +777,90 @@ check_readiness() { return 0 } +# ---- END 60_mod_ppk_checks.sh ---- + + +# ---- BEGIN 99_main.sh ---- +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + +# 0_termux-setupv2.sh +# - Termux bootstrap (packages, wakelock) +# - proot-distro + Debian bootstrap +# - ADB wireless pair/connect via Termux:API notifications (no Shizuku) +# - Optional PPK / phantom-process tweaks (best-effort) + +# ------------------------- +# Defaults +# ------------------------- +STATE_DIR="${HOME}/.iiab-android" +ADB_STATE_DIR="${STATE_DIR}/adbw_pair" +LOG_DIR="${STATE_DIR}/logs" +mkdir -p "$STATE_DIR" "$ADB_STATE_DIR" + +HOST="127.0.0.1" +CONNECT_PORT="" +TIMEOUT_SECS=180 +CLEANUP_OFFLINE=1 +DEBUG=0 + +RESET_DEBIAN=0 +ONLY_CONNECT=0 + +CHECK_NO_ADB=0 +CHECK_SDK="" +CHECK_MON="" +CHECK_PPK="" + +# Modes are mutually exclusive (baseline is default) +MODE="baseline" # baseline|with-adb|adb-only|connect-only|ppk-only|check|all +MODE_SET=0 + +usage() { + cat <<'EOF' +Usage: + ./0_termux-setupv2.sh + -> Termux baseline + Debian bootstrap (idempotent). No ADB prompts. + + ./0_termux-setupv2.sh --with-adb + -> Termux baseline + Debian bootstrap + ADB pair/connect if needed (skips if already connected). + + ./0_termux-setupv2.sh --adb-only + -> Only ADB pair/connect if needed (no Debian; skips if already connected). + + ./0_termux-setupv2.sh --connect-only [CONNECT_PORT] + -> Connect-only (no pairing). Use this after the device was already paired before. + + ./0_termux-setupv2.sh --ppk-only + -> Set PPK only: max_phantom_processes=256 (requires ADB already connected). + Android 14-16 usually achieve this via "Disable child process restrictions" in Developer Options. + + ./0_termux-setupv2.sh --check + -> Check readiness: developer options flag (if readable), + (Android 14+) "Disable child process restrictions" proxy flag, and (Android 12-13) PPK effective value. + + ./0_termux-setupv2.sh --all + -> baseline + Debian + ADB pair/connect if needed + (Android 12-13 only) apply --ppk + run --check. + + Optional: + --connect-port 41313 (5 digits) Skip CONNECT PORT prompt + --timeout 180 Seconds to wait per prompt + --reset-debian Reset (reinstall) Debian in proot-distro + --no-log Disable logging + --log-file /path/file Write logs to a specific file + --debug Extra logs + +Notes: +- ADB prompts require: `pkg install termux-api` + Termux:API app installed + notification permission. +- Wireless debugging must be enabled. +- This script never uses adb root. +EOF +} + +trap 'cleanup_notif >/dev/null 2>&1 || true; release_wakelock >/dev/null 2>&1 || true' EXIT INT TERM + +# NOTE: Termux:API prompts live in 40_mod_termux_api.sh + # ------------------------- # Self-check # ------------------------- @@ -1015,3 +1113,7 @@ main() { } main "$@" + +# ---- END 99_main.sh ---- + + diff --git a/android/termux-setup/00_lib_common.sh b/android/termux-setup/00_lib_common.sh new file mode 100644 index 0000000..556fb7a --- /dev/null +++ b/android/termux-setup/00_lib_common.sh @@ -0,0 +1,13 @@ +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + +RED="\033[31m"; YEL="\033[33m"; GRN="\033[32m"; BLU="\033[34m"; RST="\033[0m"; BOLD="\033[1m" + +log() { printf "${BLU}[iiab]${RST} %s\n" "$*"; } +ok() { printf "${GRN}[iiab]${RST} %s\n" "$*"; } +warn() { printf "${YEL}[iiab] WARNING:${RST} %s\n" "$*" >&2; } +warn_red() { printf "${RED}${BOLD}[iiab] WARNING:${RST} %s\n" "$*" >&2; } + +have() { command -v "$1" >/dev/null 2>&1; } +need() { have "$1" || return 1; } +die() { echo "[!] $*" >&2; exit 1; } diff --git a/android/termux-setup/10_mod_logging.sh b/android/termux-setup/10_mod_logging.sh new file mode 100644 index 0000000..2b4c2db --- /dev/null +++ b/android/termux-setup/10_mod_logging.sh @@ -0,0 +1,76 @@ +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + +# ------------------------- +# Logging +# ------------------------- +LOG_ENABLED=1 +LOG_FILE="" # if empty, auto-generate under $LOG_DIR +LOG_KEEP=20 # keep last N logs + +rotate_logs() { + [[ -d "${LOG_DIR:-}" ]] || return 0 + local n=$((LOG_KEEP + 1)) + while IFS= read -r f; do + [[ -n "${f:-}" ]] || continue + [[ -n "${LOG_FILE:-}" && "$f" == "$LOG_FILE" ]] && continue + rm -f -- "$f" 2>/dev/null || true + done < <(ls -1t "$LOG_DIR"/*.log 2>/dev/null | tail -n +"$n" || true) +} + +setup_logging() { + # Save original console fds so interactive tools still work after we redirect stdout/stderr. + exec 3>&1 4>&2 + + # If logging is disabled, still allow --debug to trace to console. + if [[ "${LOG_ENABLED:-1}" -ne 1 ]]; then + if [[ "${DEBUG:-0}" -eq 1 ]]; then + set -x + ok "Debug trace enabled (bash -x) -> console (logging disabled)" + fi + return 0 + fi + + mkdir -p "$LOG_DIR" 2>/dev/null || true + + if [[ -z "${LOG_FILE:-}" ]]; then + LOG_FILE="${LOG_DIR}/0_termux-setupv2.$(date +%Y%m%d-%H%M%S).log" + else + # Best-effort: ensure parent dir exists + mkdir -p "$(dirname -- "$LOG_FILE")" 2>/dev/null || true + fi + + # Header (best-effort) + local started + started="$(date -Is 2>/dev/null || date 2>/dev/null || echo "?")" + { + echo "=== iiab termux setup v2 log ===" + echo "Started: $started" + echo "Script: $0" + echo "Args: ${*:-}" + echo "Android SDK=${ANDROID_SDK:-?} Release=${ANDROID_REL:-?}" + echo "PWD: $(pwd 2>/dev/null || true)" + echo "================================" + } >>"$LOG_FILE" 2>/dev/null || true + + # Best-effort: restrict log readability (may include debug/xtrace) + chmod 600 "$LOG_FILE" 2>/dev/null || true + + rotate_logs + + # Duplicate stdout/stderr to console + log (strip ANSI in log) + exec \ + > >(tee >(sed -E 's/\x1B\[[0-9;]*[ -/]*[@-~]//g' >>"$LOG_FILE")) \ + 2> >(tee >(sed -E 's/\x1B\[[0-9;]*[ -/]*[@-~]//g' >>"$LOG_FILE") >&2) + + ok "Logging to: $LOG_FILE" + + # If --debug, send xtrace only to log + if [[ "${DEBUG:-0}" -eq 1 ]]; then + exec 9>>"$LOG_FILE" + export BASH_XTRACEFD=9 + export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' + set -x + ok "Debug trace enabled (bash -x) -> log only" + fi +} diff --git a/android/termux-setup/20_mod_termux_base.sh b/android/termux-setup/20_mod_termux_base.sh new file mode 100644 index 0000000..6da5e08 --- /dev/null +++ b/android/termux-setup/20_mod_termux_base.sh @@ -0,0 +1,115 @@ +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + +# Termux apt options (avoid conffile prompts) +TERMUX_APT_OPTS=( "-y" "-o" "Dpkg::Options::=--force-confdef" "-o" "Dpkg::Options::=--force-confold" ) +termux_apt() { apt-get "${TERMUX_APT_OPTS[@]}" "$@"; } + +# ------------------------- +# Android info +# ------------------------- +get_android_sdk() { getprop ro.build.version.sdk 2>/dev/null || true; } +get_android_release() { getprop ro.build.version.release 2>/dev/null || true; } +ANDROID_SDK="$(get_android_sdk)" +ANDROID_REL="$(get_android_release)" + +# ------------------------- +# Wakelock (Termux:API) +# ------------------------- +WAKELOCK_HELD=0 +acquire_wakelock() { + if have termux-wake-lock; then + if termux-wake-lock; then + WAKELOCK_HELD=1 + ok "Wakelock acquired (termux-wake-lock)." + else + warn "Failed to acquire wakelock (termux-wake-lock)." + fi + else + warn "termux-wake-lock not available. Install: pkg install termux-api + Termux:API app." + fi +} +release_wakelock() { + if [[ "$WAKELOCK_HELD" -eq 1 ]] && have termux-wake-unlock; then + termux-wake-unlock || true + ok "Wakelock released (termux-wake-unlock)." + fi +} + +# ------------------------- +# One-time repo selector +# ------------------------- +step_termux_repo_select_once() { + local stamp="$STATE_DIR/stamp.termux_repo_selected" + [[ -f "$stamp" ]] && return 0 + if ! have termux-change-repo; then + warn "termux-change-repo not found; skipping mirror selection." + return 0 + fi + + if [[ -r /dev/tty ]]; then + printf "\n${YEL}[iiab] One-time setup:${RST} Select a nearby Termux repository mirror for faster downloads.\n" >&2 + local ans="Y" + printf "[iiab] Launch termux-change-repo now? [Y/n]: " > /dev/tty + if ! read -r ans < /dev/tty; then + warn "No interactive TTY available; skipping mirror selection (run script directly to be prompted)." + return 0 + fi + ans="${ans:-Y}" + if [[ "$ans" =~ ^[Yy]$ ]]; then + # Logging redirects stdout/stderr to pipes, which can break the UI. + # Run it using /dev/tty and the original console fds (3/4). + if [[ -r /dev/tty ]]; then + termux-change-repo &3 2>&4 || true + else + termux-change-repo || true + fi + ok "Mirror selection completed (or skipped inside the UI)." + else + warn "Mirror selection skipped by user." + fi + date > "$stamp" + return 0 + fi + + warn "No /dev/tty available; skipping mirror selection." + return 0 +} + +# ------------------------- +# Baseline packages +# ------------------------- +step_termux_base() { + local stamp="$STATE_DIR/stamp.termux_base" + if [[ -f "$stamp" ]]; then + ok "Termux baseline already prepared (stamp found)." + return 0 + fi + + log "Updating Termux packages (noninteractive) and installing baseline dependencies..." + export DEBIAN_FRONTEND=noninteractive + termux_apt update || true + termux_apt upgrade || true + + termux_apt install \ + ca-certificates \ + curl \ + coreutils \ + grep \ + sed \ + gawk \ + openssh \ + proot proot-distro \ + android-tools \ + termux-api \ + || true + + if have proot-distro && \ + have adb && have termux-notification && \ + have termux-dialog; then + ok "Termux baseline ready." + date > "$stamp" + else + warn_red "Baseline incomplete (missing proot-distro/adb/termux-notification). Not stamping; rerun later." + fi +} diff --git a/android/termux-setup/30_mod_debian.sh b/android/termux-setup/30_mod_debian.sh new file mode 100644 index 0000000..5f7e757 --- /dev/null +++ b/android/termux-setup/30_mod_debian.sh @@ -0,0 +1,79 @@ +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + +# ------------------------- +# Debian bootstrap +# ------------------------- +debian_exists() { + have proot-distro || return 1 + proot-distro login debian -- true >/dev/null 2>&1 +} + +ensure_proot_distro() { + if have proot-distro; then return 0; fi + warn "proot-distro not found; attempting to install..." + termux_apt install proot-distro || true + have proot-distro +} + +proot_install_debian_safe() { + local out rc + set +e + out="$(proot-distro install debian 2>&1)" + rc=$? + set -e + if [[ $rc -eq 0 ]]; then return 0; fi + if echo "$out" | grep -qi "already installed"; then + warn "Debian is already installed; continuing." + return 0 + fi + printf "%s\n" "$out" >&2 + return $rc +} + +step_debian_bootstrap_default() { + if ! ensure_proot_distro; then + warn "Unable to ensure proot-distro; skipping Debian bootstrap." + return 0 + fi + + if [[ "$RESET_DEBIAN" -eq 1 ]]; then + warn "Reset requested: reinstalling Debian (clean environment)..." + if proot-distro help 2>/dev/null | grep -qE '\breset\b'; then + proot-distro reset debian || true + else + if debian_exists; then proot-distro remove debian || true; fi + proot_install_debian_safe || true + fi + else + if debian_exists; then + ok "Debian already present in proot-distro. Not reinstalling." + else + log "Installing Debian (proot-distro install debian)..." + proot_install_debian_safe || true + fi + fi + + log "Installing minimal tools inside Debian (noninteractive)..." + if ! debian_exists; then + warn_red "Debian is not available in proot-distro (install may have failed). Rerun later." + return 0 + fi + local rc=0 + set +e + proot-distro login debian -- bash -lc ' + set -e + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold \ + install ca-certificates curl coreutils + ' + rc=$? + set -e + if [[ $rc -eq 0 ]]; then + ok "Debian bootstrap complete." + else + warn_red "Debian bootstrap incomplete (inner apt-get failed, rc=$rc)." + warn "You can retry later with: proot-distro login debian" + fi +} diff --git a/android/termux-setup/40_mod_termux_api.sh b/android/termux-setup/40_mod_termux_api.sh new file mode 100644 index 0000000..9e8cad6 --- /dev/null +++ b/android/termux-setup/40_mod_termux_api.sh @@ -0,0 +1,131 @@ +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + +# ------------------------- +# Termux:API notifications + prompts +# ------------------------- +NOTIF_BASE_ID=9400 +NOTIF_SEQ=0 +LAST_NOTIF_ID="" + +# Termux:API sanity check (notifications) +termux_api_ready() { + have termux-notification || return 1 + have termux-dialog || return 1 + + # Quick probe: some Samsung setups fail until Termux:API app is installed/allowed. + # We try a harmless notification and remove it. + local msg="iiab test notification" + if ! termux-notification --id "$NOTIF_BASE_ID" --title "iiab" --content "$msg" --priority max --sound >/dev/null 2>&1; then + return 1 + fi + if have termux-notification-remove; then + termux-notification-remove "$NOTIF_BASE_ID" >/dev/null 2>&1 || true + fi + return 0 +} + +sanitize_timeout() { + # Ensure TIMEOUT_SECS is a positive integer + if ! [[ "${TIMEOUT_SECS:-}" =~ ^[0-9]+$ ]]; then + warn "Invalid --timeout='${TIMEOUT_SECS:-}'. Falling back to 180." + TIMEOUT_SECS=180 + elif (( TIMEOUT_SECS < 5 )); then + warn "Very low --timeout=${TIMEOUT_SECS}. Forcing minimum 5 seconds." + TIMEOUT_SECS=5 + fi +} + +cleanup_notif() { + have termux-notification-remove || return 0 + termux-notification-remove "$NOTIF_BASE_ID" >/dev/null 2>&1 || true + if [[ -n "${LAST_NOTIF_ID:-}" ]]; then + termux-notification-remove "$LAST_NOTIF_ID" >/dev/null 2>&1 || true + fi +} + +notify_ask_one() { + # args: key title content + local key="$1" title="$2" content="$3" + local out="$ADB_STATE_DIR/$key.txt" + rm -f "$out" + local action + + # Fresh notification each time + sound (use a new ID so Android plays sound each time) + local nid + nid=$((NOTIF_BASE_ID + 1 + NOTIF_SEQ)) + NOTIF_SEQ=$((NOTIF_SEQ + 1)) + LAST_NOTIF_ID="$nid" + + if have termux-notification-remove; then + termux-notification-remove "$nid" >/dev/null 2>&1 || true + fi + + # termux-notification does not provide a reply input; use termux-dialog to capture text. + if ! have termux-dialog; then + warn "termux-dialog not available. Install: pkg install termux-api + Termux:API app." + return 1 + fi + + action="sh -lc 'termux-dialog -i -t \"${title}\" -h \"${content}\" | tr -d \"\\n\" | sed -n \"s/.*\\\"text\\\"[[:space:]]*:[[:space:]]*\\\"\\([^\\\"]*\\)\\\".*/\\1/p\" > \"${out}\"'" + + termux-notification \ + --id "$nid" \ + --ongoing \ + --priority max \ + --title "$title" \ + --content "$content" \ + --sound \ + --button1 "Answer" \ + --button1-action "$action" \ + || return 1 + + local start now + start="$(date +%s)" + while true; do + if [[ -f "$out" ]]; then + if have termux-notification-remove; then + termux-notification-remove "$nid" >/dev/null 2>&1 || true + fi + tr -d '\r\n' < "$out" + return 0 + fi + now="$(date +%s)" + if (( now - start >= TIMEOUT_SECS )); then + if have termux-notification-remove; then + termux-notification-remove "$nid" >/dev/null 2>&1 || true + fi + return 1 + fi + sleep 1 + done +} + +ask_port_5digits() { + # args: key title + local key="$1" title="$2" v="" + while true; do + v="$(notify_ask_one "$key" "$title" "(5 digits)")" || return 1 + v="${v//[[:space:]]/}" + [[ "$v" =~ ^[0-9]{5}$ ]] || continue + echo "$v" + return 0 + done +} + +ask_code_6digits() { + local v="" + while true; do + v="$(notify_ask_one code "PAIR CODE" "(6 digits)")" || return 1 + v="${v//[[:space:]]/}" + [[ -n "$v" ]] || continue + [[ "$v" =~ ^[0-9]+$ ]] || continue + # Normalize to 6 digits (allow missing leading zeros) + if ((${#v} < 6)); then + v="$(printf "%06d" "$v")" + fi + [[ "$v" =~ ^[0-9]{6}$ ]] || continue + echo "$v" + return 0 + done +} diff --git a/android/termux-setup/50_mod_adb.sh b/android/termux-setup/50_mod_adb.sh new file mode 100644 index 0000000..99185c5 --- /dev/null +++ b/android/termux-setup/50_mod_adb.sh @@ -0,0 +1,183 @@ +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + +# ------------------------- +# ADB wireless pair/connect wizard +# ------------------------- +cleanup_offline_loopback() { + local keep_serial="$1" # e.g. 127.0.0.1:41313 + local serial state rest + while read -r serial state rest; do + [[ -n "${serial:-}" ]] || continue + [[ "$serial" == ${HOST}:* ]] || continue + [[ "$state" == "offline" ]] || continue + [[ "$serial" == "$keep_serial" ]] && continue + adb disconnect "$serial" >/dev/null 2>&1 || true + done < <(adb devices 2>/dev/null | tail -n +2 | sed '/^[[:space:]]*$/d') +} + +adb_pair_connect() { + need adb || die "Missing adb. Install: pkg install android-tools" + + # Only require Termux:API when we will prompt the user + if [[ "$ONLY_CONNECT" != "1" || -z "${CONNECT_PORT:-}" ]]; then + termux_api_ready || die "Termux:API not ready." + fi + + echo "[*] adb: $(adb version | head -n 1)" + adb start-server >/dev/null 2>&1 || true + + if [[ "$ONLY_CONNECT" == "1" ]]; then + if [[ -n "$CONNECT_PORT" ]]; then + CONNECT_PORT="${CONNECT_PORT//[[:space:]]/}" + [[ "$CONNECT_PORT" =~ ^[0-9]{5}$ ]] || die "Invalid CONNECT PORT (must be 5 digits): '$CONNECT_PORT'" + else + echo "[*] Asking CONNECT PORT..." + CONNECT_PORT="$(ask_port_5digits connect "CONNECT PORT")" || die "Timeout waiting CONNECT PORT." + fi + + local serial="${HOST}:${CONNECT_PORT}" + adb disconnect "$serial" >/dev/null 2>&1 || true + echo "[*] adb connect $serial" + adb connect "$serial" >/dev/null || die "adb connect failed to $serial. Verify Wireless debugging is enabled and CONNECT PORT is correct." + + if [[ "$CLEANUP_OFFLINE" == "1" ]]; then + cleanup_offline_loopback "$serial" + fi + + echo "[*] Devices:" + adb devices -l + + echo "[*] ADB check (shell):" + adb -s "$serial" shell sh -lc 'echo "it worked: adb shell is working"; id' || true + + cleanup_notif + ok "ADB connected (connect-only): $serial" + return 0 + fi + + if [[ -n "$CONNECT_PORT" ]]; then + CONNECT_PORT="${CONNECT_PORT//[[:space:]]/}" + [[ "$CONNECT_PORT" =~ ^[0-9]{5}$ ]] || die "Invalid --connect-port (must be 5 digits): '$CONNECT_PORT'" + else + echo "[*] Asking CONNECT PORT..." + CONNECT_PORT="$(ask_port_5digits connect "CONNECT PORT")" || die "Timeout waiting CONNECT PORT." + fi + + echo "[*] Asking PAIR PORT..." + local pair_port + pair_port="$(ask_port_5digits pair "PAIR PORT")" || die "Timeout waiting PAIR PORT." + + echo "[*] Asking PAIR CODE..." + local code + code="$(ask_code_6digits)" || die "Timeout waiting PAIR CODE." + + local serial="${HOST}:${CONNECT_PORT}" + adb disconnect "$serial" >/dev/null 2>&1 || true + + echo "[*] adb pair ${HOST}:${pair_port}" + printf '%s\n' "$code" | adb pair "${HOST}:${pair_port}" || die "adb pair failed. Verify PAIR PORT and PAIR CODE (and that the pairing dialog is showing)." + + echo "[*] adb connect $serial" + adb connect "$serial" >/dev/null || die "adb connect failed after pairing. Re-check CONNECT PORT and Wireless debugging." + + if [[ "$CLEANUP_OFFLINE" == "1" ]]; then + cleanup_offline_loopback "$serial" + fi + + echo "[*] Devices:" + adb devices -l + + echo "[*] ADB check (shell):" + adb -s "$serial" shell sh -lc 'echo "it worked: adb shell is working"; getprop ro.product.model; getprop ro.build.version.release' || true + + cleanup_notif + ok "ADB connected: $serial" +} + +# Return state for an exact serial (e.g. "device", "offline", empty) +adb_device_state() { + local s="$1" + adb devices 2>/dev/null | awk -v s="$s" 'NR>1 && $1==s {print $2; exit}' +} + +# Return first loopback serial in "device" state (e.g. 127.0.0.1:41313) +adb_any_loopback_device() { + adb devices 2>/dev/null | awk -v h="$HOST" ' + NR>1 && $2=="device" && index($1, h":")==1 {print $1; found=1; exit} + END { exit (found ? 0 : 1) } + ' +} + +# Pick the loopback serial we will operate on: +# - If CONNECT_PORT is set, require that exact HOST:PORT to be in "device" state. +# - Otherwise, return the first loopback device. +adb_pick_loopback_serial() { + if [[ -n "${CONNECT_PORT:-}" ]]; then + local p="${CONNECT_PORT//[[:space:]]/}" + [[ "$p" =~ ^[0-9]{5}$ ]] || return 1 + local target="${HOST}:${p}" + [[ "$(adb_device_state "$target")" == "device" ]] && { echo "$target"; return 0; } + return 1 + fi + adb_any_loopback_device +} + +# If already connected, avoid re-pairing/re-connecting prompts (useful for --all), +# BUT only consider loopback/target connections as "already connected". +adb_pair_connect_if_needed() { + need adb || die "Missing adb. Install: pkg install android-tools" + adb start-server >/dev/null 2>&1 || true + + local serial="" + + # If user provided a connect-port, insist on that exact target serial. + if [[ -n "${CONNECT_PORT:-}" ]]; then + CONNECT_PORT="${CONNECT_PORT//[[:space:]]/}" + [[ "$CONNECT_PORT" =~ ^[0-9]{5}$ ]] || die "Invalid --connect-port (must be 5 digits): '$CONNECT_PORT'" + + local target="${HOST}:${CONNECT_PORT}" + + if [[ "$(adb_device_state "$target")" == "device" ]]; then + ok "ADB already connected to target: $target (skipping pair/connect)." + return 0 + fi + + # Try connect-only first (in case it was already paired before) + adb connect "$target" >/dev/null 2>&1 || true + if [[ "$(adb_device_state "$target")" == "device" ]]; then + ok "ADB connected to target: $target (connect-only succeeded; skipping pair)." + return 0 + fi + + # Not connected: run full wizard (pair+connect) + adb_pair_connect + return $? + fi + + # No explicit port: only skip if we already have a loopback device connected. + if serial="$(adb_any_loopback_device 2>/dev/null)"; then + ok "ADB already connected (loopback): $serial (skipping pair/connect)." + return 0 + fi + + adb_pair_connect +} + +require_adb_connected() { + need adb || { warn_red "Missing adb. Install: pkg install android-tools"; return 1; } + adb start-server >/dev/null 2>&1 || true + if ! adb_pick_loopback_serial >/dev/null 2>&1; then + warn_red "No ADB device connected." + warn "If already paired before: run --connect-only [PORT]." + warn "Otherwise: run --adb-only to pair+connect." + return 1 + fi + return 0 +} + +adb_loopback_serial_or_die() { + local s + s="$(adb_pick_loopback_serial 2>/dev/null)" || return 1 + echo "$s" +} diff --git a/android/termux-setup/60_mod_ppk_checks.sh b/android/termux-setup/60_mod_ppk_checks.sh new file mode 100644 index 0000000..e08ab03 --- /dev/null +++ b/android/termux-setup/60_mod_ppk_checks.sh @@ -0,0 +1,140 @@ +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + +# PPK / phantom-process checks and tuning via ADB (best-effort) +# Moved out of 99_main.sh to keep it as an orchestrator. + +# ------------------------- +# PPK / phantom-process tuning (best-effort) +# ------------------------- +ppk_fix_via_adb() { + need adb || die "Missing adb. Install: pkg install android-tools" + + local serial + if ! serial="$(adb_pick_loopback_serial)"; then + CHECK_NO_ADB=1 + warn "No ADB loopback device connected (expected ${HOST}:${CONNECT_PORT:-*})." + return 1 + fi + ok "Using ADB device: $serial" + + log "Setting PPK: max_phantom_processes=256" + # Some Android versions may ignore/rename this; we don't hard-fail. + adb -s "$serial" shell sh -lc ' + set -e + # Persist device_config changes if supported + if command -v device_config >/dev/null 2>&1; then + device_config set_sync_disabled_for_tests persistent >/dev/null 2>&1 || true + device_config put activity_manager max_phantom_processes 256 || true + echo "dumpsys effective max_phantom_processes:" + dumpsys activity settings 2>/dev/null | grep -i "max_phantom_processes=" | head -n 1 || true + else + echo "device_config not found; skipping." + fi + ' || true + + ok "PPK set done (best effort)." + return 0 +} + +# Prefer Android 14+ feature-flag override if present: +# OFF -> true, ON -> false +adb_get_child_restrictions_flag() { + local serial="$1" + adb -s "$serial" shell getprop persist.sys.fflag.override.settings_enable_monitor_phantom_procs \ + 2>/dev/null | tr -d '\r' || true +} + +# ------------------------- +# Check readiness (best-effort) +# ------------------------- +check_readiness() { + # Reset exported check signals so final_advice() never sees stale values + CHECK_NO_ADB=0 + CHECK_SDK="" + CHECK_MON="" + CHECK_PPK="" + + need adb || die "Missing adb. Install: pkg install android-tools" + adb start-server >/dev/null 2>&1 || true + + local serial + if ! serial="$(adb_pick_loopback_serial)"; then + CHECK_NO_ADB=1 + # Best-effort: keep local SDK so final_advice can still warn on A12-13. + CHECK_SDK="${ANDROID_SDK:-}" + warn_red "No ADB device connected. Cannot run checks." + warn "If already paired before: run --connect-only [PORT]." + warn "Otherwise: run --adb-only to pair+connect." + return 1 + fi + + ok "Check using ADB device: $serial" + + local dev_enabled sdk rel mon mon_fflag ds ppk_eff + sdk="$(adb -s "$serial" shell getprop ro.build.version.sdk 2>/dev/null | tr -d '\r' || true)" + rel="$(adb -s "$serial" shell getprop ro.build.version.release 2>/dev/null | tr -d '\r' || true)" + dev_enabled="$(adb -s "$serial" shell settings get global development_settings_enabled 2>/dev/null | tr -d '\r' || true)" + mon_fflag="$(adb_get_child_restrictions_flag "$serial")" + if [[ "$mon_fflag" == "true" || "$mon_fflag" == "false" ]]; then + mon="$mon_fflag" + else + mon="$(adb -s "$serial" shell settings get global settings_enable_monitor_phantom_procs 2>/dev/null | tr -d '\r' || true)" + fi + + # Get effective value from dumpsys (device_config get may return 'null' even when an effective value exists) + ds="$(adb -s "$serial" shell dumpsys activity settings 2>/dev/null | tr -d '\r' || true)" + ppk_eff="$(printf '%s\n' "$ds" | awk -F= '/max_phantom_processes=/{print $2; exit}' | tr -d '[:space:]' || true)" + + # Export check signals for the final advice logic + CHECK_SDK="${sdk:-}" + CHECK_MON="${mon:-}" + CHECK_PPK="${ppk_eff:-}" + + log " Android release=${rel:-?} sdk=${sdk:-?}" + + if [[ "${dev_enabled:-}" == "1" ]]; then + ok " Developer options: enabled (development_settings_enabled=1)" + elif [[ -n "${dev_enabled:-}" ]]; then + warn " Developer options: unknown/disabled (development_settings_enabled=${dev_enabled})" + else + warn " Developer options: unreadable (permission/ROM differences)." + fi + + # Android 14+ only: "Disable child process restrictions" proxy flag + if [[ "${sdk:-}" =~ ^[0-9]+$ ]] && (( sdk >= 34 )); then + if [[ "${mon:-}" == "false" ]]; then + ok " Child restrictions: OK (monitor=false)" + elif [[ "${mon:-}" == "true" ]]; then + warn " Child restrictions: NOT OK (monitor=true)" + elif [[ -n "${mon:-}" && "${mon:-}" != "null" ]]; then + warn " Child restrictions: unknown (${mon})" + else + warn " Child restrictions: unreadable/absent" + fi + fi + + # Android 12-13 only: PPK matters (use effective value from dumpsys) + if [[ "${sdk:-}" =~ ^[0-9]+$ ]] && (( sdk >= 31 && sdk <= 33 )); then + if [[ "${ppk_eff:-}" =~ ^[0-9]+$ ]]; then + if (( ppk_eff >= 256 )); then + ok " PPK: OK (max_phantom_processes=${ppk_eff})" + else + warn " PPK: low (max_phantom_processes=${ppk_eff}) -> suggest: run --ppk-only" + fi + else + warn " PPK: unreadable (dumpsys max_phantom_processes='${ppk_eff:-}')." + fi + fi + + log " dumpsys (phantom-related):" + printf '%s\n' "$ds" | grep -i phantom || true + + if [[ "${sdk:-}" =~ ^[0-9]+$ ]] && (( sdk >= 34 )); then + log " Note: On A14+, max_phantom_processes is informational; rely on Child restrictions." + fi + if [[ "${sdk:-}" =~ ^[0-9]+$ ]] && (( sdk >= 34 )) && [[ "${mon:-}" == "false" ]]; then + log " Child restrictions OK." + fi + return 0 +} diff --git a/android/termux-setup/99_main.sh b/android/termux-setup/99_main.sh new file mode 100644 index 0000000..650d55b --- /dev/null +++ b/android/termux-setup/99_main.sh @@ -0,0 +1,332 @@ +# shellcheck shell=bash +# Module file (no shebang). Bundled by build_bundle.sh + +# 0_termux-setupv2.sh +# - Termux bootstrap (packages, wakelock) +# - proot-distro + Debian bootstrap +# - ADB wireless pair/connect via Termux:API notifications (no Shizuku) +# - Optional PPK / phantom-process tweaks (best-effort) + +# ------------------------- +# Defaults +# ------------------------- +STATE_DIR="${HOME}/.iiab-android" +ADB_STATE_DIR="${STATE_DIR}/adbw_pair" +LOG_DIR="${STATE_DIR}/logs" +mkdir -p "$STATE_DIR" "$ADB_STATE_DIR" + +HOST="127.0.0.1" +CONNECT_PORT="" +TIMEOUT_SECS=180 +CLEANUP_OFFLINE=1 +DEBUG=0 + +RESET_DEBIAN=0 +ONLY_CONNECT=0 + +CHECK_NO_ADB=0 +CHECK_SDK="" +CHECK_MON="" +CHECK_PPK="" + +# Modes are mutually exclusive (baseline is default) +MODE="baseline" # baseline|with-adb|adb-only|connect-only|ppk-only|check|all +MODE_SET=0 + +usage() { + cat <<'EOF' +Usage: + ./0_termux-setupv2.sh + -> Termux baseline + Debian bootstrap (idempotent). No ADB prompts. + + ./0_termux-setupv2.sh --with-adb + -> Termux baseline + Debian bootstrap + ADB pair/connect if needed (skips if already connected). + + ./0_termux-setupv2.sh --adb-only + -> Only ADB pair/connect if needed (no Debian; skips if already connected). + + ./0_termux-setupv2.sh --connect-only [CONNECT_PORT] + -> Connect-only (no pairing). Use this after the device was already paired before. + + ./0_termux-setupv2.sh --ppk-only + -> Set PPK only: max_phantom_processes=256 (requires ADB already connected). + Android 14-16 usually achieve this via "Disable child process restrictions" in Developer Options. + + ./0_termux-setupv2.sh --check + -> Check readiness: developer options flag (if readable), + (Android 14+) "Disable child process restrictions" proxy flag, and (Android 12-13) PPK effective value. + + ./0_termux-setupv2.sh --all + -> baseline + Debian + ADB pair/connect if needed + (Android 12-13 only) apply --ppk + run --check. + + Optional: + --connect-port 41313 (5 digits) Skip CONNECT PORT prompt + --timeout 180 Seconds to wait per prompt + --reset-debian Reset (reinstall) Debian in proot-distro + --no-log Disable logging + --log-file /path/file Write logs to a specific file + --debug Extra logs + +Notes: +- ADB prompts require: `pkg install termux-api` + Termux:API app installed + notification permission. +- Wireless debugging must be enabled. +- This script never uses adb root. +EOF +} + +trap 'cleanup_notif >/dev/null 2>&1 || true; release_wakelock >/dev/null 2>&1 || true' EXIT INT TERM + +# NOTE: Termux:API prompts live in 40_mod_termux_api.sh + +# ------------------------- +# Self-check +# ------------------------- +self_check() { + log "Self-check summary:" + log " Android release=${ANDROID_REL:-?} sdk=${ANDROID_SDK:-?}" + + if have proot-distro; then + log " proot-distro: present" + log " proot-distro list:" + proot-distro list 2>/dev/null | sed 's/^/ /' || true + if debian_exists; then ok " Debian: present"; else warn " Debian: not present"; fi + else + warn " proot-distro: not present" + fi + + if have adb; then + log " adb: present" + adb devices -l 2>/dev/null | sed 's/^/ /' || true + local serial + if serial="$(adb_pick_loopback_serial 2>/dev/null)"; then + log " adb shell id (first device):" + adb -s "$serial" shell id 2>/dev/null | sed 's/^/ /' || true + fi + else + warn " adb: not present" + fi + + if have termux-wake-lock; then ok " Termux:API wakelock: available"; else warn " Termux:API wakelock: not available"; fi + if have termux-notification; then ok " Termux:API notifications: command present"; else warn " Termux:API notifications: missing"; fi +} + +final_advice() { + # 1) Android-related warnings (only meaningful if we attempted checks) + local sdk="${CHECK_SDK:-${ANDROID_SDK:-}}" + local adb_connected=0 + local serial="" mon="" mon_fflag="" + + # Best-effort: detect whether an ADB loopback device is already connected. + # (We do NOT prompt/pair here; we only check current state.) + if have adb; then + adb start-server >/dev/null 2>&1 || true + if adb_pick_loopback_serial >/dev/null 2>&1; then + adb_connected=1 + serial="$(adb_pick_loopback_serial 2>/dev/null || true)" + fi + fi + + # Baseline safety gate: + # On Android 12-13 (SDK 31-33), IIAB/proot installs can fail if PPK is low (often 32). + # Baseline mode does NOT force ADB pairing nor run check_readiness(), so PPK may be unknown. + # If PPK is not determined, suggest running --all BEFORE telling user to proceed to proot-distro. + if [[ "$MODE" == "baseline" ]]; then + if [[ "$sdk" =~ ^[0-9]+$ ]] && (( sdk >= 31 && sdk <= 33 )); then + # If we didn't run checks, CHECK_PPK will be empty. Even with adb_connected=1, baseline + # still doesn't populate CHECK_PPK unless user ran --check/--all. + if [[ "${CHECK_PPK:-}" != "" && "${CHECK_PPK:-}" =~ ^[0-9]+$ ]]; then + : # PPK determined -> ok to continue with normal advice below + else + warn "Android 12-13: PPK value hasn't been verified (max_phantom_processes may be low, e.g. 32)." + warn "Before starting the IIAB install, run the complete setup so it can apply/check PPK=256; otherwise the installation may fail:" + ok " ./0_termux-setupv2.sh --all" + return 0 + fi + elif [[ "$sdk" =~ ^[0-9]+$ ]] && (( sdk >= 34 )); then + # On Android 14+, rely on "Disable child process restrictions" + # Proxy signals: settings_enable_monitor_phantom_procs (or the fflag override). + # Baseline does not run check_readiness(), so CHECK_MON is usually empty. + if [[ "${CHECK_MON:-}" == "false" ]]; then + : # Verified OK (rare in baseline) -> continue + else + # If ADB is already connected, try to read the flag best-effort (no prompts). + if [[ "$adb_connected" -eq 1 && -n "${serial:-}" ]]; then + mon_fflag="$(adb_get_child_restrictions_flag "$serial")" + if [[ "$mon_fflag" == "true" || "$mon_fflag" == "false" ]]; then + mon="$mon_fflag" + else + mon="$(adb -s "$serial" shell settings get global settings_enable_monitor_phantom_procs 2>/dev/null | tr -d '\r' || true)" + fi + fi + + if [[ "${mon:-}" == "false" ]]; then + : # Restrictions already disabled -> ok to continue + else + if [[ "${mon:-}" == "true" ]]; then + warn "Android 14+: child process restrictions appear ENABLED (monitor=true)." + else + warn "Android 14+: child process restrictions haven't been verified (monitor flag unreadable/unknown)." + fi + warn "Before starting the IIAB install, run the complete setup (--all) so it can guide you to verify such setting; otherwise the installation may fail:" + ok " ./0_termux-setupv2.sh --all" + return 0 + fi + fi + fi + fi + + if [[ "${CHECK_NO_ADB:-0}" -eq 1 ]]; then + # If we could not check, still warn on A12-13 because PPK is critical there + if [[ "$sdk" =~ ^[0-9]+$ ]] && (( sdk >= 31 && sdk <= 33 )); then + warn "A12-13: verify PPK=256 before installing IIAB." + fi + else + # A14+ child restrictions proxy (only if readable) + if [[ "$sdk" =~ ^[0-9]+$ ]] && (( sdk >= 34 )) && [[ "${CHECK_MON:-}" == "true" ]]; then + warn "A14+: disable child process restrictions before installing IIAB." + fi + + # Only warn about PPK on A12-13 (A14+ uses child restrictions) + if [[ "$sdk" =~ ^[0-9]+$ ]] && (( sdk >= 31 && sdk <= 33 )); then + if [[ "${CHECK_PPK:-}" =~ ^[0-9]+$ ]] && (( CHECK_PPK < 256 )); then + warn "PPK is low (${CHECK_PPK}); consider --ppk-only." + fi + fi + fi + + # 2) Debian “next step” should only be shown for modes that actually bootstrap Debian + case "$MODE" in + baseline|with-adb|all) + if debian_exists; then + ok "Next: proot-distro login debian" + else + warn "Debian not present. Run: proot-distro install debian" + fi + ;; + *) + # adb-only/connect-only/ppk-only/check: do not suggest Debian login as a generic ending + ;; + esac +} + +# ------------------------- +# Args +# ------------------------- +set_mode() { + local new="$1" + if [[ "$MODE_SET" -eq 1 ]]; then + die "Modes are mutually exclusive. Already set: --${MODE}. Tried: --${new}" + fi + MODE="$new" + MODE_SET=1 +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --with-adb) set_mode "with-adb"; shift ;; + --adb-only) set_mode "adb-only"; shift ;; + --connect-only) + set_mode "connect-only" + ONLY_CONNECT=1 + # Optional positional port (5 digits) + if [[ "${2:-}" =~ ^[0-9]{5}$ ]]; then + CONNECT_PORT="$2" + shift 2 + else + shift + fi + ;; + --ppk-only) set_mode "ppk-only"; shift ;; + --check) set_mode "check"; shift ;; + --all) set_mode "all"; shift ;; + --connect-port) CONNECT_PORT="${2:-}"; shift 2 ;; + --timeout) TIMEOUT_SECS="${2:-180}"; shift 2 ;; + --host) HOST="${2:-127.0.0.1}"; shift 2 ;; + --reset-debian|--clean-debian) RESET_DEBIAN=1; shift ;; + --no-log) LOG_ENABLED=0; shift ;; + --log-file) LOG_FILE="${2:-}"; shift 2 ;; + --debug) DEBUG=1; shift ;; + -h|--help) usage; exit 0 ;; + *) shift ;; + esac +done + +# ------------------------- +# Main flows +# ------------------------- +main() { + setup_logging "$@" + sanitize_timeout + acquire_wakelock + + case "$MODE" in + baseline) + step_termux_repo_select_once + step_termux_base + step_debian_bootstrap_default + ;; + + with-adb) + step_termux_repo_select_once + step_termux_base + step_debian_bootstrap_default + adb_pair_connect_if_needed + ;; + + adb-only) + step_termux_base + adb_pair_connect_if_needed + ;; + + connect-only) + step_termux_base + adb_pair_connect + ;; + + ppk-only) + # No baseline, no Debian. Requires adb already available + connected. + require_adb_connected || exit 1 + ppk_fix_via_adb || true + ;; + + check) + step_termux_base + check_readiness || true + ;; + + all) + step_termux_repo_select_once + step_termux_base + step_debian_bootstrap_default + adb_pair_connect_if_needed + + # Android 12-13 only (SDK 31-33): apply PPK tuning automatically + if [[ "${ANDROID_SDK:-}" =~ ^[0-9]+$ ]] && (( ANDROID_SDK >= 31 && ANDROID_SDK <= 33 )); then + log "Android SDK=${ANDROID_SDK} detected -> applying --ppk automatically (12-13 rule)." + ppk_fix_via_adb || true + else + log "Android SDK=${ANDROID_SDK:-?} -> skipping auto-PPK (only for Android 12-13)." + fi + check_readiness || true + ;; + + *) + die "Unknown MODE='$MODE'" + ;; + esac + + self_check + ok "0_termux-setupv2.sh completed (mode=$MODE)." + log "---- Mode list ----" + log "Connect-only: --connect-only [PORT]" + log "Pair+connect: --adb-only" + log "Check: --check" + log "Apply PPK: --ppk-only" + log "Base+Debian+Pair+connect: --with-adb" + log "Full run: --all" + log "Reset Debian: --reset-debian" + log "-------------------" + final_advice +} + +main "$@" diff --git a/android/termux-setup/README.md b/android/termux-setup/README.md new file mode 100644 index 0000000..f850497 --- /dev/null +++ b/android/termux-setup/README.md @@ -0,0 +1,27 @@ +# termux-setup modules + +This project is maintained as multiple Bash "modules" that are bundled into a single script: +`0_termux-setup_v2.sh` (the file served for: `curl ... | bash`). + +## Rules + +- Modules MUST NOT include a shebang (`#!...`). +- Modules SHOULD NOT run top-level code (prefer functions), except `99_main.sh`. +- Do not add `set -euo pipefail` in modules (the bundle already sets it once). +- Keep module names stable and ordered via `manifest.sh`. + +Recommended header for every module: + +``` +# termux-setup module. +# DO NOT add a shebang or "set -euo pipefail" here. +# Keep only function/variable definitions (no top-level execution). +# See: termux-setup/README.md +``` + +## Rebuild: + +``` +cd termux-setup +bash build_bundle.sh +``` diff --git a/android/termux-setup/build_bundle.sh b/android/termux-setup/build_bundle.sh new file mode 100644 index 0000000..8e137cb --- /dev/null +++ b/android/termux-setup/build_bundle.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +set -euo pipefail + +# build_bundle.sh +# Bundles modules listed in manifest.sh into ../0_termux-setup_v2.sh + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$SCRIPT_DIR" + +MANIFEST="${ROOT_DIR}/manifest.sh" +[[ -f "$MANIFEST" ]] || { echo "[!] Missing manifest.sh at: $MANIFEST" >&2; exit 1; } + +# Modules live next to build_bundle.sh (same dir) +MOD_DIR="$ROOT_DIR" + +PARENT_DIR="$(cd -- "${ROOT_DIR}/.." && pwd)" +OUT_DIR="${ROOT_DIR}/dist" +OUT_FILE="${PARENT_DIR}/0_termux-setup_v2.sh" +TMP_FILE="${OUT_DIR}/.0_termux-setup_v2.tmp.$RANDOM$RANDOM" + +mkdir -p "$OUT_DIR" + +# Load MODULES array +# shellcheck source=/dev/null +source "$MANIFEST" + +# With "set -u", don't reference an unset array directly. +if ! declare -p MODULES >/dev/null 2>&1; then + echo "[!] manifest.sh did not define MODULES array." >&2 + exit 1 +fi +if (( ${#MODULES[@]} < 1 )); then + echo "[!] MODULES array is empty in manifest.sh." >&2 + exit 1 +fi + +cleanup_tmp() { rm -f -- "$TMP_FILE" 2>/dev/null || true; } +trap cleanup_tmp EXIT + +# Bundle header +{ + echo '#!/data/data/com.termux/files/usr/bin/bash' + echo 'set -euo pipefail' + echo + echo '# -----------------------------------------------------------------------------' + echo '# GENERATED FILE: do not edit directly.' + echo '# Source modules: termux-setup/*.sh + manifest.sh' + echo '# Rebuild: (cd termux-setup && bash build_bundle.sh)' + echo '# -----------------------------------------------------------------------------' + echo +} >"$TMP_FILE" + +# Append each module +for mod in "${MODULES[@]}"; do + src="${MOD_DIR}/${mod}" + + if [[ ! -f "$src" ]]; then + echo "[!] Missing module: $src" >&2 + exit 1 + fi + + # Disallow standalone scripts: modules must not have a shebang + if head -n 1 "$src" | grep -q '^#!'; then + echo "[!] Module must NOT include a shebang: $src" >&2 + exit 1 + fi + + { + echo + echo "# ---- BEGIN ${mod} ----" + cat "$src" + echo + echo "# ---- END ${mod} ----" + echo + } >>"$TMP_FILE" +done + +# Ensure final newline +printf '\n' >>"$TMP_FILE" + +# Install bundle atomically +chmod 700 "$TMP_FILE" 2>/dev/null || true +mv -f -- "$TMP_FILE" "$OUT_FILE" +chmod 700 "$OUT_FILE" 2>/dev/null || true + +echo "[ok] Wrote: $OUT_FILE" diff --git a/android/termux-setup/manifest.sh b/android/termux-setup/manifest.sh new file mode 100644 index 0000000..6c2c8e5 --- /dev/null +++ b/android/termux-setup/manifest.sh @@ -0,0 +1,13 @@ +# termux-setup manifest +# Order matters: later modules can rely on functions/vars defined earlier. + +MODULES=( + "00_lib_common.sh" + "10_mod_logging.sh" + "20_mod_termux_base.sh" + "30_mod_debian.sh" + "40_mod_termux_api.sh" + "50_mod_adb.sh" + "60_mod_ppk_checks.sh" + "99_main.sh" +)