Skip to content

Releases: CangWeiohh/KeywordSMSAlert

v1.4.2 稳定性专项修复

Choose a tag to compare

@CangWeiohh CangWeiohh released this 14 Oct 04:47

1.4.2 稳定性专项修复(14 项)

核心修复

  • ksactl restart 不再 kill SpringBoard(仅重启 imagent)
  • 删除假桩 KSAHookInstallerDaemon.m 与死代码/误导日志
  • KSAConfig.reloadIfNeeded 加 mtime+size 快速路径
  • _publishStatusWithConfig: 节流 30s
  • sqlite3_close_v2 + @finally finalize + _noteError 去重
  • _baselineEstablished 修复空库后首条短信漏报
  • _readWatchTimer 在 stop() 中正确取消 + 移除通知观察者
  • 状态通知改为在当前队列同步 post(不再依赖主 run loop)
  • postinst 改用 launchctl kickstart → SIGTERM → SIGKILL 渐进重启
  • control Description 更正
  • SpringBoard 过滤 plist 移入 Sources/Diagnostics/
  • 新增 tests/verify_release_artifact.sh(49 项不变量断言)
  • 新增 tools/ksa-diagnose.sh(只读诊断脚本)
  • %ctor 全包 @try/@catch + safemode 早退 + 低频资源采样

1.4.1 - 已读停止改为提醒期间每 0.5 秒检查(不再受 5 秒轮询限制)

Choose a tag to compare

@CangWeiohh CangWeiohh released this 10 Oct 03:38

1.4.1:把"已读停止"从 5 秒轮询里解耦 —— 提醒期间每 0.5 秒检查一次

你问的问题:对,会有延迟

is_read 的检查原先搭在检测轮询的同一拍上,所以:

  • 你的 PollInterval = 5 秒 → 停止延迟 0~5 秒(平均约 2.5 秒);
  • 另外数据库若被占用,sqlite3_busy_timeout(500) 最多再加 0.5 秒;
  • 极端情况下(轮询间隔恰好被重新排定)还会多跳一拍。

修法:同一条通道,只改节奏(不引入任何新子系统)

提醒播放期间另起一个 0.5 秒的定时器,只做那一句主键查询:

SELECT is_read FROM message WHERE ROWID = ?
  • 仅在提醒进行中运行(由提醒引擎的状态变化通知触发启动/停止),空闲时零开销、零唤醒;
  • 检测轮询本身仍是 PollInterval(你的 5 秒)—— 因为它只管"有没有新短信",与停止无关;
  • 仍然是只读同一个数据库连接:无 HID、无 Darwin 观察者、无 Hook、无额外进程。

新的停止延迟:约 0~0.5 秒(平均 0.25 秒),数据库忙时再加最多 0.5 秒。

核验(发布产物逐项检查)

启动安全不变量(必须不存在,均确认 False):IOHIDEventSystemClientCreate、notify_register_dispatch、com.apple.springboard.lockstate、com.apple.iokit.hid.displayStatus、SBLockScreenManager、MSHookMessageEx。

功能(必须存在,均确认 True):SELECT is_read FROM message WHERE ROWID、fast read check started、com.keyword.smsalert.alertStateChanged、SELECT MAX(ROWID) FROM message。

链接库与 1.3.1/1.4.0 完全一致(Foundation / CoreFoundation / AudioToolbox / sqlite3 / libroothide,无 AVFoundation);host 单测 48 passed / 0 failed。

备注

如果你的 PollInterval 设得更大(例如 10 秒),停止延迟不受影响(仍然是 0.5 秒级);它只影响"新短信被发现的延迟"。

1.4.0 - 打开短信即停止提醒(复用只读轮询,零新增子系统)

Choose a tag to compare

@CangWeiohh CangWeiohh released this 10 Oct 02:45

1.4.0:把"打开短信(标记已读)"作为停止方式 —— 复用已有的只读轮询,不引入任何新子系统

思路

1.3.1 已经证明"只轮询 + 提醒引擎"能稳定启动。既然轮询 sms.db 是本插件唯一被长期验证的通道,那就继续只用它来实现停止:

iOS 在你打开那条短信(或对它的通知执行打开动作)时,会把 message.is_read 置为 1。我们本来就在轮询这个库,于是多查一个主键字段就能知道"用户已经看到了"。

实现(KSASMSWatcher):

  • 记录最近触发过提醒的 ROWID(最多 8 条);
  • 仅在提醒进行中,每次轮询对这几条做一次 SELECT is_read FROM message WHERE ROWID = ?(主键查找,极便宜);
  • 一旦为 1 → 立即 stopAlertWithReason:@"SMS marked as read"。

这条路径不需要:HID 客户端、Darwin 观察者(notify_register_dispatch)、SpringBoard 注入、任何额外进程 —— 因此启动安全性等同于已验证的 1.3.1。

使用方式

  • 收到提醒 → 在通知上点开(或直接打开「信息」里那条短信)→ 提醒立即停止;
  • 不打开的话,仍然到设定时长自动结束(SoundDuration / VibrationDuration,可自行调短)。

核验(发布产物逐项检查)

必须不存在(启动安全不变量,均确认 False):

IOHIDEventSystemClientCreate / notify_register_dispatch /
com.apple.springboard.lockstate / com.apple.iokit.hid.displayStatus /
SBLockScreenManager / MSHookMessageEx

必须存在(功能,均确认 True):

SELECT is_read FROM message WHERE ROWID   (新的停止检查)
SELECT MAX(ROWID) FROM message            (原有轮询)
AudioServicesPlaySystemSound              (震动 + 铃声通道)

链接库与 1.3.1 完全一致:Foundation / CoreFoundation / AudioToolbox / sqlite3 / libroothide(无 AVFoundation)。host 单测 48 passed / 0 failed。

如果这样还不够"随手静音"

还有一个单变量的候选方案:把解锁/锁定状态再作为停止条件(1.2.7 的行为,当时能正常启动,但 1.3.0 那个版本恰好也带着它并且黑屏过,所以我不敢默认开启)。若你需要"不打开短信也能静音",我可以把它做成设置里的开关、默认关闭,你先当实验开启验证一次启动是否正常 —— 一次只加一个变量,不再一次塞三样。

1.3.1 - 最小稳定版(仅保留已实机验证内容)

Choose a tag to compare

@CangWeiohh CangWeiohh released this 10 Oct 02:25

1.3.1 稳定版:只保留"已实机验证过"的内容

先说一个反常事实

1.2.8 能正常启动,而 1.3.0 持续黑屏 —— 但 1.2.8 的代码比 1.3.0 多。

我对比了两个版本的发布产物:

版本 detector 里的 IOHID 代码 启动
1.2.8 有 IOHIDEventSystemClientCreate 正常(你读到了设置里的状态行)
1.3.0 没有(已全部删除) 持续黑屏

三个版本的包内容完全一致(detector dylib + filter plist + 设置面板 + ksactl;无 LaunchDaemon、无 SpringBoard 组件)。也就是说 1.3.0 是 1.2.8 的真子集,反而黑屏了。

这说明黑屏很可能不是(至少不只是)本插件代码造成的,而是设备状态相关。最可疑的是电话助手:它的 root LaunchDaemon 里写着硬编码的 jbroot 路径(.jbroot-2ED827453A76B860)。每次"强制重启 + 重新越狱"都会生成新的随机 jbroot,于是它的守护进程再也起不来,而它的 SpringBoard 组件还在跑 —— 这正好会表现为"系统活着、屏幕不亮"。这也解释了最早的现象:只装电话助手时它自己就有约 1 秒黑屏。

1.3.1:最小稳定版(安不安全,一测便知)

已移除:IOHID 观察器、显示/锁定状态观察器(notify_register_dispatch)、所有 Hook、SpringBoard 相关代码、独立进程/守护进程。
产物核验(逐项确认不存在):

IOHIDEventSystemClientCreate      -> False
com.apple.springboard.lockstate   -> False
com.apple.iokit.hid.displayStatus -> False
notify_register_dispatch          -> False
SBLockScreenManager               -> False
MSHookMessageEx                   -> False

保留(都已实机验证过):

  • 只读轮询 sms.db + 关键词匹配(1.1.x 起长期验证);
  • 提醒引擎:震动 + 铃声/提醒通道声音(1.2.0 验证可正常响);
  • 设置面板、ksactl、boot-quiet(启动后 10 秒才开始工作)。

功能变化(明确告知):提醒不再响应电源键,而是到设定时长自动结束(SoundDuration / VibrationDuration,默认 10 秒声音、5 秒震动)。这是唯一一个"只保留已验证内容"后仍能让功能成立的做法。

建议的验证顺序(先确认责任方)

  1. 强制重启:音量 + → 音量 − → 长按侧边键,直到出现 Apple 标志;
  2. 在 Sileo 里卸载 KeywordSMSAlert → 重启用户空间:
    • 仍然持续黑屏 → 与本插件无关(几乎可以确定是电话助手的 jbroot 路径失效/设备状态)→ 若如此,在 Sileo 里把电话助手也重装一次(让它按当前 jbroot 重写守护进程路径)再重启;
    • 恢复正常 → 责任在本插件,那就装 1.3.1(已是最小集合)。
  3. 装 1.3.1 后再重启一次,确认启动正常。

核验

  • host 单测 48 passed / 0 failed
  • 包内容:detector dylib + filter plist + 设置面板 bundle + ksactl(无其它二进制、无 LaunchDaemon)
  • detector 依赖:Foundation / CoreFoundation / AudioToolbox / sqlite3 / libroothide(无 AVFoundation)

1.3.0 - 彻底移除 IOHID 监听(未生效且引发启动黑屏)

Choose a tag to compare

@CangWeiohh CangWeiohh released this 10 Oct 02:11

1.3.0:彻底移除 IOHID 监听(它从未生效,并且 1.2.9 的探测把启动黑屏带回来了)

先说结论

1.2.9 的 IOHID 相关新增是这次黑屏的嫌疑源头,因此 1.3.0 把整个 IOHID 观察器从发布构建里彻底删除。

理由很直接:它从来没有真正生效过 —— 1.2.8 第一次给它加计数后,实测是"零事件"。一个从未生效的功能,不值得拿设备启动去冒险。

1.2.9 相对 1.2.8 只多了这些(都在 imagent 里、启动后 10 秒执行):

  • IOHIDEventSystemClientCopyServices 探测"能看见多少 HID 服务";
  • 同时创建 3 个 客户端(主 run loop / dispatch queue / 独立线程 run loop);
  • 新增一个独立线程跑 CFRunLoopRun()。

而 1.2.7(1 个客户端 + 主 run loop)与 1.2.8(1 个客户端 + dispatch queue)都能正常启动。所以增量里最可疑的是对 HID 服务做查询与多客户端/新线程:HID 事件系统同时驱动着显示唤醒链路,与它"较劲"在这个环境下显然不安全。

1.3.0 的内容

  • 发布构建里不再包含任何 IOHID 代码(产物已核验:IOHIDEventSystemClientCreate / CopyServices / IOHIDEventGetType 等符号全部不存在);
  • 提醒的停止方式回到只依据锁定状态(1.2.7 已验证可正常启动的形态):
    • 解锁(密码 / Face ID)→ 停止;
    • 锁定(用手机时按电源键,或自动锁定)→ 停止;
    • 通知点亮屏幕、点击屏幕、抬手唤醒、自动熄屏 —— 都不会停止提醒;
  • 保留 boot-quiet(启动窗口内零活动)、铃声通道提醒、关键词检测、设置面板。

请这样恢复设备

  1. 强制重启:按一下「音量 +」→ 按一下「音量 −」→ 长按侧边电源键,直到出现 Apple 标志;
  2. 重启后系统是未越狱状态,Sileo 可正常使用;
  3. 在 Sileo 里安装本版本(或先卸载 KeywordSMSAlert 再决定);
  4. 需要时再用 Dopamine 越狱。

抱歉又让你经历一次黑屏 —— 这次我不再在 HID 这条路上继续试探了:证据是它从未生效,继续迭代只会重复拿启动安全冒险。

核验

  • host 单测 48 passed / 0 failed
  • 发布 detector:零 IOHID 符号、零 Hook、未链接 AVFoundation、无 SpringBoard 组件与 LaunchDaemon
  • 包内仍只有 detector dylib + 设置面板 + ksactl

1.2.9 - 更正上一轮结论:HID 客户端能建但事件从未到达;一次安装问清原因

Choose a tag to compare

@CangWeiohh CangWeiohh released this 10 Oct 01:59

1.2.9:更正上一轮的结论,并把 HID 的"为什么收不到"一次性问清楚

先更正我上一轮的错误

我说"1.2.7 显示 已启动 · 电源键监听(HID) 就证明事件到达了"——这是错的。查 git 历史确认:

  • 1.2.7 的那行逻辑只有 if (HIDAvailable) → "电源键监听(HID)",而 HIDAvailable 仅仅表示 "客户端创建成功";
  • "收不到事件"这个区分是 1.2.8 才加进去的。

所以你 1.2.7 看到的文案只证明客户端能创建,不证明有事件。而 1.2.8 第一次真正测量了它,结果是你看到的:零事件。

也就是说:客户端能在 imagent 里创建成功,但事件从来没到过。真正的原因只可能是"沙箱/权限不允许事件投递",或者"投递方式不对"。

1.2.9:一次安装把这两个问题都问清楚

  1. 能看见多少 HID 服务:调用 IOHIDEventSystemClientCopyServices 数一下 imagent 到底能看到几个 HID 服务。如果是 0,说明沙箱直接封死了 HID 事件系统,任何投递方式都不可能成功——那样我就不会再让你折腾这条路了。
  2. 三种投递方式依次自动尝试(每次一个独立客户端,互不干扰):
    • A main-runloop:挂 imagent 主 run loop(1.2.7 用的方式,但当时没测过是否真有事件);
    • B dispatch-queue:IOHIDEventSystemClientSetDispatchQueue(1.2.8 用的,实测 0 事件);
    • C thread-runloop:独立线程 + 自己的 run loop。
      每 8 秒如果还没有任何事件,就启动下一种;哪种先收到事件就会记录下来,并显示在设置里。
  3. 匹配仍然放宽(Consumer 0x0C/0x30、AppleVendor 0xFF01/0x0B、Keyboard 0x07/0x66),键盘事件仍会把真实的 page/usage/down 记录下来。

设置里现在会显示什么

  • 有事件:已启动 · 电源键监听(HID)· 事件 N/键盘 M · 末次 0x<page>/0x<usage>↓<策略>
  • 没事件:已启动 · HID 已注册但收不到事件(可见 HID 服务 N · <当前策略>)

请这样测(一次就够)

  1. 安装 1.2.9,重启一次用户空间(旧的 boot-quiet 延迟 10 秒生效,属正常);
  2. 进「诊断 → 提醒服务状态」,按一下音量键,看计数与"可见 HID 服务"的数字;
  3. 整行发我(含括号内内容)。

结果怎么解读

看到的现象 结论与下一步
可见 HID 服务 0 沙箱封死,HID 这条路走不通 → 保留"锁定/解锁"兜底,不再折腾
服务 > 0 但仍无事件,且三种策略都试过 事件投递被拦(可能只差一个 entitlement,而它无法追加)→ 同样放弃 HID
有事件、键盘 M 增长、末次 显示某个 page/usage 有救:把 page/usage 发我,我加进匹配表即可
有事件且 末次 是 0xC/0x30 或 0xFF01/0xB 那说明匹配本身没问题,问题在别处,我继续查

核验

  • host 单测 48 passed / 0 failed(matcher 覆盖 3 种候选 usage)
  • detector 产物确认含服务探测、三条投递策略、零 Hook、未链接 AVFoundation、无 SpringBoard 组件与 LaunchDaemon
  • 两个 Localizable.strings 均通过 plutil -lint

1.2.8 - HID 收得到事件,真正的问题是 usage 匹配(已放宽)+ 设置里显示计数

Choose a tag to compare

@CangWeiohh CangWeiohh released this 10 Oct 01:41

1.2.8:HID 事件其实收得到 —— 真正的问题是"匹配不到电源键"

你给的那行状态是决定性证据

你看到的是 已启动 · 电源键监听(HID)。这行文案只在 HID 事件真的到达时才会显示(收不到事件时显示的是「HID 已注册但收不到事件」)。所以:

imagent 完全有权接收 HID 事件,客户端也真的在被喂数据 —— 我之前担心的 entitlement / 沙箱拦截并不存在。

那按电源键为什么没反应?只剩一个解释:这台设备的电源键事件不是我匹配的那个 usage。1.2.7 只接受 Consumer 页 0x0C / usage 0x30,而 iPhone 的锁屏/电源键在很多机型上是以 AppleVendor 页 0xFF01 / usage 0x0B(Screensave) 上报的。

1.2.8 的修改

  1. 放宽匹配:现在接受三种候选 usage,其余按键(音量、Home、媒体键等)仍然一律不匹配,不会误停:
    • Consumer 0x0C / 0x30(Power)
    • AppleVendor 0xFF01 / 0x0B(Screensave,iPhone 锁屏键常见上报方式)
    • Keyboard 0x07 / 0x66(Power)
  2. 事件投递更可靠:1.2.7 把客户端挂到 imagent 的主 run loop 上,而没人保证主线程在跑默认模式。现在优先使用 IOHIDEventSystemClientSetDispatchQueue(完全不依赖 run loop),没有该符号时改用独立线程 + 自己的 run loop。
  3. 把原始计数显示在设置里(这样下一次测试就能直接读出答案,无需终端):
    已启动 · 电源键监听(HID)· 事件 N/键盘 M · 末次 0x<page>/0x<usage>↓
    • 事件 一直涨 → 客户端在被喂数据;
    • 键盘 M 在你按电源键/音量键后 +1;
    • 末次 0x…/0x… 就是这颗设备真实的 page/usage —— 如果电源键不是上面三种之一,把这两个值发我,我直接加进匹配表即可。

顺带修掉一个真 bug

英文 Localizable.strings 里少了一个分号("RuntimeHIDLast" = "…%@;"),导致设置面板 bundle 编译失败、一直用旧二进制——这也是为什么 1.2.7 里看不到计数。现已修复并加了 plutil -lint 验证。

请这样测

  1. 安装 1.2.8;
  2. 进「诊断 → 提醒服务状态」,先按一下音量键(故意产生一个键盘事件)→ 看 事件/键盘 是否增长、末次 0x…/0x… 出现什么值;
  3. 触发一次提醒(「立即测试提醒」),提醒中按电源键 → 应从 1.2.8 起能停止;
  4. 若仍不能停止,把状态行整行(含 末次 与括号内容)发我。

核验

  • host 单测 48 passed / 0 failed(新增 4 项 usage 匹配用例:AppleVendor/Screensave 接受、Keyboard/Power 接受、Screensave 抬起忽略、Consumer/Menu 不误判)
  • detector:投递改为 dispatch-queue/独立 run loop、键盘事件带 page/usage/down 日志、零 Hook、未链接 AVFoundation、无 SpringBoard 组件与 LaunchDaemon
  • 两个 .strings 均通过 plutil -lint

1.2.7 - 不再用屏幕状态停止提醒;真正的电源键监听 + 锁定/解锁兜底

Choose a tag to compare

@CangWeiohh CangWeiohh released this 10 Oct 01:10

1.2.7:屏幕状态不再停止提醒;改为「真正的电源键监听」+「锁定/解锁」兜底

你这次的现象说明了根本问题

自动熄屏后提醒正常(1.2.6 有效)→ 点击屏幕亮屏 → 提醒立刻停止

因为"点击屏幕亮屏"和"按电源键亮屏"在显示状态层面完全一样(都是 熄屏 → 亮屏)。我上一版把"亮屏"当成按了电源键,所以点一下屏幕就把提醒停了。

结论:用显示状态推断电源键,原理上就做不到。 显示状态的每一次变化都有好几个来源:

显示变化 可能的来源
熄屏 → 亮屏 按电源键、点击屏幕、抬手唤醒、短信通知点亮锁屏
亮屏 → 熄屏 按电源键、自动熄屏、通知唤醒超时、自动锁定

前两版我每修掉一个误判源,下一次实测就冒出下一个(先被通知唤醒停、再被通知超时停、再被点击屏幕停)。所以这次彻底不再用显示状态停止提醒。

1.2.7 的两条停止途径

  1. 真正的电源键监听(首选):直接在 imagent 内用 IOKit 的 IOHIDEventSystemClient 只观察物理电源键事件(Consumer 页 / Power 用法 / 按下沿)。事件从不派发、从不消费、从不改写,不影响锁屏/唤醒/SOS。

    • 这是唯一能精确区分"电源键"与"点屏幕"的信号;
    • 但 imagent 没有 HID entitlement,是否放行只能靠实测——所以做了兜底并把它显示在设置里。
  2. 锁定 / 解锁(兜底,一定有效):通知、点击屏幕、抬手唤醒都不会改变锁定状态,所以这两个动作可以安全地作为停止条件:

    • 锁定(用手机时按电源键,或自动锁定)→ 停止;
    • 解锁(输密码 / Face ID)→ 停止(你已经看到提醒了)。

屏幕状态现在只用于诊断

它仍会被记录(设置面板可查),但永不停止提醒。

请在设置里看这一行判断 HID 是否生效

设置 → KeywordSMSAlert → 诊断 →「提醒服务状态」:

  • 显示 已启动 · 电源键监听(HID) → 精确按键监听生效:按电源键立即停止,点屏幕/抬手/通知都不会误停;
  • 显示 已启动 · 停止方式:锁定/解锁手机 → HID 未放行,退化为"锁定/解锁停止"(按电源键点亮屏幕不会停;解锁手机或按电源键锁屏会停)。

括号里还会带上 HIDStatus,把失败原因(例如缺少 entitlement / IOHID 符号)直接写出来。

请这样测

  1. 安装 1.2.7(旧包是 boot-quiet,imagent 启动后约 10 秒才开始工作,属正常);
  2. 发短信 → 等提醒响 → 点一下屏幕亮屏 → 提醒应继续;
  3. 等到自动熄屏 → 提醒仍应继续;
  4. 然后看「提醒服务状态」显示的是哪一种,再按提示的停止方式验证一次。

核验

  • host 单测 44 passed / 0 failed
  • detector:HID 观察代码已编入、显示状态停止规则已确认移除、锁定/解锁停止路径存在;零 Hook、未链接 AVFoundation、包内无 SpringBoard 组件与 LaunchDaemon

1.2.6 - 自动熄屏不再中断提醒

Choose a tag to compare

@CangWeiohh CangWeiohh released this 10 Oct 00:58

1.2.6:手机自动熄屏后提醒不再中断

现象与根因

收到短信 → 通知点亮锁屏 → 震动 + 响铃正常 → 几秒后手机自动熄屏 → 震动和声音一起停了。

根因还是"按电源键停止"的实现方式:它用屏幕状态推断按键,而我把 亮屏 → 熄屏 无条件当成"按了电源键"。系统自己也会产生这个切换——通知点亮锁屏后,几秒内会自己再熄掉——于是提醒刚响几秒就被自己停掉了(震动和声音同时消失正是 stopAlert 的行为)。

1.2.6 的判定规则(逐条明确)

事件 处理
熄屏 → 亮屏,且在提醒开始 4 秒内 判为短信通知唤醒 → 忽略,并记住这次是通知唤醒的
熄屏 → 亮屏,在 4 秒之后 停止(睡着的手机上按电源键唤醒)
锁屏状态变为已锁定 停止(真正的锁定动作;通知唤醒不改变锁定状态,不会误判)
亮屏 → 熄屏,且刚才是那条"通知唤醒"息的 忽略(通知自己的超时)→ 提醒继续
亮屏 → 熄屏,且设备当前锁定 忽略(通知超时/自动熄屏)→ 提醒继续
亮屏 → 熄屏,且设备未锁定(正在用手机) 停止(按电源键锁屏)

净效果:通知点亮屏幕、以及它几秒后的自动熄屏,都不再打断提醒。

怎么停止提醒

  • 屏幕是黑的:按一下电源键(点亮)→ 停止;
  • 你正在用手机(未锁定):按一下电源键(锁屏)→ 停止;
  • 屏幕上正显示锁屏界面:第一下会被判为"通知超时"忽略,再按一下即停止。

另外:抬手唤醒(拿起手机)也会被识别为用户操作而停止提醒 —— 系统层面它和"按电源键唤醒"完全无法区分。若不希望拿起手机就停,可以去掉"熄屏→亮屏"这一条,只保留锁定与未锁定熄屏两种判定。

核验

  • host 单测 44 passed / 0 failed
  • detector:三条新判定路径的日志字符串已在产物中确认;零 Hook、未链接 AVFoundation、包内无 SpringBoard 组件与 LaunchDaemon

1.2.5 - 启动窗口内不做任何事(boot-quiet)

Choose a tag to compare

@CangWeiohh CangWeiohh released this 09 Oct 04:36

1.2.5:启动窗口内本插件不做任何事(boot-quiet)

先回答"为什么重启用户空间后先亮屏、再黑 7 秒"

这个包里没有任何能控制屏幕的代码。 1.2.0 起提醒引擎和检测都在 imagent 内,包内既没有 SpringBoard 组件、也没有背光/锁屏相关调用;对屏幕它只做两件事:只读两个 Darwin 通知(com.apple.iokit.hid.displayStatus / com.apple.springboard.lockstate),用来在提醒时判断"是否按了电源键"。观察状态不会改变状态,所以它不可能把屏幕关掉。

那么"黑 7 秒"只可能来自两处:

  1. 用户空间重启本身的时序:Dopamine 重启用户空间时,backboardd/SpringBoard 要整体重启,屏幕本来就会熄一下再亮;这段时间长短取决于系统负载与已装插件数量(电话助手本身也会造成约 1 秒黑屏)。
  2. imagent 被拖慢:注入到 imagent 的 dylib 会在启动窗口内被 dyld 加载并开始运行。与"你确认正常"的 Diagnostic B 相比,1.2.x 多了提醒引擎、AudioToolbox 依赖和两个 Darwin 通知注册——如果 imagent 因此晚就绪,依赖它的启动流程也会晚,屏幕就晚亮。

1.2.5 的做法

把运行期工作整体延后:%ctor 只做进程判定和一行日志,10 秒后才真正启动轮询、提醒引擎和状态监听。也就是说在"重启用户空间"这段最敏感的窗口里,本插件除了被 dyld 加载之外什么都不做:不打开 sms.db、不注册通知、不初始化引擎。

代价:imagent 启动后最初约 10 秒内到达的短信不会被提醒(基线在此之前建立)。这个数字可以按你的需要调整。

请帮我做一次判定(只有你能测)

  1. 先装 1.2.5,重启用户空间,掐表记录黑屏秒数,连做 2 次;
  2. 再卸载 KeywordSMSAlert(Sileo 卸载即可),重启用户空间,同样掐表 2 次;
  3. 把 4 个数字告诉我。

如果卸载后仍然是 7 秒左右 → 与我们无关,是电话助手/Dopamine 的重启时序;如果卸载后回到 1 秒、装 1.2.5 是 7 秒 → 说明确实是我们拖慢了 imagent,我再把 AudioToolbox 改成首次提醒时才动态加载(让本插件的依赖集合与已验证正常的版本完全一致)。

核验

  • host 单测 44 passed / 0 failed
  • detector:boot-quiet 标记存在、零 Hook、未链接 AVFoundation、屏幕状态监听仍在