/* meow_bgmgr.h — 批③ 后台管理页 逻辑层(数据汇聚 + kill 门控)。自包含、依赖注入、可独立单测。 * * 定位:把「后台常驻集 → 展示行(图标/名字/CPU/内存/状态)→ kill/一键清」的**纯逻辑**从 UI 抽出, * 便于宿主单测(test_bgmgr.c)与 launcher 页面(bg_page.c)复用。同 meow_bgplay 的 ops 注入手法: * UI 提供真实 enumerate/app_meta/proc_sample;单测用假件。**单一写者=launcher 主循环**,无锁。 * * 铁律遵循:kill/一键清 带 !protected_app 门控(防误杀系统/受保护 App,对齐 app_launcher.c:2088 教训)。 * **收口:门控位不是 app.json 的 `framework`**——那是「用 meow_ui.a 的 SDK 框架 App」标志 * (docs/APP_DEV_GUIDE.md §9,推荐且现有 App 全写 1),拿它当「禁止 kill」会让所有后台 App 变成 * 不可杀(2026-11-04 真机:dev.player.ncm 声明 framework:1 → 后台页杀按钮全被置灰点不动)。 * 不新建 daemon;不引 GDBus;不碰 /data/recovery_all。 */ #ifndef MEOW_BGMGR_H #define MEOW_BGMGR_H #include #include /* pid_t */ #include "meow_ipc.h" /* MEOW_APP_ID_MAX */ /* 定义于 meow_bgplay.h,此处仅注释:与 launcher 后台表同槽数(MEOW_BG_MAX=4)。 */ #define MEOW_BGMR_ROW_MAX 4 /* 后台管理页一行。enumerate 填 app_id/pid/keepalive/on_card/bg_since_ms; * bgmgr_collect 再经 app_meta/proc_sample 补元数据/CPU/RSS,并预判 killable。 */ typedef struct bgmgr_row { char app_id[MEOW_APP_ID_MAX]; char name[49]; /* display name(回退 app_id) */ char icon[129]; /* 图标 path(空=无,页面用首字/占位) */ char protected_app; /* '1'=系统级受保护 App(禁止 kill)。**非** app.json framework; * 目前 launcher 自身组件(状态栏/设置页…)不在 App 表/后台表里, * 故实际恒为 0,保留为将来受保护组件的显式标记位。 */ pid_t pid; /* >0 后台进程 */ int keepalive; /* 1=声明常驻(background_keepalive,无限期不清) */ int is_fg; /* 1=当前前台 App(不 kill,标注「前台」) */ int on_card; /* 1=二进制在 TF 卡 */ uint64_t bg_since_ms; /* 转后台时刻(monotonic ms) */ double pcpu; /* 0-100 */ long rss_kb; /* 常驻内存 */ int killable; /* 预判:bgmgr_can_kill()==1 */ } bgmgr_row_t; /* 依赖注入(**必须全非空**;enumerate 返回行数,app_meta/proc_sample 返回 0=成功)。 */ typedef struct bgmgr_ops { /* 枚举后台常驻集:每调一次填 rows 的 app_id/pid/keepalive/on_card/bg_since_ms, * 返回行数(0..max;<0 视 0)。UI=读 launcher meow_bg 表;单测=假表。 */ int (*enumerate)(void *ctx, bgmgr_row_t *rows, int max); /* 按 app_id 补 App 元数据:name/icon/protected_app/is_fg。返回 0=找到;<0=未注册(name 回退 app_id)。 */ int (*app_meta)(void *ctx, const char *app_id, pid_t pid, char *name, int nl, char *icon, int il, char *protected_app, int *is_fg); /* 每进程 CPU/RSS 取样:返回 0=ok(写 pcpu 与 rss_kb);<0=无数据(pcpu=0/rss=0)。 */ int (*proc_sample)(void *ctx, pid_t pid, double *pcpu, long *rss_kb); void (*log)(const char *fmt, ...); /* 可空=静默 */ void *ctx; } bgmgr_ops_t; /* 枚举后台常驻并补全元数据/CPU/RSS。返回行数(>=0);app_id 空/重复加固在 enumerate 侧。 * 行序=enumerate 返回序(UI 端期望 bg_since_ms 升序=最久在前)。 */ int bgmgr_collect(const bgmgr_ops_t *ops, bgmgr_row_t *rows, int max); /* !protected_app 门控 + 非前台 + pid>0:返回 1=可 kill;0=禁止(受保护/当前前台/无进程)。 */ int bgmgr_can_kill(const bgmgr_row_t *r); /* 一键清:遍历 rows,bgmgr_can_kill() 的条目逐一调 kill_fn(app_id)。返回发出 kill 数。 * **不杀受保护 App/当前前台/非后台**;调用方负责对返回 0 弹「无后台 App 可清」之类提示。 */ int bgmgr_kill_all(const bgmgr_ops_t *ops, bgmgr_row_t *rows, int n, void (*kill_fn)(const char *app_id)); /* 状态文案(用于行内「状态」列):「常驻」keepalive=1;「后台」宽限期类;「前台」is_fg=1;空=非后台。 */ const char *bgmgr_status_str(const bgmgr_row_t *r); /* 内存文案(rss_kb → "x.xMB"/"xxKB")。返回静态缓冲区;勿复用变长调用。 */ const char *bgmgr_mem_str(long rss_kb); #endif /* MEOW_BGMGR_H */