- 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
140 lines
7.9 KiB
C
140 lines
7.9 KiB
C
/* 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 */
|