feat(launcher): M4 后台服务保活+后台播放(FRAMEWORK_DESIGN §5/§9 落地)
- app.json 新字段 background/background_audio 解析入 struct app/meow_acct 注册表
- meow_ipc.h:MSG_REGISTER(m_register_t)/SERVICE_OK(m_ack_t.status=pid)/SERVICE_STOP/
MSG_SYS(m_sys_t, SHUTDOWN=1/SVC_DEAD=2) 负载结构落地
- 新模块 meow_bgplay.c/h(自包含+ops 注入,仿 meow_media,宿主可单测):
· 后台播放保活——声明 background_audio 且媒体会话 PLAYING 的 App nav-exit 不终止,
进程+音频流保留;UI 冻结走 §10.5 /tmp/meow_notif_active SDK present 自抑(非字面
SIGSTOP:冻结同进程播放线程断供 PCM,§10.5 真机实证);点图标/媒体卡片回前台=
原进程恢复(chain/picker 忽略),停播后再退出=真退 SIGTERM
· 无头后台服务——MSG_REGISTER spawn/stop/reap + 退避重启 1/2/4/10s cap +
max_restart 熔断(清该 App 通知 + 注册连接广播 MSG_SYS{SVC_DEAD},重注册复位)
· 卡拔例外(卡上二进制后台 App/服务仍 SIGTERM 防 SIGBUS)、崩溃照收、
关机前 meow_bgplay_kill_all 有界回收(含 MSG_SYS{SHUTDOWN} 广播)
- util.c:MSG_REGISTER/MSG_SERVICE_STOP 分发(权限/声明校验链)+ ipc_conn_alive 连接
存活查询 + meow_ipc_process 末尾挂 meow_bg_housekeep(drain/reap/退避 tick)
- app_launcher.c:launch_app_chain 入口 resume(bg_take 原进程回前台)+ nav-exit
保活分支 + 转后台出口(杀看门狗/写自抑标记/复位顶条,跳过退出尾)+ 冷启动自动
拉起静态 keepalive(mode=0) 服务(exec 路径约定=App 同目录 <svc> 文件)
- main_menu.c:应用列表「·后台」标注(宫格/列表,1s tick 按 change_seq 刷新)+
两条退出尾 kill_all + unlink NOTIF_ACTIVE
- 宿主单测 test_bgplay.c 50 项全过(真 fork 子进程走状态机 + 假时钟退避熔断);
test_media/test_notif_panel/test_notif_tf/test_vol_ramp_jack 回归全过;
zig-mipsel-cc -Wall -Wextra 0 warning。launcher md5=c07d011c0769fca14d3bfb752d735463
This commit is contained in:
1 parent
7db8197f9c
commit
5aabede0cd
13 files changed
+1461
-40
No files matched your search
@@ -870,6 +870,22 @@ static pthread_t ipc_pthread;
|
||||
|
||||
static struct meow_ipc_conn ipc_conns[MEOW_IPC_CONNS];
|
||||
|
||||
/* M4:连接存活跟踪(ipc_thread 单写者维护计数;主循环 meow_ipc_conn_alive 读)。
|
||||
* 竞态最坏=误判一帧(best-effort 熔断广播可接受;ipc_conns 前 ipc_conn_count 项恒为
|
||||
* 最新存活集,因关闭时shift压实)。 */
|
||||
static volatile int ipc_conn_count = 0;
|
||||
|
||||
/* M4:查 fd 是否为当前存活 IPC 连接(熔断广播 MSG_SYS 投递前判定,防发到已断连接)。 */
|
||||
int meow_ipc_conn_alive(int fd)
|
||||
{
|
||||
int n = ipc_conn_count;
|
||||
if (fd < 0) return 0;
|
||||
if (n > MEOW_IPC_CONNS) n = MEOW_IPC_CONNS;
|
||||
for (int i = 0; i < n; i++)
|
||||
if (ipc_conns[i].fd == fd) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* §10.5:待处理的 App 前台「展开通知中心」请求(ipc_dispatch 单写者置位;launch_app waitpid
|
||||
* 循环 meow_notif_open_take 读取并清除)。仅当 g_app_session_active(App 前台会话)期间才置位,
|
||||
* 否则回 ERR(无 App 前台,不展开)。 */
|
||||
@@ -1046,6 +1062,7 @@ static void *ipc_thread(void *arg)
|
||||
fds[nfds].events = POLLIN;
|
||||
fds[nfds].revents = 0;
|
||||
nfds++;
|
||||
ipc_conn_count = nfds - 1;
|
||||
logmsg("ipc: conn %d accept (nfds=%d)", c, nfds - 1);
|
||||
} else {
|
||||
close(c);
|
||||
@@ -1066,6 +1083,7 @@ static void *ipc_thread(void *arg)
|
||||
ipc_conns[j - 1] = ipc_conns[j];
|
||||
}
|
||||
nfds--;
|
||||
ipc_conn_count = nfds - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1091,6 +1109,9 @@ int meow_ipc_init(void)
|
||||
g_media_ops.log = logmsg;
|
||||
meow_media_init(&g_media_ops);
|
||||
|
||||
/* M4:注入后台保活/后台服务 ops(logmsg/sd_device_present/通知清理/熔断发帧)。 */
|
||||
meow_bgplay_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));
|
||||
@@ -1119,6 +1140,9 @@ void meow_ipc_process(void)
|
||||
ipc_head++;
|
||||
}
|
||||
__sync_synchronize();
|
||||
/* M4:后台表周期维护挂点(主循环单写者排空点,run_menu/前台会话/idle/ipc-only
|
||||
* 四处循环都经此处)——drain 后台 App stdout + 回收/卡拔 + 服务退避重启。 */
|
||||
meow_bg_housekeep();
|
||||
}
|
||||
|
||||
|
||||
@@ -1269,6 +1293,72 @@ static void ipc_dispatch(struct meow_ipc_msg *m)
|
||||
n.app_id, n.nav_type, n.depth, n.current);
|
||||
break;
|
||||
}
|
||||
/* M4 后台服务注册(FRAMEWORK_DESIGN §5/§9):无头服务 spawn/退避重启/熔断。
|
||||
* 校验链:acct 存在且 exec 非空(NOENT)→ 权限(app.json background=true 或
|
||||
* flags 含 MF_BACKGROUND,否则 PERM)→ service.<svc> 静态声明存在(NOENT)→
|
||||
* meow_svc_register 拉起。成功回 MSG_SERVICE_OK(m_ack_t:code=0,status=pid);
|
||||
* 失败回 MSG_ERR(仿 ACT_QUERY 模式)。注册后调用方须保持连接(熔断广播投递判定)。 */
|
||||
case MSG_REGISTER: {
|
||||
m_register_t r;
|
||||
memset(&r, 0, sizeof r);
|
||||
memcpy(&r, m->payload, m->plen < (uint16_t)sizeof r ? m->plen : (uint16_t)sizeof r);
|
||||
r.app_id[MEOW_APP_ID_MAX - 1] = 0;
|
||||
r.svc[15] = 0;
|
||||
struct meow_acct *ac = meow_acct_find(r.app_id);
|
||||
const struct meow_svc *sv = ac ? meow_acct_svc_find(ac, r.svc) : NULL;
|
||||
int code = MEOW_OK;
|
||||
int pid = 0;
|
||||
if (!ac || !ac->exec[0])
|
||||
code = MEOW_ERR_NOENT;
|
||||
else if (!(meow_acct_background_perm(ac) & 1) && !(r.flags & MF_BACKGROUND))
|
||||
code = MEOW_ERR_PERM;
|
||||
else if (!sv)
|
||||
code = MEOW_ERR_NOENT;
|
||||
else {
|
||||
char sexec[160];
|
||||
meow_svc_exec_path(ac->exec, r.svc, sexec, sizeof sexec);
|
||||
pid = meow_svc_register(r.app_id, r.svc, sexec,
|
||||
sv->restart, sv->max, sv->window_ms,
|
||||
strncmp(sexec, MEOWAPPS, strlen(MEOWAPPS)) == 0,
|
||||
m->fd);
|
||||
if (pid < 0) { code = pid; pid = 0; }
|
||||
}
|
||||
if (code == MEOW_OK) {
|
||||
m_ack_t a;
|
||||
a.code = MEOW_OK;
|
||||
a.status = (uint32_t)pid; /* 服务子进程 pid */
|
||||
ipc_send(m->fd, MSG_SERVICE_OK, m->req_id, &a, sizeof a);
|
||||
logmsg("ipc: REGISTER app=%s svc=%s -> SERVICE_OK pid=%d", r.app_id, r.svc, pid);
|
||||
} else {
|
||||
m_err_t e;
|
||||
memset(&e, 0, sizeof e);
|
||||
e.code = (uint32_t)code;
|
||||
const char *msg = (code == MEOW_ERR_PERM) ? "no background permission"
|
||||
: (code == MEOW_ERR_EXISTS) ? "service already running"
|
||||
: (code == MEOW_ERR_NOMEM) ? "service slots full"
|
||||
: "app/service not found";
|
||||
e.msg_len = (uint16_t)strlen(msg);
|
||||
memcpy(e.msg, msg, e.msg_len);
|
||||
ipc_send(m->fd, MSG_ERR, m->req_id, &e, sizeof e);
|
||||
logmsg("ipc: REGISTER app=%s svc=%s -> ERR(%d)", r.app_id, r.svc, code);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* M4:请求停止自己已注册的无头服务(SIGTERM,标记 stopping 不触发退避重启)。 */
|
||||
case MSG_SERVICE_STOP: {
|
||||
m_service_stop_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.svc[15] = 0;
|
||||
int rc = meow_svc_stop(s.app_id, s.svc);
|
||||
m_ack_t a;
|
||||
a.code = (uint32_t)(rc < 0 ? -rc : MEOW_OK);
|
||||
a.status = 0;
|
||||
ipc_send(m->fd, MSG_ACK, m->req_id, &a, sizeof a);
|
||||
logmsg("ipc: SERVICE_STOP app=%s svc=%s -> ACK code=%u", s.app_id, s.svc, a.code);
|
||||
break;
|
||||
}
|
||||
/* AMSD_DESIGN §4.3:amsd `am start`/`am kill` 控制请求。主循环单写者仅置位 pending
|
||||
* (run_menu 前台循环 / launch_* waitpid 循环消费),不回执启动结果(一期 best-effort)。 */
|
||||
case MSG_AMS_START: {
|
||||
|
||||
Reference in new issue
Block a user