Android 应用冻结/启停工具,基于 root 权限实现事件驱动冻结、进程级隐藏与残留清理。
两段式架构:Java 应用负责配置、授权与部署,原生 Guard 进程专职「前台监听 + 系统级禁用」,职责清晰、互不干扰。另有脚本管理模块,支持以 root 权限运行 SH 脚本与编译后的二进制文件。
Guard 的核心设计哲学是事件驱动——不轮询、不常驻、不浪费。通过系统事件日志感知前台应用变化,按需处理,空闲时进程开销趋近于零。
传统冻结方案存在根本缺陷:要么定时轮询浪费资源,要么依赖系统服务暴露自身。Guard 的事件驱动架构从根本上规避了这些问题:
| 方案 | 原理 | 致命缺陷 |
|---|---|---|
| 定时轮询 | 每隔 N 秒检查前台应用 | 持续占用 CPU,轮询间隔内冻结不生效,频繁唤醒耗电 |
| AccessibilityService | 监听无障碍事件 | 需特殊权限,系统设置中可见,容易被检测,部分 ROM 限制后台运行 |
| 后台常驻 Service | 持续运行检测逻辑 | 需前台通知(Android 8+),内存常驻,被系统杀死后冻结失效 |
| Guard 事件驱动 | logcat 事件日志 + poll 阻塞 | 无上述所有缺陷 |
1. 零开销空闲
Guard 进程用 poll() 阻塞在 logcat 管道上,当没有前台切换事件时,进程完全休眠:
- CPU 占用:0%(poll 是内核级阻塞,不消耗任何 CPU 时间片)
- 内存占用:仅 native 进程基础开销(几 MB),无 Java 虚拟机、无 Service 对象
- 耗电:零(不唤醒 CPU,不触发 wakelock)
- 无前台通知、无后台 Service、无定时器、无 AlarmManager
2. 毫秒级响应
事件驱动意味着前台切换的瞬间就触发冻结,没有任何轮询延迟:
- 用户打开目标应用 → 系统
wm_on_resume_called事件 → Guardpoll()返回 → 提取组件名 → 匹配目标 → 批量pm disable - 全链路耗时 < 1 秒,其中
pm disable是主要开销 - 对比定时轮询方案:若轮询间隔 5 秒,最坏情况冻结延迟 5 秒,期间目标应用已可正常运行被冻结的应用
3. 配置即改即生效
事件驱动的热重载机制使配置更新零延迟:
- 每次事件触发时,Guard 用
stat()检查配置文件(O(1) 系统调用,仅读 inode 元数据) - 若修改时间或文件大小变化 → 热重载全部配置
- 用户在界面勾选/取消应用 → 写入 config.txt → 下一次事件即可生效
- 无需重启服务、无需重新部署、无需重新授权
- 应用以 root 权限拉起原生 Guard 进程,二者通过标准输出管道通信,运行日志实时回传界面
- Guard 进程 fork 子进程执行
logcat -b events -v brief -T 1,仅过滤两类窗口事件:wm_on_resume_called:Activity 的onResume被调用wm_on_top_resumed_gained_called:窗口获得前台焦点
- 父进程用
poll()阻塞等待管道数据,事件驱动消费,无 busy loop - poll 设置 2 秒超时,用于周期性检查小窗状态是否已退出(见「小窗状态检测」)
- 其余事件(
*:S)全部静默——logcat 只推送这两类事件给 Guard,I/O 开销极小
组件名匹配支持三种形态,适配 Android 系统的各种组件命名方式:
| 组件形态 | 示例 | 匹配方式 |
|---|---|---|
| 纯包名 | com.target.app |
直接比较 |
| 包名/类名 | com.target.app/.MainActivity |
取 / 前部分比较 |
| 完整类名 | com.target.app/com.target.app.MainActivity |
取 / 前部分比较 |
配合前缀匹配,用户只需配置包名即可,无需关心目标应用的具体 Activity 类名。目标应用更新、更换入口 Activity 都不影响匹配。
包名变体匹配:当配置的目标包名无法精确匹配前台组件时,自动尝试去掉包名最后一段重新匹配。例如配置 bin.mt.plus.canary(金丝雀版),前台组件 bin.mt.plus.MainLightIcon(正式版)也能命中——取 bin.mt.plus 作为公共前缀匹配,适配同一应用不同版本/变体共存的情况。
输入法窗口过滤:组件名包含非 ASCII 字符(如输入法窗口 l.ۡ᩸᩷)或格式异常(少于 2 个点号)时直接跳过,避免输入法弹出被误判为离开目标应用。
- 全局
active标志保证操作幂等:已禁用时不会重复pm disable,已恢复时不会重复pm enable - 每次禁用/恢复后回读应用状态(
pm list packages -d/-e),命令失败或状态未变输出警告日志 - Guard 退出时
cleanup()恢复所有被修改过的应用,确保任何退出方式都不留半禁用状态
当目标应用处于小窗(Picture-in-Picture)模式时,用户切到其他应用不应触发恢复——目标仍在小窗中运行,禁用列表应保持生效。
Guard 通过双重来源检测小窗状态:
dumpsys window中包含pip关键字dumpsys activity activities中包含pip或picture.in.picture关键字
任一来源命中即判定为小窗激活。检测时机分两种:
- 事件触发时:非目标应用进入前台,若检测到小窗激活,保持禁用状态不恢复
- 轮询超时时:poll 2 秒超时后检查,若之前在小窗中、现已退出小窗,立即触发恢复
配置文件不存在时,Guard 仅创建带注释的空配置(不含预设应用),目标和禁用列表由 UI 勾选后写入。空配置下 Guard 正常启动并监听事件,前台事件输出「等待配置」提示,用户勾选应用后下次事件即触发热重载生效——无需重启服务。
| 维度 | 指标 |
|---|---|
| 空闲 CPU | 0%(poll 内核阻塞) |
| 空闲内存 | ~几 MB(native 进程) |
| 空闲耗电 | 零(不唤醒 CPU) |
| 响应延迟 | < 1 秒(事件即时触发) |
| 配置更新 | 即改即生效(stat 热重载) |
| 后台痕迹 | 无 Service、无通知、无定时器 |
| 幂等保证 | active 标志 + 状态回读校验 |
| 退出恢复 | cleanup() 恢复所有修改 |
| 小窗感知 | 双来源检测 + 轮询补检 |
| 输入法过滤 | 非 ASCII + 格式校验双重过滤 |
Guard 的隐藏能力集中在脚本管理模块。脚本管理提供轻量终端,以 root 权限执行 SH 脚本和 ELF 二进制,执行完毕后通过两段式进程清理彻底抹除执行痕迹。
脚本管理的核心设计目标:执行完毕后,系统中不留任何可被检测的进程链。
具体表现为:
- 无残留进程链:脚本执行时产生的 su → sh → 脚本/二进制 → 子进程链条,在清理后被完全杀灭。任何进程扫描工具(
ps、top、htop)都无法发现脚本执行过的痕迹 - 包装层剥离:当用户执行 ELF 二进制时,可选择「保留用户程序」模式——清理时仅杀灭包装层(su、sh、终端进程),用户程序以孤儿进程继续独立运行,与 Guard 无进程树关联
- 逃逸进程追杀:通过
setsid、nohup、管道|、daemon等方式脱离原进程组的进程,cgroup 扫描会将其找出并杀灭 - 日志自管理:所有操作日志存储在应用内,不写入系统 logcat 持久缓冲区,可随时一键清除
- pm 方式优势:相比
am force-stop,pm disable使应用处于disabled状态,应用自身无法感知被冻结(不会触发 ANR 检测),解冻后正常启动无副作用
- 通过 root 浏览系统各目录(含
/data等受限路径),自动识别 ELF 二进制并直接执行,SH 脚本按脚本运行 - 注入完整环境变量(
PATH、HOME、LANG、TERM) - 交互式运行窗口实时回显输出,底部输入框可随时向进程发送数字或内容,满足二进制程序的选择/输入需求
- 输出自动过滤 ANSI 颜色与转义码,避免乱码
- 轻量终端支持常驻 root shell、逐行输入与直接运行脚本
脚本或二进制执行完毕后,Guard 执行两段式进程清理,确保无残留:
根据执行模式选择最快路径:
- SH 脚本(不保留):
kill -9 -PGID一次内核调用杀灭整个进程组(含子进程、孙进程),这是最快的进程清理方式,仅一次 syscall - ELF 二进制(保留用户程序):
pgrep -P精确查找 su/sh 的直接子进程列表,跳过需要保留的二进制 PID,逐个kill -9
Phase 1 覆盖正常进程树中的绝大多数进程。
针对通过 setsid、nohup、管道 |、daemon 等方式脱离原进程组的逃逸进程:
- 读取
/proc/self/cgroup获取当前 cgroup 路径和 UID 段(/uid_XXXXX) - 遍历
/proc/[0-9]*,读取每个进程的cgroup文件 - 匹配条件:cgroup 路径精确匹配或包含相同 UID 段
- 直接
kill -9(SIGKILL),跳过 app 自身、preservePid、已知 PID、$$、$PPID
| 优化点 | 传统方案 | Guard |
|---|---|---|
| 信号策略 | SIGTERM → sleep 1s → SIGKILL | 直接 SIGKILL |
| 扫描轮次 | 两轮(TERM + KILL) | 一轮 |
| sleep | 有(1 秒) | 无 |
| cgroup 解析 | awk + sort + head + cut 管道 |
grep 单命令 |
| 总耗时 | 5-10 秒 | ~0.2 秒 |
- 用户点击「清理」按钮:立即输出日志,后台执行两段式清理,关闭弹窗
- 脚本自然结束:执行线程
finally块自动触发清理 - 进程异常退出:
finally块兜底清理,确保任何情况下都不残留
Guard 自身(com.example.guard)可被加入禁用列表。由于守护进程是独立运行的 ELF 二进制(进程名 Guard),pm disable 仅禁用 Guard 的 UI(图标隐藏、界面不可打开),后台守护进程不受影响。用户进入目标应用时,Guard UI 随其他禁用列表应用一同被冻结;离开目标应用后自动恢复。
- 持久化存储:所有运行日志写入应用私有目录
guard.log,下次启动自动加载,查看禁用期间发生的事件 - 大小限制:日志文件上限 64K,超出时自动截断保留后半部分(按行对齐),避免无限增长
- 实时尾随:服务运行时启动文件尾随线程,实时读取二进制写入日志文件的新内容并显示
- 会话分隔:每次启动输出会话分隔标记,便于区分不同会话的日志
- 静默启动:启动时不重复输出 root 授权和加载应用数量的提示,避免日志冗余
- 包名显示:勾选应用时日志输出应用名称和包名,浮窗提示也显示包名
从 Releases 页面获取最新 APK。
- Android 设备已 root(Magisk / KernelSU / 其他 root 方案均可)
- arm64-v8a 架构
- 需要授予 root 权限
- 纯 Java 单 Activity 实现,无原生框架依赖
- 原生组件:
guard.c(C 语言),通过logcat -b events监听 +poll()事件驱动 +pm disable/enable系统级禁用 - 编译产物:classes.dex + 资源 + guard 二进制(arm64-v8a)
- 签名:内置证书自签名
MIT