#!/usr/bin/env bash # 宿主机连接守护:ADB + WS Agent 常驻保活(处理长任务时也不断链) # 用法: # bash sdk/scripts/connection_keeper_daemon.sh # 前台 # bash sdk/scripts/connection_keeper_daemon.sh --bg # 后台 # bash sdk/scripts/connection_keeper_daemon.sh --once # 只跑一轮稳定化 set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SDK_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" LAN_ENV="${SDK_ROOT}/config/lan.env" REQUESTED_ADB_PORT="${ANDROID_ADB_SERVER_PORT:-}" [[ -f "${LAN_ENV}" ]] && source "${LAN_ENV}" || true [[ -n "${REQUESTED_ADB_PORT}" ]] && export ANDROID_ADB_SERVER_PORT="${REQUESTED_ADB_PORT}" SDK_PORT="${WORKPHONE_SDK_PORT:-8899}" INTERVAL="${KEEPER_INTERVAL:-15}" FAST_INTERVAL="${KEEPER_FAST_INTERVAL:-8}" DEVICE_SERIAL="${DEVICE_SERIAL:-}" DEVICE_ID="${DEVICE_ID:-xgfe65eimrrofyws}" export ANDROID_ADB_SERVER_PORT="${ANDROID_ADB_SERVER_PORT:-5037}" ADB=(adb -P "${ANDROID_ADB_SERVER_PORT}") LOG="${SDK_ROOT}/logs/connection_keeper.log" PID_FILE="${SDK_ROOT}/logs/connection_keeper.pid" mkdir -p "${SDK_ROOT}/logs" log() { echo "[$(date '+%F %T')] $*" | tee -a "$LOG"; } is_phone_serial() { local s="$1" local brand brand=$("${ADB[@]}" -s "$s" shell getprop ro.product.brand 2>/dev/null | tr -d '\r') [[ "$brand" != "haier" ]] } pick_serial() { if [[ -n "$DEVICE_SERIAL" ]]; then if "${ADB[@]}" -s "$DEVICE_SERIAL" get-state 2>/dev/null | grep -qx device && is_phone_serial "$DEVICE_SERIAL"; then echo "$DEVICE_SERIAL" return fi fi local s while IFS= read -r s; do [[ -z "$s" ]] && continue is_phone_serial "$s" || continue echo "$s" return done < <("${ADB[@]}" devices 2>/dev/null | awk '/\tdevice$/{print $1}') } usb_xiaomi_present() { ioreg -p IOUSB -l 2>/dev/null | grep -q "Xiaomi" 2>/dev/null } try_wireless_ports() { local host="${1:-192.168.1.12}" if usb_xiaomi_present; then log "USB Xiaomi detected → skip wireless port scan (优先等 MTP/授权)" return 1 fi ping -c 1 -W 2 "$host" >/dev/null 2>&1 || return 1 "${ADB[@]}" reconnect offline >/dev/null 2>&1 || true local p open_port="" hits=0 for p in 5555 $(seq 37000 37005); do if nc -z -G 1 "$host" "$p" 2>/dev/null; then hits=$((hits + 1)) [[ "$hits" -gt 3 ]] && break log "wireless port open ${host}:${p} → adb connect" ( "${ADB[@]}" connect "${host}:${p}" & pid=$!; sleep 3; kill $pid 2>/dev/null; wait $pid 2>/dev/null ) || true if "${ADB[@]}" -s "${host}:${p}" get-state 2>/dev/null | grep -qx device; then DEVICE_SERIAL="${host}:${p}" echo "$DEVICE_SERIAL" return 0 fi "${ADB[@]}" disconnect "${host}:${p}" >/dev/null 2>&1 || true fi done return 1 } ws_online_count() { curl -s "http://127.0.0.1:${SDK_PORT}/api/v3/connection/status" 2>/dev/null | \ python3 -c "import sys,json;d=json.load(sys.stdin).get('data',{});print(d.get('online_ws_count',0))" 2>/dev/null || echo 0 } u2_online() { curl -s "http://127.0.0.1:${SDK_PORT}/api/v3/devices/${DEVICE_ID}" 2>/dev/null | \ python3 -c "import sys,json;d=json.load(sys.stdin).get('data',{});print('1' if d.get('quick_status',{}).get('u2') else '0')" 2>/dev/null || echo 0 } ensure_adb_server() { if nc -z 127.0.0.1 "${ANDROID_ADB_SERVER_PORT}" 2>/dev/null; then return 0 fi "${ADB[@]}" start-server 2>/dev/null || true sleep 1 } adb_devices_safe() { ( "${ADB[@]}" devices "$@" & pid=$!; sleep 8; kill "$pid" 2>/dev/null; wait "$pid" 2>/dev/null ) 2>/dev/null || true } keeper_once() { ensure_adb_server ws="$(ws_online_count)" u2_ok="$(u2_online)" if [[ "$ws" -ge 1 && "$u2_ok" == "1" ]]; then log "ok ws_online=${ws} u2=1" return 0 fi if [[ "$ws" -ge 1 ]]; then log "ws_online=${ws} but u2=${u2_ok} → continue adb/u2 recovery" fi serial="$(pick_serial || true)" if [[ -z "$serial" ]]; then if usb_xiaomi_present; then log "USB 已插入但未 adb device → 请在手机选「传输文件」并允许 USB 调试;或开启无线调试" # USB 未授权时仍尝试无线 ADB(与 USB 并行) for ip in ${WIRELESS_IPS:-192.168.110.80 192.168.3.1}; do if serial="$(try_wireless_ports "$ip" || true)" && [[ -n "$serial" ]]; then log "wireless adb ok ${serial}" break fi done serial="$(pick_serial || true)" else for ip in ${WIRELESS_IPS:-192.168.110.80 192.168.110.60 192.168.110.197 192.168.3.1 192.168.1.12}; do if serial="$(try_wireless_ports "$ip" || true)" && [[ -n "$serial" ]]; then break fi ( "${ADB[@]}" connect "${ip}:5555" & pid=$!; sleep 3; kill "$pid" 2>/dev/null; wait "$pid" 2>/dev/null ) || true done serial="$(pick_serial || true)" fi fi if [[ -z "$serial" ]]; then log "no adb device — 若手机 Termux Agent 已配置 NAS/frp 寻服,可忽略;否则 USB 授权一次做 OTA" bash "${SCRIPT_DIR}/sdk_connection_watcher.sh" --once >>"$LOG" 2>&1 || true ws="$(ws_online_count)" [[ "$ws" -ge 1 ]] && log "sdk_watcher recovered ws=${ws}" && return 0 return 1 fi state="$("${ADB[@]}" -s "$serial" get-state 2>/dev/null || echo offline)" if [[ "$state" == "unauthorized" ]]; then log "adb unauthorized serial=${serial} → 请在手机点「允许 USB 调试」" return 1 fi if [[ "$state" != "device" ]]; then log "adb not ready serial=${serial} state=${state}" return 1 fi ws="$(ws_online_count)" if [[ "$ws" -ge 1 ]]; then log "ok serial=${serial} ws_online=${ws}" return 0 fi log "adb ok but ws=0 → termux/on-device agent serial=${serial}" DEVICE_SERIAL="$serial" DEVICE_ID="${DEVICE_ID:-xgfe65eimrrofyws}" \ bash "${SCRIPT_DIR}/termux_push_and_start_agent.sh" >>"$LOG" 2>&1 || true ws="$(ws_online_count)" if [[ "$ws" -ge 1 ]]; then log "termux/oneclick ok ws_online=${ws}" return 0 fi log "still ws=0 after termux_push" return 1 } keeper_loop() { log "connection_keeper start interval=${INTERVAL}s fast=${FAST_INTERVAL}s port=${SDK_PORT} lan=${WORKPHONE_LAN_IP:-auto}" while true; do if keeper_once; then sleep "$INTERVAL" else sleep "$FAST_INTERVAL" fi done } case "${1:-}" in --once) keeper_once ;; --bg) if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then echo "已在运行 PID $(cat "$PID_FILE")" exit 0 fi nohup bash "$0" >>"$LOG" 2>&1 & echo $! >"$PID_FILE" echo "connection_keeper 后台 PID $(cat "$PID_FILE") log=$LOG" ;; --stop) if [[ -f "$PID_FILE" ]]; then kill "$(cat "$PID_FILE")" 2>/dev/null || true rm -f "$PID_FILE" echo "已停止" fi ;; *) keeper_loop ;; esac