feat(bt): 批③后台管理页 — bg_page(顶栏'后台'入口+sub_page_t列表[名字/CPU/内存/状态/tap=kill]+一键清除动作行+空态,2s实时)+meow_bgmgr(collect/can_kill !framework门控+kill_all不杀框架)+meow_bg_terminate复用回收序列;amsd FG/BG收编(重编57e19cde含handle_bg/fg);test_bgmgr 20项ALL PASS,0warning

This commit is contained in:
AskaEth committed 2026-09-04 02:19:06 +08:00
1 parent 2f22e26de7
commit 211c5a556a
9 files changed
+624 -1

No files matched your search

+242
View File
@@ -0,0 +1,242 @@
/* bg_page.c — 批③ 后台管理页(桌面顶栏居右按钮进入)。
*
* 容器:sub_page_t 脚手架(subpage.c,与 wifi_page/bt_page 同款)。顶栏=meow_topappbar:
* 返回箭头 + 标题「后台管理」(+ 右上无开关)。
* 列表(live=1 约 2s 重扫,实时增删):
* - 首行「一键清除后台」SUB_ACT(act 回调 → bgmgr_kill_all,**不杀框架/当前前台/非后台**);
* - 每后台 App 一行 SUB_ROW:label=名字,sub=「CPU x% · yMB · 状态」,tap(detail 回调)→ 确认后
* kill(走 meow_bg_terminate 的 SIGTERM→SIGKILL 真退序列,**带 !framework 门控**)。
* - 空态「无后台 App」。
* 数据源=amsd FG/BG 注册表(后台常驻集=launcher meow_bg 表,EV BG/FG 上报进 amsd)+ 每进程
* pcpu/rss(meow_svc_perf 同源 /proc 聚合,本页在 launcher 内同口径取)。
*
* 说明(与「每行最右 kill 按钮 + 顶栏右侧清除按钮」的像素级差异):sub_page_t 行无内嵌按钮、
* 顶栏仅返回+开关;故 kill 用「点行→确认」,清除用「顶置动作行」。像素级自绘版见 design 备忘。
*/
#include "meow_launcher_internal.h"
#include "meow_bgplay.h"
#include "meow_bgmgr.h"
#include <ctype.h>
#define BGMR_CLR_ACT 9999 /* 一键清除动作码 */
#define BGMR_KILL_BASE 1000 /* kill app i:BGMR_KILL_BASE+i */
typedef struct {
struct app *apps;
int napps;
bgmgr_row_t rows[MEOW_BGMR_ROW_MAX];
int n;
char toast[64];
long toast_ms;
} bgm_ctx_t;
/* 找 struct app(launcher 应用表)by id */
static struct app *bgm_app_find(struct app *apps, int napps, const char *app_id)
{
if (!apps || !app_id) return NULL;
for (int i = 0; i < napps; i++)
if (apps[i].id[0] && !strcmp(apps[i].id, app_id)) return &apps[i];
return NULL;
}
/* ---- meow_bgmgr ops 注入(UI 侧真实实现)---- */
static int bgm_app_meta(void *ctx, const char *app_id, pid_t pid,
char *name, int nl, char *icon, int il,
char *framework, int *is_fg)
{
(void)pid;
bgm_ctx_t *b = ctx;
struct app *a = bgm_app_find(b->apps, b->napps, app_id);
*framework = 0;
*is_fg = 0;
if (!a) return -1; /* 未注册 → name 回退 app_id */
if (a->name[0]) snprintf(name, nl, "%s", a->name);
if (icon && il > 0) snprintf(icon, il, "%s", a->icon[0] ? a->icon : "");
*framework = (a->framework == '1') ? '1' : 0;
return 0;
}
static int bgm_enumerate(void *ctx, bgmgr_row_t *rows, int max)
{
(void)ctx;
int n = meow_bg_count();
if (n > max) n = max;
for (int i = 0; i < n; i++) {
const meow_bg_entry_t *e = meow_bg_at(i);
if (!e || !e->used || !e->app_id[0]) continue;
memset(&rows[i], 0, sizeof rows[i]);
snprintf(rows[i].app_id, sizeof rows[i].app_id, "%s", e->app_id);
rows[i].pid = e->pid;
rows[i].keepalive = (e->keepalive ? 1 : 0);
rows[i].on_card = (e->on_card ? 1 : 0);
rows[i].bg_since_ms = e->bg_since_ms;
}
return n;
}
/* proc_sample:/proc/<pid>/stat(utime+stime 差分) + /proc/<pid>/status(VmRSS)。
* 与 meow_svc_perf 897-983 每进程 pcpu/rss 聚合同口径(同 /proc 源)。 */
static long g_cpu_pid[MEOW_BG_MAX] = {0};
static unsigned long long g_cpu_tick[MEOW_BG_MAX] = {0};
static int bgm_proc_sample(void *ctx, pid_t pid, double *pcpu, long *rss_kb)
{
(void)ctx;
if (pid <= 0) return -1;
char path[64], line[512];
long rss = 0;
snprintf(path, sizeof path, "/proc/%d/status", pid);
int fd = open(path, O_RDONLY);
if (fd >= 0) {
ssize_t nr = read(fd, line, sizeof line - 1); close(fd);
if (nr > 0) { line[nr] = 0;
char *p = strstr(line, "VmRSS:");
if (p) p = strchr(p, ' ');
if (p) { while (*p == ' ' || *p == '\t') p++; rss = atol(p); }
}
}
double pc = 0.0;
snprintf(path, sizeof path, "/proc/%d/stat", pid);
fd = open(path, O_RDONLY);
if (fd >= 0) {
ssize_t nr = read(fd, line, sizeof line - 1); close(fd);
if (nr > 0) {
line[nr] = 0;
char *rp = strrchr(line, ')');
if (rp) {
char *p = rp + 2;
unsigned long long ut[2] = {0, 0};
for (int f = 0; f < 14; f++) {
while (*p == ' ') p++;
char *end; unsigned long long v = strtoull(p, &end, 10);
if (end == p) { p++; continue; }
p = end;
if (f == 11) ut[0] = v; /* utime */
if (f == 12) ut[1] = v; /* stime */
}
unsigned long long cur = ut[0] + ut[1];
static double hz = 0.0;
if (hz <= 0) { hz = (double)sysconf(_SC_CLK_TCK); if (hz <= 0) hz = 100.0; }
unsigned long long prev = 0; int had = 0, slot = -1;
for (int i = 0; i < MEOW_BG_MAX; i++) {
if (g_cpu_pid[i] == pid) { prev = g_cpu_tick[i]; had = 1; slot = i; break; }
}
if (!had) {
for (int i = 0; i < MEOW_BG_MAX; i++) if (g_cpu_pid[i] == 0) { slot = i; break; }
if (slot < 0) slot = 0;
}
if (slot >= 0) { g_cpu_pid[slot] = pid; g_cpu_tick[slot] = cur; }
if (had) { pc = 100.0 * (double)(cur - prev) / hz; if (pc < 0) pc = 0; }
}
}
}
*pcpu = pc;
*rss_kb = rss;
return 0;
}
/* ---- rows(填 sub_row_t 列表)---- */
static int bgm_rows(void *ctx, sub_row_t *rbuf, int max)
{
bgm_ctx_t *b = ctx;
bgmgr_ops_t ops;
memset(&ops, 0, sizeof ops);
ops.ctx = b;
ops.enumerate = bgm_enumerate;
ops.app_meta = bgm_app_meta;
ops.proc_sample = bgm_proc_sample;
b->n = bgmgr_collect(&ops, b->rows, MEOW_BGMR_ROW_MAX);
int r = 0;
if (r < max) {
rbuf[r].kind = SUB_ACT;
rbuf[r].label = "一键清除后台";
rbuf[r].sub = NULL;
rbuf[r].act = BGMR_CLR_ACT;
r++;
}
if (b->n == 0) {
if (r < max) {
rbuf[r].kind = SUB_ROW;
rbuf[r].label = meow_str_zh("bgmgr.empty", "无后台 App");
rbuf[r].sub = NULL;
rbuf[r].act = -1;
r++;
}
return r;
}
for (int i = 0; i < b->n && r < max; i++) {
char sub[80];
snprintf(sub, sizeof sub, "CPU %.1f%% · %s · %s", b->rows[i].pcpu,
bgmgr_mem_str(b->rows[i].rss_kb), bgmgr_status_str(&b->rows[i]));
rbuf[r].kind = SUB_ROW;
rbuf[r].label = b->rows[i].name[0] ? b->rows[i].name : b->rows[i].app_id;
rbuf[r].sub = sub;
rbuf[r].act = i; /* detail 回调用 i 定位行 */
r++;
}
return r;
}
/* kill 通道(bgmgr_kill_all 的函数指针签名:void(*)(const char*)) */
static void bgm_kill_fn(const char *app_id) { meow_bg_terminate(app_id); }
/* ---- detail(点 SUB_ROW 行=kill 该后台 App)---- */
static int bgm_detail(void *ctx, int idx, int act)
{
(void)idx;
bgm_ctx_t *b = ctx;
int i = act;
if (i < 0 || i >= b->n) return 0;
if (!bgmgr_can_kill(&b->rows[i])) {
snprintf(b->toast, sizeof b->toast, "该 App 不可杀(框架/前台)");
b->toast_ms = (long)now_ms();
return 1;
}
meow_bg_terminate(b->rows[i].app_id);
snprintf(b->toast, sizeof b->toast, "已请求终止 %s", b->rows[i].app_id);
b->toast_ms = (long)now_ms();
return 1;
}
/* ---- act(点 SUB_ACT:一键清除)---- */
static int bgm_act(void *ctx, int idx, int act)
{
(void)idx;
bgm_ctx_t *b = ctx;
if (act == BGMR_CLR_ACT) {
bgmgr_ops_t ops;
memset(&ops, 0, sizeof ops);
ops.ctx = b;
ops.enumerate = bgm_enumerate; /* 行已由 bgm_rows 建 */
int k = bgmgr_kill_all(&ops, b->rows, b->n, bgm_kill_fn);
if (k > 0) snprintf(b->toast, sizeof b->toast, "已清 %d 个后台 App", k);
else snprintf(b->toast, sizeof b->toast, "无后台 App 可清");
b->toast_ms = (long)now_ms();
return 1;
}
return 0;
}
/* ---- 入口:运行后台管理页(阻塞到返回)---- */
int run_bg_page(struct app *apps, int napps)
{
static bgm_ctx_t b;
memset(&b, 0, sizeof b);
b.apps = apps;
b.napps = napps;
sub_page_t p;
memset(&p, 0, sizeof p);
p.title = "后台管理";
p.rows = bgm_rows;
p.detail = bgm_detail;
p.act = bgm_act;
p.live = 1; /* 后台表/进程变化实时重扫 */
p.ctx = &b;
meow_subpage_run(&p);
/* 清除 CPU 差分缓存(页面会话结束) */
memset(g_cpu_pid, 0, sizeof g_cpu_pid);
memset(g_cpu_tick, 0, sizeof g_cpu_tick);
return 0;
}