Files
butubb 2580cfaead feat: 设备端 Agent 首个版本——剪贴板注入 / 应用商店 / 身份显示 / 配置下发
配合 auto_control 平台的设备端补位 App(接口契约见 INTERFACE.md,与平台仓库
doc/DEVICE_AGENT.md 是同一份)。

四个 adb 可拉起的 Activity + 一个商店界面:
- ClipActivity    剪贴板注入(透明 Activity,**必须等窗口获得焦点再写** ——
                  Android 10+ 只有前台应用能写剪贴板,这正是 u2 set_clipboard
                  "成功但内容被丢弃"的原因);写完立即退出,不驻留
- InfoActivity    全屏大字显示设备身份(名称/IP/序列号/平台地址),
                  替代原来"推 HTML 让设备浏览器打开"那套
- ConfigActivity  一次性配置下发(平台用 adb 把 server/token/指纹推进来),
                  一批设备不用一台台手输
- PingActivity    自检(版本/指纹/配置状态),结果写 logcat + 外部私有目录的
                  status.json,平台据此判断"装没装、哪一版"
- MainActivity    应用商店:拉平台清单 → 选应用 → 安装;已装的显示「已安装」,
                  有新版显示「更新到 vX」

实机踩出来的几个坑(都已处理):
- **应用身份读不到 ro.serialno**(shell 能读、应用被 SELinux 挡)→ 指纹改由平台
  通过 ConfigActivity 下发(平台本来就知道),本机 getprop 只作兜底
- **Android 9+ 默认拦明文 HTTP** → 平台是局域网 http,需 usesCleartextTraffic
- **Android 11+ 软件包可见性** → 不加 QUERY_ALL_PACKAGES 会"明明装了却显示未安装"
- **MIUI 安装确认不止一步** → 装完检测窗口给到 3 分钟(人会依次点继续→勾选→继续更新)
- 上报带重试(实测遇到过一次网络抖动导致 timeout)

构建:JDK 17 + Android SDK(minSdk 29 / targetSdk 34),只依赖 androidx.core,
HTTP 用 HttpURLConnection、JSON 用系统内置 org.json。
2026-09-13 16:30:16 +08:00

72 lines
3.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 设备 Agent(DeviceAgent)
装在每台 Android 手机上的**设备端补位 App**,配合
[auto_control](https://gitea.butubb.cn/butubb/auto_control) 平台使用。
平台通过 **adb** 驱动设备;这个 App 负责三件 adb 干不好/干不了的事:
| 能力 | 为什么 adb 不行 | 入口 |
|------|----------------|------|
| **剪贴板注入** | Android 10+ 只有**有焦点**的应用能写剪贴板,shell/后台写会被系统静默丢弃 | `ClipActivity`(透明 Activity,adb 拉起) |
| **应用分发(商店)** | `adb install` 会被 MIUI 的「USB 安装」拦成 `USER_RESTRICTED`;**由设备上的 App 自己调系统安装器**走的是普通安装流程,不触发那道拦截 | `MainActivity`(商店界面,自己联网拉清单) |
| **设备身份显示** | 以前要把 HTML 大字页推给设备浏览器打开,依赖浏览器 | `InfoActivity`(全屏大字,adb 拉起) |
## 接口契约
**平台的接口协议见 `../auto_control/doc/DEVICE_AGENT.md`**(那是两个仓库共享的契约;
改协议必须同时改两边)。本仓库里也留了一份副本:[INTERFACE.md](INTERFACE.md)。
## 快速开始
```bash
# 1. 构建(需要 JDK 17 + Android SDK;也可以用 Android Studio 直接打开)
./gradlew assembleDebug
# 2. 装到设备(平台用网络 adb 管理设备,serial 形如 IP:5555)
adb -s 192.168.20.100:5555 install -r app/build/outputs/apk/debug/app-debug.apk
# 3. 一次性配置平台地址与令牌(不用在手机上手输)
adb -s 192.168.20.100:5555 shell am start -n com.example.deviceagent/.ConfigActivity \
--es server "http://192.168.20.220:18050" --es token "<平台面板里的设备令牌>"
# 4. 自检:确认装上了、看是哪一版
adb -s 192.168.20.100:5555 shell am start -n com.example.deviceagent/.PingActivity
adb -s 192.168.20.100:5555 shell cat /sdcard/Android/data/com.example.deviceagent/files/status.json
# 5. 手动试试剪贴板注入
adb -s 192.168.20.100:5555 shell am start -n com.example.deviceagent/.ClipActivity \
--es text_b64 "$(printf '测试中文' | base64)"
# 6. 让设备显示自己的身份大字页
adb -s 192.168.20.100:5555 shell am start -n com.example.deviceagent/.InfoActivity \
--es name "A08" --es ip "192.168.20.100" --es serial "192.168.20.100:5555" \
--es server "http://192.168.20.220:18050"
```
然后用手机打开 App 图标 → 就是应用商店(前提是平台侧已启用设备端商店并给了令牌)。
## 权限说明
只申请三个,都是必需的:
- `INTERNET` —— 从平台拉应用清单、下载 APK
- `REQUEST_INSTALL_PACKAGES` —— 调系统安装器装应用(**首次会引导去系统设置里授权**)
- `ACCESS_NETWORK_STATE` —— 判断有没有网
**不需要 root、不需要设备管理员、不常驻后台、不申请任何读取隐私的权限。**
## 代码结构
```
app/src/main/java/com/example/deviceagent/
MainActivity.kt 应用商店主界面(清单/下载/调安装器/回报)
ClipActivity.kt 剪贴板注入(透明,等窗口获得焦点后再写)
InfoActivity.kt 设备身份全屏大字页
ConfigActivity.kt 一次性配置下发(平台用 adb 推地址+令牌)
PingActivity.kt 自检(版本/指纹/配置状态,写 logcat + status.json)
Api.kt 平台接口客户端(HttpURLConnection,无第三方库)
Prefs.kt 配置存储 + 设备指纹(getprop ro.serialno)
```
刻意**只依赖 androidx.core**(就为了 FileProvider):HTTP 用 `HttpURLConnection`、
JSON 用系统内置的 `org.json`,依赖越少越不容易被构建问题绊住。