Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/test_overlay_gate.c
T
AskaEth 986e3814bc fix(launcher,lock): §310 M6.7 锁屏页自睡——空闲超时自动灭屏(294 遗留补)
背景:锁屏页(run_lock_screen)此前常亮不灭屏——用户停在锁屏页不操作,屏幕一直亮着耗电。
M6.6 上机后用户反馈,补 294 遗留项「锁屏页未做空闲超时自动息屏」。

实现(/usr/bin 只读,待 M6.7 固件打包,fw_content.conf 未动):
- run_lock_screen 循环加空闲跟踪(last_act)+ 自睡状态(asleep):
  ① 阈值 = overlay_lock_idle_off_sec(screen_off_timeout) = min(系统 screen_off_timeout, 30s)。
     系统未设/设 0(常亮)/超 30 → 锁屏页封顶 30s(锁屏页不应常亮耗电);系统设更短 → 尊重更短。
  ② 活跃态空闲超阈值 → meow_panel_off()(与息屏路径一致:panel 模式 FBIOBLANK 4 挂起 goodix /
     black 模式仅背光 0)+ asleep=1 —— 锁态保持,仍留在锁屏循环(=「锁屏页自己也会睡」)。
  ③ asleep 后电源键按下边沿 或 触摸活动 → meow_panel_on() + lk_draw 重画锁屏页(本循环唤醒链
     回锁),输入活动(任意按键 change / 触摸)都重置 last_act。
  ④ 音量键本页不动,仅计入输入活动重置计时;自睡期面板已关,不重绘(唤醒时统一重画)。
- EVIOCGRAB 与灭屏交互(核对结论):灭屏期 grab **保持**(tfd 不关)。panel 模式 FBIOBLANK 4
  挂起 goodix → 灭屏期无触摸事件,唤醒只走电源键;black 模式触摸仍活动且被抓取者(launcher)独占
  → 唤醒第一下触摸进锁屏(不穿透 App、不被吞——被锁屏作为活动消费,可能命中 PIN 键)。唤醒后
  FBIOBLANK 0 + grab 仍在,全程无穿透。
- App 前台叠加锁屏(run_app_lock_overlay)期间 App 的 /tmp/meow_notif_active 标记在整个自睡/
  唤醒周期保持(仅锁屏退出才 unlink),故媒体保活 App 自睡期仍被 present 抑制,不覆盖锁屏。

overlay_gate.h 增 overlay_lock_idle_off_sec / overlay_lock_should_sleep / overlay_lock_wake_now
(纯决策,可单测)。test_overlay_gate.c 扩展 S4(12 断言:阈值 tradeoff / 超时灭屏 / 唤醒回锁屏 /
屏幕关不重睡)。launcher 全量宿主回归 + SDK 叠加/状态栏/输入回归全 PASS;mipsel 交叉 -Wall -Wextra
0 warning。md5 42b6633afde37035585fc36b074a73c6(待 M6.7 固件打包,不部署)。
2026-09-02 13:35:56 +08:00

96 lines
6.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* test_overlay_gate.c — §310 三连 bug 的**纯决策**宿主单测(overlay_gate.h + lock_screen.h 谓词)。
*
* 覆盖三个症状对应的关闭/恢复决策,不依赖信号/设备/文件系统:
* S1 锁屏期触摸不穿透 → overlay_drain_required():App 被 SIGSTOP 过(非保活)必须在
* unlink /tmp/meow_notif_active 前先 SIGCONT + 排窗,让 SDK 输入线程
* 排空冻结期缓冲的 evdev 事件副本;OVERLAY_DRAIN_WINDOW_MS > SDK 200ms 节流。
* S2 解锁状态栏恢复 → overlay_force_sb_resync():解锁后必须强制 g_sb_mode_last=-1 重叠加,
* 不等 app 循环 1s 慢刷(消除「解锁回 App 无状态栏」残留)。
* S3 App 死亡触发主屏重绘 → lock_overlay_app_died_exit():锁屏叠加期 App 崩溃(僵尸)必须走
* 统一退出路径(交回 waitpid 循环回收 + run_menu 重绘主屏含状态栏)。
* S1b 未冻结 App 输入被排 → overlay_keepalive_needs_grab():媒体保活 App(保持运行、SDK 输入
* 线程实时读 evdev)在锁屏期必须靠 launcher EVIOCGRAB 独占触控设备 +
* 标记让 SDK 排空——而非冻结 App 的 SIGCONT 排空(drain_required=0)。
* S1c 手动息屏触发锁屏 → overlay_wake_should_lock():超时息屏(auto_slept)与电源键手动息屏
* (watchdog blank → manual_panel_off)两类唤醒都须走 lock 语义。
* S2b 锁屏重铺盖 App 残帧 → OVERLAY_LOCK_COVER_MS:锁屏进场后重铺全屏不透明底的阈值必须
* > SDK present 抑制标记节流窗(200ms),盖住 App 抑制窗内的残帧。
* S4 锁屏页自睡(294 遗留) → overlay_lock_idle_off_sec():阈值 = min(系统 screen_off_timeout, 30s)
* (0/超30→30,尊重更短);overlay_lock_should_sleep()/overlay_lock_wake_now()
* 判定超时灭屏(锁态保持)与唤醒回锁屏。
*
* 编译运行(在 patch_v2/meow_launcherd 下,无其它依赖):
* gcc -Wall -Wextra -o test_overlay_gate test_overlay_gate.c && ./test_overlay_gate
* 全 PASS -> exit 0。
*/
#include <stdio.h>
#include "overlay_gate.h"
#include "lock_screen.h"
static int g_pass = 0, g_fail = 0;
#define CHECK(cond, msg) do { \
if (cond) { g_pass++; printf("PASS %s\n", msg); } \
else { g_fail++; printf("FAIL %s\n", msg); } \
} while (0)
int main(void)
{
/* ---- S1 输入穿透:SIGSTOP 过的 App 关闭时必须先排空再 unlink ---- */
CHECK(overlay_drain_required(0) == 1, "S1 drain required (app was SIGSTOP/frozen)");
CHECK(overlay_drain_required(1) == 0, "S1 no drain needed (media keep-alive, never stopped)");
/* 排空窗必须大于 SDK overlay_input_suppressed 的 200ms 标记节流窗,保证恢复后确实重新 stat 到
* 标记再整批丢弃缓冲事件;此处断言契约不被踩线。 */
CHECK(OVERLAY_DRAIN_WINDOW_MS > 200, "S1 drain window > SDK 200ms throttle");
/* ---- S1b 未冻结(媒体保活)App 输入被排:靠 EVIOCGRAB 独占隔离,非 SIGCONT 排空 ----
* 保活 App 从不 SIGSTOP(保 PCM),SDK 输入线程实时读 evdev;launcher 只能靠 EVIOCGRAB 独占
* 触控设备(真机 goodix 已证 EVIOCGRAB 返回 0)使任何前台进程锁屏期收不到触摸。 */
CHECK(overlay_keepalive_needs_grab(1) == 1, "S1b keep-alive app needs EVIOCGRAB isolation");
CHECK(overlay_keepalive_needs_grab(0) == 0, "S1b frozen app -> SIGCONT drain, not grab");
/* 保活 App 隔离与冻结 App 排空互斥:一条路径而非两条。 */
CHECK(overlay_drain_required(1) == 0 && overlay_keepalive_needs_grab(1) == 1,
"S1b keep-alive: grab path (drain==0)");
CHECK(overlay_drain_required(0) == 1 && overlay_keepalive_needs_grab(0) == 0,
"S1b frozen: drain path (grab==0)");
/* ---- S2 解锁状态栏恢复:必须强制重同步 ---- */
CHECK(overlay_force_sb_resync() == 1, "S2 force status bar re-sync after unlock");
/* ---- S2b 锁屏重铺盖 App 残帧(问题2-②):阈值必须 > SDK present 抑制窗(200ms) ---- */
CHECK(OVERLAY_LOCK_COVER_MS > 200, "S2b lock re-cover ms > SDK suppression window (200ms)");
CHECK(OVERLAY_LOCK_COVER_MS == 300, "S2b lock re-cover ms = 300 (marker-window margin)");
/* ---- S1c 手动息屏触发锁屏(问题1):超时(auto_slept)与手动(manual_panel_off)唤醒都锁 ---- */
CHECK(overlay_wake_should_lock(0, 0) == 0, "S1c wake, no sleep -> no lock");
CHECK(overlay_wake_should_lock(1, 0) == 1, "S1c timeout sleep (auto) -> lock");
CHECK(overlay_wake_should_lock(0, 1) == 1, "S1c manual power sleep (manual) -> lock (was missing)");
CHECK(overlay_wake_should_lock(1, 1) == 1, "S1c both -> lock");
/* ---- S4 锁屏页自睡(294 遗留):阈值 min(系统,30s),超时灭屏锁态保持,唤醒回锁屏 ---- */
CHECK(overlay_lock_idle_off_sec(0) == 30, "S4 system 0 (never) -> lock caps 30s");
CHECK(overlay_lock_idle_off_sec(-1) == 30, "S4 system absent -> lock caps 30s");
CHECK(overlay_lock_idle_off_sec(30) == 30, "S4 system 30 -> 30");
CHECK(overlay_lock_idle_off_sec(60) == 30, "S4 system 60 -> 30 (cap, shorter)");
CHECK(overlay_lock_idle_off_sec(10) == 10, "S4 system 10 -> 10 (respect shorter)");
CHECK(overlay_lock_idle_off_sec(1) == 1, "S4 system 1 -> 1 (respect shorter)");
CHECK(overlay_lock_should_sleep(1, 30000, 30000) == 1, "S4 idle>=off & screen on -> self sleep");
CHECK(overlay_lock_should_sleep(1, 29999, 30000) == 0, "S4 idle<off -> not yet");
CHECK(overlay_lock_should_sleep(0, 99999, 30000) == 0, "S4 screen off -> no re-sleep");
CHECK(overlay_lock_wake_now(1, 0) == 1, "S4 power edge -> wake to lock");
CHECK(overlay_lock_wake_now(0, 1) == 1, "S4 touch -> wake to lock");
CHECK(overlay_lock_wake_now(0, 0) == 0, "S4 no wake input -> stay asleep");
/* ---- S3 App 死亡触发主屏重绘:崩溃必须走统一退出路径 ---- */
CHECK(lock_overlay_app_died_exit(1) == 1, "S3 app died -> unified exit path (redraw home)");
CHECK(lock_overlay_app_died_exit(0) == 0, "S3 app alive -> normal restore");
/* ---- §310 锁屏叠加期中止谓词(lock_screen.h 纯判定)---- */
CHECK(lck_overlay_should_abort(0, 0) == 0, "S3 abort=0 (menu lock, no overlay pid)");
CHECK(lck_overlay_should_abort(0, 1) == 0, "S3 abort=0 (no overlay pid, zombie ignored)");
CHECK(lck_overlay_should_abort(4321, 0) == 0, "S3 abort=0 (overlay pid, app alive)");
CHECK(lck_overlay_should_abort(4321, 1) == 1, "S3 abort=1 (overlay pid + zombie -> abort lock)");
printf("\n%s (%d passed, %d failed)\n", g_fail ? "SOME TESTS FAILED" : "ALL PASS", g_pass, g_fail);
return g_fail ? 1 : 0;
}