Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/meow_bgplay.h
T

163 lines
10 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
/* 批①+宽限:无声明/媒体不活跃 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 */