github IamCanincan/KeyLens v3.0.2
KeyLens v3.0.2

6 hours ago

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.sh 205 项逐字节对拍(含 preview 11 例、env 缓存 24 条)、cargo test 72 项、
    tests/*.sh 50 套(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 写路径的夹具真写)。

Don't miss a new KeyLens release

NewReleases is sending notifications on new releases.