根因(2026-11-04 真机 dev.player.ncm pid=7520): - bg_page.c bgm_app_meta 把 app.json 的 framework 字段映射进 bgmgr_row.framework,而 bgmgr_can_kill 把它当「系统/框架 App 禁止 kill」。但 framework=1 在 MeowOS 里意为「用 meow_ui.a 的 SDK 框架 App」(docs/APP_DEV_GUIDE.md §9,推荐值,真机 4 个 App 全写 1) → 后台页每行「杀」按钮被置灰,bgm_row_tap 命中后直接 return 1、不调 meow_bg_terminate; 「一键清除后台」同理 0 行可杀。整条链路无日志,故只能靠翻 app.json 才定位(全部设备日志里 "后台管理页终止后台App" 出现 0 次 = 4 次进页从未真正杀过)。 - 面板 killfg 正常是因为走 am kill → amsd → MSG_AMS_KILL,无 framework 门控;但它其实靠 amsd 1200ms×2 超时兜底 kill(pid,SIGTERM) 才杀成:util.c 原本在无前台 App 会话时只回 ACK -2((uint32_t)MEOW_ERR_NOENT),ACK 及时到达时 amsd 判「launcher refused」完全不杀 (ams_20260915_031339.log 实证),ACK 迟到才侥幸成功(2026-11-04 真机)。 修复: - meow_bgmgr: 门控位 framework → protected_app(系统级受保护标记;当前无 App 置位,launcher 自身组件本就不在 App 表/后台表内)。bg_page 不再把 a->framework 送进门控。 - bg_page: 行副文本改存 ctx 持久缓冲(原 rbuf[].sub 指向 bgm_rows 栈帧 → 悬垂,副标题乱码); 门控拒绝/未命中后台表各留一行 logmsg,「点了没反应」从此可查。 - util.c MSG_AMS_KILL: 先查后台保活表 → meow_bg_terminate 就地走真退序列(与页面「杀」按钮 同一通道,故 am kill 与页面按钮行为对称),无前台会话也能杀;否则仍走 pending / ACK -2。 - amsd cmd_kill/cmd_start: ipc 失败原因细分(connect failed / timeout, no reply / ...),日志 不再笼统写 "launcher offline"(响应串保留该字样,MeowPanel api_ams_ctl 依此映射 503)。 - meow_bgplay: meow_bg_terminate 注释/日志写明「后台管理页 / am kill」双入口同通道。 测试(宿主 gcc,产物在 $TMPDIR): - test_bgpage 40 checks:新增 framework=1 仍判可杀且按钮真 terminate(本 bug 回归)、 受保护行按钮不杀但留诊断日志、sub 持久化 + 栈抖动不变(悬垂回归)。 - test_bgmgr 30 checks:protected_app 门控矩阵 + framework=1 可杀回归。 - test_bgplay 92 PASS:新增 4b meow_bg_terminate 全链路(受理→term_at_ms→tick SIGTERM→ on_bg_dead→清槽;空/不在表分别 INVAL/NOENT);TEST_SVC_PATH 改取 $TMPDIR (Termux 无 /tmp,原 fputs(NULL) 直接 abort)。 - amsd 宿主集成(fake launcher 桩,4 例):ACK 0→terminate 且不盲杀;ACK -2→refused 不盲杀; 无 launcher→direct SIGTERM (connect failed);挂起不回→direct SIGTERM (timeout, no reply)。 0warning:util.c 与 main 同告警逐行一致(仅 SDK 头既有 typedef 重定义),其余文件干净。
76 lines
4.7 KiB
C
76 lines
4.7 KiB
C
/* 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 <stdint.h>
|
||
#include <sys/types.h> /* 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 */
|