有了 root,可以把闲置安卓作为一个可观察的设备节点,查看 CPU、内存、磁盘和网络状态。这次把 Redmi K60 接入自己的 Komari 面板。文章重点是独立设备配置、Android 运行环境、指标核对与可停止的开机服务,不给手机安装面板管理员账号。
面板先建一个独立节点
在自己的 Komari 面板中创建手机节点,取得对应的 endpoint 和 token。每台手机使用独立身份,后续可单独撤销或更换。公开示例使用 tz.example.com 和占位 token,不对应任何实际账号。
本次选择 Komari Agent 1.5.11 的 Linux ARM64 静态候选程序。先核对架构、版本和哈希,再在手机上执行帮助命令:
adb shell getprop ro.product.cpu.abi
adb push komari-agent-linux-arm64 /data/local/tmp/
adb shell chmod 700 /data/local/tmp/komari-agent-linux-arm64
adb shell /data/local/tmp/komari-agent-linux-arm64 --help
本次候选文件的 SHA256 为:
ff8e33dc12ef2b5738fa291a58498bd15dcbf8ef51966ab65fd67679a0abc87d
帮助命令能运行只证明程序能够启动;Linux ARM64 不是 Android 兼容性承诺,采集与联网要继续实测。
用私有 JSON 保存配置
新建 komari-config.private.json,把 endpoint 和 token 换成自己节点的值:
{
"endpoint": "https://tz.example.com",
"token": "REPLACE_WITH_KOMARI_DEVICE_TOKEN",
"interval": 5,
"disable_auto_update": true,
"disable_web_ssh": true,
"ignore_unsafe_cert": false,
"enable_gpu": false,
"include_mountpoints": "/data",
"prefer_ip_version": "4"
}
本例每 5 秒采样,限定统计 /data,关闭远程控制、自动更新和 GPU 采集,并保留证书校验。这里的采样间隔是本次配置选择,实际耗电需要测量。官方配置说明列出了字段和优先级;JSON 会覆盖环境变量及命令行,因此不要以为加一个命令行参数就一定覆盖了旧 JSON。
在手机上把配置保存在仅 root 可访问的目录,目录 700、文件 600。通过 ADB 中转时,复制成功后删除临时文件:
adb push komari-config.private.json /data/local/tmp/
adb shell '/debug_ramdisk/su -c "mkdir -p /data/adb/bismih-mondrian"'
adb shell '/debug_ramdisk/su -c "chmod 700 /data/adb/bismih-mondrian"'
adb shell '/debug_ramdisk/su -c "cp /data/local/tmp/komari-config.private.json /data/adb/bismih-mondrian/"'
adb shell '/debug_ramdisk/su -c "chmod 600 /data/adb/bismih-mondrian/komari-config.private.json"'
adb shell rm /data/local/tmp/komari-config.private.json
DNS、CA 与时间要逐项验证
这台 Android 设备缺少 Linux 程序预期的 resolv.conf。只运行 Komari 时,可以先验证它提供的 custom_dns 是否足够;服务器地址应选择手机当前网络可达的解析器。本次还要同时运行 frp,因此共用一个私有挂载命名空间,给两个进程提供兼容目录和 DNS 文件。
这个方案保留真实网络、proc 和磁盘视图,改变的挂载不传播回 Android。根证书指向系统已有目录:
export SSL_CERT_DIR=/apex/com.android.conscrypt/cacerts:/system/etc/security/cacerts
export GOMAXPROCS=2
还要确认手机时间与实际日期一致。如果没有可用网络,先处理网络;如果是证书错误,检查时间、域名、证书链和 CA 路径。不能通过忽略证书或关闭 SELinux 把错误掩盖过去。
把探针交给一个 Magisk 监督器
本次模块使用 service.sh 的 late_start 阶段,等待系统启动完成后运行探针,具体机制参考 Magisk 启动脚本文档。模块检查 PID 与启动时间,防止多开;进程崩溃后按上限为 60 秒的退避重新启动,并定期限制日志体积。
模块可仅配置 Komari,也可与 frp 共用同一实例。没有相应私有配置的服务不会启动;不需要两份 Magisk 模块或两份 service.d 脚本。当前模块只适配本文的 mondrian;下面提供脚本源码与构建工具。
adb shell '/debug_ramdisk/su -c "/system/bin/sh /data/adb/modules/bismih_mondrian/control.sh start"'
adb shell '/debug_ramdisk/su -c "/system/bin/sh /data/adb/modules/bismih_mondrian/control.sh status"'
adb shell '/debug_ramdisk/su -c "/system/bin/sh /data/adb/modules/bismih_mondrian/control.sh stop"'
上面的命令用于已安装的本次模块,单纯复制 JSON 不会自动生成启动脚本。若同时配置了 frp,stop 和模块动作按钮会一起停止两个服务。手动停止标志会保留,直到再次 start。
本文使用的模块为 1.1,仅适配 mondrian。下载模块脚本源码与构建工具(约 9 KB,无二进制和凭据)。解压后先阅读 README.txt,运行 python3 build_module.py,工具会从官方发布地址下载固定版本的两个 ARM64 程序、检查哈希并生成可安装 ZIP;在 Magisk 的模块页安装生成的 ZIP,写入自己的私有配置后重启。源码包 SHA256:1d94fb8f8b379173396e2f6679ca3c5ba7c474afd02e94cbb043371adbfeb5f4。两个上游程序各自遵循其许可证;公开源码包没有重新分发它们的二进制。
还有一个与客户端无关的故障:系统把当前 Wi-Fi 判为“无互联网”,把保存的配置永久禁用,导致重启后不自动连接。本次确认 HTTPS 实际可达后,记住“仍然连接”并重新启用该配置;修复后息屏关闭 Wi-Fi 12 秒再打开,约 6.3 秒自动连回,之后重启也能自动连接。仅为这个网络保存选择,没有关闭全局网络验证。其行为可对照 Android 的无互联网网络确认界面源码。
在线之后,先验证读数
| 项目 | 核对方法 |
|---|---|
| 上报是否持续 | 至少观察两次采样的报告时间前进,避免把注册记录当成持续在线 |
| CPU | 对照手机实际 CPU 与负载变化,检查是否存在读取失败 |
| 内存 | 和 /proc/meminfo 对照;区分缓存、空闲与已用口径 |
| 磁盘 | 对照 df 的 /data 文件系统容量,避免把多个映射挂载点重复累计 |
| 流量 | 产生已知网络请求,检查实际联网接口的变化;切换 Wi-Fi、蜂窝或 VPN 后重新核对 |
| 不可读指标 | 标明不可用或不展示,不把权限不足、传感器缺失写成真实 0 值 |
Android 有不少虚拟网卡,不能把所有接口的字节数相加就当成真实上网流量。CPU 和内存读数也不需要单调增加;持续上报的依据是时间与合理的数据变化。探针显示的操作系统、主机名也要核对,避免被兼容目录误标成另一台 Linux 服务器。
锁屏、断网与耗电
先确认手机发出的是真实采样,再断开 USB、锁屏观察,短暂断网后恢复网络,最后重启。分别记录回到在线状态的时间和进程数量。开机启动和重试不会绕过所有 Android 省电策略,也不保证零耗电。
本次不长期持有唤醒锁。耗电应在相近条件下做有探针和无探针的对照,记录采样间隔、联网方式、观察时长、温度和电量变化;连接电脑充电时的电量增长不能替代这个测试。
停止与卸载
临时停止用 control.sh 或动作按钮,长期停用用 Magisk 开关,永久删除用模块卸载入口。卸载会清理本机配置;如果之后不再使用这台节点,再到面板撤销身份。日志也要保管好,连接失败的信息可能携带鉴权参数,公开排错前应脱敏。
本次实测记录(2026-10-16)
| 项目 | 结果 |
|---|---|
| Android 环境 | LineageOS 24 / Android 17、Magisk 30.7,SELinux Enforcing |
| 真实采样 | 初次验收两次报告相隔约 32 秒,uptime 271→303 秒,CPU 2.84%→3.71% |
| 内存 | 总量 15,890,251,776 字节,与 /proc/meminfo 的 15,517,824 KiB 一致 |
| /data 磁盘 | 总量 240,090,329,088 字节,与 df 的 234,463,212 KiB 一致;不等同厂商标称存储总容量 |
| 流量 | 初次两样本上传/下载累计增加 28,918 / 34,822 字节;本次联网接口为 wlan0 |
| 断网与重启 | 息屏断网后自动重新连接;受控重启后模块启动,服务端确认两次新的报告 |
| 停止和清理 | stop、disable、卸载脚本清理及重配启动通过;重复 start 不产生第二监督器 |
指标也有明确限制:探针把 CPU 名称识别为 Cortex-A510,这是异构 CPU 中的一类核心,不能当作完整 SoC 型号。GPU 采集已关闭,温度未验证为有效传感器数据;接口里的 GPU/温度 0 应按不可用处理,不能解释为 GPU 空闲或 0°C。
本次还遇到了一个采样间隔冲突:15 秒上报时,v2 WebSocket 大约每 15 秒出现 EOF 并重连。实际服务端为 Hub 1.5.1,对应版本的 presence.go把读等待阈值设为 11 秒,而 Agent 1.5.11固定每 30 秒发一次心跳;15 秒报告与 30 秒心跳都晚于该阈值。只把手机上报间隔改为 5 秒后,息屏观察至少 3 分钟保持同一连接,读取错误为 0,服务端采样继续刷新。无需修改全局 Hub 或反代超时。后续版本的阈值和行为仍需重新核对,短时通过也不代表所有网络或长时间休眠下都不会断线。
本次短时测试全程由 USB 供电,验证了息屏、断网、重启和控制脚本。尚未做拔线后的长时间深度休眠或耗电对照,因此不能据此保证全天在线或某个续航数值。
