docs: add adb wireless keeper design
This commit is contained in:
332
docs/superpowers/specs/2026-04-05-adb-wireless-keeper-design.md
Normal file
332
docs/superpowers/specs/2026-04-05-adb-wireless-keeper-design.md
Normal file
@@ -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 <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`:
|
||||
|
||||
```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 <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 文档入口。
|
||||
|
||||
这样做的原因是:先把可测试的核心逻辑收口,再把平台相关的守护安装步骤补上,调试成本最低。
|
||||
Reference in New Issue
Block a user