163 lines
10 KiB
C
163 lines
10 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
|
||
/* 批①+宽限:无声明/媒体不活跃 App 入后台的宽限期(默认 30 分钟)。
|
||
* 语义:转后台(bg_since_ms)后超 MEOW_BG_GRACE_MS 未再启动 → meow_bg_tick 走真退序列
|
||
* SIGTERM→SIGKILL 回收(摘表 + 通知 amsd)。**声明常驻(keepalive=1)或媒体活跃
|
||
* (meow_media_app_resident=1)不清**。本批为编译期常量(30*60*1000 = 1800000ms);
|
||
* 后续可进设置/按 App 可配(待拍板)。 */
|
||
#define MEOW_BG_GRACE_MS 1800000u
|
||
/* 无头后台服务运行时槽上限(与 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 兜底计时) */
|
||
uint64_t bg_since_ms; /* 批①:转后台时刻(monotonic ms;宽限超时回收/槽满 LRU 掐最久用) */
|
||
char keepalive; /* 批①+宽限:1=声明常驻(background_keepalive,无限期不清);0=宽限期类
|
||
* (未声明/background_audio,媒体不活跃时超 bg_grace_ms 回收) */
|
||
} 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 原始状态;正常/异常由回调方判);
|
||
* media_resident:该 App 媒体会话是否「活跃可驻留」(launcher 注入 meow_media_app_resident)。
|
||
* 批①+宽限用:宽限超时回收 / 槽满 LRU 判「媒体活跃不清不回收」;NULL=恒 0(视为不活跃)。
|
||
* 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);
|
||
int (*media_resident)(const char *app_id);
|
||
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);
|
||
|
||
/* ---- 后台播放保活判定(纯函数,无副作用)---- */
|
||
/* 退出时是否转后台保活(批①三参化):
|
||
* keepalive_declared=1(声明 background_keepalive)→ 恒保活(通用常驻,不依赖媒体会话);
|
||
* 否则 bg_audio_declared(声明 background_audio)且 media_resident(该 App 媒体会话活跃
|
||
* =PLAYING/PAUSED/BUFFER,见 meow_media_app_resident)→ 保活。
|
||
* 未声明/无活跃会话 → 0(走原真退序列)。 */
|
||
int meow_bg_should_keep(int bg_audio_declared, int media_resident, int keepalive_declared);
|
||
|
||
/* ---- 后台保活 App 表 ---- */
|
||
/* 转后台:登记 app_id/pid/out_fd,keepalive 标记常驻类型(1=background_keepalive 类=无限期;
|
||
* 0=宽限期类=未声明/background_audio,媒体不活跃时超 bg_grace_ms 回收)。批①+宽限:**全 App**
|
||
* nav-exit 都经此入表;槽满(MEOW_BG_MAX=4)时 LRU 回收最久未激活(bg_since_ms 最旧)的**未声明**
|
||
* 槽(不回收声明常驻/媒体活跃);全为声明常驻/媒体活跃 → MEOW_ERR_NOMEM(调用方回落真退,保安全)。
|
||
* 返回 0=成功;MEOW_ERR_EXISTS=已在后台;MEOW_ERR_NOMEM=满且无可回收。 */
|
||
int meow_bg_add(const char *app_id, pid_t pid, int out_fd, int on_card, int keepalive);
|
||
/* 回前台:按 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 清槽)。
|
||
* 终止触发(SIGTERM,TERM_GRACE_MS 后 SIGKILL):
|
||
* - 卡拔例外:on_card 槽在卡不在时终止(防 SIGBUS);
|
||
* - 批①+宽限 超时回收:未声明(keepalive=0)且媒体不活跃(media_resident=0)的槽,转后台超
|
||
* MEOW_BG_GRACE_MS 未再启动 → 回收(走真退序列 + 摘表 + 通知 amsd)。声明常驻/媒体活跃不清。 */
|
||
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 */
|