diff --git a/patch_v2/meow_amsd/meow_amsd b/patch_v2/meow_amsd/meow_amsd index 9057612..fadd0b0 100755 Binary files a/patch_v2/meow_amsd/meow_amsd and b/patch_v2/meow_amsd/meow_amsd differ diff --git a/patch_v2/meow_amsd/meow_amsd.c b/patch_v2/meow_amsd/meow_amsd.c index 8182789..12e05bf 100644 --- a/patch_v2/meow_amsd/meow_amsd.c +++ b/patch_v2/meow_amsd/meow_amsd.c @@ -1171,7 +1171,8 @@ static int cmd_start(const char *spec, char *resp, int rn) hist_push(a, "ams_start", "intent (forwarded)", (long)time(NULL), read_uptime_sec()); snprintf(detail, sizeof detail, "ok forwarded"); emit_log("AMS start %s%s forwarded to launcher", pkg, act[0] ? "/" : ""); - } else if (rt == 0 && code == (uint32_t)MEOW_ERR_BUSY) { + } else if (rt == 0 && code == (uint32_t)(-MEOW_ERR_BUSY)) { + /* launcher ACK ack.code 携带正幅度(busy=8,见 util.c `rc<0?-rc:MEOW_OK`),非二补。 */ snprintf(detail, sizeof detail, "busy (app running)"); emit_log("AMS start %s -> BUSY (app running)", pkg); } else { diff --git a/patch_v2/meow_amsd/meow_amsd_host b/patch_v2/meow_amsd/meow_amsd_host index 4338029..5d71b54 100755 Binary files a/patch_v2/meow_amsd/meow_amsd_host and b/patch_v2/meow_amsd/meow_amsd_host differ diff --git a/patch_v2/meow_launcherd/app_launcher.c b/patch_v2/meow_launcherd/app_launcher.c index 7237845..cb2eb9b 100644 --- a/patch_v2/meow_launcherd/app_launcher.c +++ b/patch_v2/meow_launcherd/app_launcher.c @@ -1,6 +1,7 @@ /* app_launcher.c — MeowOS launcher 拆分模块(LAUNCHER_SPLIT_PLAN 拆分)。*/ /* 本文件由拆分工具从 meow_launcherd.c 按职责搬移;逻辑与原文 0-diff(仅 static 可见性按跨模块需求调整)。*/ #include "meow_launcher_internal.h" +#include "meow_ams_start.h" /* MSG_AMS_START pending/拉起决策(meow_ams_start.c) */ static struct meow_acct g_accts[MEOW_MAX_ACTS]; /* 主循环单写者 */ @@ -201,30 +202,9 @@ static int reap_wait(pid_t pid, int *status) } } -/* ---- amsd start/kill 挂点(主循环单写者)---- */ -static struct { char pkg[MEOW_APP_ID_MAX]; char act[MEOW_ACT_MAX]; int pending; } g_ams_start; +/* ---- amsd kill 挂点(主循环单写者;start 侧见 meow_ams_start.c)---- */ 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; @@ -241,34 +221,38 @@ int meow_ams_kill_pending(const char *pkg) return 1; } -/* ---- AMSD_DESIGN §6.2 二期:picker 上下文(随 pending-start 置位,launch_app_chain 消费后清)---- - * amsd `am picker` → MSG_AMS_START 扩载荷(picker=1)→ ipc_dispatch 经 set_ex 置位 + 武装 - * g_ams_pick_armed;main_menu 消费 pending-start(照旧 launch_app/launch_activity)时, - * launch_app_chain 读到武装位 → 注入 MEOW_PICKER_ACTION/REQ_ID/CALLER 到子进程 → 解除武装。 - * 单一写者(ipc_dispatch / launch_app_chain),且同一时刻仅一个 pending-start 在飞(v1 单前台 App), - * 故武装位不会错配到非 picker 启动。 */ -static char g_ams_pick_action[24]; -static char g_ams_pick_caller[MEOW_APP_ID_MAX]; -static uint32_t g_ams_pick_req; -static int g_ams_pick_armed; +/* ---- amsd-start ops 组装(launch_app/launch_activity + resolve_chain + logmsg)---- + * meow_ams_start_consume 经 ops.launch 复用既有启动链路真正拉起 App(chain 空=裸启动、 + * 非空=activity 链)。ops.launch 的 idx→struct app 映射经 g_ams_ctx 完成(run_menu 每次 + * 消费前 meow_ams_ctx_set(apps, napps))。 */ +static struct app *g_ams_ctx_apps; +static int g_ams_ctx_napps; -int meow_ams_pending_start_set_ex(const char *pkg, const char *act, - const char *pick_action, uint32_t pick_req, - const char *pick_caller) +void meow_ams_ctx_set(struct app *apps, int napps) { - int rc = meow_ams_pending_start_set(pkg, act); - if (rc != 0) return rc; /* 拒绝(有前台 App / 参数非法) */ - g_ams_pick_action[0] = 0; - g_ams_pick_caller[0] = 0; - g_ams_pick_req = 0; - g_ams_pick_armed = 0; - if (pick_action && pick_action[0]) { - snprintf(g_ams_pick_action, sizeof g_ams_pick_action, "%s", pick_action); - if (pick_caller) snprintf(g_ams_pick_caller, sizeof g_ams_pick_caller, "%s", pick_caller); - g_ams_pick_req = pick_req; - g_ams_pick_armed = 1; - } - return 0; + g_ams_ctx_apps = apps; + g_ams_ctx_napps = napps; +} + +static int ams_launch_cb(int idx, const char *chain) +{ + if (!g_ams_ctx_apps || idx < 0 || idx >= g_ams_ctx_napps) return -MEOW_ERR_NOENT; + if (chain && chain[0]) return launch_activity(&g_ams_ctx_apps[idx], chain); + return launch_app(&g_ams_ctx_apps[idx]); +} + +static int ams_resolve_cb(const char *app_id, const char *activity, char *chain, int *depth) +{ + return resolve_chain(app_id, activity, chain, depth); +} + +void meow_ams_launcher_init(void) +{ + static struct meow_ams_ops ops; + ops.launch = ams_launch_cb; + ops.resolve = ams_resolve_cb; + ops.log = logmsg; + meow_ams_ops_init(&ops); } /* 子进程 exec 前注入 picker env(action/req/caller 已由 launch_app_chain 入口捕获并解除全局武装, @@ -1452,17 +1436,13 @@ static int run_app_notif_overlay(struct app *a, pid_t pid) static int launch_app_chain(struct app *a, const char *chain) { logmsg("launch: %s (%s) chain=\"%s\"", a->name, a->exec, chain ? chain : ""); - /* §6.2 二期:入口即捕获 picker 上下文并解除全局武装——即便后续 exec 失败(!a->ok)返回, - * 武装位也已清,不会错配到下一次普通启动(单飞不泄漏)。 */ + /* §6.2 二期:入口即捕获 picker 上下文并解除武装(meow_ams_take_picker)——即便后续 + * exec 失败(!a->ok)返回,武装位也已清,不会错配到下一次普通启动(单飞不泄漏)。 */ char pick_action[24] = ""; char pick_caller[MEOW_APP_ID_MAX] = ""; uint32_t pick_req = 0; - if (g_ams_pick_armed) { - snprintf(pick_action, sizeof pick_action, "%s", g_ams_pick_action); - snprintf(pick_caller, sizeof pick_caller, "%s", g_ams_pick_caller); - pick_req = g_ams_pick_req; - g_ams_pick_armed = 0; - } + meow_ams_take_picker(pick_action, sizeof pick_action, + pick_caller, sizeof pick_caller, &pick_req); if (!a->ok) { logmsg("launch blocked: exec not ok"); return -1; } meow_dep_clear_reason(); /* 每次 launch 入口清 stale 依赖阻断原因(防上一 app 残留) */ diff --git a/patch_v2/meow_launcherd/build_launcher.sh b/patch_v2/meow_launcherd/build_launcher.sh index c0c763c..5139b00 100755 --- a/patch_v2/meow_launcherd/build_launcher.sh +++ b/patch_v2/meow_launcherd/build_launcher.sh @@ -28,6 +28,7 @@ SYSROOT="$ROOT/.tools/mipsel-linux-muslsf-cross/mipsel-linux-muslsf" subpage.c wifi_page.c bt_page.c now_playing.c \ font_theme.c keys.c util.c meow_dep.c \ meow_touch.c meow_settings.c meow_notif.c meow_media.c meow_bgplay.c \ + meow_ams_start.c \ "../sdk/meow_ui.a" "$SYSROOT/lib/libz.a" -lpthread -o meow_launcherd echo "== meow_launcherd ==" ls -la meow_launcherd diff --git a/patch_v2/meow_launcherd/main_menu.c b/patch_v2/meow_launcherd/main_menu.c index c08b88a..4faf14e 100644 --- a/patch_v2/meow_launcherd/main_menu.c +++ b/patch_v2/meow_launcherd/main_menu.c @@ -1,6 +1,7 @@ /* main_menu.c — MeowOS launcher 拆分模块(LAUNCHER_SPLIT_PLAN 拆分)。*/ /* 本文件由拆分工具从 meow_launcherd.c 按职责搬移;逻辑与原文 0-diff(仅 static 可见性按跨模块需求调整)。*/ #include "meow_launcher_internal.h" +#include "meow_ams_start.h" /* MSG_AMS_START 拉起决策(meow_ams_start.c):run_menu 消费用 */ /* set by run_menu: the player's panel was blanked (screen timeout) when the @@ -885,43 +886,28 @@ 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 时拉起)。 + /* AMSD_DESIGN §4.3:amsd `am start` → meow_ams_start_consume 消费 pending-start, + * 复用既有启动链路(launch_app/launch_activity)真正拉起目标 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) { - launch_fail_toast(toast, sizeof toast, apps[idx].name); - toast_ms = TOAST_MS; - } - menu_after_app_exit(ret == 0); - last_input_ms = now_ms(); /* M2.17:回主界面即视为输入 */ - dirty = 1; - } + const char *ids[MAX_APPS]; + for (int i = 0; i < napps; i++) ids[i] = apps[i].id; + meow_ams_ctx_set(apps, napps); /* ops.launch 的 idx→struct app 映射 */ + int app_ran = 0, idx = -1; + int r = meow_ams_start_consume(ids, napps, state, toast, sizeof toast, + &app_ran, &idx); + if (r == 1) { + if (!app_ran && idx >= 0 && idx < napps) { + /* 启动失败:复用 run_menu 的失败 toast(缺依赖/exec 失败,含 app 名)。 */ + launch_fail_toast(toast, sizeof toast, apps[idx].name); } + toast_ms = TOAST_MS; + menu_after_app_exit(app_ran); + last_input_ms = now_ms(); /* M2.17:回主界面即视为输入 */ + dirty = 1; + } else if (r == 2) { + /* 应用列表未就绪:pending 保留(下次重扫即拉起),仅提示,不红冲免忙循环。 */ + toast_ms = TOAST_MS; } } meow_notif_tick(); /* M2a:通知 TTL 10s 转储(§8)*/ diff --git a/patch_v2/meow_launcherd/meow_ams_start.c b/patch_v2/meow_launcherd/meow_ams_start.c new file mode 100644 index 0000000..1c1d763 --- /dev/null +++ b/patch_v2/meow_launcherd/meow_ams_start.c @@ -0,0 +1,129 @@ +/* meow_ams_start.c — MSG_AMS_START 拉起决策(AMSD_DESIGN §4.3)。 + * + * 独立模块(无 launcher 内部依赖,仅 meow_ipc.h):持有 pending-start 状态 + 拉起决策, + * 经 ops 注入真实启动链路(launch_app/launch_activity + resolve_chain + logmsg), + * 使「am start」在 run_menu 主循环中真正拉起目标 App。 + * + * 根因修复:历史实现 `meow_ams_pending_start_set` 返回 `-MEOW_ERR_BUSY`(=+8,正)而非 + * 负错误码,ipc_dispatch 的 `rc<0?-rc:MEOW_OK` 因此把「有前台 App 拒绝」误判为 OK(0), + * 且拒绝发生在置 pending 之前 → amsd 报 OK 但 App 永不拉起。本模块统一为负 MEOW_ERR_*。 + * + * 单一写者约定:pending 状态只在主循环上下文写入/消费(ipc_dispatch 写、run_menu 消费), + * 与 launcher 「单写者」纪律一致,无需锁。 + */ +#include "meow_ams_start.h" + +#include +#include + +struct meow_ams_start_state { + char pkg[MEOW_APP_ID_MAX]; + char act[MEOW_ACT_MAX]; + int pending; + /* picker 上下文(二期;pick_armed=1 时 next launch 注入 env) */ + char pick_action[24]; + char pick_caller[MEOW_APP_ID_MAX]; + uint32_t pick_req; + int pick_armed; +}; + +static struct meow_ams_start_state g_ams; /* 主循环单写者,无锁 */ +static struct meow_ams_ops g_ops; +static int g_busy; /* =g_app_session_active 镜像(主循环每圈同步) */ + +void meow_ams_ops_init(const struct meow_ams_ops *ops) +{ + if (ops) g_ops = *ops; + else memset(&g_ops, 0, sizeof g_ops); +} + +void meow_ams_set_busy(int busy) +{ + g_busy = busy; +} + +int meow_ams_pending_start_set(const char *pkg, const char *act) +{ + if (!pkg || !pkg[0]) return MEOW_ERR_INVAL; /* 负错误码(根因修复) */ + if (g_busy) return MEOW_ERR_BUSY; /* 有前台 App:拒绝(负错误码) */ + snprintf(g_ams.pkg, sizeof g_ams.pkg, "%s", pkg); + g_ams.act[0] = 0; + if (act && act[0]) snprintf(g_ams.act, sizeof g_ams.act, "%s", act); + g_ams.pending = 1; + return 0; +} + +int meow_ams_pending_start_set_ex(const char *pkg, const char *act, + const char *pick_action, uint32_t pick_req, + const char *pick_caller) +{ + int rc = meow_ams_pending_start_set(pkg, act); + if (rc != 0) return rc; /* 拒绝(有前台 App / 参数非法) */ + g_ams.pick_action[0] = 0; + g_ams.pick_caller[0] = 0; + g_ams.pick_req = 0; + g_ams.pick_armed = 0; + if (pick_action && pick_action[0]) { + snprintf(g_ams.pick_action, sizeof g_ams.pick_action, "%s", pick_action); + if (pick_caller) snprintf(g_ams.pick_caller, sizeof g_ams.pick_caller, "%s", pick_caller); + g_ams.pick_req = pick_req; + g_ams.pick_armed = 1; + } + return 0; +} + +int meow_ams_take_picker(char *action, int action_len, + char *caller, int caller_len, uint32_t *req) +{ + if (!g_ams.pick_armed) return 0; + g_ams.pick_armed = 0; + if (action && action_len > 0) snprintf(action, action_len, "%s", g_ams.pick_action); + if (caller && caller_len > 0) snprintf(caller, caller_len, "%s", g_ams.pick_caller); + if (req) *req = g_ams.pick_req; + return 1; +} + +int meow_ams_start_consume(const char *const *ids, int napps, int state, + char *toast, int toast_len, int *app_ran, int *idx_out) +{ + if (app_ran) *app_ran = 0; + if (idx_out) *idx_out = -1; + if (!g_ams.pending || g_busy) return 0; /* 无待拉起 / 前台 App 会话中 */ + + if (state != 0) { + /* 应用列表未就绪:保留 pending(不丢「am start」),等 run_menu 2s 重扫后重试。 */ + if (toast && toast_len > 0) snprintf(toast, toast_len, "AMS START: app list not ready"); + return 2; + } + + int idx = -1; + for (int i = 0; i < napps; i++) + if (ids[i] && !strcmp(ids[i], g_ams.pkg)) { idx = i; break; } + if (idx < 0) { + g_ams.pending = 0; + if (toast && toast_len > 0) snprintf(toast, toast_len, "AMS START: NO APP %s", g_ams.pkg); + return 1; + } + + char chain[MEOW_CHAIN_MAX] = ""; + if (g_ams.act[0]) { + int depth = 0; + int rc = g_ops.resolve ? g_ops.resolve(g_ams.pkg, g_ams.act, chain, &depth) + : MEOW_ERR_NOENT; + if (rc != MEOW_OK) { + g_ams.pending = 0; + if (toast && toast_len > 0) + snprintf(toast, toast_len, "AMS: NO ROUTE %s/%s", g_ams.pkg, g_ams.act); + return 1; + } + } + + g_ams.pending = 0; + if (idx_out) *idx_out = idx; + /* 复用既有启动链路(launch_app/launch_activity 阻塞至 app 退出或 exec 失败) + * chain 空=裸启动;非空=经 resolve_chain 的 activity 链(含参透传)。 + * 启动失败(lr<0)不在此设 toast:调用方经 idx_out 拿到 apps[idx].name 打 launch_fail_toast。 */ + int lr = g_ops.launch ? g_ops.launch(idx, chain[0] ? chain : NULL) : -MEOW_ERR_INTERNAL; + if (app_ran) *app_ran = (lr == 0); + return 1; +} diff --git a/patch_v2/meow_launcherd/meow_ams_start.h b/patch_v2/meow_launcherd/meow_ams_start.h new file mode 100644 index 0000000..8796aaa --- /dev/null +++ b/patch_v2/meow_launcherd/meow_ams_start.h @@ -0,0 +1,62 @@ +/* meow_ams_start.h — MSG_AMS_START 拉起决策(AMSD_DESIGN §4.3)。 + * + * 独立模块以便宿主单测:pending 状态 + 拉起决策都在此,启动/解析/日志经 ops 注入 + * (生产=launcher 既有链路 launch_app/launch_activity + resolve_chain + logmsg; + * 测试=假件,见 test_ams_start.c)。 + * + * 约定(与 launcher 其余模块一致):本模块函数返回负的 MEOW_ERR_*(-1=INVAL / -8=BUSY) + * 表示拒绝,0 表示受理。ipc_dispatch 用 `a.code=(uint32_t)(rc<0?-rc:MEOW_OK)` 把负错误码 + * 转成正的 ack.code(幅度);amsd 侧按 `(uint32_t)(-MEOW_ERR_BUSY)` 识别 busy。 + * —— 历史上 set() 误返回 `-MEOW_ERR_BUSY`(=+8,正)导致 ipc_dispatch 把拒绝当 OK, + * 表现为「am start 报 OK 但 App 未拉起」;本模块统一为负错误码(根因修复)。 + */ +#ifndef MEOW_AMS_START_H +#define MEOW_AMS_START_H + +#include "meow_ipc.h" /* MEOW_APP_ID_MAX / MEOW_ACT_MAX / MEOW_CHAIN_MAX / MEOW_ERR_* */ + +/* 启动/解析/日志回调注入(单测=假件;生产在 meow_ipc_init 末尾组装)。 */ +struct meow_ams_ops { + /* 执行启动。idx=调用方应用表下标;chain 空=裸启动。 + * 复用 launcher 既有链路:launch_app(&apps[idx]) / launch_activity(&apps[idx], chain)。 + * 返回 0=已受理(app 已运行至退出);<0=启动失败/未找到。 */ + int (*launch)(int idx, const char *chain); + /* 解析 activity 链(resolve_chain)。返回 MEOW_OK / MEOW_ERR_NOENT。 */ + int (*resolve)(const char *app_id, const char *activity, char *chain, int *depth); + /* 日志(可空=静默)。 */ + void (*log)(const char *fmt, ...); +}; + +/* 组装 ops(launcher meow_ipc_init 末尾 / 测试 main 调用)。launch/resolve 须非 NULL。 */ +void meow_ams_ops_init(const struct meow_ams_ops *ops); + +/* 同步「前台 App 会话」占用位(=g_app_session_active)。 + * busy=1 时 pending_start_set 拒绝(-MEOW_ERR_BUSY)且 consume 不动作。 */ +void meow_ams_set_busy(int busy); + +/* 置 pending(ipc_dispatch MSG_AMS_START 时调)。 + * pkg 空 → -MEOW_ERR_INVAL;busy → -MEOW_ERR_BUSY;否则置位并返回 0。 */ +int meow_ams_pending_start_set(const char *pkg, const char *act); + +/* 二期 picker 扩载荷:置 pending + 武装 picker 上下文(pick_action 非空时,launch_app_chain + * 入口经 meow_ams_take_picker 注入 MEOW_PICKER_ACTION/REQ_ID/CALLER)。错误语义同 set。 */ +int meow_ams_pending_start_set_ex(const char *pkg, const char *act, + const char *pick_action, uint32_t pick_req, + const char *pick_caller); + +/* 消费 pending(各主循环排空 IPC 后 / run_menu 循环调用)。 + * ids=napps 个应用主键串指针(apps[i].id);state=菜单态(0=就绪;非 0=列表未就绪不拉起, + * pending 保留待重扫重试)。toast/toast_len=错误提示槽(可空)。 + * app_ran=输出:1=app 已运行;0=未运行(拒绝/失败)。idx_out=输出:本次使用的应用表下标 + * (-1=未命中应用;>=0 且 app_ran=0 表示启动失败,调用方应据 apps[idx].name 打 launch_fail_toast)。 + * 返回:0=无 pending 或 busy(暂不动作);1=已消费(拉起成功/拒绝并清 pending,toast 已设); + * 2=列表未就绪(pending 保留待重扫重试,toast 已设「not ready」,调用方勿红冲)。 */ +int meow_ams_start_consume(const char *const *ids, int napps, int state, + char *toast, int toast_len, int *app_ran, int *idx_out); + +/* 消费拾取 picker 上下文(launch_app_chain 入口调用,一次性清位)。 + * 返回 1=有 picker 上下文;0=无。 */ +int meow_ams_take_picker(char *action, int action_len, + char *caller, int caller_len, uint32_t *req); + +#endif /* MEOW_AMS_START_H */ diff --git a/patch_v2/meow_launcherd/meow_launcher_internal.h b/patch_v2/meow_launcherd/meow_launcher_internal.h index 34d883d..0c18cbe 100644 --- a/patch_v2/meow_launcherd/meow_launcher_internal.h +++ b/patch_v2/meow_launcherd/meow_launcher_internal.h @@ -670,16 +670,23 @@ 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 / §6.2 二期):主循环单写者,跨模块声明 ---- - * 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=无 */ + * pending-start 状态/拉起决策在 meow_ams_start.c(自包含、可单测,见 meow_ams_start.h); + * pending-kill 在 app_launcher.c。仅在主循环上下文写入与消费(ipc_dispatch 写、 + * run_menu 前台循环/菜单循环读)。 */ void meow_ams_pending_kill_set(const char *pkg); /* 请求终止某 pkg */ int meow_ams_kill_pending(const char *pkg); /* 匹配则清除并返回 1(前台循环轮询) */ -/* §6.2 二期:带 picker 上下文的 pending-start(amsd `am picker` 经 MSG_AMS_START 扩载荷)。 - * pick_action 非空 → launch_app_chain 注入 MEOW_PICKER_ACTION/REQ_ID/CALLER 到子进程。 */ -int meow_ams_pending_start_set_ex(const char *pkg, const char *act, - const char *pick_action, uint32_t pick_req, - const char *pick_caller); +/* run_menu 消费 pending-start:复用 launch_app/launch_activity 链真正拉起目标 App。 + * 返回 0=无 pending/busy;1=已消费(拉起成功/拒绝);2=列表未就绪(pending 保留待重扫)。 + * app_ran=1=app 已运行(据以设 touch 冷却);idx_out>=0 且 app_ran=0 → 启动失败,打 launch_fail_toast。 */ +int meow_ams_start_consume(const char *const *ids, int napps, int state, + char *toast, int toast_len, int *app_ran, int *idx_out); +/* launch_app_chain 入口拾取 picker 上下文(一次性清位)。 */ +int meow_ams_take_picker(char *action, int action_len, + char *caller, int caller_len, uint32_t *req); +/* 组装 amsd-start ops(生产=launch_app/launch_activity+resolve_chain+logmsg;meow_ipc_init 末尾调)。 */ +void meow_ams_launcher_init(void); +/* 供 run_menu/idle 挂当前 apps[] 上下文(ops.launch 的 idx→app 映射用)。 */ +void meow_ams_ctx_set(struct app *apps, int napps); /* §6.2 二期:amsd 上报通道(best-effort,app_launcher.c 的 ams_send 公开包装)。 * util.c 转发运行时 declare 与 picker call/result 给 amsd 用。 */ void meow_ams_send(const char *payload, int len); diff --git a/patch_v2/meow_launcherd/meow_launcherd b/patch_v2/meow_launcherd/meow_launcherd index fa057af..2863997 100755 Binary files a/patch_v2/meow_launcherd/meow_launcherd and b/patch_v2/meow_launcherd/meow_launcherd differ diff --git a/patch_v2/meow_launcherd/test_ams_start.c b/patch_v2/meow_launcherd/test_ams_start.c new file mode 100644 index 0000000..db0fbfb --- /dev/null +++ b/patch_v2/meow_launcherd/test_ams_start.c @@ -0,0 +1,172 @@ +/* test_ams_start.c — 宿主单测:MSG_AMS_START 拉起决策(meow_ams_start.c)。 + * + * 覆盖(AMSD_DESIGN §4.3 缺口回归): + * 1) 合法 MSG_AMS_START(裸启动 act 空)→ 置 pending → consume → ops.launch(idx, NULL); + * 2) 合法 MSG_AMS_START + activity → resolve → consume → ops.launch(idx, chain); + * 3) 参数非法(pkg 空)→ meow_ams_pending_start_set 返回 MEOW_ERR_INVAL,不置位、不拉起、不崩; + * 4) 有前台 App(busy)→ set 返回 MEOW_ERR_BUSY(负错误码,根因修复),不拉起; + * 5) 列表未就绪(state!=0)→ consume 返回 2(pending 保留),不拉起;后就绪再 consume → 拉起; + * 6) pkg 不在应用表 → consume 返回 1,toast「NO APP」,不拉起; + * 7) activity 无路由 → consume 返回 1,toast「NO ROUTE」,不拉起; + * 8) 启动失败(ops.launch 返 <0)→ consume 返回 1,app_ran=0,idx_out 命中(供 launch_fail_toast); + * 9) picker 扩载荷:set_ex 武装 → take_picker 取回并清位。 + * 只依赖 meow_ams_start.c + meow_ipc.h(纯 C),宿主 gcc 直编: + * gcc -Wall -Wextra -std=gnu99 test_ams_start.c meow_ams_start.c -o test_ams_start + */ +#include +#include +#include +#include +#include "meow_ams_start.h" +#include "meow_ipc.h" + +static int g_fails = 0; +#define CHECK(cond, msg) do { \ + if (cond) printf("PASS: %s\n", msg); \ + else { printf("FAIL: %s\n", msg); g_fails++; } } while (0) + +/* ---- 假件 ---- */ +static int g_launch_calls = 0; /* ops.launch 被调次数 */ +static int g_launch_last_idx = -1; +static char g_launch_last_chain[MEOW_CHAIN_MAX]; +static int g_launch_ret = 0; /* 可注入:0=成功 / <0=启动失败 */ +static int g_resolve_calls = 0; +static int g_resolve_rc = MEOW_OK; /* 可注入:MEOW_OK / MEOW_ERR_NOENT */ +static int g_log_calls = 0; + +static int fake_launch(int idx, const char *chain) +{ + g_launch_calls++; + g_launch_last_idx = idx; + if (chain) snprintf(g_launch_last_chain, sizeof g_launch_last_chain, "%s", chain); + else g_launch_last_chain[0] = 0; + return g_launch_ret; +} + +static int fake_resolve(const char *app_id, const char *activity, + char *chain, int *depth) +{ + (void)app_id; + g_resolve_calls++; + if (g_resolve_rc != MEOW_OK) return g_resolve_rc; + snprintf(chain, MEOW_CHAIN_MAX, "%s", activity); /* 简化:chain=activity */ + if (depth) *depth = 1; + return MEOW_OK; +} + +static void fake_log(const char *fmt, ...) { (void)fmt; g_log_calls++; } + +static void reset_fakes(void) +{ + g_launch_calls = 0; g_launch_last_idx = -1; + g_launch_last_chain[0] = 0; g_launch_ret = 0; + g_resolve_calls = 0; g_resolve_rc = MEOW_OK; + g_log_calls = 0; + meow_ams_set_busy(0); +} + +/* 应用表:id 主键串 + 用于 idx 断言 */ +static const char *g_ids[3] = { "dev.player.ncm", "os.meow.files", "os.meow.hello" }; + +int main(void) +{ + static struct meow_ams_ops ops; + ops.launch = fake_launch; + ops.resolve = fake_resolve; + ops.log = fake_log; + meow_ams_ops_init(&ops); + + char toast[64]; + int app_ran = 0, idx = -1; + + /* ---- 1) 合法裸启动(act 空)→ consume → ops.launch ---- */ + reset_fakes(); + CHECK(meow_ams_pending_start_set("dev.player.ncm", "") == 0, "set(bare): 受理"); + int r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); + CHECK(r == 1 && g_launch_calls == 1, "consume(bare): 拉起链路被调"); + CHECK(g_launch_last_idx == 0, "consume(bare): idx 命中 dev.player.ncm"); + CHECK(g_launch_last_chain[0] == 0, "consume(bare): chain 空(裸启动)"); + CHECK(app_ran == 1, "consume(bare): app_ran=1"); + + /* ---- 2) activity 链 → resolve → consume → ops.launch(chain) ---- */ + reset_fakes(); + CHECK(meow_ams_pending_start_set("os.meow.files", "pick") == 0, "set(act): 受理"); + r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); + CHECK(r == 1 && g_resolve_calls == 1, "consume(act): resolve 被调"); + CHECK(g_launch_calls == 1 && g_launch_last_idx == 1, "consume(act): ops.launch(idx=1)"); + CHECK(g_launch_last_chain[0] && strcmp(g_launch_last_chain, "pick") == 0, + "consume(act): chain 透传 pick"); + + /* ---- 3) 参数非法(pkg 空)→ INVAL,不置位、不拉起 ---- */ + reset_fakes(); + CHECK(meow_ams_pending_start_set("", "") == MEOW_ERR_INVAL, "set(空 pkg): 返回负错误码 MEOW_ERR_INVAL"); + CHECK(meow_ams_pending_start_set(NULL, NULL) == MEOW_ERR_INVAL, "set(NULL pkg): MEOW_ERR_INVAL"); + r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); + CHECK(r == 0 && g_launch_calls == 0, "consume: 空 pkg 未置位 → 不拉起"); + + /* ---- 4) busy(有前台 App)→ BUSY,不拉起 ---- */ + reset_fakes(); + meow_ams_set_busy(1); + CHECK(meow_ams_pending_start_set("os.meow.hello", "") == MEOW_ERR_BUSY, + "set(busy): 返回负错误码 MEOW_ERR_BUSY(根因修复:旧实现返回 +8 被误判 OK)"); + r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); + CHECK(r == 0 && g_launch_calls == 0, "consume(busy): 不拉起"); + meow_ams_set_busy(0); + + /* ---- 5) 列表未就绪(state!=0)→ 保留 pending,后就绪拉起 ---- */ + reset_fakes(); + CHECK(meow_ams_pending_start_set("dev.player.ncm", "") == 0, "set: 受理"); + r = meow_ams_start_consume(g_ids, 3, 2, toast, sizeof toast, &app_ran, &idx); + CHECK(r == 2 && g_launch_calls == 0, "consume(state!=0): 返回 2 保留 pending,不拉起"); + CHECK(toast[0] && strstr(toast, "not ready"), "consume(state!=0): toast=列表未就绪"); + r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); + CHECK(r == 1 && g_launch_calls == 1 && g_launch_last_idx == 0, + "consume(后就绪): pending 保留后拉起成功"); + + /* ---- 6) pkg 不在应用表 → NO APP,不拉起 ---- */ + reset_fakes(); + CHECK(meow_ams_pending_start_set("no.such.app", "") == 0, "set(未知app): 受理"); + r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); + CHECK(r == 1 && g_launch_calls == 0, "consume(未知app): 不拉起"); + CHECK(toast[0] && strstr(toast, "NO APP"), "consume(未知app): toast=NO APP"); + + /* ---- 7) activity 无路由 → NO ROUTE,不拉起 ---- */ + reset_fakes(); + g_resolve_rc = MEOW_ERR_NOENT; + CHECK(meow_ams_pending_start_set("os.meow.files", "nope") == 0, "set(nope): 受理"); + r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); + CHECK(r == 1 && g_launch_calls == 0, "consume(无路由): 不拉起"); + CHECK(toast[0] && strstr(toast, "NO ROUTE"), "consume(无路由): toast=NO ROUTE"); + g_resolve_rc = MEOW_OK; + + /* ---- 8) 启动失败(ops.launch<0)→ app_ran=0,idx_out 命中 ---- */ + reset_fakes(); + g_launch_ret = -2; /* MEOW_ERR_NOENT */ + CHECK(meow_ams_pending_start_set("dev.player.ncm", "") == 0, "set: 受理"); + r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); + CHECK(r == 1 && g_launch_calls == 1, "consume(启动失败): 拉起被调(失败)"); + CHECK(app_ran == 0 && idx == 0, "consume(启动失败): app_ran=0 且 idx_out=0(供 launch_fail_toast)"); + g_launch_ret = 0; + + /* ---- 9) picker 扩载荷:set_ex 武装 → take_picker 取回并清位 ---- */ + reset_fakes(); + CHECK(meow_ams_pending_start_set_ex("os.meow.files", "pick", "pick_file", 7, "dev.player.ncm") == 0, + "set_ex(picker): 受理"); + char act[24] = "", caller[MEOW_APP_ID_MAX] = ""; + uint32_t req = 0; + CHECK(meow_ams_take_picker(act, sizeof act, caller, sizeof caller, &req) == 1, + "take_picker: 有上下文"); + CHECK(strcmp(act, "pick_file") == 0 && req == 7 && strcmp(caller, "dev.player.ncm") == 0, + "take_picker: action/req/caller 正确"); + CHECK(meow_ams_take_picker(act, sizeof act, caller, sizeof caller, &req) == 0, + "take_picker: 二次取回=0(一次性清位)"); + + /* 非 picker 启动:set(非 set_ex)不武装 picker */ + reset_fakes(); + CHECK(meow_ams_pending_start_set("dev.player.ncm", "") == 0, "set(bare): 受理"); + CHECK(meow_ams_take_picker(act, sizeof act, caller, sizeof caller, &req) == 0, + "take_picker: 普通启动无 picker 上下文"); + + printf("\n%s (%d fails)\n", g_fails ? "FAILED" : "ALL PASS", g_fails); + return g_fails ? 1 : 0; +} diff --git a/patch_v2/meow_launcherd/util.c b/patch_v2/meow_launcherd/util.c index ee79f4f..7170bd9 100644 --- a/patch_v2/meow_launcherd/util.c +++ b/patch_v2/meow_launcherd/util.c @@ -1,6 +1,7 @@ /* util.c — MeowOS launcher 拆分模块(LAUNCHER_SPLIT_PLAN 拆分)。*/ /* 本文件由拆分工具从 meow_launcherd.c 按职责搬移;逻辑与原文 0-diff(仅 static 可见性按跨模块需求调整)。*/ #include "meow_launcher_internal.h" +#include "meow_ams_start.h" /* MSG_AMS_START pending/拉起决策(meow_ams_start.c):set/set_ex + busy 同步 + ops 组装 */ /* M2.x 应用日志目录简化:一次性迁移 logs/apps//logs → logs/(定义见下,log_open_card 先调用)。 */ static void log_migrate_apps(void); @@ -1112,6 +1113,9 @@ int meow_ipc_init(void) /* M4:注入后台保活/后台服务 ops(logmsg/sd_device_present/通知清理/熔断发帧)。 */ meow_bgplay_launcher_init(); + /* AMSD_DESIGN §4.3:注入 amsd-start ops(launch=launch_app/launch_activity,resolve=resolve_chain)。 */ + meow_ams_launcher_init(); + ipc_event_fd = eventfd(0, EFD_NONBLOCK); if (ipc_event_fd < 0) logmsg("ipc: eventfd fail %s (10ms poll drains anyway)", strerror(errno)); @@ -1130,6 +1134,9 @@ int meow_ipc_init(void) * 事件fd 仅为低延迟优化;若为 -1,10ms 轮询仍保证每圈排空。 */ void meow_ipc_process(void) { + /* AMSD_DESIGN §4.3:同步「前台 App 会话」占用位 → meow_ams_pending_start_set 据此 + * 在收到 MSG_AMS_START 时正确拒绝(-MEOW_ERR_BUSY),杜绝「报 OK 但未拉起」。 */ + meow_ams_set_busy(g_app_session_active); if (ipc_event_fd >= 0) { uint64_t v; while (read(ipc_event_fd, &v, sizeof v) == (ssize_t)sizeof v) {} diff --git a/scripts/build/fw_content.conf b/scripts/build/fw_content.conf index a5579ce..254d348 100644 --- a/scripts/build/fw_content.conf +++ b/scripts/build/fw_content.conf @@ -28,7 +28,7 @@ # ---- 用户态/服务守护二进制(usr/bin,全部 chmod 755 静态 MIPS)---- patch_v2/bootmenu/meow_bootmenu|/usr/bin/meow_bootmenu|4441822d87e76bfce2ce584604ac3830|引导选单 M2.85(sysui让位 agent-216) -patch_v2/meow_launcherd/meow_launcherd|/usr/bin/meow_launcherd|899ac22c10f596909d7f35ea685744de|主屏 launcher M4(后台服务保活+后台播放:MSG_REGISTER/SERVICE_STOP/MSG_SYS 退避熔断;nav-exit 播放中转后台保活,SDK present 自抑非 SIGSTOP;app.json depends 二进制依赖启动前检查:缺 required 阻断/optional 放行) +patch_v2/meow_launcherd/meow_launcherd|/usr/bin/meow_launcherd|a8200ae1b0aa68d7262749db87377d49|主屏 launcher M4(后台服务保活+后台播放:MSG_REGISTER/SERVICE_STOP/MSG_SYS 退避熔断;nav-exit 播放中转后台保活,SDK present 自抑非 SIGSTOP;app.json depends 二进制依赖启动前检查:缺 required 阻断/optional 放行);M2.98 待打包:amsd MSG_AMS_START 拉起缺口修复(pending-start 抽 meow_ams_start 模块复用启动链真正拉起;负错误码符修复 busy/空pkg 正确拒,不再误报 OK) patch_v2/meow_watchdog/meow_watchdog|/usr/bin/meow_watchdog|553840cdc79d8d7bed766a5e3981ac57|应用看门狗 M2.97(音量反转 + DS1039 平方锥度 + 插孔消抖) patch_v2/meow_svcd/meow_svcd|/usr/bin/meow_svcd|605646c4cc93f2baf29071ea6bc870ea|服务管理器 M2.77(+崩溃上报 meow_crash agent-187) mips-repo/bins/meow_mdns/bin/meow_mdns|/usr/bin/meow_mdns|d6b0151bda26768f19952dae1342610d|mDNS 应答器 M2.78(启动竞态+换IP重检 agent-194) @@ -46,7 +46,7 @@ patch_v2/meow_powermgr/meow_powermgr|/usr/bin/meow_power|469dddb74de424f7b9bc9c1 # ---- 开机 init 脚本(etc/init.d,数字决定启动顺序,按文件名自然序)---- patch_v2/meow_sysui/S70meow_sysui|/etc/init.d/S70meow_sysui|e435affcb56499e5229728c6ae2524b9|sysui 自启 M2.85(launcher.conf enabled=1+迁移) patch_v2/meow_svcd/S89meow_svcd|/etc/init.d/S89meow_svcd|f6ded7f835dba882c17dac52b9430a45|svcd 自启 M2.97(meow_route.conf → audiod.conf + 清理旧 route conf + 预置 meow_powermgr.conf/迁移热更 exec→/usr/bin,agent-244/246/267) -patch_v2/meow_amsd/meow_amsd|/usr/bin/meow_amsd|da9a5004c6fdb7827c45c27daa825919|活动管理器 M2.91(注册表自愈:卡未挂载扫空 3s 快重试+30s 合并重扫+am rescan;兼容旧 launcher 5 段 EV LAUNCH;agent-250) +patch_v2/meow_amsd/meow_amsd|/usr/bin/meow_amsd|7ddc677ce7938bc9b95c83c17bc78295|活动管理器 M2.91(注册表自愈:卡未挂载扫空 3s 快重试+30s 合并重扫+am rescan;兼容旧 launcher 5 段 EV LAUNCH;agent-250);M2.98 待打包:am start busy 识别修复(对照 launcher ACK 正幅度 busy=8,bool 误判二补 0xFFFFFFF8 导致 busy 落入 launcher offline) patch_v2/meow_amsd/am|/usr/bin/am|9e323faf1aeb0c6a6fb0425565cf525d|amsd 用户侧CLI(+resolve/picker/rescan) patch_v2/bootmenu/S90meow_bootmenu|/etc/init.d/S90meow_bootmenu|e0ace111ce5ee446a1e3c6605ac1e9c9|bootmenu 自启 M2.85(注释同步) patch_v2/bootmenu/S92_03_start_music_player|/etc/init.d/S92_03_start_music_player|c17257aa3d93f75da72903b57cbe1034|§4.3 兜底 patch(rcS 正常路径被 S90 阻塞不执行)