feat(launcher): 通知查询/清除 IPC + App崩溃直报crash (agent-199) — MSG_NOTIFY_QUERY(0x25)/QUERY_RESULT(0x27)/DISMISS_ALL(0x26,保留媒体/进度/常驻红线), DISMISS(0x22)复用既有; 序列化在主循环单写者做; launch_app waitpid 回收处 notify_app_crash(信号杀/非0退出上报 /tmp/meow_crash.sock best-effort, 干净exit不报)
launcher 61f6b0db→3c7c2ec7; qemu IPC 往返+宿主存储单测+mock crash socket 全过; 顺带清理既有告警(util.c/app_launcher.c/meow_ipc.c)
This commit is contained in:
1 parent
29e1b35da8
commit
2d93298f8a
7 files changed
+272
-8
No files matched your search
@@ -771,6 +771,50 @@ static void app_overlay_statusbar(void)
|
|||||||
|
|
||||||
/* (definition follows) */
|
/* (definition follows) */
|
||||||
|
|
||||||
|
#define MEOW_CRASH_SOCK "/tmp/meow_crash.sock" /* meow_crash 崩溃上报 socket(best-effort,见 meow_crash.md §2)*/
|
||||||
|
|
||||||
|
/* App 异常退出(信号杀 / 非 0 退出码)→ best-effort 直报 meow_crash。
|
||||||
|
* 格式:`app <pkg> sig=<n>` / `app <pkg> exit=<n>`(参照 svcd notify_crash(),但用 app 域前缀)。
|
||||||
|
* 全程非阻塞:socket O_NONBLOCK;connect 失败即弃;EINPROGRESS 短 poll(POLLOUT) ≤150ms 排空,
|
||||||
|
* 仍不可写即丢弃;单行小消息一次 write(失败即弃),绝不重试。meow_crash 不在线 → 静默丢,
|
||||||
|
* 绝不拖垮 launcher。调用方负责只对异常退出调用(干净 exit(0) 不上报)。 */
|
||||||
|
static void notify_app_crash(const char *pkg, int status)
|
||||||
|
{
|
||||||
|
const char *kind;
|
||||||
|
int num;
|
||||||
|
if (WIFSIGNALED(status)) { kind = "sig"; num = WTERMSIG(status); }
|
||||||
|
else { kind = "exit"; num = WIFEXITED(status) ? WEXITSTATUS(status) : 1; }
|
||||||
|
|
||||||
|
char msg[192];
|
||||||
|
int len = snprintf(msg, sizeof msg, "app %s %s=%d\n", pkg, kind, num);
|
||||||
|
if (len <= 0) return;
|
||||||
|
|
||||||
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0) return;
|
||||||
|
int fl = fcntl(fd, F_GETFL, 0);
|
||||||
|
if (fl >= 0) fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
||||||
|
|
||||||
|
struct sockaddr_un a;
|
||||||
|
memset(&a, 0, sizeof a);
|
||||||
|
a.sun_family = AF_UNIX;
|
||||||
|
strncpy(a.sun_path, MEOW_CRASH_SOCK, sizeof a.sun_path - 1);
|
||||||
|
|
||||||
|
if (connect(fd, (struct sockaddr *)&a, sizeof a) != 0) {
|
||||||
|
if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
|
||||||
|
struct pollfd pf;
|
||||||
|
pf.fd = fd; pf.events = POLLOUT; pf.revents = 0;
|
||||||
|
int r = poll(&pf, 1, 150);
|
||||||
|
if (r <= 0 || !(pf.revents & POLLOUT)) { close(fd); return; }
|
||||||
|
} else {
|
||||||
|
close(fd);
|
||||||
|
return; /* crash 不在线:静默丢 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ssize_t w = write(fd, msg, (size_t)len);
|
||||||
|
(void)w;
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
int launch_app(struct app *a)
|
int launch_app(struct app *a)
|
||||||
{
|
{
|
||||||
logmsg("launch: %s (%s)", a->name, a->exec);
|
logmsg("launch: %s (%s)", a->name, a->exec);
|
||||||
@@ -939,7 +983,7 @@ int launch_app(struct app *a)
|
|||||||
meow_write_app_state(a->immersive == '1' ? 1 : 0);
|
meow_write_app_state(a->immersive == '1' ? 1 : 0);
|
||||||
|
|
||||||
/* watchdog for virtual back key / gestures */
|
/* watchdog for virtual back key / gestures */
|
||||||
char wd1[192], wd2[192], pidstr[16];
|
char wd1[192], pidstr[16];
|
||||||
pid_t wpid = 0;
|
pid_t wpid = 0;
|
||||||
snprintf(pidstr, sizeof pidstr, "%d", pid);
|
snprintf(pidstr, sizeof pidstr, "%d", pid);
|
||||||
if (access(WATCHDOG1, X_OK) == 0)
|
if (access(WATCHDOG1, X_OK) == 0)
|
||||||
@@ -1134,6 +1178,11 @@ int launch_app(struct app *a)
|
|||||||
/* 顶条收合/恢复过渡期把循环睡得短些(~20ms),驱动更平滑的上移出屏;稳定后回 100ms。 */
|
/* 顶条收合/恢复过渡期把循环睡得短些(~20ms),驱动更平滑的上移出屏;稳定后回 100ms。 */
|
||||||
usleep(meow_ui_statusbar_animating() ? 20000 : 100000);
|
usleep(meow_ui_statusbar_animating() ? 20000 : 100000);
|
||||||
}
|
}
|
||||||
|
/* 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))
|
||||||
|
notify_app_crash(a->id, status);
|
||||||
if (wpid > 0) {
|
if (wpid > 0) {
|
||||||
/* reap the watchdog: SIGTERM then wait up to 2s (it exits when the
|
/* reap the watchdog: SIGTERM then wait up to 2s (it exits when the
|
||||||
* app dies; bounded so we never hang if it is stuck) */
|
* app dies; bounded so we never hang if it is stuck) */
|
||||||
|
|||||||
@@ -158,7 +158,6 @@ static int cmd_declare(const char *app_id, int nent, char **ents)
|
|||||||
strncpy(d.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
strncpy(d.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||||||
int n = 0;
|
int n = 0;
|
||||||
char *p = d.entries;
|
char *p = d.entries;
|
||||||
int cap = (int)sizeof(d.entries);
|
|
||||||
for (int i = 0; i < nent && n < 8; i++) {
|
for (int i = 0; i < nent && n < 8; i++) {
|
||||||
size_t l = strlen(ents[i]);
|
size_t l = strlen(ents[i]);
|
||||||
if (l + 1 > (size_t)(d.entries + sizeof d.entries - p)) break;
|
if (l + 1 > (size_t)(d.entries + sizeof d.entries - p)) break;
|
||||||
@@ -272,6 +271,47 @@ static int cmd_dismiss(uint32_t id)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 查询当前内存活跃通知集 → MSG_NOTIFY_QUERY,期望 MSG_NOTIFY_QUERY_RESULT(打印 JSON 文本)。
|
||||||
|
* 可选 <app_id> 过滤(缺省=全部)。 */
|
||||||
|
static int cmd_querynotif(const char *app_id)
|
||||||
|
{
|
||||||
|
int fd = connect_path(g_sock);
|
||||||
|
if (fd < 0) return 1;
|
||||||
|
uint8_t rbuf[MEOW_IPC_MAX];
|
||||||
|
int rt = xchg(fd, MSG_NOTIFY_QUERY, 8, app_id, app_id ? (uint32_t)strlen(app_id) : 0,
|
||||||
|
rbuf, sizeof rbuf);
|
||||||
|
close(fd);
|
||||||
|
if (rt == MSG_NOTIFY_QUERY_RESULT) {
|
||||||
|
m_notify_query_result_t r;
|
||||||
|
memcpy(&r, rbuf, sizeof r);
|
||||||
|
uint32_t tl = r.text_len < sizeof r.text ? r.text_len : sizeof r.text - 1;
|
||||||
|
printf("QUERY ok: code=%d len=%u\n%s\n", r.code, (unsigned)tl, r.text);
|
||||||
|
return r.code == MEOW_OK ? 0 : 1;
|
||||||
|
}
|
||||||
|
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||||||
|
fprintf(stderr, "QUERY: unexpected resp type 0x%04x\n", rt);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 一键清除(保留 ongoing/media/progress)→ MSG_NOTIFY_DISMISS_ALL,期望 MSG_ACK(status=清除数)。 */
|
||||||
|
static int cmd_dismissall(void)
|
||||||
|
{
|
||||||
|
int fd = connect_path(g_sock);
|
||||||
|
if (fd < 0) return 1;
|
||||||
|
uint8_t rbuf[256];
|
||||||
|
int rt = xchg(fd, MSG_NOTIFY_DISMISS_ALL, 9, NULL, 0, rbuf, sizeof rbuf);
|
||||||
|
close(fd);
|
||||||
|
if (rt == MSG_ACK) {
|
||||||
|
m_ack_t a;
|
||||||
|
memcpy(&a, rbuf, sizeof a);
|
||||||
|
printf("DISMISS_ALL ok: code=%d cleared=%u\n", a.code, a.status);
|
||||||
|
return a.code == MEOW_OK ? 0 : 1;
|
||||||
|
}
|
||||||
|
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||||||
|
fprintf(stderr, "DISMISS_ALL: unexpected resp type 0x%04x\n", rt);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
const char *env = getenv("MEOW_IPC_SOCK");
|
const char *env = getenv("MEOW_IPC_SOCK");
|
||||||
@@ -329,6 +369,13 @@ int main(int argc, char **argv)
|
|||||||
if (i + 1 >= argc) { fprintf(stderr, "dismiss needs <id>\n"); return 1; }
|
if (i + 1 >= argc) { fprintf(stderr, "dismiss needs <id>\n"); return 1; }
|
||||||
return cmd_dismiss((uint32_t)strtoul(argv[i + 1], NULL, 0));
|
return cmd_dismiss((uint32_t)strtoul(argv[i + 1], NULL, 0));
|
||||||
}
|
}
|
||||||
|
if (!strcmp(cmd, "querynotif")) {
|
||||||
|
const char *app = (i + 1 < argc) ? argv[i + 1] : NULL;
|
||||||
|
return cmd_querynotif(app);
|
||||||
|
}
|
||||||
|
if (!strcmp(cmd, "dismissall")) {
|
||||||
|
return cmd_dismissall();
|
||||||
|
}
|
||||||
fprintf(stderr, "unknown cmd %s; try --help\n", cmd);
|
fprintf(stderr, "unknown cmd %s; try --help\n", cmd);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -45,6 +45,9 @@ enum {
|
|||||||
MSG_NOTIFY_DISMISS = 0x0022, /* ↑ 撤下通知 */
|
MSG_NOTIFY_DISMISS = 0x0022, /* ↑ 撤下通知 */
|
||||||
MSG_NOTIFY_LIST = 0x0023, /* ↓ 下发通知清单(重同步) */
|
MSG_NOTIFY_LIST = 0x0023, /* ↓ 下发通知清单(重同步) */
|
||||||
MSG_NOTIFY_PROGRESS = 0x0024,/* ↑ 实时更新通知进度(仅改内存活跃槽,不落盘) */
|
MSG_NOTIFY_PROGRESS = 0x0024,/* ↑ 实时更新通知进度(仅改内存活跃槽,不落盘) */
|
||||||
|
MSG_NOTIFY_QUERY = 0x0025, /* ↑ 查询内存活跃通知集(MeowPanel 实时化前置) */
|
||||||
|
MSG_NOTIFY_DISMISS_ALL = 0x0026,/* ↑ 一键清除(保留 ongoing/media/progress 进行中,同通知中心 UI) */
|
||||||
|
MSG_NOTIFY_QUERY_RESULT = 0x0027, /* ↓ 活跃通知集序列化文本(JSON,字段同 TF 归档) */
|
||||||
MSG_ACT_DECLARE = 0x0030, /* ↑ (重)声明路由表 */
|
MSG_ACT_DECLARE = 0x0030, /* ↑ (重)声明路由表 */
|
||||||
MSG_ACT_QUERY = 0x0031, /* ↑ 解析 activity 链路 */
|
MSG_ACT_QUERY = 0x0031, /* ↑ 解析 activity 链路 */
|
||||||
MSG_ACT_RESULT = 0x0032, /* ↓ 返回链路 */
|
MSG_ACT_RESULT = 0x0032, /* ↓ 返回链路 */
|
||||||
@@ -169,6 +172,16 @@ typedef struct m_notify_progress {
|
|||||||
int32_t progress; /* 0-100;<0=清除进度条 */
|
int32_t progress; /* 0-100;<0=清除进度条 */
|
||||||
} m_notify_progress_t; /* 8 B */
|
} m_notify_progress_t; /* 8 B */
|
||||||
|
|
||||||
|
/* MSG_NOTIFY_QUERY_RESULT ↓:活跃通知集序列化文本(JSON 数组;NUL 结尾)。
|
||||||
|
* 字段与 TF 归档一致:id/app_id/category/summary/body/urgency/ts/progress/ongoing/media/
|
||||||
|
* target_activity(+app_icon)。payload 定长 LE,整个结构 < MEOW_IPC_MAX。 */
|
||||||
|
typedef struct m_notify_query_result {
|
||||||
|
uint32_t code; /* MEOW_OK / MEOW_ERR_* */
|
||||||
|
uint16_t text_len; /* text 有效字节(不含 NUL) */
|
||||||
|
uint16_t pad;
|
||||||
|
char text[MEOW_IPC_MAX - 12]; /* 序列化 JSON 数组 */
|
||||||
|
} m_notify_query_result_t; /* 4096 B */
|
||||||
|
|
||||||
/* ================= 专项服务 IPC(SYSTEM_FRAMEWORK_SERVICES §2.2/§2.3)= */
|
/* ================= 专项服务 IPC(SYSTEM_FRAMEWORK_SERVICES §2.2/§2.3)= */
|
||||||
|
|
||||||
/* ---- MSG_PERF_QUERY 子集位(0=全部)---- */
|
/* ---- MSG_PERF_QUERY 子集位(0=全部)---- */
|
||||||
|
|||||||
Binary file not shown.
@@ -601,6 +601,121 @@ int meow_notif_update_progress(uint32_t id, int progress)
|
|||||||
return NOTIF_OK;
|
return NOTIF_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- 一键清除(MSG_NOTIFY_DISMISS_ALL / 通知中心 UI 同语义)----
|
||||||
|
* 红线(用户 2026-08-30,与 notif_center.c ncp_clear_all 的 mnp_clearable 同判据):
|
||||||
|
* ongoing 常驻 / media 媒体 / 进行中进度(progress>=0)一律保留;其余 dismiss(= 归档留档 + 释放槽)。
|
||||||
|
* 顺序遍历固定 slot 表,dismiss 只释放当前槽,不会跳条。 */
|
||||||
|
int meow_notif_clear_active(void)
|
||||||
|
{
|
||||||
|
if (!g_inited) return -NOTIF_ERR_NOTINIT;
|
||||||
|
int n = 0;
|
||||||
|
for (int i = 0; i < MEOW_NOTIF_MAX_SLOTS; i++) {
|
||||||
|
meow_notif_t *s = &g_slots[i];
|
||||||
|
if (!s->active || s->dismissed) continue;
|
||||||
|
if (s->ongoing || s->media || s->progress >= 0) continue; /* 保留红线项 */
|
||||||
|
if (meow_notif_dismiss(s->id) == NOTIF_OK) n++;
|
||||||
|
}
|
||||||
|
notif_log("clear_active: dismissed %d (retained ongoing/media/progress)", n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 活跃通知集 JSON 序列化(MeowPanel 实时化前置;字段同 TF 归档)---- */
|
||||||
|
|
||||||
|
/* JSON 字符串转义:`"`/`\` 反斜杠转义,<0x20 控制字符转 \\u00XX;写到 dst,返回字符数(不含 NUL)。 */
|
||||||
|
static int json_escape(const char *s, char *dst, int cap)
|
||||||
|
{
|
||||||
|
int o = 0;
|
||||||
|
if (!s) s = "";
|
||||||
|
for (const char *p = s; *p; p++) {
|
||||||
|
unsigned char c = (unsigned char)*p;
|
||||||
|
if (c == '"' || c == '\\') {
|
||||||
|
if (o + 2 >= cap) break;
|
||||||
|
dst[o++] = '\\';
|
||||||
|
dst[o++] = (char)c;
|
||||||
|
} else if (c < 0x20) {
|
||||||
|
const char *short_esc = NULL;
|
||||||
|
switch (c) {
|
||||||
|
case '\n': short_esc = "\\n"; break;
|
||||||
|
case '\r': short_esc = "\\r"; break;
|
||||||
|
case '\t': short_esc = "\\t"; break;
|
||||||
|
case '\b': short_esc = "\\b"; break;
|
||||||
|
case '\f': short_esc = "\\f"; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
if (short_esc) {
|
||||||
|
if (o + 2 >= cap) break;
|
||||||
|
dst[o++] = short_esc[0];
|
||||||
|
dst[o++] = short_esc[1];
|
||||||
|
} else {
|
||||||
|
if (o + 6 >= cap) break;
|
||||||
|
int n = snprintf(dst + o, cap - o, "\\u%04x", (unsigned)c);
|
||||||
|
if (n < 0) break;
|
||||||
|
o += n;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (o + 1 >= cap) break;
|
||||||
|
dst[o++] = (char)c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dst[o] = 0;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 把一条活跃通知 append 成 JSON 对象到 buf+*o。返回 0=成功;-1=缓冲放不下(中止)。 */
|
||||||
|
static int json_append_notif(meow_notif_t *s, char *buf, int buf_len, int *o)
|
||||||
|
{
|
||||||
|
char a_app [2 + 2 * MEOW_NOTIF_APP_ID_MAX + 2];
|
||||||
|
char a_cat [2 + 2 * MEOW_NOTIF_CAT_MAX + 2];
|
||||||
|
char a_sum [2 + 2 * MEOW_NOTIF_SUMMARY_MAX + 2];
|
||||||
|
char a_body[2 + 2 * MEOW_NOTIF_BODY_MAX + 2];
|
||||||
|
char a_tgt [2 + 2 * MEOW_NOTIF_ACTIVITY_MAX + 2];
|
||||||
|
char a_icon[2 + 2 * MEOW_NOTIF_ICON_MAX + 2];
|
||||||
|
json_escape(s->app_id, a_app, sizeof a_app);
|
||||||
|
json_escape(s->category, a_cat, sizeof a_cat);
|
||||||
|
json_escape(s->summary, a_sum, sizeof a_sum);
|
||||||
|
json_escape(s->body, a_body, sizeof a_body);
|
||||||
|
json_escape(s->target_activity, a_tgt, sizeof a_tgt);
|
||||||
|
json_escape(s->app_icon, a_icon, sizeof a_icon);
|
||||||
|
int n = snprintf(buf + *o, buf_len - *o,
|
||||||
|
"{\"id\":%u,\"app_id\":\"%s\",\"category\":\"%s\",\"summary\":\"%s\","
|
||||||
|
"\"body\":\"%s\",\"urgency\":%u,\"ts\":%llu,\"progress\":%d,\"ongoing\":%u,"
|
||||||
|
"\"media\":%u,\"target_activity\":\"%s\",\"app_icon\":\"%s\"}",
|
||||||
|
(unsigned)s->id, a_app, a_cat, a_sum, a_body,
|
||||||
|
(unsigned)s->urgency, (unsigned long long)s->created_ms, (int)s->progress,
|
||||||
|
s->ongoing ? 1u : 0u, s->media ? 1u : 0u, a_tgt, a_icon);
|
||||||
|
if (n < 0) return -1;
|
||||||
|
if (*o + n >= buf_len) return -1; /* 放不下:中止(调用方保前缀截断) */
|
||||||
|
*o += n;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int meow_notif_serialize_json(const char *app_id, char *buf, int buf_len)
|
||||||
|
{
|
||||||
|
if (!g_inited) return -NOTIF_ERR_NOTINIT;
|
||||||
|
if (!buf || buf_len <= 0) return -NOTIF_ERR_INVAL;
|
||||||
|
int o = 0;
|
||||||
|
buf[0] = 0;
|
||||||
|
if (o + 1 >= buf_len) return 0; /* 放不下 '[' */
|
||||||
|
buf[o++] = '[';
|
||||||
|
int first = 1;
|
||||||
|
for (int i = 0; i < MEOW_NOTIF_MAX_SLOTS; i++) {
|
||||||
|
meow_notif_t *s = &g_slots[i];
|
||||||
|
if (!s->active || s->dismissed) continue;
|
||||||
|
if (app_id && app_id[0] && strcmp(s->app_id, app_id)) continue;
|
||||||
|
if (!first) {
|
||||||
|
if (o + 1 >= buf_len) break;
|
||||||
|
buf[o++] = ',';
|
||||||
|
}
|
||||||
|
if (json_append_notif(s, buf, buf_len, &o) != 0) break; /* 满:保留已完成前缀 */
|
||||||
|
first = 0;
|
||||||
|
}
|
||||||
|
if (o + 1 < buf_len) buf[o++] = ']';
|
||||||
|
buf[o] = 0;
|
||||||
|
notif_log("serialize_json: app=%s active=%d -> %dB",
|
||||||
|
app_id && app_id[0] ? app_id : "*", meow_notif_count(app_id), o);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- count ---- */
|
/* ---- count ---- */
|
||||||
int meow_notif_count(const char *app_id)
|
int meow_notif_count(const char *app_id)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -132,6 +132,17 @@ void meow_notif_set_icon_cb(meow_notif_icon_cb cb);
|
|||||||
* 立即归档(落盘快照)并(非 ONGOING)释放槽——对应「里程碑落盘」。返回 NOTIF_OK/NOTIF_ERR_NOENT。 */
|
* 立即归档(落盘快照)并(非 ONGOING)释放槽——对应「里程碑落盘」。返回 NOTIF_OK/NOTIF_ERR_NOENT。 */
|
||||||
int meow_notif_update_progress(uint32_t id, int progress);
|
int meow_notif_update_progress(uint32_t id, int progress);
|
||||||
|
|
||||||
|
/* 一键清除(MSG_NOTIFY_DISMISS_ALL / 通知中心 UI 同语义):dismiss 所有「可清除」活跃通知
|
||||||
|
* (非 ongoing / 非 media / 无进行中进度 progress<0——红线段一律保留)。每条 dismiss = 归档留档
|
||||||
|
* + 释放槽。返回清除条数;<0=错误(NOTIF_ERR_*)。 */
|
||||||
|
int meow_notif_clear_active(void);
|
||||||
|
|
||||||
|
/* 把当前活跃通知集序列化为 JSON 数组文本写入 buf(≤buf_len,NUL 结尾)。app_id 空/NULL=全部,
|
||||||
|
* 非空=仅该 app 的活跃通知。字段同 TF 归档:id/app_id/category/summary/body/urgency/ts/
|
||||||
|
* progress/ongoing/media/target_activity(+app_icon);ts=created_ms(epoch ms)。
|
||||||
|
* 返回文本字节数(不含 NUL);<0=错误(NOTIF_ERR_*)。 */
|
||||||
|
int meow_notif_serialize_json(const char *app_id, char *buf, int buf_len);
|
||||||
|
|
||||||
/* == TF 卡转储(通知#6 / M2c,NOTIFICATION_SYSTEM.md §5/§8 + MEOWOS_SPEC_REF §5.3)==
|
/* == TF 卡转储(通知#6 / M2c,NOTIFICATION_SYSTEM.md §5/§8 + MEOWOS_SPEC_REF §5.3)==
|
||||||
* 把 dump_cb 接到 meow_notif_dump_tf 即把每条通知追加为一行 CSV 到
|
* 把 dump_cb 接到 meow_notif_dump_tf 即把每条通知追加为一行 CSV 到
|
||||||
* <卡根>/MeowOS/notifications/notifications.log,并按日志总大小滚动截断
|
* <卡根>/MeowOS/notifications/notifications.log,并按日志总大小滚动截断
|
||||||
|
|||||||
@@ -459,8 +459,9 @@ int ui_width(const char *s, int size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 文本高度(字库行高 or 回退 7×scale 估算),用于垂直居中 / 行高预算。 */
|
/* 文本高度(字库行高 or 回退 7×scale 估算),用于垂直居中 / 行高预算。
|
||||||
static int ui_text_height(int size)
|
* (拆分后无调用点,保留接口;unused 满足 -Wall 0 warning) */
|
||||||
|
static int __attribute__((unused)) ui_text_height(int size)
|
||||||
{
|
{
|
||||||
if (mfont_ready) return meow_font_line_height(size);
|
if (mfont_ready) return meow_font_line_height(size);
|
||||||
return 7 * ((size >= 16) ? 2 : 1);
|
return 7 * ((size >= 16) ? 2 : 1);
|
||||||
@@ -480,8 +481,9 @@ static int ui_text_height(int size)
|
|||||||
void meow_dirty_full(void) { meow_gfx_mark_full(); }
|
void meow_dirty_full(void) { meow_gfx_mark_full(); }
|
||||||
|
|
||||||
|
|
||||||
/* 清空脏集:SDK present 上屏后自清;launcher 侧无需再清(保留接口兼容)。 */
|
/* 清空脏集:SDK present 上屏后自清;launcher 侧无需再清(保留接口兼容)。
|
||||||
static void meow_dirty_reset(void) { }
|
* (拆分后无调用点,保留钩子;unused 满足 -Wall 0 warning) */
|
||||||
|
static void __attribute__((unused)) meow_dirty_reset(void) { }
|
||||||
|
|
||||||
|
|
||||||
/* 标记一个矩形脏:桥进 SDK 脏追踪(相交合并/超限转全屏由 SDK dirty_add 处理)。 */
|
/* 标记一个矩形脏:桥进 SDK 脏追踪(相交合并/超限转全屏由 SDK dirty_add 处理)。 */
|
||||||
@@ -891,12 +893,12 @@ static void ipc_enqueue_frame(struct meow_ipc_conn *c)
|
|||||||
static int ipc_conn_read(struct meow_ipc_conn *c)
|
static int ipc_conn_read(struct meow_ipc_conn *c)
|
||||||
{
|
{
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (c->hdr_n < MEOW_IPC_HDR_LEN) {
|
if ((unsigned)c->hdr_n < MEOW_IPC_HDR_LEN) {
|
||||||
ssize_t r = read(c->fd, c->hdr + c->hdr_n, MEOW_IPC_HDR_LEN - c->hdr_n);
|
ssize_t r = read(c->fd, c->hdr + c->hdr_n, MEOW_IPC_HDR_LEN - c->hdr_n);
|
||||||
if (r < 0) { if (errno == EAGAIN) return 0; return -1; }
|
if (r < 0) { if (errno == EAGAIN) return 0; return -1; }
|
||||||
if (r == 0) return -1;
|
if (r == 0) return -1;
|
||||||
c->hdr_n += (int)r;
|
c->hdr_n += (int)r;
|
||||||
if (c->hdr_n < MEOW_IPC_HDR_LEN) return 0;
|
if ((unsigned)c->hdr_n < MEOW_IPC_HDR_LEN) return 0;
|
||||||
uint32_t magic, len;
|
uint32_t magic, len;
|
||||||
uint16_t ver;
|
uint16_t ver;
|
||||||
memcpy(&magic, c->hdr, 4);
|
memcpy(&magic, c->hdr, 4);
|
||||||
@@ -1191,6 +1193,33 @@ static void ipc_dispatch(struct meow_ipc_msg *m)
|
|||||||
logmsg("ipc: NOTIFY_PROGRESS nid=%u p=%d rc=%d -> ACK", p.nid, p.progress, rc);
|
logmsg("ipc: NOTIFY_PROGRESS nid=%u p=%d rc=%d -> ACK", p.nid, p.progress, rc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* 查询当前内存活跃通知集(MeowPanel 实时化前置)。序列化在主循环出队后做(单写者),
|
||||||
|
* 绝不把通知存储/ring 读到 IPC 线程;应答经 MSG_NOTIFY_QUERY_RESULT 回 JSON 数组文本。 */
|
||||||
|
case MSG_NOTIFY_QUERY: {
|
||||||
|
char filter[MEOW_APP_ID_MAX] = "";
|
||||||
|
if (m->plen > 0) {
|
||||||
|
uint32_t cp = m->plen < MEOW_APP_ID_MAX - 1 ? m->plen : MEOW_APP_ID_MAX - 1;
|
||||||
|
memcpy(filter, m->payload, cp);
|
||||||
|
filter[MEOW_APP_ID_MAX - 1] = 0;
|
||||||
|
}
|
||||||
|
m_notify_query_result_t r;
|
||||||
|
memset(&r, 0, sizeof r);
|
||||||
|
int n = meow_notif_serialize_json(filter[0] ? filter : NULL, r.text, sizeof r.text);
|
||||||
|
r.code = (n >= 0) ? MEOW_OK : MEOW_ERR_INTERNAL;
|
||||||
|
r.text_len = (n >= 0) ? (uint16_t)n : 0;
|
||||||
|
ipc_send(m->fd, MSG_NOTIFY_QUERY_RESULT, m->req_id, &r, sizeof r);
|
||||||
|
logmsg("ipc: NOTIFY_QUERY app=%s -> %dB", filter[0] ? filter : "*", n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MSG_NOTIFY_DISMISS_ALL: {
|
||||||
|
int rc = meow_notif_clear_active();
|
||||||
|
m_ack_t a;
|
||||||
|
a.code = (rc >= 0) ? MEOW_OK : (uint32_t)rc;
|
||||||
|
a.status = (uint32_t)(rc > 0 ? rc : 0); /* 清除条数 */
|
||||||
|
ipc_send(m->fd, MSG_ACK, m->req_id, &a, sizeof a);
|
||||||
|
logmsg("ipc: NOTIFY_DISMISS_ALL -> %d cleared", rc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case MSG_NOTIFY_LIST:
|
case MSG_NOTIFY_LIST:
|
||||||
/* ↓ 守护→App 下发通知清单(重同步)。本帧不来自客户端(无请求类型),故不在此应答;
|
/* ↓ 守护→App 下发通知清单(重同步)。本帧不来自客户端(无请求类型),故不在此应答;
|
||||||
* 需用时由守护主动经 meow_notif_serialize_list() + ipc_send 下发(M5 通知中心接入)。 */
|
* 需用时由守护主动经 meow_notif_serialize_list() + ipc_send 下发(M5 通知中心接入)。 */
|
||||||
|
|||||||
Reference in new issue
Block a user