fix(§10.5): 通知中心叠加媒体保活——播放中 App 不 SIGSTOP(修播放中滑通知中心音乐停)

- launcher: run_app_notif_overlay 入口 meow_media_app_playing() 判定(当前源+PLAYING+app_id 匹配)
  → 不 SIGSTOP/不 SIGCONT,App 续跑保 PCM;非播放中维持 SIGSTOP 模板;关闭时 unlink 标记挪到
  恢复帧上屏后(防未冻结 App 抢 present 一帧回闪)。
- SDK gfx: present() 见 /tmp/meow_notif_active(env MEOW_NOTIF_ACTIVE_FILE 覆写)自抑上屏,
  仅 App 进程(系统 UI/launcher 不抑制),200ms 节流 stat,脏区累积解除后一次上屏;
  meow_gfx_overlay_suppressed_count 供单测断言。
- SDK input: 叠加期排空丢弃本进程 evdev 副本 + EVIOCGKEY 位图静默对齐(防面板点击被 App
  双重消费/解除后边沿误判),进入沿复位触摸状态机。
- 宿主:新 test_overlay_suppress T1-T4 + test_media §8(8 用例)全 PASS;test_notif_*/
  test_sdk_*/test_vol_ramp_jack 等回归全过;zig-mipsel -Wall -Wextra 全 TU 0 warning。
- meow_launcherd fbb82036(/usr/bin 只读待固件,fw_content.conf 已更)/ meow_ui.a d6242fc2 /
  hello a073535a / files 2b799d07(已热更到卡);ncm 未重链(ncm_app 他 agent 在改)。
This commit is contained in:
MeowOS committed 2026-09-01 15:23:32 +08:00
1 parent 820d1e52bf
commit ad9778476a
19 files changed
+254 -13

No files matched your search

+34 -12
View File
@@ -1201,6 +1201,9 @@ static void notify_app_crash(const char *pkg, int status)
* ① SIGSTOP App → ② 写 /tmp/meow_notif_active(+ /tmp/meow_power_menu_active 让 watchdog 让位)
* → ③ pm_capture_app_frame 读回 App 帧当背景 → ④ run_notif_center(apps={a},1) 叠加渲染
* + 接管输入 → ⑤ 关闭后恢复 App 帧 → unlink active → SIGCONT App。
* 媒体保活分支(定案):当前媒体源==本 App 且 PLAYING(meow_media_app_playing)→ ①不 SIGSTOP、
* ⑤不 SIGCONT;App 继续跑保 PCM 供流,上屏由 SDK present 层见 ②的标记自抑,输入由 SDK
* 输入线程见标记排空(防面板点击被 App 双重消费)。非播放中 App 维持 SIGSTOP 模板。
* 差异:背景=App 帧(非应用列表);关闭后回 App(非回 run_menu);点通知首期 = dismiss + 关面板
* + SIGCONT 回当前 App(不跨 app 启动,见 NOTIF_CENTER_APP_OVERLAY_PLAN §3.2)。
* App 崩溃探测:run_notif_center 循环内 read /proc/<pid>/stat 状态位(zombie)周期探测
@@ -1210,14 +1213,25 @@ static void notify_app_crash(const char *pkg, int status)
static int run_app_notif_overlay(struct app *a, pid_t pid)
{
if (!back_buf_ok || !fb) { logmsg("notif-overlay: no back_buf (direct mode) skip"); return 0; }
logmsg("notif-overlay: open (app=%s pid=%d) SIGSTOP", a->name, pid);
if (kill(pid, SIGSTOP) < 0) {
logmsg("notif-overlay: SIGSTOP %d fail: %s", pid, strerror(errno));
/* 媒体保活(定案):当前媒体源==本前台 App 且 state==PLAYING → 不 SIGSTOP。ncm 等播放类
* App 的播放线程同进程,冻结即 PCM 断供(真机报告:播放中滑下通知中心音乐停)。改为:
* App 继续跑(音频照常喂 audiod、媒体上报照常),SDK present 层见 /tmp/meow_notif_active
* 自抑上屏(只画进 back_buf 不 present,防重绘穿透叠加层);关闭时不补 SIGCONT(没 STOP 过)。
* 非播放中 App 维持现状 SIGSTOP。 */
int keep_running = meow_media_app_playing(a->id);
if (keep_running) {
logmsg("notif-overlay: open (app=%s pid=%d) PLAYING -> no SIGSTOP (SDK present suppressed)",
a->name, pid);
} else {
/* 等 App 进入 stopped 态(/proc/pid/stat 状态位 'T';超时兜底);frozen 后才能读回帧 */
for (int i = 0; i < 100; i++) {
if (proc_is_stopped(pid)) break;
usleep(10000);
logmsg("notif-overlay: open (app=%s pid=%d) SIGSTOP", a->name, pid);
if (kill(pid, SIGSTOP) < 0) {
logmsg("notif-overlay: SIGSTOP %d fail: %s", pid, strerror(errno));
} else {
/* 等 App 进入 stopped 态(/proc/pid/stat 状态位 'T';超时兜底);frozen 后才能读回帧 */
for (int i = 0; i < 100; i++) {
if (proc_is_stopped(pid)) break;
usleep(10000);
}
}
}
/* 让位标记:/tmp/meow_notif_active(§10.5 语义同 powmenu_active)。当前 watchdog 只认
@@ -1240,19 +1254,27 @@ static int run_app_notif_overlay(struct app *a, pid_t pid)
g_notif_ctrl_pid = 0;
int died = g_notif_ctrl_died;
unlink(NOTIF_ACTIVE);
unlink(POWMENU_ACTIVE);
if (died) {
/* App 已死:不 SIGCONT、不恢复死帧;back_buf 交还 run_menu 统一重绘 */
unlink(NOTIF_ACTIVE);
unlink(POWMENU_ACTIVE);
logmsg("notif-overlay: app died during session -> unified exit path");
return 1;
}
/* 恢复 App 帧(run_notif_center 关面板后 back_buf 已还原为背景=App 帧;再拍一次兜底
* 覆盖非动画关闭路径)+ SIGCONT。 */
* 覆盖非动画关闭路径)。媒体保活路径(keep_running):先恢复帧再 unlink 标记——
* App 未被冻结仍在跑,标记多留一拍可让 SDK present 自抑覆盖到恢复帧上屏之后,
* 避免「标记先消失 → App 抢在恢复帧前 present 新帧 → 又被旧帧盖回」的一帧回闪。 */
pm_capture_app_frame();
meow_ui_flush();
kill(pid, SIGCONT);
logmsg("notif-overlay: closed -> SIGCONT app");
unlink(NOTIF_ACTIVE);
unlink(POWMENU_ACTIVE);
if (!keep_running) {
kill(pid, SIGCONT);
logmsg("notif-overlay: closed -> SIGCONT app");
} else {
logmsg("notif-overlay: closed -> app never stopped (media keep-alive), no SIGCONT");
}
return 0;
}
Binary file not shown.
+7
View File
@@ -120,6 +120,13 @@ int meow_media_has_source(void)
return (s && s->state != MEOW_MEDIA_STOPPED) ? 1 : 0;
}
int meow_media_app_playing(const char *app_id)
{
const meow_media_session_t *s = meow_media_current();
if (!s || !app_id || !app_id[0]) return 0;
return (s->state == MEOW_MEDIA_PLAYING && strcmp(s->app_id, app_id) == 0) ? 1 : 0;
}
static int send_playctl(const meow_media_session_t *s, uint32_t cmd, int64_t seek_ms)
{
if (!s || !s->used || !g_ops || !g_ops->send) return -1;
+4
View File
@@ -72,6 +72,10 @@ const meow_media_session_t *meow_media_current(void);
/* 是否有活动媒体源:当前源存在且 state != 0(停止)。通知中心卡片/播放页据此显示。 */
int meow_media_has_source(void);
/* §10.5 叠加会话保活判定:当前源(最近活跃会话)存在、state==PLAYING 且 app_id 匹配。
* launcher 据此决定通知中心叠加不 SIGSTOP 该 App(保 PCM 供流),SDK present 层自抑上屏。 */
int meow_media_app_playing(const char *app_id);
/* 向当前源下发 PLAYCTL(卡片按钮/播放页/媒体键/控制器)。返回 0=已发,-1=无源/发送失败。 */
int meow_media_playctl(uint32_t cmd, int64_t seek_ms);
+21
View File
@@ -7,6 +7,7 @@
* 4) PLAYCTL 下发字节格式(socketpair 对端可读回 MSG_PLAYCTL 帧,cmd/seek 正确);
* 5) meow_media_playctl 无源 → -1;
* 6) clear(app) 清会话 → current()=NULL。
* 8) meow_media_app_playing(§10.5 叠加保活分支:当前源+PLAYING+app_id 匹配才 1)。
* 只依赖 meow_media.c + meow_ipc.h(纯 C),宿主 gcc 直编。
*/
#include <stdio.h>
@@ -170,6 +171,26 @@ int main(void)
fill_meta(&m, "", MEOW_MEDIA_PLAYING, 0, 0, "x", "y");
CHECK(meow_media_meta(&m, sv[0]) == -1, "meta empty app_id == -1");
/* 8) §10.5 叠加保活分支判定 meow_media_app_playing(launcher 不 SIGSTOP 的入口判据) */
meow_media_clear(NULL);
CHECK(meow_media_app_playing("com.ncm") == 0, "app_playing: no source == 0");
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 1000, 240000, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
CHECK(meow_media_app_playing("com.ncm") == 1, "app_playing: current+PLAYING+match == 1");
CHECK(meow_media_app_playing("com.other") == 0, "app_playing: app_id mismatch == 0");
CHECK(meow_media_app_playing("") == 0, "app_playing: empty app_id == 0");
CHECK(meow_media_app_playing(NULL) == 0, "app_playing: NULL app_id == 0");
fill_meta(&m, "com.ncm", MEOW_MEDIA_PAUSED, 1000, 240000, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
CHECK(meow_media_app_playing("com.ncm") == 0, "app_playing: PAUSED == 0 (SIGSTOP as usual)");
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 2000, 240000, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
/* 当前源被别的 App 抢走(后者 PAUSED 也抢走「最近活跃」)→ 本 App 播放中也不再保活 */
fill_meta(&m, "com.player2", MEOW_MEDIA_PAUSED, 0, 180000, "Song B", "Artist B");
meow_media_meta(&m, sv[0]);
CHECK(meow_media_app_playing("com.ncm") == 0, "app_playing: not current source == 0");
meow_media_clear(NULL);
printf("\n%s (%d fails)\n", g_fails ? "FAILED" : "ALL PASS", g_fails);
return g_fails ? 1 : 0;
}
Binary file not shown.
Binary file not shown.