#!/data/data/com.termux/files/usr/bin/bash set -euo pipefail # ----------------------------------------------------------------------------- # 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; } 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; } # ------------------------- # Global defaults (may be overridden via environment) # ------------------------- STATE_DIR="${STATE_DIR:-${HOME}/.iiab-android}" ADB_STATE_DIR="${ADB_STATE_DIR:-${STATE_DIR}/adbw_pair}" LOG_DIR="${LOG_DIR:-${STATE_DIR}/logs}" HOST="${HOST:-127.0.0.1}" CONNECT_PORT="${CONNECT_PORT:-}" TIMEOUT_SECS="${TIMEOUT_SECS:-180}" # Defaults used by ADB flows / logging / misc CLEANUP_OFFLINE="${CLEANUP_OFFLINE:-1}" DEBUG="${DEBUG:-0}" # ---- END 00_lib_common.sh ---- # ---- BEGIN 10_mod_logging.sh ---- # 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 } # ---- END 10_mod_logging.sh ---- # ---- 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[@]}" "$@"; } # ------------------------- # 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 } # ---- 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 # ------------------------- 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 } # ---- 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 # Quick probe: some setups fail until Termux:API app is installed/allowed. 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.reply" rm -f "$out" # 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 # Direct reply: Termux:API injects the user input into $REPLY for the action. # Write it to a known file, then the main loop reads it. local action action="sh -lc 'umask 077; printf \"%s\" \"\$REPLY\" > \"${out}\"'" termux-notification \ --id "$nid" \ --ongoing \ --priority max \ --title "$title" \ --content "$content" \ --sound \ --button1 "Answer" \ --button1-action "$action" \ || return 1 local start now reply start="$(date +%s)" while true; do if [[ -f "$out" ]]; then reply="$(tr -d '\r\n' < "$out" 2>/dev/null || true)" rm -f "$out" >/dev/null 2>&1 || true if have termux-notification-remove; then termux-notification-remove "$nid" >/dev/null 2>&1 || true fi printf '%s' "$reply" 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 } # ---- 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 # ------------------------- # Local stamp so we can detect "connect-only" misuse after reinstall/clear-data. ADB_PAIRED_STAMP="${ADB_STATE_DIR}/stamp.adb_paired" adb_hostkey_fingerprint() { # Returns a stable fingerprint for THIS Termux install's adb host key. local pub="${HOME}/.android/adbkey.pub" [[ -r "$pub" ]] || return 1 if have sha256sum; then sha256sum "$pub" | awk '{print $1}' elif have shasum; then shasum -a 256 "$pub" | awk '{print $1}' elif have openssl; then openssl dgst -sha256 "$pub" 2>/dev/null | awk '{print $2}' elif have md5sum; then md5sum "$pub" | awk '{print $1}' else return 1 fi } adb_stamp_write() { # args: mode serial local mode="$1" serial="$2" fp="" fp="$(adb_hostkey_fingerprint 2>/dev/null || true)" { echo "ts=$(date -Is 2>/dev/null || date || true)" echo "mode=${mode}" echo "host=${HOST}" echo "serial=${serial}" echo "connect_port=${CONNECT_PORT:-}" echo "hostkey_fp=${fp}" } >"$ADB_PAIRED_STAMP" 2>/dev/null || true chmod 600 "$ADB_PAIRED_STAMP" 2>/dev/null || true } adb_stamp_read_fp() { [[ -r "$ADB_PAIRED_STAMP" ]] || return 1 sed -n 's/^hostkey_fp=//p' "$ADB_PAIRED_STAMP" 2>/dev/null | head -n 1 } adb_warn_connect_only_if_suspicious() { # Called only in connect-only flows. local cur_fp old_fp cur_fp="$(adb_hostkey_fingerprint 2>/dev/null || true)" old_fp="$(adb_stamp_read_fp 2>/dev/null || true)" if [[ ! -f "$ADB_PAIRED_STAMP" ]]; then warn "connect-only assumes THIS Termux install has been paired before." warn "No local pairing stamp found ($ADB_PAIRED_STAMP)." warn "If you reinstalled Termux / cleared data / changed user, you must re-pair (run: --adb-only)." [[ -n "$cur_fp" ]] && warn "Current ADB hostkey fingerprint: ${cur_fp:0:12}..." return 0 fi if [[ -n "$old_fp" && -n "$cur_fp" && "$old_fp" != "$cur_fp" ]]; then warn_red "ADB host key changed since last pairing stamp." warn "Old fingerprint: ${old_fp:0:12}... Current: ${cur_fp:0:12}..." warn "Android Wireless debugging -> Paired devices: remove the old entry, then run: --adb-only" fi } adb_connect_verify() { # args: serial (HOST:PORT) local serial="$1" out rc start now state set +e out="$(adb connect "$serial" 2>&1)" rc=$? set -e # Always verify via `adb devices` (adb may exit 0 even on failure). start="$(date +%s)" while true; do state="$(adb_device_state "$serial" || true)" [[ "$state" == "device" ]] && { printf '%s\n' "$out"; return 0; } now="$(date +%s)" (( now - start >= 5 )) && break sleep 1 done warn_red "adb connect did not result in a usable device entry for: $serial (state='${state:-none}')." warn "adb connect output: ${out:-}" warn "If you recently reinstalled Termux/cleared data, the phone may show an OLD paired device. Remove it and re-pair." return 1 } 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 adb_warn_connect_only_if_suspicious 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_verify "$serial" >/dev/null || die "adb connect failed to $serial. Verify Wireless debugging is enabled, and pairing exists for THIS Termux install." 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 adb_stamp_write "connect-only" "$serial" 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_verify "$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 adb_stamp_write "paired" "$serial" 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" } # ---- 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) # ------------------------- 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 } self_check_android_flags() { have adb || return 0 adb start-server >/dev/null 2>&1 || true local serial sdk rel mon mon_fflag ds ppk_eff serial="$(adb_pick_loopback_serial 2>/dev/null)" || { warn "ADB: no loopback device connected. Tip: run --adb-only (pair+connect) or --check for more info." return 0 } 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)" log " Android flags (quick): release=${rel:-?} sdk=${sdk:-?} serial=$serial" if [[ "$sdk" =~ ^[0-9]+$ ]] && (( sdk >= 34 )); 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 if [[ "$mon" == "false" ]]; then ok " Child restrictions: OK (monitor=false)" elif [[ "$mon" == "true" ]]; then warn " Child restrictions: NOT OK (monitor=true) -> check Developer Options" else warn " Child restrictions: unknown/unreadable (monitor='${mon:-}')" fi fi if [[ "$sdk" =~ ^[0-9]+$ ]] && (( sdk >= 31 && sdk <= 33 )); then 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)" 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: --ppk-only" fi else warn " PPK: unreadable (max_phantom_processes='${ppk_eff:-}')" fi fi # Avoid redundant tip when we're already in --check mode. if [[ "${MODE:-}" != "check" && "${MODE:-}" != "all" ]]; then log " Tip: run --check for full details." fi } # ---- 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 # ------------------------- # NOTE: Core defaults live in 00_lib_common.sh to guarantee availability for all modules. # Ensure state directories exist (safe even if user overrides via environment). mkdir -p "$STATE_DIR" "$ADB_STATE_DIR" "$LOG_DIR" 2>/dev/null || true 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 CONNECT_PORT_FROM="" # "", "flag", "positional" 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 [--connect-port PORT] -> Only ADB pair/connect if needed (no Debian; skips if already connected). Tip: --connect-port skips the CONNECT PORT prompt (you’ll still be asked for PAIR PORT + PAIR CODE). ./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 used with --adb-only --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 # renable in need for verbose output. # 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 # Quick Android flags check (best-effort; no prompts) self_check_android_flags || true 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 [[ -n "${CONNECT_PORT_FROM:-}" && "${CONNECT_PORT_FROM}" != "positional" ]] && \ die "CONNECT PORT specified twice (positional + --connect-port). Use only one." CONNECT_PORT="$2" CONNECT_PORT_FROM="positional" shift 2 else shift fi ;; --ppk-only) set_mode "ppk-only"; shift ;; --check) set_mode "check"; shift ;; --all) set_mode "all"; shift ;; --connect-port) [[ -n "${CONNECT_PORT_FROM:-}" && "${CONNECT_PORT_FROM}" != "flag" ]] && \ die "CONNECT PORT specified twice (positional + --connect-port). Use only one." CONNECT_PORT="${2:-}" CONNECT_PORT_FROM="flag" 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 validate_args() { 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'" case "$MODE" in adb-only|with-adb|connect-only|ppk-only|check|all) : ;; baseline) log "--connect-port requires an ADB mode." die "Use along with: --adb-only / --with-adb / --connect-only / --check / --ppk-only / --all" ;; *) die "--connect-port is not valid with mode=$MODE" ;; esac fi } # ------------------------- # Main flows # ------------------------- main() { setup_logging "$@" validate_args 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 [--connect-port PORT]" 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 "$@" # ---- END 99_main.sh ----