From e649e2f9ac27d048a3c1924eb9cf4a73fcb9d228 Mon Sep 17 00:00:00 2001 From: kris Date: Sun, 5 Apr 2026 06:00:06 +0800 Subject: [PATCH] docs: add adb wireless keeper design --- .../2026-04-05-adb-wireless-keeper-design.md | 332 ++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-05-adb-wireless-keeper-design.md diff --git a/docs/superpowers/specs/2026-04-05-adb-wireless-keeper-design.md b/docs/superpowers/specs/2026-04-05-adb-wireless-keeper-design.md new file mode 100644 index 0000000..b30030b --- /dev/null +++ b/docs/superpowers/specs/2026-04-05-adb-wireless-keeper-design.md @@ -0,0 +1,332 @@ +# 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 :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`: + +```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. `serialHint` 与 `ipHint` 允许为空,但至少应有一个可用于匹配。 +4. `adbPath` 为空时,优先走 PATH 中的 `adb`。 + +## 6. 运行态模型 + +运行态文件 `data/adb-wireless-keeper-state.json` 至少记录: + +```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 :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 :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 文档入口。 + +这样做的原因是:先把可测试的核心逻辑收口,再把平台相关的守护安装步骤补上,调试成本最低。