Files
boss/docs/superpowers/specs/2026-04-05-adb-wireless-keeper-design.md
2026-04-05 06:00:06 +08:00

9.9 KiB
Raw Permalink Blame History

Boss Android ADB 无线保活设计

1. 背景

当前 Boss 仓库已经有原生 Android 客户端、APK/AAB 构建脚本和真机验证习惯,但没有一套稳定的 Android 无线 ADB 保活方案。实际调试中Android 的无线调试会因为重启、切 Wi-Fi、ADB server 重启、USB 调试切换等原因自动失效,导致每次真机回归都需要重新恢复连接。

这次工作的目标不是绕过 Android 系统限制,也不是把设备管理做成 MDM而是在本机增加一套可长期后台运行的自动重连守护流程尽量降低“设备本来还允许 TCP/IP ADB但连接自己掉了”这类问题的人工恢复成本。

2. 目标与非目标

2.1 目标

  1. 支持多台 Android 设备在同一局域网内自动重连 ADB 无线连接。
  2. 后台常驻运行,不要求人工一直开终端窗口守着。
  3. 自动扫描当前所在子网的 5555 端口,但只对白名单设备或历史已成功授权的设备执行自动重连。
  4. 提供明确的状态、日志和失败退避,避免误连和刷屏。
  5. 与 Boss 当前本机运维方式保持一致,优先走 scripts/ + launchd

2.2 非目标

  1. 不负责替 Android 设备开启开发者选项、USB 调试或无线调试。
  2. 不尝试绕过 Android 的系统限制,不能承诺“永久保持无线调试开启”。
  3. 不做跨子网、跨 VPN、跨公网的设备发现。
  4. 不做完整移动设备管理平台,不接入 Web/Android 前台页面。

3. 方案对比

3.1 方案 A全网段无差别扫描并直接 adb connect

做法:周期性扫描当前子网所有开放 5555 的主机,发现后直接 adb connect <ip>:5555

优点:

  • 配置最少。
  • 自动化程度最高。

缺点:

  • 容易误连同网段里的其他 Android、电视盒子或调试设备。
  • 日志会混入大量非目标主机。
  • 风险和噪音都偏高,不适合默认行为。

结论:不采用。

3.2 方案 B仅对固定 IP 清单做重连

做法:只维护一份静态 IP 列表,循环对这些地址做 adb connect

优点:

  • 最稳、最可控。
  • 实现简单。

缺点:

  • 手机 IP 一变就失效。
  • 不符合“自动扫描同网段”的目标。

结论:可作为降级模式保留,但不作为主方案。

3.3 方案 C自动扫描同网段但只对白名单或历史已授权设备重连

做法:周期性推断当前活跃子网,扫描开放 5555 的主机;只对白名单设备或历史上曾成功连接并被缓存的设备执行 adb connect

优点:

  • 满足自动扫描同网段的要求。
  • 风险可控,不会默认接入陌生主机。
  • 适合多设备长期保活。

缺点:

  • 需要维护配置文件与运行态缓存。
  • 新设备第一次仍需手工建立一次可信连接。

结论:采用本方案。

4. 目标架构

新增一套本机运维脚本与守护配置:

  • scripts/adb-wireless-keeper.mjs
  • scripts/adb-wireless-keeper-once.sh
  • scripts/adb-wireless-keeper-status.sh
  • scripts/install-adb-wireless-keeper.sh
  • scripts/uninstall-adb-wireless-keeper.sh
  • config/adb-wireless-keeper.json
  • deployment/launchd/com.hyzq.boss.adb-wireless-keeper.plist
  • data/adb-wireless-keeper-state.json
  • data/adb-wireless-keeper.log.jsonl

职责分层:

  1. adb-wireless-keeper.mjs
    • 负责扫描、识别、重连、退避和状态落盘。
  2. *-once.sh
    • 负责单次执行,便于人工调试。
  3. *-status.sh
    • 负责打印当前守护状态、最近扫描和最近失败原因。
  4. install/uninstall
    • 负责安装和卸载 launchd 常驻服务。
  5. launchd plist
    • 负责开机自启、异常自动拉起。

5. 配置模型

配置文件建议为 config/adb-wireless-keeper.json

{
  "subnets": [],
  "scanIntervalSeconds": 30,
  "connectTimeoutMs": 4000,
  "adbPath": "",
  "logFile": "data/adb-wireless-keeper.log.jsonl",
  "stateFile": "data/adb-wireless-keeper-state.json",
  "allowedDevices": [
    {
      "label": "boss-oppo-main",
      "serialHint": "8KE0219724012168",
      "ipHint": "192.168.31.18"
    }
  ]
}

约束:

  1. subnets 为空时,脚本自动根据当前默认路由推断活跃子网。
  2. allowedDevices 是自动重连的上限范围;未知设备默认只记录日志,不直接接管。
  3. serialHintipHint 允许为空,但至少应有一个可用于匹配。
  4. adbPath 为空时,优先走 PATH 中的 adb

6. 运行态模型

运行态文件 data/adb-wireless-keeper-state.json 至少记录:

{
  "lastSubnet": "192.168.31.0/24",
  "lastScanAt": "2026-04-05T12:00:00.000Z",
  "nextScanAt": "2026-04-05T12:00:30.000Z",
  "knownDevices": {
    "boss-oppo-main": {
      "serial": "8KE0219724012168",
      "lastKnownIp": "192.168.31.18",
      "lastConnectedAt": "2026-04-05T11:59:58.000Z",
      "lastFailureAt": "",
      "failureCount": 0,
      "backoffUntil": ""
    }
  }
}

用途:

  1. 记住最近成功连接的 serial <-> ip 映射。
  2. 为失败退避提供依据。
  3. status 脚本直接读取当前状态,而不必解析全量日志。

7. 扫描与重连策略

7.1 子网推断

  1. 读取当前默认路由对应的活跃网卡。
  2. 获取该网卡 IPv4 地址。
  3. 默认把扫描范围收敛到当前主机所在的 /24
  4. 如果配置文件显式给了 subnets,优先使用显式配置。

这样做的原因是:家庭/办公网络里经常出现大网段,直接扫更大的 CIDR 成本和噪音都太高。

7.2 每轮扫描步骤

  1. 先执行 adb devices -l,记录当前已在线设备。
  2. 扫描目标子网中开放 5555 的主机。
  3. 对每个开放地址:
    • 若命中白名单或命中历史成功缓存,进入候选集。
    • 若是陌生地址,仅记录 device_skipped_unknown 日志,不直接接入。
  4. 对候选集里当前离线的设备执行 adb connect <ip>:5555
  5. 成功则更新状态文件;失败则更新失败计数和退避时间。

7.3 新设备接入

新设备第一次不自动纳管。建议流程:

  1. 用户先通过 USB 或手工 adb connect 成功连一次。
  2. 将设备补进 allowedDevices
  3. 守护脚本后续自动接管该设备的无线重连。

8. 失败处理与退避

脚本必须把“掉线可恢复”和“系统级失效不可恢复”分开处理。

8.1 可恢复场景

  1. 局域网短时抖动。
  2. adb 连接断开,但设备仍允许 TCP/IP ADB。
  3. ADB server 重启后需要重新 connect

这些场景应自动恢复。

8.2 不可恢复场景

  1. 设备重启后无线调试被系统关闭。
  2. 用户关闭开发者选项或 USB 调试。
  3. 手机切到其他 Wi-Fi、热点或 VPN 导致不在同一子网。
  4. 设备根本不再监听 5555

这些场景脚本只能记录并退避,不能声称“已经修复”。

8.3 退避规则

建议采用分级退避:

  • 第 1 次失败30 秒后再试
  • 第 2-3 次失败2 分钟后再试
  • 第 4 次及以上失败5 分钟后再试

目的:

  1. 避免在设备失效时疯狂刷 adb connect
  2. 保留可读日志。
  3. 降低对局域网的无意义探测。

9. 日志设计

日志采用 JSONL每行一个事件路径默认 data/adb-wireless-keeper.log.jsonl

字段建议:

  • ts
  • event
  • subnet
  • ip
  • serial
  • label
  • result
  • reason

事件枚举建议:

  • scan_started
  • scan_finished
  • host_port_open
  • device_allowed
  • device_skipped_unknown
  • device_already_online
  • connect_attempt
  • connect_success
  • connect_failed
  • backoff_applied

10. 运维入口

10.1 安装

scripts/install-adb-wireless-keeper.sh

职责:

  1. 校验 adb 是否存在。
  2. 校验配置文件是否存在。
  3. 渲染并安装 launchd plist。
  4. 启动守护进程。

10.2 卸载

scripts/uninstall-adb-wireless-keeper.sh

职责:

  1. 停止并卸载 launchd 服务。
  2. 保留日志和状态文件,不自动删除。

10.3 状态查看

scripts/adb-wireless-keeper-status.sh

输出至少包含:

  1. 当前守护状态。
  2. 当前扫描子网。
  3. 当前在线设备。
  4. 最近失败原因。
  5. 下次扫描时间。

10.4 单次调试

scripts/adb-wireless-keeper-once.sh

用于本地人工验证“一轮扫描 + 一轮重连”,不需要先装 launchd

11. 验收标准

必须同时满足以下 4 条:

  1. 已纳管设备掉线后,守护脚本能在设定周期内自动恢复 adb connect
  2. 陌生 5555 主机不会被自动长期接入,只会记录日志。
  3. 设备重启或系统撤销无线调试时,脚本进入退避,不会疯狂重试。
  4. status 命令能直接给出当前子网、在线设备、最近失败原因和下一次扫描时间。

12. 测试策略

优先补 Node 侧单测,不引入 Android 侧改动。

建议覆盖:

  1. 配置解析。
  2. 子网收敛逻辑。
  3. 白名单过滤逻辑。
  4. 历史成功设备匹配逻辑。
  5. 失败退避逻辑。
  6. 状态文件读写。
  7. 日志事件格式。

此外保留一轮人工验证:

  1. 先手工把一台已纳管设备连上无线 ADB。
  2. 手工执行 adb disconnect <ip>:5555
  3. 观察守护脚本是否在预期周期内自动恢复。

13. 风险与边界

  1. Android 系统没有官方“永久保持无线调试开启”的机制,所以这个方案只能做自动恢复,不能承诺永不失效。
  2. 自动扫描网络天然有噪音,必须坚持“只对白名单或历史已授权设备自动重连”的边界。
  3. 大网段环境下必须收敛扫描范围,默认仅扫当前 /24
  4. 如果用户要求“绝不掉线”,仍应保留 USB 作为最终兜底方案。

14. 实施建议

推荐按以下顺序实施:

  1. 先完成 adb-wireless-keeper.mjs 的纯逻辑与单测。
  2. 再补 status/once/install/uninstall 四个壳脚本。
  3. 最后补 launchd plist 与 README 文档入口。

这样做的原因是:先把可测试的核心逻辑收口,再把平台相关的守护安装步骤补上,调试成本最低。