feat(amsd): meow_amsd 活动管理器一期 + am CLI (agent-212, 用户定案) — svcd托管(trigger=boot); 生命周期注册表(状态/时长/退出码/信号/崩溃计数+历史环形); launcher launch/exit/stdout管道捕获上报(logcat式,崩溃尾巴drain不丢); am status/list/history/start/kill(root门); crash双保险转发; S89预置conf

amsd f8d7f61b / am cfc4cbd4 / launcher 97c1b222(含字库扫描签名门控修CPU100%)
宿主+qemu全链路验证通过
This commit is contained in:
MeowOS committed 2026-08-31 12:45:29 +08:00
1 parent b81e7438e4
commit 5fbf30755c
16 files changed
+1722 -4

No files matched your search

+255 -3
View File
@@ -10,6 +10,189 @@ int g_naccts = 0;
volatile int g_app_session_active = 0; /* §10.5:1=launch_app waitpid 前台会话中(util.c 读) */
/* ---- amsd 上报 / 控制挂点(AMSD_DESIGN §4):best-effort,amsd 不在不影响 launcher ---- */
#define MEOW_AMS_SOCK_DEFAULT "/usr/data/meow_ams.sock"
/* amsd socket 路径:运行时读 env MEOW_AMS_SOCK(宿主/qemu 覆写),缺省用默认。 */
static const char *ams_sock_path(void)
{
static char p[108];
static int once = 0;
if (!once) {
const char *e = getenv("MEOW_AMS_SOCK");
snprintf(p, sizeof p, "%s", (e && e[0]) ? e : MEOW_AMS_SOCK_DEFAULT);
once = 1;
}
return p;
}
/* up 秒(/proc/uptime 首字段)。失败 0。 */
static long ams_uptime(void)
{
FILE *f = fopen("/proc/uptime", "r");
if (!f) return 0;
char b[64];
long up = 0;
if (fgets(b, sizeof b, f)) up = (long)atof(b);
fclose(f);
return up;
}
/* 向 amsd 发一条多行负载:非阻塞 connect + 单次 write + close;失败静默丢。 */
static void ams_send(const char *payload, int len)
{
if (!payload || len <= 0) return;
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) return;
int fl = fcntl(fd, F_GETFL, 0);
if (fl >= 0) fcntl(fd, F_SETFL, fl | O_NONBLOCK);
struct sockaddr_un a;
memset(&a, 0, sizeof a);
a.sun_family = AF_UNIX;
strncpy(a.sun_path, ams_sock_path(), sizeof a.sun_path - 1);
if (connect(fd, (struct sockaddr *)&a, sizeof a) != 0) {
if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
struct pollfd pf;
pf.fd = fd; pf.events = POLLOUT; pf.revents = 0;
int r = poll(&pf, 1, 150);
if (r <= 0 || !(pf.revents & POLLOUT)) { close(fd); return; }
} else { close(fd); return; }
}
ssize_t w = write(fd, payload, (size_t)len);
(void)w;
close(fd);
}
/* launch 上报:pkg/activity/chain/pid。amsd 建表项 + 开 stdout 会话。 */
static void ams_report_launch(const struct app *a, const char *chain, pid_t pid)
{
if (!a) return;
char act[MEOW_ACT_MAX + 1] = "";
char ch[MEOW_CHAIN_MAX + 1] = "-";
if (chain && chain[0]) {
const char *top = strrchr(chain, ';');
top = top ? top + 1 : chain;
snprintf(act, sizeof act, "%s", top);
snprintf(ch, sizeof ch, "%s", chain);
}
char line[512];
int l = snprintf(line, sizeof line, "EV LAUNCH %s %s %s %d %ld %ld\n",
a->id, act, ch, (int)pid, (long)time(NULL), ams_uptime());
if (l > 0) ams_send(line, l);
}
/* exit 上报:kind(exit|sig)/code/dur_ms。异常退出由 amsd 判 crash + 转发 meow_crash。 */
static void ams_report_exit(const struct app *a, int status, long long dur_ms)
{
if (!a) return;
const char *kind;
int code;
if (WIFSIGNALED(status)) { kind = "sig"; code = WTERMSIG(status); }
else { kind = "exit"; code = WIFEXITED(status) ? WEXITSTATUS(status) : 1; }
char line[256];
int l = snprintf(line, sizeof line, "EV EXIT %s %s %d %lld %ld %ld\n",
a->id, kind, code, dur_ms, (long)time(NULL), ams_uptime());
if (l > 0) ams_send(line, l);
}
/* ---- stdout/stderr 管道捕获(AMSD_DESIGN §5):前台 waitpid 循环排空 + 按行转发 ---- */
static char g_ams_out_line[1024]; /* 未完结行缓冲(无 \n) */
static int g_ams_out_n = 0;
static char g_ams_out_flush[4096]; /* 已完结、待转发行 */
static int g_ams_out_flush_n = 0;
static int g_ams_out_had = 0; /* 本次迭代是否排空到数据(驱动短睡) */
static void ams_out_flush(void)
{
if (g_ams_out_flush_n > 0) {
ams_send(g_ams_out_flush, g_ams_out_flush_n);
g_ams_out_flush_n = 0;
}
}
/* 追加一行(line 为无 \n 的 NUL 串)到 flush 缓冲;满了先 flush。 */
static void ams_out_line(const struct app *a, pid_t pid, const char *line)
{
if (!a || !line) return;
int need = snprintf(NULL, 0, "EV OUT %s %d %ld %ld %s\n",
a->id, (int)pid, (long)time(NULL), ams_uptime(), line);
if (g_ams_out_flush_n + need + 1 > (int)sizeof g_ams_out_flush) ams_out_flush();
if (need < (int)sizeof g_ams_out_flush - 1 - g_ams_out_flush_n)
g_ams_out_flush_n += snprintf(g_ams_out_flush + g_ams_out_flush_n,
sizeof g_ams_out_flush - g_ams_out_flush_n,
"EV OUT %s %d %ld %ld %s\n",
a->id, (int)pid, (long)time(NULL), ams_uptime(), line);
}
/* 排空读端全部可用字节,按行切分转发;超长行截断标记。 */
static void ams_drain_stdout(const struct app *a, int fd, pid_t pid)
{
char tmp[2048];
ssize_t n;
while ((n = read(fd, tmp, sizeof tmp)) > 0) {
g_ams_out_had = 1;
for (ssize_t i = 0; i < n; i++) {
char c = tmp[i];
if (c == '\n') {
g_ams_out_line[g_ams_out_n] = '\0';
ams_out_line(a, pid, g_ams_out_line);
g_ams_out_n = 0;
} else if (c == '\r') {
/* 去尾 \r */
} else if (g_ams_out_n < (int)sizeof g_ams_out_line - 1) {
g_ams_out_line[g_ams_out_n++] = c;
} else {
/* 超长行:截断标记 + 重置(当前字符丢弃) */
g_ams_out_line[ (int)sizeof g_ams_out_line - 2 ] = '\0';
ams_out_line(a, pid, g_ams_out_line);
ams_out_line(a, pid, "[...trunc]");
g_ams_out_n = 0;
}
}
}
}
/* ---- amsd start/kill 挂点(主循环单写者)---- */
static struct { char pkg[MEOW_APP_ID_MAX]; char act[MEOW_ACT_MAX]; int pending; } g_ams_start;
static struct { char pkg[MEOW_APP_ID_MAX]; int pending; } g_ams_kill;
int meow_ams_pending_start_set(const char *pkg, const char *act)
{
if (!pkg || !pkg[0]) return -MEOW_ERR_INVAL;
if (g_app_session_active) return -MEOW_ERR_BUSY;
snprintf(g_ams_start.pkg, sizeof g_ams_start.pkg, "%s", pkg);
g_ams_start.act[0] = 0;
if (act && act[0]) snprintf(g_ams_start.act, sizeof g_ams_start.act, "%s", act);
g_ams_start.pending = 1;
return 0;
}
int meow_ams_take_start(char *pkg, int pn, char *act, int an)
{
if (!g_ams_start.pending) return 0;
g_ams_start.pending = 0;
if (pkg && pn > 0) snprintf(pkg, pn, "%s", g_ams_start.pkg);
if (act && an > 0) snprintf(act, an, "%s", g_ams_start.act);
return 1;
}
void meow_ams_pending_kill_set(const char *pkg)
{
if (!pkg || !pkg[0]) return;
snprintf(g_ams_kill.pkg, sizeof g_ams_kill.pkg, "%s", pkg);
g_ams_kill.pending = 1;
}
int meow_ams_kill_pending(const char *pkg)
{
if (!g_ams_kill.pending) return 0;
if (pkg && strcmp(g_ams_kill.pkg, pkg) != 0) return 0;
g_ams_kill.pending = 0;
g_ams_kill.pkg[0] = 0;
return 1;
}
/* ---- 阶段 D · M1:路由注册表辅助(主循环单写者,无锁)---- */
struct meow_acct *meow_acct_find(const char *app_id)
{
@@ -990,9 +1173,23 @@ static int launch_app_chain(struct app *a, const char *chain)
if (pipe(execpipe) < 0) { logmsg("exec pipe failed: %s", strerror(errno)); return -1; }
fcntl(execpipe[1], F_SETFD, FD_CLOEXEC);
/* AMSD_DESIGN §5:stdout/stderr 管道捕获(logcat 思路)。fork 前建管道;子进程 dup2 到
* stdout/stderr;父进程保留读端(O_NONBLOCK)+ 前台 waitpid 循环排空转发 amsd。
* 管道建失败仅 log 不崩(App 照常无捕获运行)。写端设 CLOEXEC:exec 成功后关闭写端,
* App 死 → 读端 EOF(崩溃尾巴可排空)。 */
int out_pipe[2] = { -1, -1 };
if (pipe(out_pipe) != 0) {
out_pipe[0] = -1; out_pipe[1] = -1;
logmsg("ams: stdout pipe fail: %s", strerror(errno));
} else {
fcntl(out_pipe[1], F_SETFD, FD_CLOEXEC);
}
pid_t pid = fork();
if (pid < 0) {
close(execpipe[0]); close(execpipe[1]);
if (out_pipe[0] >= 0) close(out_pipe[0]);
if (out_pipe[1] >= 0) close(out_pipe[1]);
logmsg("fork failed: %s", strerror(errno)); return -1;
}
if (pid == 0) {
@@ -1002,6 +1199,14 @@ static int launch_app_chain(struct app *a, const char *chain)
* (读全局 log.level),使 SDK meow_log() 按 app 分离写 logs/<id>/<ts>_<pid>.log 并做级别过滤。
* M2.x 简化:MEOW_LOG_DIR 从 logs/apps/<id>/logs 改注入 logs/<id>(目录不存在则建)。 */
close(execpipe[0]);
/* AMSD_DESIGN §5:stdout/stderr 合并重定向到管道写端(dup2 不清 CLOEXEC),
* 原始 out_pipe 两根 fd 关闭;成功 exec 后仅 fd1/2 持有写端,App 死 → EOF。 */
if (out_pipe[1] >= 0) {
dup2(out_pipe[1], STDOUT_FILENO);
dup2(out_pipe[1], STDERR_FILENO);
close(out_pipe[1]);
close(out_pipe[0]);
}
char *argv[] = { a->exec, NULL };
char env_path[320], env_log[320], env_data[320], env_cache[320];
char app_log_dir[288];
@@ -1130,6 +1335,12 @@ static int launch_app_chain(struct app *a, const char *chain)
_exit(127);
}
close(execpipe[1]);
if (out_pipe[1] >= 0) close(out_pipe[1]); /* parent: 关闭写端 */
int out_fd = out_pipe[0]; /* parent 保留读端,前台循环排空 */
if (out_fd >= 0) {
int fl = fcntl(out_fd, F_GETFL, 0);
if (fl >= 0) fcntl(out_fd, F_SETFL, fl | O_NONBLOCK);
}
int exe_err = 0;
ssize_t er = read(execpipe[0], &exe_err, sizeof exe_err);
close(execpipe[0]);
@@ -1138,9 +1349,13 @@ static int launch_app_chain(struct app *a, const char *chain)
strerror(exe_err), exe_err);
int st = 0;
waitpid(pid, &st, 0); /* reap the failed child */
if (out_fd >= 0) close(out_fd);
return -1;
}
/* AMSD_DESIGN §4.1:启动上报(pkg/activity/chain/pid),amsd 建表项 + 开 stdout 捕获会话。 */
ams_report_launch(a, chain, pid);
/* GESTURE_BACK §2.4:应用启动即写 immersive 状态,watchdog 依此隐藏/保留虚拟返回键 */
meow_write_app_state(a->immersive == '1' ? 1 : 0);
@@ -1181,6 +1396,8 @@ static int launch_app_chain(struct app *a, const char *chain)
* never fires on this unit, §18.12). Volume is snapped on the first
* tap and restored when the sequence completes. */
int status = 0;
unsigned long long ams_t0 = now_ms(); /* AMSD_DESIGN §4.1:运行时长起点 */
int ams_status_valid = 0; /* 仅真实 waitpid 回收才置 1(规避 sigterm/重启路径 status=0 假干净退出) */
int nav_clean_exit = 0; /* M3:MSG_NAV{EXIT} 主动退出 → 不按崩溃上报 */
int app_prev_vold = 0;
int app_seq_hits = 0;
@@ -1218,18 +1435,31 @@ static int launch_app_chain(struct app *a, const char *chain)
meow_notif_open_clear();
for (;;) {
int r = waitpid(pid, &status, WNOHANG);
if (r == pid) break;
if (r == pid) { ams_status_valid = 1; break; }
if (r < 0 && errno != EINTR) break;
if (sigterm_flag) { logmsg("sigterm during app"); kill(pid, SIGTERM); break; }
/* AMSD_DESIGN §5.1:排空 App stdout/stderr 管道(非阻塞),按行转发 amsd;App 写满不阻塞。 */
g_ams_out_had = 0;
if (out_fd >= 0) ams_drain_stdout(a, out_fd, pid);
/* M3 导航栈:前台会话排空 IPC(App 经 MSG_NAV REPORT/EXIT 上报导航/请求退出)。
* REPORT 更新 nav_stack(meow_ipc_process 内单写者);EXIT → 终止应用回主屏。 */
meow_ipc_process();
/* AMSD_DESIGN §4.3:amsd `am kill` → 前台循环消费 → SIGTERM(执行器=launcher)。 */
if (meow_ams_kill_pending(a->id)) {
logmsg("ams: kill %s -> SIGTERM", a->name);
kill(pid, SIGTERM);
usleep(300000);
if (waitpid(pid, &status, WNOHANG) != pid) { kill(pid, SIGKILL); waitpid(pid, &status, 0); }
ams_status_valid = 1;
break;
}
if (meow_nav_exit_pending()) {
logmsg("nav: app %s MSG_NAV{EXIT} -> terminate, return to home", a->name);
nav_clean_exit = 1;
kill(pid, SIGTERM);
usleep(300000);
if (waitpid(pid, &status, WNOHANG) != pid) { kill(pid, SIGKILL); waitpid(pid, &status, 0); }
ams_status_valid = 1;
break;
}
/* §10.5:App 前台顶部下滑(MSG_NOTIF_OPEN)→ 展开通知中心叠加会话。复用电源菜单
@@ -1248,6 +1478,7 @@ static int launch_app_chain(struct app *a, const char *chain)
/* App 会话中崩溃(overlay 内 proc_is_zombie 探测):回收僵尸取真实状态,
* 走统一应用退出路径(不 SIGCONT 死进程、不恢复死帧;launcher 不崩)。 */
waitpid(pid, &status, 0);
ams_status_valid = 1;
break;
}
}
@@ -1259,6 +1490,7 @@ static int launch_app_chain(struct app *a, const char *chain)
kill(pid, SIGTERM);
usleep(300000);
if (waitpid(pid, &status, WNOHANG) != pid) { kill(pid, SIGKILL); waitpid(pid, &status, 0); }
ams_status_valid = 1;
break;
}
/* in-app kill: VOL_DOWN x3 quick taps */
@@ -1323,6 +1555,7 @@ static int launch_app_chain(struct app *a, const char *chain)
usleep(500000);
kill(pid, SIGKILL);
waitpid(pid, &status, 0);
ams_status_valid = 1;
break;
}
}
@@ -1373,12 +1606,31 @@ static int launch_app_chain(struct app *a, const char *chain)
app_overlay_statusbar();
}
}
/* 顶条收合/恢复过渡期把循环睡得短些(~20ms),驱动更平滑的上移出屏;稳定后回 100ms。 */
usleep(meow_ui_statusbar_animating() ? 20000 : 100000);
/* 顶条收合/恢复过渡期把循环睡得短些(~20ms),驱动更平滑的上移出屏;稳定后回 100ms。
* AMSD_DESIGN §5.1:本轮排空到 stdout 数据 → 也睡短些,防止 App 写满管道被阻塞。 */
usleep((meow_ui_statusbar_animating() || g_ams_out_had) ? 20000 : 100000);
}
/* §10.5:App 前台会话结束,清会话标记(ipc_dispatch 不再受理 MSG_NOTIF_OPEN) */
g_app_session_active = 0;
meow_notif_open_clear();
/* AMSD_DESIGN §5.1:崩溃尾巴不丢 —— App 已死,终局排空剩余 stdout/stderr(含未完结行)
* 先转发 amsd,再报 EXIT / crash。仅当有读端(管道建立成功)才 drain。 */
if (out_fd >= 0) {
g_ams_out_had = 0;
ams_drain_stdout(a, out_fd, pid); /* 排空全部剩余(App 死 → read 返回 0=EOF) */
if (g_ams_out_n > 0) { /* 未完结尾行:强制提交 */
g_ams_out_line[g_ams_out_n] = '\0';
ams_out_line(a, pid, g_ams_out_line);
g_ams_out_n = 0;
}
ams_out_flush();
close(out_fd);
out_fd = -1;
}
/* AMSD_DESIGN §4.1:waitpid 回收上报(真实回收才报;规避 sigterm/电源菜单重启 status=0 的
* 假干净退出)。amsd 依 kind/code 判 crash 并转发 meow_crash(SVC 格式)。 */
if (ams_status_valid)
ams_report_exit(a, status, (long long)(now_ms() - ams_t0));
/* M2.x:App 异常退出(信号杀 / 非 0 退出码)→ best-effort 直报 meow_crash。
* 判据与 svcd reap_children 一致:failed = !(WIFEXITED && WEXITSTATUS==0)。干净 exit(0)
* 不上报;sigterm 关停/电源菜单重启路径 status 保持 0(未 waitpid)→ 视为干净退出不上报。 */
+76 -1
View File
@@ -683,6 +683,33 @@ static unsigned long long apps_dir_sig(void)
return sig;
}
/* M2.83 修补:字库目录签名(根+各 family 目录 mtime)。meow_font_scan_presets 内含逐套
* sha256 校验(gbk 套 6.8MB 级),绝不能每 2s 轮询全量扫——只在目录签名变化时扫。 */
static unsigned long long fonts_dir_sig(void)
{
char root[300];
snprintf(root, sizeof root, "/usr/data/mnt/sd_0/%s", MEOW_FONTS_CFG);
unsigned long long sig = 1469598103934665603ULL;
struct stat st;
if (stat(root, &st) != 0) return 0;
sig ^= (unsigned long long)st.st_mtime; sig *= 1099511628211ULL;
DIR *d = opendir(root);
if (d) {
struct dirent *de;
while ((de = readdir(d))) {
if (de->d_name[0] == '.') continue;
char p[400];
snprintf(p, sizeof p, "%s/%s", root, de->d_name);
if (stat(p, &st) == 0 && S_ISDIR(st.st_mode)) {
sig ^= (unsigned long long)st.st_mtime * 256 + (unsigned char)de->d_name[0];
sig *= 1099511628211ULL;
}
}
closedir(d);
}
return sig;
}
static void run_menu(struct app apps[MAX_APPS], int napps, int state,
long sim_tap_ms, long sim_exit_ms)
{
@@ -700,6 +727,7 @@ static void run_menu(struct app apps[MAX_APPS], int napps, int state,
long sim_tap_done = 0, sim_exit_done = 0;
long meow_last_poll = 0; /* M2.2:MeowOS 后台拔营(卡/网)轮询时间戳 */
unsigned long long apps_sig = 0; /* M2.77:应用目录签名(0=未初始化,首圈建立基线) */
unsigned long long fonts_sig = 0; /* M2.83:字库目录签名(门控 scan_presets,防 2s 轮询 sha256 风暴) */
load_config(); /* meow.conf on the card: bg_color etc. */
menu_icons_load(apps, napps); /* MD3:应用图标解码一次进缓存(不每帧解码) */
@@ -824,6 +852,45 @@ static void run_menu(struct app apps[MAX_APPS], int napps, int state,
/* 阶段 D · M0:菜单/主屏循环内排空 IPC(非阻塞;应用可随时查询/声明路由) */
meow_ipc_process();
/* AMSD_DESIGN §4.3:amsd `am start` → 消费 pending-start(仅菜单态/无前台 App 时拉起)。
* 镜像 sim-tap 启动路径:launch 后 menu_after_app_exit + dirty 重绘菜单。 */
{
char spkg[MEOW_APP_ID_MAX], sact[MEOW_ACT_MAX];
if (meow_ams_take_start(spkg, sizeof spkg, sact, sizeof sact)) {
if (state != 0) {
snprintf(toast, sizeof toast, "AMS START: app list not ready");
toast_ms = TOAST_MS;
} else {
int idx = -1;
for (int i = 0; i < napps; i++)
if (!strcmp(apps[i].id, spkg)) { idx = i; break; }
if (idx < 0) {
snprintf(toast, sizeof toast, "AMS START: NO APP %s", spkg);
toast_ms = TOAST_MS;
} else {
int ret;
if (sact[0]) {
char chain[MEOW_CHAIN_MAX]; int depth;
if (resolve_chain(spkg, sact, chain, &depth) == MEOW_OK)
ret = launch_activity(&apps[idx], chain);
else {
snprintf(toast, sizeof toast, "AMS: NO ROUTE %s/%s", spkg, sact);
toast_ms = TOAST_MS; ret = 0;
}
} else {
ret = launch_app(&apps[idx]);
}
if (ret != 0) {
snprintf(toast, sizeof toast, "CANNOT START: %s", apps[idx].name);
toast_ms = TOAST_MS;
}
menu_after_app_exit(ret == 0);
last_input_ms = now_ms(); /* M2.17:回主界面即视为输入 */
dirty = 1;
}
}
}
}
meow_notif_tick(); /* M2a:通知 TTL 10s 转储(§8)*/
meow_media_tick(); /* 阶段D附录B:媒体源死亡(播放中无 META 超时)清槽回空态 */
@@ -898,7 +965,15 @@ static void run_menu(struct app apps[MAX_APPS], int napps, int state,
int was_nocard = (state == 1);
if (!mfont_ready && meow_font_ensure()) { dirty = 1; logmsg("hotplug: font loaded late"); }
if (mfont_ready) meow_font_reload_multifont(); /* M2.5x:卡挂载后重配多字体(此前无全局可配) */
meow_font_scan_presets(); /* M2.82+:卡就绪后重扫字库(启动时卡未挂载会只有默认项) */
/* M2.83 修补:字库重扫改为签名门控——扫描含逐套 sha256(gbk 套 6.8MB 级),
* 每 2s 全量扫会把 CPU 烧满(真机实测 100%)。只在字体目录签名变化时扫。 */
{
unsigned long long fsig = fonts_dir_sig();
if (fsig != fonts_sig) {
fonts_sig = fsig;
meow_font_scan_presets();
}
}
if (was_nocard || state != 0 || napps == 0) {
struct app fresh[MAX_APPS];
int fn = scan_apps(fresh);
+18
View File
@@ -72,6 +72,10 @@ enum {
MSG_PICKER_RESULT = 0x0071, /* ↓ picker 返回值(候选结果回 A) */
MSG_META = 0x0072, /* ↑ 媒体会话上报元数据/进度/状态(阶段 D 附录 B) */
MSG_PLAYCTL = 0x0073, /* ↓ 播放控制下发/↑ 控制器转发到最近活跃媒体会话 */
/* ---- amsd 控制(AMSD_DESIGN §4.2):amsd→launcher,申请拉起/终止某 App ----
* 语义:amsd 为「启动/终止」决策者 → launcher(执行器)执行。payload 见下。 */
MSG_AMS_START = 0x00A0, /* → 请求 launcher 拉起某 App(pkg + 可选 activity) */
MSG_AMS_KILL = 0x00A1, /* → 请求 launcher 终止某 App(SIGTERM 语义) */
MSG_SYS = 0x00F0 /* ↓ 系统事件(守护关闭/服务死亡/播放器状态) */
};
@@ -319,4 +323,18 @@ typedef struct m_power_status {
char msg[96]; /* 结果/状态文本 */
} m_power_status_t;
/* ---- amsd 控制(AMSD_DESIGN §4.2)---- */
/* MSG_AMS_START →:请求 launcher 拉起某 App。app_id=包名(权威);activity 空=裸启动
* (launcher launch_app),非空=resolve_chain+launch_activity。 */
typedef struct m_ams_start {
char app_id[MEOW_APP_ID_MAX]; /* 32 */
char activity[MEOW_ACT_MAX]; /* 64;空=裸启动 */
} m_ams_start_t; /* 96 B */
/* MSG_AMS_KILL →:请求 launcher 终止某 App(SIGTERM 语义,执行器=launcher)。 */
typedef struct m_ams_kill {
char app_id[MEOW_APP_ID_MAX]; /* 32 */
} m_ams_kill_t; /* 32 B */
#endif /* MEOW_IPC_H */
@@ -642,6 +642,12 @@ void meow_nav_report(const char *app_id, uint32_t depth, const char *current);
void meow_nav_exit(void); /* 清栈(应用退出/EXIT) */
void meow_nav_request_exit(void); /* MSG_NAV{EXIT}:置位让前台 waitpid 循环终止 app */
int meow_nav_exit_pending(void); /* 读取并清除退出请求(前台 launch_* 循环轮询) */
/* ---- amsd 控制挂点(AMSD_DESIGN §4.3):主循环单写者,跨模块声明 ----
* pending-start/kill 仅在主循环上下文写入与消费(ipc_dispatch 写、run_menu 前台循环/菜单循环读)。 */
int meow_ams_pending_start_set(const char *pkg, const char *act); /* 有前台 App 时拒绝(返回 -MEOW_ERR_BUSY) */
int meow_ams_take_start(char *pkg, int pn, char *act, int an); /* 1=取到待拉起 App(清位);0=无 */
void meow_ams_pending_kill_set(const char *pkg); /* 请求终止某 pkg */
int meow_ams_kill_pending(const char *pkg); /* 匹配则清除并返回 1(前台循环轮询) */
void meow_dac_vol_write(int pct);
void meow_dac_vol_write_throttled(void);
Binary file not shown.
+33
View File
@@ -1190,6 +1190,39 @@ static void ipc_dispatch(struct meow_ipc_msg *m)
n.app_id, n.nav_type, n.depth, n.current);
break;
}
/* AMSD_DESIGN §4.3:amsd `am start`/`am kill` 控制请求。主循环单写者仅置位 pending
* (run_menu 前台循环 / launch_* waitpid 循环消费),不回执启动结果(一期 best-effort)。 */
case MSG_AMS_START: {
m_ams_start_t s;
memset(&s, 0, sizeof s);
memcpy(&s, m->payload, m->plen < (uint16_t)sizeof s ? m->plen : (uint16_t)sizeof s);
s.app_id[MEOW_APP_ID_MAX - 1] = 0;
s.activity[MEOW_ACT_MAX - 1] = 0;
int rc = meow_ams_pending_start_set(s.app_id, s.activity);
m_ack_t a;
a.code = (uint32_t)(rc < 0 ? -rc : MEOW_OK); /* 0=OK;MEOW_ERR_BUSY=有前台 App */
a.status = 0;
ipc_send(m->fd, MSG_ACK, m->req_id, &a, sizeof a);
logmsg("ipc: AMS_START app=%s act=\"%s\" -> ACK code=%u", s.app_id, s.activity, a.code);
break;
}
case MSG_AMS_KILL: {
m_ams_kill_t k;
memset(&k, 0, sizeof k);
memcpy(&k, m->payload, m->plen < (uint16_t)sizeof k ? m->plen : (uint16_t)sizeof k);
k.app_id[MEOW_APP_ID_MAX - 1] = 0;
m_ack_t a;
if (g_app_session_active) {
meow_ams_pending_kill_set(k.app_id);
a.code = MEOW_OK;
} else {
a.code = (uint32_t)MEOW_ERR_NOENT; /* 无前台 App 可终止 */
}
a.status = 0;
ipc_send(m->fd, MSG_ACK, m->req_id, &a, sizeof a);
logmsg("ipc: AMS_KILL app=%s -> ACK code=%u", k.app_id, a.code);
break;
}
/* M2a:通知存储(NOTIFICATION_SYSTEM.md §4/§7/§8)。IPC 线程只入队,此处在主循环
* 单写者落存储 + 回 ACK(与 HELLO/QUERY 同一线程模型,绝不在 socket IO 阻塞)。 */
case MSG_NOTIFY: {