Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/test_overlay_gate.c
T
AskaEth a3d6e21db5 fix(launcher,lock): §310 M6.5 追加两修——App 内手动息屏不锁屏 + 锁屏脏区被 App 覆盖
真机 M6.4 反馈(c730333 EVIOCGRAB 之后仍未好全):

P1 App 内电源键手动息屏→亮屏无锁屏(桌面/超时路径都有锁、唯独此路径丢)
  根因:App 会话内电源键单击息屏由 **watchdog** 处理(meow_watchdog.c handle_power →
  wd_panel_off,直接 blank 面板背光 0),**不置** meow_auto_slept;而 launcher app 会话循环的
  唤醒锁屏只在 `if (meow_auto_slept)`(超时路径 set)内触发(app_launcher.c:1996/2029)。
  手动路径唤醒时 meow_auto_slept==0 → 不锁屏(真机实测)。
  修法(app_launcher.c):App 循环每圈以读背光 `==0` 补判 watchdog 手动息屏(app_panel_manual_off),
  唤醒分支改 `meow_auto_slept || app_panel_manual_off`,两类唤醒都走 lck_wake_should_lock 锁屏,
  两条路径对齐。overlay_gate.h 增 overlay_wake_should_lock 判据。

P2 锁屏脏区被 App 覆盖(App 刷新控件盖到锁屏上 / 未刷新区显示锁屏自带脏区)
  根因:媒体保活 App 不冻结仍在绘制;SDK present 抑制(/tmp/meow_notif_active)有 ≤200ms
  标记节流窗(meow_ui_gfx.c MEOW_OVERLAY_CHK_MS)——锁屏首扑后 App 仍可能进 1-2 帧,其
  部分脏区落在锁屏上(App 控件覆盖),未覆盖区才是锁屏。另外锁屏关闭需整帧重绘消残影。
  修法:
   ① lk_draw 首帧已 fill_rect(0,0,xres,yres,th->surface) 全屏不透明底(复核满足);
   ② run_lock_screen 进场 300ms(OVERLAY_LOCK_COVER_MS > 200ms 标记窗)后**重铺一次全屏
      不透明底**盖掉 App 抑制窗内的 1-2 帧残影;App 此后被抑制不再上屏,锁屏稳定遮盖;
   ③ 锁屏关闭(run_app_lock_overlay 关闭路径)已 pm_capture_app_frame+meow_ui_flush+
      app_overlay_statusbar 整帧重绘含状态栏(复核满足)。

新增 test_lock_render.c(6 断言:首帧整帧不透明覆盖断言 + 部分填充漏区对照)+ test_overlay_gate.c
扩展(S1c 手动息屏触发锁屏 4 断言 + S2b 重铺阈值>200ms 2 断言)。launcher 全量宿主回归 +
SDK 叠加/状态栏/输入回归全 PASS;mipsel 交叉 -Wall -Wextra 0 warning。
md5 62c817c204b9d9751a4d8f411d1eb69b(/usr/bin 只读,待 M6.5 固件打包;fw_content.conf 未动)。
2026-09-02 11:49:37 +08:00

79 lines
5.5 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 抑制窗内的残帧。
*
* 编译运行(在 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");
/* ---- 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;
}