feat(launcher): M4 后台服务保活+后台播放(FRAMEWORK_DESIGN §5/§9 落地)

- app.json 新字段 background/background_audio 解析入 struct app/meow_acct 注册表
- meow_ipc.h:MSG_REGISTER(m_register_t)/SERVICE_OK(m_ack_t.status=pid)/SERVICE_STOP/
  MSG_SYS(m_sys_t, SHUTDOWN=1/SVC_DEAD=2) 负载结构落地
- 新模块 meow_bgplay.c/h(自包含+ops 注入,仿 meow_media,宿主可单测):
  · 后台播放保活——声明 background_audio 且媒体会话 PLAYING 的 App nav-exit 不终止,
    进程+音频流保留;UI 冻结走 §10.5 /tmp/meow_notif_active SDK present 自抑(非字面
    SIGSTOP:冻结同进程播放线程断供 PCM,§10.5 真机实证);点图标/媒体卡片回前台=
    原进程恢复(chain/picker 忽略),停播后再退出=真退 SIGTERM
  · 无头后台服务——MSG_REGISTER spawn/stop/reap + 退避重启 1/2/4/10s cap +
    max_restart 熔断(清该 App 通知 + 注册连接广播 MSG_SYS{SVC_DEAD},重注册复位)
  · 卡拔例外(卡上二进制后台 App/服务仍 SIGTERM 防 SIGBUS)、崩溃照收、
    关机前 meow_bgplay_kill_all 有界回收(含 MSG_SYS{SHUTDOWN} 广播)
- util.c:MSG_REGISTER/MSG_SERVICE_STOP 分发(权限/声明校验链)+ ipc_conn_alive 连接
  存活查询 + meow_ipc_process 末尾挂 meow_bg_housekeep(drain/reap/退避 tick)
- app_launcher.c:launch_app_chain 入口 resume(bg_take 原进程回前台)+ nav-exit
  保活分支 + 转后台出口(杀看门狗/写自抑标记/复位顶条,跳过退出尾)+ 冷启动自动
  拉起静态 keepalive(mode=0) 服务(exec 路径约定=App 同目录 <svc> 文件)
- main_menu.c:应用列表「·后台」标注(宫格/列表,1s tick 按 change_seq 刷新)+
  两条退出尾 kill_all + unlink NOTIF_ACTIVE
- 宿主单测 test_bgplay.c 50 项全过(真 fork 子进程走状态机 + 假时钟退避熔断);
  test_media/test_notif_panel/test_notif_tf/test_vol_ramp_jack 回归全过;
  zig-mipsel-cc -Wall -Wextra 0 warning。launcher md5=c07d011c0769fca14d3bfb752d735463
This commit is contained in:
MeowOS committed 2026-09-01 18:09:09 +08:00
1 parent 7db8197f9c
commit 5aabede0cd
13 files changed
+1461 -40

No files matched your search

+22 -3
View File
@@ -467,6 +467,11 @@ static void menu_draw_grid_cell(const struct app *a, meow_img *icon, int x, int
if (!a->ok) {
int ew = ui_width("[缺失]", 14);
ui_draw(x + (w - ew) / 2, name_y + meow_font_line_height(18) + 2, "[缺失]", th->error, 14);
} else if (meow_bg_is_backgrounded(a->id)) {
/* M4:后台保活中标注(播放中转后台的 App;run_menu 1s tick 按 change_seq 刷新) */
int bw = ui_width("·后台", 14);
ui_draw(x + (w - bw) / 2, name_y + meow_font_line_height(18) + 2, "·后台",
th->on_surface_variant, 14);
}
}
@@ -496,9 +501,11 @@ static void menu_draw_list_cell(const struct app *a, meow_img *icon, int x, int
sub_y = y + 30;
}
ui_draw(tx, name_y, nm, a->ok ? th->on_surface : th->error, 20);
/* 副标题:package(无则 id),on_surface_variant */
char sub[40];
snprintf(sub, sizeof sub, "%s", a->package[0] ? a->package : (a->id[0] ? a->id : ""));
/* 副标题:package(无则 id),on_surface_variant;M4 后台保活中前缀「·后台 」 */
char sub[48];
snprintf(sub, sizeof sub, "%s%s",
meow_bg_is_backgrounded(a->id) ? "·后台 " : "",
a->package[0] ? a->package : (a->id[0] ? a->id : ""));
if (sub[0]) {
char subf[44];
menu_name_fit(sub, w - (tx - x) - 12, 14, subf, sizeof subf);
@@ -1621,6 +1628,12 @@ static void run_menu(struct app apps[MAX_APPS], int napps, int state,
draw_clock(); /* Stage-One:喂 SDK 全局状态栏 + 标脏顶条;SDK present() 重绘 */
fb_norm_buf0();
meow_ui_flush(); /* blit 状态栏整条,不动列表 */
/* M4:后台保活表变化(转后台/回前台/回收)→ 重绘列表刷新「·后台」标注 */
{
static uint32_t bg_seq_last = 0;
uint32_t seq = meow_bgplay_change_seq();
if (seq != bg_seq_last) { bg_seq_last = seq; dirty = 1; }
}
}
usleep(POLL_MS * 1000);
@@ -1964,6 +1977,10 @@ int main(int argc, char **argv)
"(state=%d napps=%d), drain-to-home immediately", state, napps);
run_menu(apps, napps, state, sim_tap, sim_exit);
logmsg("=== meow_launcherd exit (MeowOS mode) ===");
/* M4:关机/退出前回收后台保活 App + 无头服务(有界 SIGTERM→SIGKILL→reap);
* 清 §10.5 自抑标记,防 launcher 重启后残留标记抑制后续 App present。 */
meow_bgplay_kill_all();
unlink(NOTIF_ACTIVE);
if (log_fd >= 0) { close(log_fd); log_fd = -1; }
ipc_shutdown = 1; unlink(ipc_sock_path);
unlink(PID_PATH);
@@ -2168,6 +2185,8 @@ int main(int argc, char **argv)
if (player_stopped) { fb_restore_player(); restore_backlight(); }
cont_player();
logmsg("=== meow_launcherd exit ===");
meow_bgplay_kill_all(); /* M4:退出前回收后台保活 App + 无头服务(有界) */
unlink(NOTIF_ACTIVE); /* M4:清 §10.5 自抑标记,防残留抑制后续 App present */
if (log_fd >= 0) { close(log_fd); log_fd = -1; }
ipc_shutdown = 1; unlink(ipc_sock_path);
unlink(PID_PATH);