3.0.2 - 2026-11-01
这一版才是「命令层只剩二进制」的发布包。已发布的 v3.0.1 资产仍是纯 shell/awk 那份(396 KB、
19 份backends/*.awk、没有bin/keylens),与本文档当时写的阶段 5 内容并不一致 —— 阶段 5 的
成果是从这一版开始真正进包的。
变更
- 命令层的实现只剩
bin/keylens,awk 参照实现不再随模块发布(阶段 5 收尾):19 份backends/*.awk
移进仓库的tests/fixtures/backends/(继续当 shell↔Rust 对拍的参照),发布包里的module/backends/
只剩被 source 的tricky.sh。配套三条:安装脚本发现包里没带bin/keylens就中止安装
(没有二进制就没有命令层,留着半残模块只会变成"我明明装了却不生效");带了就点名chmod 0755
(KernelSU 解包会把执行位抹成 644);三个真机台子都加了"参照 awk 必须推进 A/B 树"的前置——
缺了它 shell 那半边退化成「缺少 X.awk」,报出来的差异看着像实现写坏了。
sh tools/deploy-webui.sh --shell会把参照一起推上去。对拍网不变:rust/parity.sh仍 175 项全绿。
打包也跟着变:node tools/build-zip.mjs现在要rust/target/aarch64-unknown-linux-musl/release/keylens,
没有就拒绝出包(--check同样报缺bin/keylens)。选 musl 而不是aarch64-linux-android是实测定的:
Android target 默认动态链 bionic,CI 那道NEEDED=0的静态判据对它永远不成立;musl 自带 crt-static,
本机不用 NDK 就能交叉编译,而真机验证跑的正是这份产物。
修复
-
带命令层二进制的发布包里,「写入前预览」会真的写入(发布前必须修,别从这版之前的提交出包):
webui.sh的接管点排在「剥preview-前缀」之后,二进制收到的是一条不带前缀的写命令。
真机复现:点preview-omk-save-config之后config.toml的security_patch从auto变成2026-10-01,
界面拿到的是saved=1、连差异都没有。现在接管段排在剥前缀之前并认识preview-*:
成功就整份采用二进制打印的信封(preview=1/changed=N/diff=,不会打两份),
退码非 0(老命令层不认识preview-*,或它自己失败)回落 shell 原地预览。
本机已安装的模块bin/是空的,所以这台机没被波及。
闸:tests/rust-dispatch.sh的 preview 接管/回落 6 条,tests/device-preview.sh的三条腿逐字节 +
信封恰好一份 + 三条腿都不许落盘。 -
命令层二进制的预览落脚点:影子根以前只认
temp_dir(),而 Android 上没有/tmp。
TMPDIR 一坏,影子根建不出来 → 子进程什么都读不到 → 父进程照样报changed=0→ 界面把"没有差异"
当成无需确认直接写。现在按TMPDIR → /data/local/tmp → temp_dir依次找落脚点,
全都建不出来就退 2 交给 shell 算预览(宁可退化,不可谎报"没变化")。 -
写前预览不再谎报生效代价:OhMyKeymint 的
[trust]保存整块代价判定(apply_scope)原本连同"重启请求 + 日志条目"
一起留在KL_PREVIEW != 1的闸门后,于是预览把一次「四个补丁字段整组热生效」(同一条命令真写时报hot)
报成apply_scope=restart,界面据此提示"要重启 keymint"。代价是判据不是副作用,现在预览照常算;
留在闸门后的只剩重启请求与日志("还没真写,不能留假条目"这条要求不变)。
命令层的影子根预览同步对齐:预览时不再写restart.keymint、也不打apply_note=已请求重启 keymint(那句在预览里是假话)。
闸:rust/parity.sh新增 preview-* 11 例(逐字节 / 夹具不落盘 /changed>0正对照,含 hot·restart·reboot 三条臂),
真机侧新增tests/device-preview.sh。 -
module.prop的权限会跟着调用者的 umask 漂:命令层改描述走"写 tmp + 改名",
而改名会把 tmp 的权限带过去。shell 那份一直有显式补 chmod,移植到二进制时漏了 ——
真机上实测漂成过-rw-rw-rw-(整棵模块目录里唯一世界可写的文件)。现在两边同口径:
改名前记下原 mode、改名后补回;tmp 名也带上 pid(原来是固定名,两个进程会踩同一个临时文件)。 -
发布链自洽:
build-zip必须打进bin/keylens否则拒绝出包;安装脚本缺二进制就中止;
CI 的产物换成aarch64-unknown-linux-musl(NEEDED=0的静态判据对它才成立)。 -
versionCode用 12 位日期码会被 KernelSU 整个丢掉:ksud module list里 keylens 那条
没有versionCode字段(同目录 nomount 有"versionCode": "605")—— 12 位码超出它解析用的
32 位范围。管理器的更新检查按Int比较(v3.3.1 的Module.versionCode: Int+obj.optInt),
且只在ModuleUpdateSignature(updateJson, versionCode, …)变化时才重取。改成 10 位YYYYMMDDHH
(2026110123,到 2147 年都不越界)后 ksud 能正常读出;tests/module-desc.sh加了范围闸
和"12 位码必须被判红"的反面样本。
性能(同机交替、各取 5~6 次最快)
- 命令层壳的固定开销:把
webui.sh的二进制接管点从第 1273 行挪到第 54 行(在umask 077、
PATH 导出之后,在脚本自己的 70 KB 函数体之前 —— mksh 是边读边解析的),每条命令 6989ms → 2537ms。 - props 页:17 次单键
getprop改成一次全量 dump 查表,311366ms → 5991ms。 - env 页:同样吃快照,并把 shell 那套 19 键探针缓存落到二进制(两边共用
/data/adb/keylens.env.cache,
键 =v1|模块目录|module.prop 的 version|/system/build.prop 的 mtime,FRRO 强调色按 路径+mtime 判重编),
经界面入口 410 → 334 → 热 70~98ms。
测试
rust/parity.sh205 项逐字节对拍(含 preview 11 例、env 缓存 24 条)、cargo test72 项、
tests/*.sh50 套(WSL 与 Git Bash 各一遍)、11 条 node 静态闸,全部进 CI。- 真机台子:
device-ab(只读 17 + 写 21)、device-preview(16)、device-run-sync(14)、
device-env-cache(28)、device-six(83,teesim/tricky 六条的写与预览)、
device-omk-write(20,OMK 写路径的夹具真写)。