fix(launcher/amsd): amsd MSG_AMS_START 拉起缺口——pending-start 抽 meow_ams_start 模块复用启动链 + 负错误码符修复
根因(真机日志实证,M2.92 遗留):meow_ams_pending_start_set 误返回正错误码 (-MEOW_ERR_BUSY=+8/-MEOW_ERR_INVAL=+1),ipc_dispatch 的 rc<0?-rc:MEOW_OK 把「有前台 App 拒绝/空包名非法」误判为 OK(0) → amsd 报 OK 但 pending 未置位 → App 永不拉起(am start 报 OK 但界面没出现/进程未起)。 launcher: - 新增自包含模块 meow_ams_start.c/.h(ops 注入 launch=launch_app/launch_activity、 resolve=resolve_chain、log=logmsg):pending 状态 + 拉起决策 + picker 上下文。 - set 统一返回负 MEOW_ERR_*(根因修复);consume 复用既有启动链路真正拉起目标 App, 列表未就绪(state!=0)保留 pending 待 2s 重扫重试,不再静默丢弃。 - main_menu run_menu 改经 meow_ams_start_consume 消费(保留 launch_fail_toast/菜单收尾); app_launcher 移除旧 g_ams_start/g_ams_pick_* 迁入模块;util meow_ipc_process 顶部同步 busy、 meow_ipc_init 末尾 meow_ams_launcher_init()。 - build_launcher.sh 编入 meow_ams_start.c;meow_launcher_internal.h 更新挂点声明。 amsd(协议层归属说明):cmd_start 的 busy 判定对照 launcher ACK 正幅度(busy=8), 原 (uint32_t)MEOW_ERR_BUSY=0xFFFFFFF8 永不匹配 → busy 落入 launcher offline,改 (uint32_t)(-MEOW_ERR_BUSY)。 验证:宿主 test_ams_start.c 33 断言全 PASS(合法->拉起链、activity chain 透传、空包名/非法/busy 不拉起不崩、列表未就绪保留 pending、无路由 NO ROUTE、启动失败 idx 回报、picker 取回); launcher 目录全量回归 test_media/bgplay/depends/notif_panel/notif_tf/settings_brightness/ vol_ramp_jack/host_icon + test_font_tier/weight 全 PASS;mipsel -Wall -Wextra 0 warning。 新 md5:meow_launcherd a8200ae1b0aa68d7262749db87377d49、meow_amsd 7ddc677ce7938bc9b95c83c17bc78295。(M2.98 待打包,未刷机)
This commit is contained in:
1 parent
7e7e45ae26
commit
980d161972
13 files changed
+446
-101
No files matched your search
Binary file not shown.
@@ -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 {
|
||||
|
||||
Binary file not shown.
@@ -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 残留) */
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)*/
|
||||
|
||||
@@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
|
||||
Binary file not shown.
@@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#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;
|
||||
}
|
||||
@@ -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/<id>/logs → logs/<id>(定义见下,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) {}
|
||||
|
||||
Reference in new issue
Block a user