Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/meow_bgplay.h
T
MeowOS 5aabede0cd 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
2026-09-01 18:09:09 +08:00

140 lines
7.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.
/* meow_bgplay.h — MeowOS M4:后台播放保活 + 无头后台服务运行时表(FRAMEWORK_DESIGN §9)。
*
* 定位:
* ① 后台播放保活——退出播放中(声明 background_audio=true 且媒体会话 PLAYING)的 App
* 不 SIGTERM:进程保留(音频经 audiod socket 天然续播),UI 冻结走 §10.5 的
* /tmp/meow_notif_active SDK 自抑机制(present 画进 back_buf 不上屏 + 输入排空)。
* **不做字面 SIGSTOP**:SIGSTOP 会冻结同进程播放线程、断供 PCM(§10.5 真机实证的坑)。
* 点图标/媒体卡片回前台=take 恢复原进程;停播后再退出=真退(走原 SIGTERM 序列)。
* ② 无头后台服务——MSG_REGISTER 注册 app.json `service.<svc>` 声明的服务:launcher
* spawn 子进程(无 UI,stdio→/dev/null),死亡按 1/2/4/10s 退避重启,超 max_restart
* 熔断(清该 App 通知 + 经注册连接广播 MSG_SYS{SVC_DEAD}),重注册可复位熔断。
*
* 边界:
* - 卡拔出例外:二进制在 TF 卡上的后台 App/服务仍 SIGTERM(防 SIGBUS),不适用保活。
* - 崩溃照收:waitpid WNOHANG 回收,异常退出经 on_bg_dead 回调走 crash 上报。
* - 关机前回收:meow_bgplay_kill_all() 有界 SIGTERM→SIGKILL→reap。
*
* 自包含:不依赖 launcher static 符号;日志/时钟/卡检测/通知清理/发帧全经
* meow_bgplay_ops 注入(launcher 用 logmsg/sd_device_present/ipc_send;宿主单测用假件)。
* 单一写者=launcher 主循环(meow_ipc_process 排空点挂 tick),无锁。可独立单测(test_bgplay.c)。
*/
#ifndef MEOW_BGPLAY_H
#define MEOW_BGPLAY_H
#include <stdint.h>
#include <stddef.h>
#include <sys/types.h>
#include "meow_ipc.h"
/* 后台保活 App 上限(同时驻留后台的播放中 App) */
#define MEOW_BG_MAX 4
/* 无头后台服务运行时槽上限(与 meow_acct.svcs 的 MEOW_MAX_SVCS 对齐) */
#define MEOW_SVC_RT_MAX 8
/* 退避重启档位(ms):1s/2s/4s/10s 封顶 */
#define MEOW_SVC_BACKOFF_1 1000u
#define MEOW_SVC_BACKOFF_2 2000u
#define MEOW_SVC_BACKOFF_4 4000u
#define MEOW_SVC_BACKOFF_10 10000u
/* 后台保活 App 槽。out_fd=其 stdout 管道读端(保活期继续 drain,防 App 写满管道阻塞)。 */
typedef struct meow_bg_entry {
int used;
char app_id[MEOW_APP_ID_MAX];
pid_t pid;
int out_fd;
int on_card; /* 二进制在 TF 卡上(卡拔需 SIGTERM 防 SIGBUS) */
uint64_t term_at_ms; /* 卡拔 SIGTERM 发出时刻(0=未发;SIGKILL 兜底计时) */
} meow_bg_entry_t;
/* 无头后台服务运行时槽(静态声明在 meow_acct.svcs,此处为运行态)。 */
typedef struct meow_svc_rt {
int used;
char app_id[MEOW_APP_ID_MAX];
char svc[16];
char exec[129];
pid_t pid; /* >0 在跑;0=等待退避重启/已熔断 */
int reg_fd; /* 注册方 IPC 连接 fd(熔断广播投递用;-1=静态自动拉起) */
int restart; /* 1=死亡后退避重启 */
int max_restart; /* 熔断阈值(窗口内重启次数上限;0=不熔断) */
int window_ms; /* 重启窗口(0=累计不滑窗) */
int restart_count; /* 当前窗口内已重启次数 */
int circuit; /* 1=已熔断(清通知+广播过,不再拉起;重注册复位) */
int stopping; /* 1=主动停止/卡拔终止中(SIGTERM 已发,不重启) */
int on_card; /* exec 在 TF 卡上 */
uint64_t window_start_ms; /* 滑窗起点 */
uint64_t next_retry_ms; /* 退避下次拉起时刻(0=无排程) */
uint64_t term_at_ms; /* SIGTERM 发出时刻(0=未发;500ms 未死 SIGKILL) */
} meow_svc_rt_t;
/* 依赖注入。全 NULL 可用(log 静默、now_ms 内部 CLOCK_MONOTONIC、card_present 恒 1、
* 通知清理/发帧跳过)。
* on_bg_dead:后台保活 App 进程退出(status=waitpid 原始状态;正常/异常由回调方判);
* on_svc_dead:服务槽最终消亡(stopping 走完/不重启死亡;熔断不算消亡,槽保留待复位);
* notif_clear_app:熔断时清该 App 全部通知,返回清除条数(<0=失败);
* send_frame:熔断广播 MSG_SYS(launcher 侧须先查连接存活再发);
* log_dir:svc 子进程 MEOW_LOG_DIR 注入(NULL 不注入)。 */
typedef struct meow_bgplay_ops {
void (*log)(const char *fmt, ...);
uint64_t (*now_ms)(void);
int (*card_present)(void);
void (*on_bg_dead)(const char *app_id, pid_t pid, int status);
void (*on_svc_dead)(const char *app_id, const char *svc);
int (*notif_clear_app)(const char *app_id);
size_t (*send_frame)(int fd, uint16_t type, uint32_t req_id,
const void *payload, uint32_t len);
const char *log_dir;
} meow_bgplay_ops;
/* 注入 ops(拷贝);NULL=全清空复位(含两张表)。launcher 在 meow_ipc_init 末尾调一次。 */
void meow_bgplay_init(const meow_bgplay_ops *ops);
/* ---- 后台播放保活判定(纯函数,无副作用)---- */
/* 退出时是否转后台保活:声明 background_audio 且该 App 媒体会话正在播放。 */
int meow_bg_should_keep(int bg_audio_declared, int media_playing);
/* ---- 后台保活 App 表 ---- */
/* 转后台:登记 app_id/pid/out_fd。返回 0=成功;MEOW_ERR_EXISTS=已在后台;MEOW_ERR_NOMEM=满。 */
int meow_bg_add(const char *app_id, pid_t pid, int out_fd, int on_card);
/* 回前台:按 app_id 摘除槽(进程归还前台会话)。成功返回 0 且 *out_fd(可 NULL)带回
* 管道读端;未在后台返回 -1。 */
int meow_bg_take(const char *app_id, int *out_fd, pid_t *pid);
/* 该 App 是否在后台保活。 */
int meow_bg_is_backgrounded(const char *app_id);
/* 当前后台保活数。 */
int meow_bg_count(void);
/* 取第 i 槽(0..MEOW_BG_MAX-1)只读指针;空槽/越界返回 NULL。供 drain out_fd。 */
const meow_bg_entry_t *meow_bg_at(int i);
/* 表变化计数(add/take/回收 递增);菜单 1s tick 比对刷新「·后台」标注用。 */
uint32_t meow_bgplay_change_seq(void);
/* 周期调(主循环 ~10ms 排空点):waitpid WNOHANG 回收死亡后台 App(→on_bg_dead 清槽);
* 卡拔例外:on_card 槽在卡不在时 SIGTERM,500ms 未死 SIGKILL。 */
void meow_bg_tick(void);
/* ---- 无头后台服务 ---- */
/* 注册/拉起服务。exec 须可执行(先 access(X_OK),拒绝返回 MEOW_ERR_NOENT 不污染槽)。
* 返回 pid(>0)=已拉起;MEOW_ERR_EXISTS=同名服务在跑;MEOW_ERR_NOENT=exec 不可执行;
* MEOW_ERR_NOMEM=槽满;MEOW_ERR_IO=fork 失败。同 svc 已熔断槽重注册=复位熔断重新拉起。 */
int meow_svc_register(const char *app_id, const char *svc, const char *exec,
int restart, int max_restart, int window_ms,
int on_card, int reg_fd);
/* 请求停止服务:SIGTERM,标记 stopping(不触发重启)。返回 0=已受理;MEOW_ERR_NOENT=无此服务。 */
int meow_svc_stop(const char *app_id, const char *svc);
/* 查服务槽(只读);无返回 NULL。 */
const meow_svc_rt_t *meow_svc_find(const char *app_id, const char *svc);
/* 周期调:回收死亡子进程;按 restart/退避档 1/2/4/10s 重启;滑窗复位计数;超
* max_restart 熔断(notif_clear_app + send_frame MSG_SYS{SVC_DEAD} + circuit=1);
* 卡拔:on_card 槽 SIGTERM + stopping=1(exec 随卡消失,不重启防空转)。 */
void meow_svc_tick(void);
/* 关机前回收:向各服务注册连接广播 MSG_SYS{SHUTDOWN}(best-effort),随后全部
* bg/svc 子进程 SIGTERM→有界等 1s→SIGKILL→有界 reap。调用方负责 unlink NOTIF_ACTIVE。 */
void meow_bgplay_kill_all(void);
/* 调试打印(经 ops->log,每槽一行)。 */
void meow_bgplay_dump(const char *prefix);
#endif /* MEOW_BGPLAY_H */