feat(launcher): M3导航栈+M5通知点击跳转 (agent-202) — nav_stack(≤16深)+MSG_NAV REPORT/EXIT+launch_activity链路注入(MEOW_ACTIVITY/MEOW_CHAIN)+栈根退出回主屏; 通知点击按route_status(0拉起/1 NO ROUTE/2 SOURCE NOT FOUND/-1 NONE关面板不动作); picker协议位预留(0x0070/0x0071); 裸App兼容

launcher 4fe4103a→a42171a1; qemu实测 declare/query/nav report/exit/notify+querynotif 全过
发现: §10.5 App前台通知中心叠加会话(MSG_NOTIF_OPEN)实际未实现,文档超前,待补
This commit is contained in:
MeowOS committed 2026-08-31 06:27:10 +08:00
1 parent 955dbe1228
commit 85b4aea24f
9 files changed
+321 -39

No files matched your search

+112 -4
View File
@@ -124,6 +124,72 @@ int resolve_chain(const char *app_id, const char *activity, char *out, int *dept
}
/* ---- M3 导航栈(FRAMEWORK_DESIGN §3):每 app ≤16 深,v1 同一时刻单前台应用 ----
* launcher 侧镜像:launch_* 冷启动时以链路深度初始化;App 经 MSG_NAV{REPORT,depth,current}
* 上报栈深变化(launcher 更新);MSG_NAV{EXIT} → 置退出请求,前台 waitpid 循环终止 app 回主屏。
* 单一写者=主循环(meow_ipc_process 排空处 / launch_* 前台循环),无需锁。 */
static struct {
char app_id[MEOW_APP_ID_MAX];
int depth; /* 0=无/未上报;1..MEOW_NAV_MAX_DEPTH */
char top[MEOW_ACT_MAX]; /* 当前顶层 activity_id(REPORT 上报) */
int active; /* 1=前台应用有导航栈 */
} g_nav;
static volatile int g_nav_exit_req = 0;
void meow_nav_begin(const char *app_id, const char *chain)
{
if (!app_id) return;
memset(&g_nav, 0, sizeof g_nav);
strncpy(g_nav.app_id, app_id, MEOW_APP_ID_MAX - 1);
g_nav.app_id[MEOW_APP_ID_MAX - 1] = 0;
if (chain && chain[0]) {
g_nav.depth = chain_depth(chain);
if (g_nav.depth > MEOW_NAV_MAX_DEPTH) g_nav.depth = MEOW_NAV_MAX_DEPTH;
const char *top = strrchr(chain, ';');
top = top ? top + 1 : chain;
strncpy(g_nav.top, top, MEOW_ACT_MAX - 1);
g_nav.top[MEOW_ACT_MAX - 1] = 0;
}
g_nav.active = 1;
logmsg("nav: begin app=%s chain=\"%s\" depth=%d top=%s",
app_id, chain ? chain : "(bare)", g_nav.depth, g_nav.top);
}
void meow_nav_report(const char *app_id, uint32_t depth, const char *current)
{
if (!app_id) return;
if (depth == 0 || depth > MEOW_NAV_MAX_DEPTH) { meow_nav_exit(); return; }
g_nav.active = 1;
strncpy(g_nav.app_id, app_id, MEOW_APP_ID_MAX - 1);
g_nav.app_id[MEOW_APP_ID_MAX - 1] = 0;
g_nav.depth = (int)depth;
if (current) { strncpy(g_nav.top, current, MEOW_ACT_MAX - 1); g_nav.top[MEOW_ACT_MAX - 1] = 0; }
logmsg("nav: report app=%s depth=%d top=%s", app_id, g_nav.depth, g_nav.top);
}
void meow_nav_exit(void)
{
g_nav.active = 0;
g_nav.depth = 0;
g_nav.top[0] = 0;
logmsg("nav: exit (cleared)");
}
void meow_nav_request_exit(void)
{
g_nav_exit_req = 1;
meow_nav_exit();
logmsg("nav: EXIT requested");
}
int meow_nav_exit_pending(void)
{
int r = g_nav_exit_req;
g_nav_exit_req = 0;
return r;
}
/* ---- M2.17 keep_awake:前台当前顶层 activity 追踪 ----
* 前台应用经 IPC MSG_ACT_FOCUS/BACK 上报其当前顶层 activity;fg_keep_awake() 依注册表判断该
* activity 是否声明 keep_awake(app.json awake.<id>=1 / route.<id>.keep_awake=1;运行时同类)。
@@ -815,10 +881,13 @@ static void notify_app_crash(const char *pkg, int status)
close(fd);
}
int launch_app(struct app *a)
/* launch_app_chain:launch_app / launch_activity 的共享核心。
* chain 非空 → 向子进程注入 MEOW_ACTIVITY/MEOW_CHAIN 并初始化 nav_stack(M3);空/NULL → 裸启动。 */
static int launch_app_chain(struct app *a, const char *chain)
{
logmsg("launch: %s (%s)", a->name, a->exec);
logmsg("launch: %s (%s) chain=\"%s\"", a->name, a->exec, chain ? chain : "");
if (!a->ok) { logmsg("launch blocked: exec not ok"); return -1; }
meow_nav_begin(a->id, chain); /* M3:冷启动/裸启动都以当前链深初始化导航栈(chain 空→depth 0) */
/* M2.12:启动前准备 MeowData/<pkg>/{data,cache}(幂等;卡不在/失败只 log 不崩)。
* 应用经环境变量 MEOW_DATA_DIR/MEOW_CACHE_DIR 定位数据(exFAT 无 symlink)。 */
@@ -863,7 +932,9 @@ int launch_app(struct app *a)
char env_tier[24];
char env_fs[20];
char env_cls[MEOW_FCLS_COUNT][2][400];
char *envp[40]; /* PATH,HOME,log_dir,log_app,log_lvl,font 全局(3),多字体(1),tier(1),class(≤12),fullscreen(1),data,cache */
char env_act[MEOW_ACT_MAX + 16]; /* M3:MEOW_ACTIVITY=<target>(链路顶端) */
char env_chain[MEOW_CHAIN_MAX + 16]; /* M3:MEOW_CHAIN=<id0>;<id1>;...;<top> */
char *envp[48]; /* PATH,HOME,log_dir,log_app,log_lvl,font 全局(3),多字体(1),tier(1),class(≤12),fullscreen(1),data,cache,act,chain */
int ne = 0;
const char *cp = getenv("PATH");
snprintf(env_path, sizeof env_path, "PATH=%s", cp ? cp : "/usr/bin:/bin");
@@ -961,6 +1032,18 @@ int launch_app(struct app *a)
envp[ne++] = env_data;
envp[ne++] = env_cache;
}
/* M3:冷启动/通知跳转注入 activity 链(FRAMEWORK_DESIGN §3)。chain 非空才注入;
* MEOW_ACTIVITY=链路顶端(最后一段=target),MEOW_CHAIN=完整链路。裸启动不注入
* (App 走 SDK 默认首页)。 */
if (chain && chain[0]) {
const char *top = strrchr(chain, ';');
top = top ? top + 1 : chain;
snprintf(env_act, sizeof env_act, "MEOW_ACTIVITY=%s", top);
snprintf(env_chain, sizeof env_chain, "MEOW_CHAIN=%s", chain);
envp[ne++] = env_act;
envp[ne++] = env_chain;
logmsg("launch: M3 env %s / %s", env_act, env_chain);
}
envp[ne] = NULL;
execve(a->exec, argv, envp);
int e = errno;
@@ -1019,6 +1102,7 @@ int launch_app(struct app *a)
* never fires on this unit, §18.12). Volume is snapped on the first
* tap and restored when the sequence completes. */
int status = 0;
int nav_clean_exit = 0; /* M3:MSG_NAV{EXIT} 主动退出 → 不按崩溃上报 */
int app_prev_vold = 0;
int app_seq_hits = 0;
unsigned long long app_seq_last = 0;
@@ -1054,6 +1138,17 @@ int launch_app(struct app *a)
if (r == pid) break;
if (r < 0 && errno != EINTR) break;
if (sigterm_flag) { logmsg("sigterm during app"); kill(pid, SIGTERM); break; }
/* M3 导航栈:前台会话排空 IPC(App 经 MSG_NAV REPORT/EXIT 上报导航/请求退出)。
* REPORT 更新 nav_stack(meow_ipc_process 内单写者);EXIT → 终止应用回主屏。 */
meow_ipc_process();
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); }
break;
}
/* M2.11 D:卡被拔(设备节点消失)且前台应用在卡上 → 主动 SIGTERM 干净退出,
* 避免应用等 SIGBUS 崩。回菜单走正常应用退出路径。 */
if (app_on_card && !sd_device_present()) {
@@ -1181,7 +1276,7 @@ int launch_app(struct app *a)
/* M2.x:App 异常退出(信号杀 / 非 0 退出码)→ best-effort 直报 meow_crash。
* 判据与 svcd reap_children 一致:failed = !(WIFEXITED && WEXITSTATUS==0)。干净 exit(0)
* 不上报;sigterm 关停/电源菜单重启路径 status 保持 0(未 waitpid)→ 视为干净退出不上报。 */
if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0))
if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0) && !nav_clean_exit)
notify_app_crash(a->id, status);
if (wpid > 0) {
/* reap the watchdog: SIGTERM then wait up to 2s (it exits when the
@@ -1205,7 +1300,20 @@ int launch_app(struct app *a)
/* M2.24:应用运行期音量由 watchdog 写(settings.conf 同源)。返回菜单前 re-load,
* 让菜单音量/状态栏与真实 DAC 一致(watchdog 可能已改)。 */
settings_load();
meow_nav_exit(); /* M3:前台应用会话结束,清导航栈 */
logmsg("app %s exited status=%d", a->name, WEXITSTATUS(status));
return 0;
}
/* M3:裸启动(无 activity 链)。保留旧签名,向下兼容不发 MSG_NAV 的 App。 */
int launch_app(struct app *a)
{
return launch_app_chain(a, NULL);
}
/* M3:以 activity 链路冷启动。注入 MEOW_ACTIVITY/MEOW_CHAIN + 设 nav_stack 栈深。 */
int launch_activity(struct app *a, const char *chain)
{
return launch_app_chain(a, chain);
}