Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/test_media.c
T
MeowOS ad9778476a fix(§10.5): 通知中心叠加媒体保活——播放中 App 不 SIGSTOP(修播放中滑通知中心音乐停)
- launcher: run_app_notif_overlay 入口 meow_media_app_playing() 判定(当前源+PLAYING+app_id 匹配)
  → 不 SIGSTOP/不 SIGCONT,App 续跑保 PCM;非播放中维持 SIGSTOP 模板;关闭时 unlink 标记挪到
  恢复帧上屏后(防未冻结 App 抢 present 一帧回闪)。
- SDK gfx: present() 见 /tmp/meow_notif_active(env MEOW_NOTIF_ACTIVE_FILE 覆写)自抑上屏,
  仅 App 进程(系统 UI/launcher 不抑制),200ms 节流 stat,脏区累积解除后一次上屏;
  meow_gfx_overlay_suppressed_count 供单测断言。
- SDK input: 叠加期排空丢弃本进程 evdev 副本 + EVIOCGKEY 位图静默对齐(防面板点击被 App
  双重消费/解除后边沿误判),进入沿复位触摸状态机。
- 宿主:新 test_overlay_suppress T1-T4 + test_media §8(8 用例)全 PASS;test_notif_*/
  test_sdk_*/test_vol_ramp_jack 等回归全过;zig-mipsel -Wall -Wextra 全 TU 0 warning。
- meow_launcherd fbb82036(/usr/bin 只读待固件,fw_content.conf 已更)/ meow_ui.a d6242fc2 /
  hello a073535a / files 2b799d07(已热更到卡);ncm 未重链(ncm_app 他 agent 在改)。
2026-09-01 15:23:32 +08:00

197 lines
8.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* test_media.c — 宿主单测:meow_media 媒体会话(阶段D附录B)。
*
* 验证:
* 1) META 上报 → 注册会话,current()=最近上报者;
* 2) 最近活跃切换(两个源,后上报者胜);
* 3) has_source()(state=0 停止 → 0);
* 4) PLAYCTL 下发字节格式(socketpair 对端可读回 MSG_PLAYCTL 帧,cmd/seek 正确);
* 5) meow_media_playctl 无源 → -1;
* 6) clear(app) 清会话 → current()=NULL。
* 8) meow_media_app_playing(§10.5 叠加保活分支:当前源+PLAYING+app_id 匹配才 1)。
* 只依赖 meow_media.c + meow_ipc.h(纯 C),宿主 gcc 直编。
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/socket.h>
#include "meow_media.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)
/* ops.send:把帧写到 fd(socketpair 一端)。 */
static size_t test_send(int fd, uint16_t type, uint32_t req_id,
const void *payload, uint32_t len)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
uint32_t magic = MEOW_IPC_MAGIC;
uint16_t ver = MEOW_IPC_VERSION;
memcpy(hdr + 0, &magic, 4);
memcpy(hdr + 4, &ver, 2);
memcpy(hdr + 6, &type, 2);
memcpy(hdr + 8, &req_id, 4);
memcpy(hdr + 12, &len, 4);
size_t off = 0;
while (off < sizeof hdr) {
ssize_t w = write(fd, hdr + off, sizeof hdr - off);
if (w <= 0) return off;
off += (size_t)w;
}
if (len && payload) {
size_t p = 0;
while (p < len) {
ssize_t w = write(fd, (const uint8_t *)payload + p, len - p);
if (w <= 0) break;
p += (size_t)w;
}
off += p;
}
return off;
}
static void fill_meta(m_meta_t *m, const char *app, uint32_t state, uint32_t pos, uint32_t dur,
const char *title, const char *artist)
{
memset(m, 0, sizeof *m);
snprintf(m->app_id, sizeof m->app_id, "%s", app);
m->state = state; m->pos_ms = pos; m->dur_ms = dur;
snprintf(m->title, sizeof m->title, "%s", title);
snprintf(m->artist, sizeof m->artist, "%s", artist);
}
/* 从 fd 读一帧(头+负载),校验 magic/ver,返回负载类型;不足返回 -1。 */
static int read_frame(int fd, uint16_t *type, uint32_t *req_id, uint8_t *rbuf, uint32_t rcap)
{
uint8_t rhdr[MEOW_IPC_HDR_LEN];
size_t off = 0;
while (off < sizeof rhdr) {
ssize_t r = read(fd, rhdr + off, sizeof rhdr - off);
if (r <= 0) return -1;
off += (size_t)r;
}
uint32_t magic, len; uint16_t ver;
memcpy(&magic, rhdr, 4); memcpy(&ver, rhdr + 4, 2); memcpy(type, rhdr + 6, 2);
memcpy(req_id, rhdr + 8, 4); memcpy(&len, rhdr + 12, 4);
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return -1;
if (len > rcap) len = rcap;
off = 0;
while (off < len) {
ssize_t r = read(fd, rbuf + off, len - off);
if (r <= 0) return -1;
off += (size_t)r;
}
return (int)len;
}
int main(void)
{
meow_media_ops ops;
ops.send = test_send;
ops.log = NULL; /* 静默 */
meow_media_init(&ops);
int sv[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) { perror("socketpair"); return 1; }
/* sv[0]=会话 fd(launcher 下发端);sv[1]=测试读回端(模拟 App)。 */
/* 1) 单源注册 */
m_meta_t m;
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 120000, 240000, "Song A", "Artist A");
CHECK(meow_media_meta(&m, sv[0]) == 0, "meta registers source");
const meow_media_session_t *cur = meow_media_current();
CHECK(cur && !strcmp(cur->app_id, "com.ncm"), "current() == com.ncm");
CHECK(cur && cur->state == MEOW_MEDIA_PLAYING, "state == PLAYING");
CHECK(meow_media_has_source() == 1, "has_source == 1 (playing)");
/* 2) 多源最近活跃切换 */
fill_meta(&m, "com.player2", MEOW_MEDIA_PLAYING, 0, 180000, "Song B", "Artist B");
CHECK(meow_media_meta(&m, sv[0]) == 0, "meta registers second source");
cur = meow_media_current();
CHECK(cur && !strcmp(cur->app_id, "com.player2"), "current() == com.player2 (newest)");
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 30000, 240000, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
cur = meow_media_current();
CHECK(cur && !strcmp(cur->app_id, "com.ncm"), "current() == com.ncm after re-report (newest)");
/* 3) state=0 停止 → has_source=0(但会话仍在,current() 仍指向它) */
fill_meta(&m, "com.ncm", MEOW_MEDIA_STOPPED, 0, 0, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
cur = meow_media_current();
CHECK(cur && !strcmp(cur->app_id, "com.ncm"), "current() still == com.ncm (stopped)");
CHECK(meow_media_has_source() == 0, "has_source == 0 (stopped)");
/* 恢复播放,准备 PLAYCTL 下发验证 */
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 1000, 240000, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
/* 4) PLAYCTL 下发:cmd=1(播放),读回端应收到 MSG_PLAYCTL/第11号 req */
CHECK(meow_media_playctl(1, 0) == 0, "playctl sends (cmd=play)");
uint8_t rbuf[512];
uint16_t type; uint32_t req;
int n = read_frame(sv[1], &type, &req, rbuf, sizeof rbuf);
CHECK(n == (int)sizeof(m_playctl_t), "downlink frame payload == m_playctl_t size");
CHECK(type == MSG_PLAYCTL, "downlink type == MSG_PLAYCTL");
m_playctl_t c;
memcpy(&c, rbuf, sizeof c);
CHECK(c.cmd == 1 && c.seek_ms == 0, "downlink cmd=1 seek=0");
/* 5) seek:cmd=5 seek=12345 */
CHECK(meow_media_playctl(5, 12345) == 0, "playctl sends (cmd=seek)");
n = read_frame(sv[1], &type, &req, rbuf, sizeof rbuf);
memcpy(&c, rbuf, sizeof c);
CHECK(type == MSG_PLAYCTL && c.cmd == 5 && c.seek_ms == 12345, "downlink seek cmd=5 seek=12345");
/* 5b) toggle 透传:cmd=6(物理播放/暂停键)原样下发,launcher 不改写 cmd */
CHECK(meow_media_playctl(6, 0) == 0, "playctl sends (cmd=toggle)");
n = read_frame(sv[1], &type, &req, rbuf, sizeof rbuf);
memcpy(&c, rbuf, sizeof c);
CHECK(type == MSG_PLAYCTL && c.cmd == 6 && c.seek_ms == 0, "downlink cmd=6 passthrough");
/* playctl_to 目标 app(另一会话) */
CHECK(meow_media_playctl_to("com.player2", 3, 0) == 0, "playctl_to player2 sends");
n = read_frame(sv[1], &type, &req, rbuf, sizeof rbuf);
memcpy(&c, rbuf, sizeof c);
CHECK(type == MSG_PLAYCTL && c.cmd == 3, "downlink to player2 cmd=3");
CHECK(meow_media_playctl_to("no.such.app", 1, 0) == -1, "playctl_to unknown app == -1");
/* 6) 无源:清全部 → playctl -1 / has_source 0 */
meow_media_clear(NULL);
CHECK(meow_media_current() == NULL, "current() == NULL after clear all");
CHECK(meow_media_playctl(1, 0) == -1, "playctl no source == -1");
CHECK(meow_media_has_source() == 0, "has_source == 0 after clear");
close(sv[0]); close(sv[1]);
/* 7) 空 app_id 拒绝 */
fill_meta(&m, "", MEOW_MEDIA_PLAYING, 0, 0, "x", "y");
CHECK(meow_media_meta(&m, sv[0]) == -1, "meta empty app_id == -1");
/* 8) §10.5 叠加保活分支判定 meow_media_app_playing(launcher 不 SIGSTOP 的入口判据) */
meow_media_clear(NULL);
CHECK(meow_media_app_playing("com.ncm") == 0, "app_playing: no source == 0");
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 1000, 240000, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
CHECK(meow_media_app_playing("com.ncm") == 1, "app_playing: current+PLAYING+match == 1");
CHECK(meow_media_app_playing("com.other") == 0, "app_playing: app_id mismatch == 0");
CHECK(meow_media_app_playing("") == 0, "app_playing: empty app_id == 0");
CHECK(meow_media_app_playing(NULL) == 0, "app_playing: NULL app_id == 0");
fill_meta(&m, "com.ncm", MEOW_MEDIA_PAUSED, 1000, 240000, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
CHECK(meow_media_app_playing("com.ncm") == 0, "app_playing: PAUSED == 0 (SIGSTOP as usual)");
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 2000, 240000, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
/* 当前源被别的 App 抢走(后者 PAUSED 也抢走「最近活跃」)→ 本 App 播放中也不再保活 */
fill_meta(&m, "com.player2", MEOW_MEDIA_PAUSED, 0, 180000, "Song B", "Artist B");
meow_media_meta(&m, sv[0]);
CHECK(meow_media_app_playing("com.ncm") == 0, "app_playing: not current source == 0");
meow_media_clear(NULL);
printf("\n%s (%d fails)\n", g_fails ? "FAILED" : "ALL PASS", g_fails);
return g_fails ? 1 : 0;
}