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:
MeowOS committed 2026-09-01 19:40:29 +08:00
1 parent 7e7e45ae26
commit 980d161972
13 files changed
+446 -101

No files matched your search

+172
View File
@@ -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;
}