Files
MeowOS-For-R3P2/APPS.md
T

46 KiB
Raw Blame History

R3 Pro II 第三方应用加载器设计文档(APPS.md)

状态:v2 定稿(Plan B:零 patch 看门狗启动器),2026-08-28 对应实现:patch_v2/meow_launcherd/(启动器守护进程)、patch_v2/meow_watchdog/(应用看门狗)、patch_v2/hello_app/(示例应用) 前置知识:ANALYSIS.md 第 9/11/12/16/17/18 节 变更记录:v1(2026-08-27)为 hiby_player 二进制 patch 路线(Qobuz 入口触发),cave 代码在真机不稳定, 已整体废弃;v2 改为零 patch 的独立守护进程 + 热键触发。


1. 目标与边界

1.1 目标

  1. 硬件热键(按住电源 + 单击音量减,默认;可配 meow.conf hotkey=power_voldown|seq)→ meow_launcherd 打开应用列表(fb0 直绘,列出 TF 卡 /usr/data/mnt/sd_0/MeowAPPs/ 下的应用)
  2. 触摸/按键导航选择某个应用 → 守护进程 SIGSTOP 冻结 hiby_player(让位),fork/execve/waitpid 启动该应用的静态 MIPS 二进制
  3. 应用进程退出 → SIGCONT 恢复 hiby_player(自动回到原界面)
  4. 应用运行期间 meow_watchdog 提供虚拟返回键/沉浸手势(SIGTERM→1s→SIGKILL 兜底)

1.2 边界(v2 刻意不做)

  • 不做应用沙箱/权限模型(应用以 root 运行,与 hiby_player 同权)
  • 不做应用安装/卸载 UI(仅支持手动往 TF 卡放目录)
  • 不做动态链接应用(只支持静态 MIPS32r2 ELF)
  • 不做应用内音频接入(见 §5,未来经 socket 通道)
  • 不做滚动列表(应用 ≤ 8 个,单屏显示)
  • 菜单内虚拟返回键不做(菜单只有「退出」语义;应用运行期的返回由看门狗提供)

1.3 安全红线

  • 只允许触碰:TF 卡(/usr/data/mnt/sd_0)、/usr/data、bind-mount 覆盖点
  • 绝不写 NAND 分区;调试全程 bind-mount/直接替换可写卷文件,重启即回退,变砖风险为零
  • 系统动作(关机/重启/升级/回 HiBy)统一经专项服务触发:App/组件不直连 sys_do_reboot、不写 NAND、不写 mtd5 —— 改走 meow_svc_power(IPC,root 免鉴权、单一命令、不覆盖系统 shutdown/reboot);升级 = 写 mtd5=ota:kernel2(引导 kernel2 自动刷,见 docs/MEOWOS_SPEC_REF.md §1.2)。M2.73 升级写保护:upgrade 写 mtd5 前先校验卡根 r3proii.upt(存在+可读+常规文件+size>0),失败不写 mtd5/不重启——只完整性、不签名(海贝官方固件无签名,签名会挡自刷;期望 md5/hash 校验二期)。二期:关键 IPC 只接受 os.meow.* 包名鉴权。[devdocs/plan/SYSTEM_FRAMEWORK_SERVICES.md]
  • hiby_player.sh 看门狗:hiby_player 退出 → sleep 1 → reboot。SIGSTOP 不触发(进程未退出);严禁 killall hiby_player,须先杀 hiby_player.sh 父 shell 再手动拉起
  • 若 meow_launcherd 意外死亡(SIGKILL/崩溃)且播放器被冻结:手动恢复 kill -CONT $(pidof hiby_player)(守护进程自身 SIGTERM 路径会自动 SIGCONT)

2. TF 卡目录规范(MeowAPPs)

挂载点:/usr/data/mnt/sd_0(TF 卡,exfat;hiby_player 内部虚拟盘符 a:\ 映射到该目录)。

/usr/data/mnt/sd_0/            # TF 卡挂载点
└── MeowAPPs/
    ├── <package>/            # 目录名 = app.json 的 package(权威 app_id)
    │   ├── app.json          # 应用描述(必填;package 字段为权威 app_id)
    │   ├── <binary>          # 静态 MIPS32r2 ELF(exec 指向)
    │   └── icon.png          # 预留:列表图标(v2 未用)
    └── ...

<package> 即 app.json 的 package 字段(反域名权威标识,如 os.meow.hello);目录名必须等于 package,launcher 以 package 为 app_id,缺失时回退目录名。

2.1 app.json 字段

{
  "name": "Hello App",
  "exec": "/usr/data/mnt/sd_0/MeowAPPs/os.meow.hello/hello",
  "package": "os.meow.hello",
  "version": "0.1",
  "author": "whoever"
}
字段 必填 说明
name ✓ 列表显示名。v2 仅支持 ASCII(UTF-8 中文留待 v3,需完整 UTF-8→UTF-16 转换)
exec ✓ 应用可执行文件绝对路径(必须是静态 MIPS32r2 ELF,soft/hard-float 均可)
package — 反域名权威标识(如 os.meow.hello),launcher 取之为 app_id;目录名=package。缺省回退目录名 de->d_name;两者存在且不同 → WARN
version / author — 保留字段,v2 不解析
fullscreen — app 级全屏:1=进入全屏(状态栏收合至提示线 + 保留左缘返回/顶部提示线/下滑通知中心,见 MEOWOS_SPEC_REF §5.1);activity 级用它 route.<id>.fullscreen。app.json fullscreen 字段此前未解析/已定未做 → 本次实现(launcher 启动/切 activity 据声明预设 SDK 全屏,env MEOW_FULLSCREEN)[源:用户 2026-08-30]
fonts — 字库档位声明:"full" / "gbk" / "common"(app 需大字库时可声明 "full",默认小字库 common≈4k;launcher 据此为该 app 加载大字库 gbk≈20k,从已扫描的 MeowOS/fonts/<family>/<weight>/ 里按同 family(其次同 weight)选大字库,M2.6)。注意:与 "fonts":["<handle>"](应用自带字库句柄数组)同名二义,见 MEOWOS_SPEC_REF §7 A-14 [源:用户 2026-08-30]
  • 目录扫描深度 1 级:只枚举 MeowAPPs/ 下的直接子目录
  • 应用计数上限 8(超出忽略;单屏 8 行)
  • app.json 解析器极简(launcher 内实现):仅匹配 "name":、"exec":、"package": 后的引号字符串(及扩展 icon/framework/immersive/route./awake.<id>(keep_awake)/service.),fullscreen(本次实现)与 fonts(档位串) 已解析,不做 JSON 校验(多行/单行均可)[源:审计 2026-08-30][源:用户 2026-08-30]
  • 列表刷新时机:每次菜单打开时重新扫描(TF 卡热插拔后立即可见)

2.2 边界情况行为(launcher 实现,全部真机验证)

场景 行为
TF 卡未挂载/未插入 菜单提示「NO STORAGE CARD / INSERT TF CARD」(state=1,/proc/mounts 无 sd_0)
卡挂载但无 MeowAPPs 目录 提示「NO APPS FOUND」+ 底部示例行 MeowAPPs/<id>/app.json(state=2)
MeowAPPs 为空目录 同上(扫描计数 0 → state=2)
app.json 损坏/缺 name 或 exec 字段 跳过该应用,列表其余项正常
二进制不存在/不可执行 行尾标 [MISSING];点击/确认该行提示 toast「CANNOT START: 」(access X_OK 检查,不 fork;launch_app 返回 -1)
二进制存在但 execve 失败 v6.8 起 launch_app 用 CLOEXEC 管道检测 execve 失败(子进程写 errno),返回 -1 → toast;不再把 127 误当「应用跑完」。toast「CANNOT START」仅在返回 -1(真启动失败)时显示,应用干净退出(返回 0)不显示(Bug 3,旧版无条件显示)
扫描读取出错(opendir/fopen/fgets 失败) 各自跳过;最终无应用时按 state=2 提示

状态机:state 0=正常 / 1=未检测到存储卡 / 2=无可用应用。


3. 应用生命周期契约

3.1 启动流程(meow_launcherd 侧)

热键(按住电源 + 单击音量减;电源按下后 60ms~2.5s 内收到音量减按下边沿触发;v6.7 起窗口下限由 300ms 改为 60ms,窗口 60–2500ms)
  → meow_launcherd 检测(EVIOCGKEY 状态轮询,10ms;电源按下即 arm,窗口外或松开即 disarm)
  → SIGSTOP hiby_player(/proc/N/exe == /usr/bin/hiby_player;等待状态 T)
  → 背光仅在面板灭时拉回(brightness==0 才写 saved/60,绝不无脑拉亮度)+ unblank 面板(先 SIGSTOP 再 unblank)→ fb0 直绘应用列表
  → 菜单循环(10ms):
      · 触摸(event1 事件读)点击行 → 启动;点击「BACK TO PLAYER」行 → 退出菜单
      · 按键(EVIOCGKEY 状态轮询):音量减/加=移动光标,播放键=启动
      · **电源 = 纯息屏/亮屏(launcher 不干预亮度/面板,不退出菜单)**;退出菜单仅「BACK TO PLAYER」触摸行(或 launcher SIGTERM)
      · 应用启动:fork → execve(exec[i])(失败 exit 127);同时 fork meow_watchdog <pid>
      · waitpid(app) 阻塞;应用内强杀 = 音量减×3,退出回菜单
  → 应用退出 → 菜单退出 → SIGCONT hiby_player → 播放器恢复重绘

3.2 让位机制(守护进程方案,2026-08-28 真机验证)

结论:SIGSTOP 整进程冻结 hiby_player(UI+音频+全部线程),菜单期间播放器完全不动作;SIGCONT 后原样恢复。hiby_player.sh 看门狗只盯进程退出,SIGSTOP 不触发 reboot。

依据(真机实证,见 ANALYSIS.md §18):

  1. SIGSTOP 不触发看门狗:进程存活(状态 T),hiby_player.sh 的 sleep 1; reboot 不执行。
  2. 恢复确定可靠:菜单每条退出路径(含 SIGTERM 处理、错误路径)都会 SIGCONT;meow_launcherd 自身被 SIGTERM 时也先 SIGCONT 再退出。
  3. 崩溃安全:应用崩溃/被杀 → waitpid 返回 → 自动 SIGCONT 恢复。若守护进程本身被 SIGKILL,播放器会冻结——手动 kill -CONT $(pidof hiby_player) 恢复(详见 §1.3)。
  4. 菜单期间播放器完全不刷新(SIGSTOP),fb0 归守护进程独占绘制,无抢绘。
  5. 音频:SIGSTOP 连音频线程一起冻结(应用运行期无声音;如需出声见 §5 未来方案——v2 的 SIGSTOP 方案下播放器无法代播,需改 waitpid-in-callback 让位或音频线程解冻,留待 v3)。
  6. 应用退出 → SIGCONT → 播放器首帧整页重绘,无需应用清屏。

3.3 应用该做什么 / 不该做什么

应用必须:

# 规则 说明
1 干净退出 任意退出码均可;守护进程 waitpid 立即返回,SIGCONT 恢复。exit(0/1) 即可,不要 abort() 留下僵尸
2 独占屏幕自绘 自行 open("/dev/fb0") + mmap 后绘制;设备帧缓冲 480×720(分辨率/色深运行时用 FBIOGET_VSCREENINFO/FSCREENINFO 探测,RGB565 或 RGBA8888 自适应)。注意:本设备 ioctl 号 VSCREENINFO=0x4600、FSCREENINFO=0x4602(勿颠倒,见 §4.3)
3 自己读输入 open("/dev/input/event*") 逐个打开,读到任意 key/touch 事件即处理。按任意键退出是推荐约定(物理事件对所有读者多播,播放器已冻结,无需担心抢事件)
4 及时退出 建议空闲超时(如 30s 无操作)自动退出,避免长期占屏(hello 60s 看门狗)

应用不该做:

# 规则 原因
1 不要尝试"恢复" fb SIGCONT 后播放器首帧即整页重绘,无需你清屏。你清屏反而闪一下
2 不要 kill hiby_player 触发 hiby_player.sh 看门狗 → 整机重启
3 不要继承占用声卡 SIGSTOP 期间声卡仍被冻结的播放器持有,直接打开会失败/挂起(v2 无音频)
4 不要 fork 出长期后台进程 你的退出应"真正退出",否则 SIGCONT 后后台进程仍占屏/抢输入
5 不要碰 NAND 分区/固件 只写 TF 卡与 /usr/data

3.4 退出与恢复

  • 应用退出 → waitpid 返回 → 菜单关闭 → SIGCONT 播放器 → 播放器恢复原界面
  • 菜单内「BACK TO PLAYER」行 → 直接 SIGCONT(不启动任何应用);电源键在菜单内不再退出菜单(v6.5 起电源=纯息屏/亮屏,仅 sys_server/内核处理背光)
  • 整机重启 → 一切回退原版(守护进程由 S90 随开机自启)

3.4.1 物理按键语义 —— 强制规范(系统统一管理,APP 禁映射)

⚠️ 红线:物理按键是系统资源,语义由 MeowOS(launcher/watchdog/内核)统一管理。任何非系统组件(MeowAPP)不得将物理按键映射成任何行为,包括但不限于:电源键→OK/确认/exit/翻页,音量键→确认/选择,播放键→确认/返回。

为什么:用户在任何界面都必须能靠物理键完成系统级操作——电源键=息屏/亮屏(MeowOS 界面也必须能息屏)。若 APP 抢走电源键当"OK/exit",用户一按电源键,屏没息反而退出了 APP,无法息屏;音量键/播放键同理。物理键必须留给系统。

约定(MeowOS 界面 = launcher 主菜单/选单/电源菜单,是唯一允许映射物理键的地方):

⚠️ 适用范围:除了电源键菜单(show_power_menu,允许"电源=确认、音量=导航")以外,所有按键信号由 MeowOS 全局接管——任何界面(含 MeowOS 自身的设置页 / 应用列表 / 选单、以及 MeowAPP)都不得私自定义按键行为。物理键的语义只有一份:电源=息屏/亮屏/长按菜单/8s强关,音量=调系统音量,播放/上下曲=媒体控制(未落地前「抓住」)。任何界面(如系统设置页)不得把电源键/播放键映射成"返回/确认"这类自定义行为;界面导航一律走触摸(返回按钮/左缘左滑),按键语义统一由系统判。

键 系统语义(APP 收到的是语义事件,不是裸 keycode)
电源 息屏/亮屏、长按弹电源菜单;APP 内=纯息屏/亮屏(绝不 exit/OK)
音量 +/− 调系统音量;APP 内=调音量(绝不选中/确认/翻页)
播放/暂停 全局媒体控制(播放/暂停当前媒体会话);APP 内=媒体控制(绝不确认/打开)
返回 触屏左滑/虚拟返回键 → SDK 语义 on_swipe(LEFT) / watchdog 信号,非物理键

MeowAPP 侧:

  • APP 不读 /dev/input/event*、不 open/mmap fb0、不处理任何物理 keycode——一切走 SDK 的语义回调(on_tap/on_swipe/on_tick;on_key 仅用于 SDk 内部状态,不含电源/音量/播放的确认/exit 语义)。
  • APP 想"返回/退出"→ 用 meow_ui_request_exit() 或左滑,绝不监听物理键映射 exit。
  • SDK 的 on_key 回调:禁止 APP 用它实现"电源=exit"这类映射。SDK 内部消化物理键的系统语义(息屏/音量/媒体),只把触摸返回/手势等语义事件给 APP。

落地:hello.c 当前 on_key(KEY_POWER)→request_exit() 违反此规范,需删除(改由 SDK/系统处理息屏);APP 退出走左滑/request_exit/系统返回。MeowOS 系统设置页(run_settings_page)曾私自定义"电源/播放键=返回",M2.45 已按本规范改为全局语义(电源=息屏/亮屏/长按菜单/8s强关,播放=抓住),退出仅靠触摸(底部返回/左缘左滑)——任何 MeowOS 界面新增时须遵守本规范,不得私自定义按键。

3.5 虚拟返回键 / 沉浸模式 / 看门狗

应用启动时,launcher 同时 fork 看门狗进程 /usr/data/meow_watchdog <app_pid>(固件 S99ssh 首启从 /usr/bin/meow_watchdog 拷到可写卷;launcher 先试 /usr/data 再试 /usr/bin):

特性 行为
虚拟返回键 看门狗独立 mmap /dev/fb0(RGB565 自适应),每 100ms 重画左上角 48×48 返回按钮(深色底+白箭头);读 /dev/input/event1 触摸,按下落在按钮区 → SIGTERM 应用 → 1s 后 SIGKILL(应用卡死也能退出)。v6.4 防抖(针对"启动误杀"):last_x/y=-1 起始(无坐标的按下丢弃)、BTN_TOUCH 按实际值匹配边沿(消除失同步翻转)、仅短按 ≤600ms 有效、启动后先 drain 一次、冷却期 2.5s(实测幽灵触摸窗口 1.2–2.25s;此窗内返回键不响应,改 COOLDOWN_MS 可调)
沉浸模式 应用写 /tmp/meowapp_state 内容为 1 → 看门狗不画按钮、只识别「左缘 24px 内起点向右滑 >50px」手势 → 发 SIGUSR1;写 0 或删除 → 恢复虚拟键
看门狗自退出 应用进程消失(kill(pid,0) ESRCH)或 launcher SIGTERM → 退出
强制退出兜底 应用运行期(launcher 处于 waitpid):音量减连按 3 次(间隔 < hotkey_seq_gap,与主序列同阈值)→ SIGTERM → 0.5s → SIGKILL → 恢复。⚠️ 响应音量键的应用(如示例 hello)会先自行退出(exit:key),强杀序列是不响应音量键/卡死应用的兜底
长按电源 硬件级强制关机(设备固有),最终兜底
测试模式 meow_watchdog <pid> --sim-tap N / --sim-swipe N,无触摸设备也可验证

应用协议约定:

  • 默认(非沉浸)由看门狗画返回键,应用不必处理返回手势
  • ⚠️ 返回键信号 = SIGTERM(看门狗 handle_touch 按钮命中 → kill(app_pid, SIGTERM)→1s→SIGKILL);SIGUSR1 仅是沉浸左缘滑动手势,不是返回键。com 与 SIGTERM 默认终止会不画 BYE 直接死,故示例 hello 安装 SIGTERM 处理器:画 BYE + exit(0),使返回键呈现确定性的「bye → 立即退出」(v6.8,Bug 2)。
  • ⚠️ 应用启动后须防 goodix 幽灵触摸(v6.8,Bug 2 根因):goodix 在启动触摸的尾部发出一段幽灵触摸(实测启动后 1.2-2.25s,§18.16),无防护时应用会误把幽灵当下按而立即退出画 BYE,且 launcher 菜单会把同一幽灵重放成行点击而重开应用(用户看到「bye 后又回 hello 主界面、需再点一次」)。hello v6.8 增加 COOLDOWN_MS=2500:启动后前 2.5s 忽略一切按下边沿,只有用户真正的下按才触发退出。launcher 侧同步在应用退出后 drain + 冷却(见 §4.7)。
  • 沉浸应用写 /tmp/meowapp_state,处理 SIGUSR1 作为「返回事件」
  • 应用异常卡死 → 虚拟返回键(若可见)→ SIGKILL;沉浸态卡死 → 音量减连按 3 次(强杀序列)
  • 应用日志:launcher 通过环境变量 MEOW_LOG_DIR 告知 TF 卡日志目录(无卡时不设),应用自行决定文件名(示例 hello 写 apps/<app_id>/logs/<ts>_<pid>.log;[源:审计 2026-08-30]);启动时先 drain 输入队列、只响应"按下边沿"退出(释放事件/轨迹事件忽略)

3.6 应用运行期卡移除(M2.11 最佳努力)

场景:前台 MeowAPP 的二进制/资源位于 TF 卡(exec 指向 /usr/data/mnt/sd_0/MeowAPPs/…/)。运行中卡被拔出 → 应用访问卡上代码页/资源可能触发 SIGBUS 崩溃。

launcher 侧(已实现,launch_app waitpid 循环内):

  • 启动应用时记录 app_on_card = (exec 路径在 MEOWAPPS 下);若是,则进入「运行期卡移除监测」。
  • waitpid 主循环每 ~100ms 采样 /dev/mmcblk0 存在性。一旦发现卡设备节点消失(被拔)且当前应用在卡上 → 主动 SIGTERM 应用(干净退出,避免等 SIGBUS 崩),usleep(300ms) 内未退则 SIGKILL 兜底,然后走正常「应用退出→回菜单」路径。菜单/日志提示「卡已移除,应用已结束」。
  • 不做:把应用二进制拷到内存/板载 staging 再 exec(该方案权衡大小/内存,本阶段先记录不做)。

应用侧规范(给第三方 MeowAPP 的约定):

  1. 别依赖卡上资源当运行必需:字体/封面/贴图等用 RAM 缓存到内存再渲染;运行期避免持续从卡 mmap/读大文件。
  2. 读错误容错:open/read/mmap 返回失败(卡没了)时别 abort()/崩溃,优雅退出。
  3. 卡掉能干净退出:收到 SIGTERM(launcher 因卡移除而发,或用户虚拟返回键)一律画 bye + exit(0);mmap 到卡文件失败时视为可恢复/可退出,不 SIGBUS 宿命。

4. meow_launcherd 实现细节

4.1 架构:被动式守护进程(零 patch、零 grab、零注入)

关键内核实证(2026-08-28,见 ANALYSIS.md §18.2):

  1. hiby_player 不持有 EVIOCGRAB:播放器运行时对 event0/event2/event1 的 EVIOCGRAB 探测全部成功(非 EBUSY)——此前"播放器独占按键"的观察不成立或仅为瞬态。
  2. 注入事件永不投递:write() 到任何 event 节点都成功、且更新设备键状态(EVIOCGKEY 可见),但任何 evdev 读者(含 grabber 自身)都收不到——厂商输入子系统修改了投递路径。⇒「grab + write 回写转发」方案在真机不可行(原方案 B 的前半段作废)。
  3. 物理事件多播:播放器自身两个 fd 同时读 event1(触摸正常),证明物理事件对所有读者多播(标准语义)。

因此 launcher 采用:

  • 按键 = EVIOCGKEY 状态轮询(10ms,只读 ioctl,与 grab/投递无关,播放器抓不抓都不影响)——音量/播放(114/115/164/163, event2)、电源/上一曲(116/165, event0)。
  • 触摸 = event1 事件读(未被抓、物理多播)——菜单点击。
  • 不 grab、不注入、不改播放器——"按键无感"由构造保证(播放器按键路径完全没被碰)。

4.2 热键与菜单键

键 设备 code 用途
音量加 event2 115 菜单光标上
音量减 event2 114 热键第二击(电源按住后单击) / 菜单光标下 / 应用内强杀序列键
播放/暂停 event2 164 菜单确认
电源 event0 116 热键第一击(按住) / 菜单/应用内=纯息屏亮屏(不再退出菜单)
下一曲 event2 163 (未用)
上一曲 event0 165 (弃用——GPIO PC28 硬件链断,§18.12)

热键判定(v6.5 定案;v6.7 起窗口下限 300ms→60ms):按住电源 → 电源按下后 60ms~2.5s 内单击音量减(在窗口内收到音量减的按下边沿且电源仍被按住)⇒ 开菜单。电源按下会触发播放器息屏流程(已接受的副作用,用 v6.2/v6.3 已验证时序保证菜单可见:SIGSTOP 播放器 → unblank 面板 → 仅灭屏时拉背光);音量减单击副作用 = 音量 -1(可接受)。电源松开或窗口超时(>2.5s)即 disarm;音量减在窗口外(<60ms 或 >2.5s)按下视为普通音量键,不触发。纯单键序列(v6 volup×3)与组合键(上一曲+音量加,GPIO 断,§18.12)均已退役,仅保留在 meow.conf hotkey=seq(历史备选,不推荐)。

热键 meow.conf 配置:

hotkey=power_voldown   # 默认:电源按住 + 单击音量减(60-2500ms 窗口;v6.7 起下限 300ms→60ms)
hotkey=seq             # 历史序列热键(单键连按 N 次,不推荐)

(hotkey_seq=/hotkey_seq_gap= 仅在 hotkey=seq 时生效。) ⚠️ v6.1 修复:早期版本 hotkey_seq 解析在行尾 \n 处死循环(外层 while 不消费 EOL),配了该行 daemon 即卡死——已修复并在真机复测。该解析器同样用于 hotkey= 行(无此问题,逐行 strncmp)。

音量副作用与恢复:v6.5 主热键(电源+音量减)的副作用是音量 -1(单击音量减,播放器收到,直接作为音量调节——无恢复需求,接受)。序列热键(hotkey=seq,默认已不使用)的每次按下仍会音量+1×N,launcher 在第 1 次按下快照 CS43198 音量、序列完成触发时写回(⚠️ 实测该 control 写无效,恢复为尽力而为)——因已退役,不再展开。

热键演变记录:v1 电源+音量减 → v2 电源+上一曲 → v3 双音量同按(物理不可能:event2 单 ADC 通道)→ v4 上一曲+音量加(上一曲 GPIO PC28 硬件链断,§18.12)→ v6 单键序列(volup×3,单键物理必可注册)→ v6.5 电源+单击音量减(定案):回到"跨设备组合键"能力(电源在 event0、音量减在 event2,两键不同 ADC/GPIO,物理可同按),利用电源按下必然触发播放器息屏的既有行为,以"按住电源 + 窗口内单击音量减"作热键,同时移除菜单/应用内电源=逐级返回(电源在菜单/应用内改为纯息屏/亮屏,见 §3.1/§3.4)。

4.3 fb0 绘图

  • 480×720 RGB565(运行时 ioctl 探测,RGB565/RGBA8888 自适应)
  • ⚠️ 本设备 fb ioctl 号(实测):FBIOGET_VSCREENINFO=0x4600、FBIOGET_FSCREENINFO=0x4602(旧式无 size 编码号)。hello.c/meow_watchdog.c 曾沿用标准头文件常量写反(0x4602/0x4600),导致 ioctl 返回垃圾、mmap EINVAL——已全部修正并用探针实测通过。
  • ⚠️ 双缓冲(v6.6):本设备 fb0 为双缓冲(实测 yres_virtual=1440=2×720、smem_len=2 帧、ypanstep=1),面板显示哪个缓冲由 fb_var.yoffset 决定,FBIOPAN_DISPLAY=0x4606 可翻页。launcher 与 hello 等 app 都只画 mmap 偏移 0(buffer0),若 yoffset 停在 buffer1(播放器电源/息屏重绘后),菜单画在 buffer0 而面板显示 buffer1 → 菜单不可见、屏幕冻结(v6.5 bug)。v6.6 起 fb_open mmap 整个虚拟帧缓冲(含两帧),菜单打开时 fb_backup_player 快照当前 yoffset 处缓冲 → fb_norm_buf0(FBIOPAN_DISPLAY 把 yoffset 归 0,菜单/app 共同绘制目标)→ 回退时 fb_restore_player 把备份拷回原 yoffset 缓冲并 pan 回原 yoffset(播放器事件驱动不主动重绘,必须恢复 yoffset)。面板 blank/亮度状态仍归播放器(§18.11 教训),launcher 只在 fb 缓冲/翻页层面介入。
  • 重绘纪律(v6.9,修复列表 ~3fps 频闪):菜单只在内容真正变化时整屏重绘——光标移动(输入)、应用退出/进入(重写了 buffer0)、toast 起止、未来通知徽标变化时置 dirty;绝无周期整屏重绘。v6.8 的「500ms 安全重绘」会在 500ms 定时把整个菜单重写进正在显示的 buffer0(就地改写产生撕裂/脉冲,且 draw_statusbar 每次调用都 amixer popen 拖慢主循环),正是频闪根因。若真需周期刷新(时钟每秒),用只刷时钟数字的轻量路径 draw_clock()(不弹 amixer、不碰应用列表),其余状态(电量/WIFI/BT/音量)仅在事件(dirty)时随整屏刷新。
  • 布局:标题 MEOWAPPS / 高亮行(光标)/ 状态提示 / BACK TO PLAYER 行 / 底部按键提示 / toast 区(y≈584-617);右下角开发版本号(v6.9)——MeowOS v<MEOWOS_VERSION> dev,右对齐画在底部行 y=700,与 BACK TO PLAYER(y≤688)和左侧按键提示(至 x≈254)均不重叠。

4.4 命令行(含测试钩子)

meow_launcherd                    # 守护模式
meow_launcherd --scan             # 打印应用列表与状态,退出
meow_launcherd --dumpkeys N       # 轮询 N 毫秒,记录所有按键状态边沿(真机抓 key code 用)
meow_launcherd --sim-hotkey N     # N 毫秒后模拟热键(默认=电源按住→N+800ms 单击音量减;hotkey=seq 时=音量加 ×3 快速连按,每次 300ms 间隔)
meow_launcherd --sim-tap N        # 菜单打开 N 毫秒后模拟点击第 0 行
meow_launcherd --sim-exit N       # 菜单打开 N 毫秒后模拟退出

测试钩子用于无物理输入时的全链路自动化验证(注入事件不投递、无法远程按物理键)。

4.5 统一日志系统(MeowAPPs 组件共用)

  • 默认位置:TF 卡 /usr/data/mnt/sd_0/MeowOS/logs/meow_<bootts>.log(如 meow_20260828_020123.log;bootts = 当前墙钟 − /proc/uptime,同一 boot 会话内多次启动共用同一文件)。系统日志(launcher/watchdog 等 MeowOS 组件)写此文件;应用日志另按 apps/<app_id>/logs/<ts>_<pid>.log 分文件(见本 §4.5「组件环境变量」)。⚠️ meow_bootmenu 仍用 MeowAPPs/logs(代码侧不一致;bootmenu 与 launcher 日志目录待统一为 MeowOS/logs,[源:审计 2026-08-30])
  • 无 TF 卡 / 卡晚挂载(S90 早于卡挂载):开不了卡日志——日志落 /tmp 启动缓冲(/tmp/meow_boot.log,上限 256KB 滚动),绝不写板载存储(/usr/data 只剩 ~10MB);卡出现后自动迁移(主循环 ~1s 周期重试 + 输入设备重试循环内也检查;无需开菜单)——log_poll_card() 打开卡日志并把 /tmp 缓冲整段搬入后删除 /tmp 文件
  • 容量上限:logs/ 目录总量 1GB,开机时超限按 mtime 删最旧
  • 组件环境变量:
    • MEOW_LOG_FILE → meow_watchdog:追加写同一 boot 日志文件(无该变量则走 stderr)
    • MEOW_LOG_DIR → 应用:日志目录(每 App MeowOS/logs/apps/<app_id>/logs/,应用写 <ts>_<pid>.log;[源:审计 2026-08-30])
  • 内容:时间戳 + 全部按键边沿/菜单/应用事件(真机按键证据)
  • PID 文件:/usr/data/meow_launcherd.pid;播放器发现:扫描 /proc/N/exe == /usr/bin/hiby_player

4.6 UI 规范(应用列表与 MeowAPPs 界面)

  • 纯黑背景为默认(menu_bg = 0x000000)
  • 可配置:TF 卡 MeowAPPs/meow.conf 支持 bg_color=RRGGBB(如 bg_color=10284f),菜单打开时重新加载
  • 40px 状态栏(v6.8 由 20px 翻倍;v6.9 时钟加秒):第 1 行 = 左侧时钟(HH:MM:SS,每秒刷新,v6.9;用 draw_clock() 只刷时钟数字,不弹 amixer、不碰应用列表——见 §4.3 重绘纪律)、右侧 电量% WIFI BT(电量读 /sys/class/power_supply/battery/capacity、WIFI 读 /sys/class/net/wlan0/operstate、蓝牙读 /sys/class/bluetooth/hci0 存在性);第 2 行 = 左侧 VOL nn%(音量读 CS43198 / amixer numid=1,值 0-255);下方 1px 分隔线(移至 y=40)。⚠️ 音量 amixer control 实测为死镜像(§18.13:播放器走 sa_hgl_dma 硬件直通,ALSA control 读回恒 0),故展示值可能恒为 0%,为已知设备限制。
  • 开发版本号(v6.9 起,现行来自 /etc/meowos_version):右下角绘制 MeowOS v<ver> dev(字体无小写,实际渲染为大写 MEOWOS V<VER> DEV)。版本号单点 = 板上 /etc/meowos_version(M2.47 起;launcher/bootmenu 运行时 load_meowos_version 读;MEOWOS_VERSION 宏仅作文件缺失时 dev 占位兜底,不再当实际版本),屏显与固件文件同源,不会两处不一致。
  • 示例应用 hello 同样黑底 + 40px 状态栏(应用内左上角 48×48 返回键由 watchdog 绘制,允许盖住状态栏左侧)

4.7 应用退出后的菜单触摸守卫 + 启动失败检测(v6.8,Bug 2/3/4)

Bug 2/4 根因(应用退出后菜单重放残留触摸):应用运行期间 launcher 的菜单触摸 fd(touch_fd)不轮询(被 launch_app 的 waitpid 阻塞),期间收集的触摸事件(goodix 幽灵触摸 + 真正退出应用的那次点击/按键)全部排队。应用退出回到菜单后,菜单下一轮 poll 会把这些残留事件重放成新的行点击——落在应用行则重开应用(症状:bye 后回 hello 主界面、需再点一次,Bug 2);落在「BACK TO PLAYER」行则关菜单 → SIGCONT 播放器(整屏闪回播放器,Bug 4)。

修复(run_menu + menu_after_app_exit()):

  1. drain:应用退出后立即读完并丢弃 touch_fd 的全部残留事件。
  2. 冷却:应用真正跑过(launch_app 返回 0)时置 menu_touch_cooldown_until = now + 2500ms,冷却窗内忽略一切触摸按下边沿(与 watchdog 的同值 COOLDOWN_MS 对齐,覆盖实测幽灵窗 1.2-2.25s);启动失败(返回 -1)不置冷却,避免无谓锁菜单。
  3. 菜单重绘:v6.8 曾把 last_redraw 从绝对时间戳 t 改为相对量 el(使 500ms 安全重绘生效)以覆盖应用退出后的残帧(如 BYE)。v6.9 起摒弃周期整屏重绘(它在 500ms 定时把整个菜单重写进正在显示的 buffer0,造成列表 ~3fps 频闪):改为仅 dirty 时整屏重绘,应用退出/进入路径显式置 dirty=1 主动覆盖应用帧;时钟每秒用轻量 draw_clock() 只刷数字。

Bug 3 根因与修复:旧代码在所有 launch_app 调用点无条件置「CANNOT START: 」toast,即使应用正常跑完。修复:仅当 launch_app 返回 -1(真启动失败) 才置 toast;应用干净退出(返回 0)不显示。launch_app 用 CLOEXEC 管道检测 execve 失败(子进程写 errno → 父进程读 >0 判失败并返回 -1),不再把子进程 _exit(127) 误当「应用跑完」。

副作用(已知取舍):应用退出后 2.5s 内菜单触摸不响应(返回键误触/退出后紧急再操作需等待;与 watchdog 相同值,实测幽灵窗 1.2-2.25s)。物理触摸的幽灵无法注入复现(§18.2 注入不投递),以上防抖逻辑在注入/sim 链路已验证,物理最终确认需用户。


5. 音频通路(未来应用的出声方案)

参考 ANALYSIS.md 第 11 节结论:

  • 声卡 = 内部唯一 card0(CS43198 双 DAC),无混音,谁打开谁独占;hiby_player 常驻持有 hw:0,0。
  • v2(SIGSTOP 让位)下播放器完全冻结,无法代播。应用直接开 ALSA 会失败。v3 候选:
    1. 改让位机制为"仅 UI 冻结"(如让播放器走 waitpid-in-callback 或独立音频线程),应用经 unix socket 汇入通道(0x73d320 区,AIRPLAY:CONNECT 类消息 + 16-bit PCM)出声——采样率定 44.1k/16bit(shairport 路径)。
    2. 应用退出后由播放器继续(现状 v2)。
  • 音量/通路切换由 hiby_player 管理(硬件音量寄存器),应用只管喂 PCM。

6. 测试流程

设备已刷 SSH 固件:ssh -i ssh_keys/<device_key> root@<IP>(登录口令见私密记录,勿入库)。 所有设备操作遵守纪律:动播放器前数实例;重启前 rm -f /data/recovery_all;息屏测试后恢复背光;结束恢复设备干净。

  1. 轮前检查:ls /data/recovery_all(存在即删+告警);ps | grep hiby_player | wc -l 应=1。
  2. 推送:meow_launcherd/meow_launcherd → /data/meow_launcherd(测试期直接跑 /data/...);meow_watchdog → /usr/data/meow_watchdog;hello → /usr/data/mnt/sd_0/MeowAPPs/os.meow.hello/hello。
  3. 验证输入路径(无物理按键时的替代证据):
    • meow_launcherd --scan → 列出应用
    • meow_launcherd --dumpkeys 6000 + 物理按键 → 日志出现 key VOL_DOWN/VOL_UP DOWN/UP
    • 或注入状态模拟:evtool send /dev/input/event2 1 114 1(仅更新键状态,无投递,供 EVIOCGKEY 观察)
  4. 全链路(自动化):meow_launcherd --sim-hotkey 2000 --sim-tap 4000;期间 cat /proc/<pid>/stat 应 T(冻结);fb0 截图验证菜单/应用画面(黑底+状态栏);注入上一曲(165)+音量加(115)按住 2s 强杀应用 → 日志 SIGCONT → 播放器回 R、实例 1、背光 101。
  5. 边界 ×5:无卡(umount sd_0 → state=1 → 重挂)、无目录(mv MeowAPPs → state=2)、空目录(state=2)、坏 app.json(跳过)、二进制缺失([MISSING] + toast),各拍 fb0 截图存 device_test/。
  6. 稳定性:守护进程跑 ≥3 分钟,播放器 R、实例 1、无 recovery_all;SIGTERM 干净退出。
  7. 固件:build_fw_apps.sh → fw_apps/r3proii_ssh_apps.upt,回验 0 diff、rootfs ≤ 45,088,768 B。
  8. 回退:重启即回退原版(守护进程不装则无热键);设备侧删除 /data/meow_launcherd 等测试文件。

7. 已知风险与未决项

  1. ⚠️ /data/recovery_all 恢复触发(设备会被出厂重置,最高优先红线):hiby_player 内部存在写该标志的代码路径(0x4823d0 无限循环 / 0x56d1c0 消息处理器)。调试期间设备已被出厂重置两次。防护规矩:每次重启播放器前 rm -f /data/recovery_all;部署后确认稳定才允许设备重启;发现该文件立即删除并告警。Plan B 全程未触发(SIGSTOP 不崩溃播放器)。
  2. 守护进程被 SIGKILL/崩溃 → 播放器冻结:SIGTERM 路径会自动 SIGCONT,但 SIGKILL 无法拦截。恢复:kill -CONT $(pidof hiby_player)。v3 可加双重守护(两个进程互保)。
  3. 热键副作用:v6.5 主热键(电源+单击音量减)会触发播放器息屏流程(菜单打开用后置时序 unblank+仅灭屏拉背光,见 §3.1)、音量 -1(可接受);不再碰电源键"逐级返回"。仅当 hotkey=seq 时才有音量 +N(amixer 音量 control 写无效无法恢复,见 §4.2;可配 hotkey_seq=play,play,play 换取播放停顿)。
  4. 菜单触摸依赖 event1 物理多播:播放器不抓触摸(已实测),若未来固件抓取,菜单仍有按键导航兜底。
  5. UTF-8 应用名:v2 仅 ASCII;中文名需实现 UTF-8→UTF-16 转换。
  6. 应用挂死且虚拟返回键不可见(沉浸态):音量减连按 3 次强杀兜底;再不行长按电源硬件关机。
  7. SIGSTOP 冻结音频线程:应用运行期无声(v3 方向见 §5)。
  8. 开机时序:S90 在 S92 播放器之前启动;输入设备由 S11 加载完成,launcher 启动时重试打开(30 次 × 1s)。

附录:MeowAPPs 开发规范 v2(包结构与数据目录/软件包)

1. 应用目录结构

MeowAPPs/<package>/        # 目录名 = app.json 的 package(权威 app_id)
├── app.json          # 应用配置(name/exec/package/路由/图标/框架声明等)
├── meow              # 程序主体(静态 MIPS 二进制;也可多文件/脚本)
└── <资源文件/图标…>   # 只读程序资源

MeowData/<package>/
├── data/             # 持久应用数据(配置、播放列表、登录态…)
└── cache/            # 缓存(可清)

<package> = app.json 的 package(反域名,如 os.meow.hello),与目录名一致;launcher/app 数据/日志/.mpk 均以它为包名。 注:TF 卡为 exFAT,不支持符号链接,故数据目录不用链接,改由 MeowOS 通过环境变量注入(见 §2)。

2. 数据目录(MeowOS 负责,用环境变量注入)

  • 本文档 <包名> = app.json 的 package(反域名权威标识,如 os.meow.hello),与目录名一致。
  • 程序主体 + 必要配置文件 放 MeowAPPs/<包名>/(可替换/可升级)。
  • 软件数据 放 MeowData/<包名>/{data,cache}(独立于程序,重装/升级保留数据)。
  • MeowOS 每次启动应用前:验证 MeowData/<包名>/{data,cache} 存在(否则建)。
  • 启动时注入环境变量给应用:
    • MEOW_DATA_DIR=/usr/data/mnt/sd_0/MeowData/<包名>/data
    • MEOW_CACHE_DIR=/usr/data/mnt/sd_0/MeowData/<包名>/cache
    • (与已有 MEOW_LOG_DIR/MEOW_ACTIVITY/MEOW_CHAIN 一致)
  • 应用写数据用 $MEOW_DATA_DIR/$MEOW_CACHE_DIR,无需链接;exFAT 也能跑。
  • 好处:数据与程序分离;大体积数据在 MeowData(卡上);换程序不丢数据。

3. 软件包 .mpk

  • 包名 = 文件名茎 = app.json 的 package(如 os.meow.hello → os.meow.hello.mpk)。
  • 格式:.mpk = tar.gz(含 app.json + meow 二进制 + 资源),不含 data/cache(运行时按设备生成)。
  • 导出:打包 MeowAPPs/<包名>/(不含 data/cache 目录)→ <包名>.mpk。
  • 导入/安装(MeowOS 软件包安装程序 meow_pkg_install()):
    1. 校验 .mpk(app.json 合法、包名不冲突/是否覆盖、校验哈希);
    2. 解压到 MeowAPPs/<包名>/(若已有同名,确认升级/覆盖);
    3. 触发首次启动数据初始化(建 MeowData/<包名>/{data,cache});
    4. 登入应用列表。
  • 更新规范 [源:用户定案 2026-08-31]:
    1. 同包名安装 = 更新:旧程序目录整体备份为 .bak_<pid> 后替换,MeowData/<pkg>/{data,cache} 数据保留;安装成功后清理自身产生的 .bak_<pid>。
    2. 不允许降级安装:新包 app.json 的 version 必须 > 已装版本(版本比较;相等视为重装,允许)。版本缺失/不可比 → 拒绝(提示版本无法判定)。
    3. 签名分级:有签名的包必须验签(失败拒装);无签名的第三方包直接安装;os.meow.* 级别必须有有效签名,无签名/验签失败一律拒装(防伪造系统包名)。
  • 后续:Web 后台(阶段 C)提供上传/安装/卸载/导出 .mpk 界面;本地文件管理器可调用安装程序 UI(meow_pkg_install 的交互入口)。
  • 导出过滤(随更新规范落地):nyai export/meow_pkg_export 打包时排除 *.pre_*(热更新备份)、*.bak_*(安装备份)、logs/ 等运行残留,只打 app.json+exec+资源(防 2MB 垃圾进包)。
  • 签名(Ⅱ期,见 devdocs/plan/SYSTEM_FRAMEWORK_SERVICES.md §6 与 devdocs/plan/MPK_SIGNATURE_DESIGN.md):.mpk 增加 signature(Ed25519,签名覆盖 app.json 的 package+payload 哈希 以防伪造 os.meow.* 包名);meow_pkg_install/nyai install 解包验签,按上条「签名分级」执行;meow_pkg_export/nyai export 保留签名(不二次签名;安装时原签包存 MeowData/<pkg>/data/<pkg>.mpk.src,导出优先复制之)。
  • 包名 os.meow.* 约定 + 系统 APP 鉴权规划(Ⅱ期):os.meow.* 为 MeowOS 系统应用保留反域名前缀(如 os.meow.hello 示例);非系统(第三方)App 不得使用 os.meow.* 前缀——.mpk 签名规划强制签名覆盖 package,且 os.meow.* 无签名即拒绝安装,从源头防伪造系统包名。二期关键 IPC 鉴权:只接受 os.meow.* 系统包(关键 IPC 走 os.meow.* 鉴权,拒绝第三方),第三方走受限能力。

4. 实现挂点(MeowOS/launcher)

  • 启动应用入口统一调 meow_app_prepare(pkg):建 MeowData/<包名>/{data,cache} + 设 MEOW_DATA_DIR/CACHE_DIR,失败回退提示。
  • meow_pkg_install/export:包安装/导出(本期可先做 CLI/接口,UI 后续)。
  • 这些为阶段 C(Web)/文件管理器预留。

5. 软件包管理器 CLI nyai(静态 MIPS,apt/yum 风格,含镜像源)

nyai 是 MeowOS 的包管理 CLI(静态 MIPS32r2/musl,无外部依赖;tar.gz 用 gzip -dc | tar -x 管道,与 launcher 一致)。包名 <pkg> = app.json 的 package(= .mpk 文件名茎 = 目录名)。部署:MeowOS/opt/bin/nyai(卡,随 TF_CARD_LAYOUT 外围工具目录)。 调用:M2.12+ 固件 meow_card_path.sh 已将 MeowOS/opt/bin 前插 PATH,nyai 可直接调用;更早固件(M2.11 及以前,无 /etc/profile.d/meow_card_path.sh)SSH 需绝对路径[源:审计 2026-08-30] /usr/data/mnt/sd_0/MeowOS/opt/bin/nyai 或 export PATH=/usr/data/mnt/sd_0/MeowOS/opt/bin:$PATH; M2.12+ 固件该目录已由 meow_card_path.sh 前插,nyai 可直接调用。

命令:

命令 说明
nyai update 读 sources.list 各源:本地读 <源>/Packages;http 用卡上 curl -fsSL <url>/Packages 缓存到 MeowOS/opt/nyai/cache/;无网正确提示、不硬失败
nyai install <包或文件.mpk> ① 参数是 .mpk 路径→直接装;② 是包名→按缓存索引取 .mpk(本地文件或 http 拉取)装;自动解析 Depends(递归)、校验 SHA256+app.json、同名先备份 .bak_<pid>、数据 init(建 MeowData//{data,cache})
nyai remove <pkg> [--purge] 卸载 MeowAPPs/(数据默认保留);--purge 连 MeowData/ 删;清理 .bak_ 残留
nyai list 列出 MeowAPPs/* 已装应用(名称/版本/占用),跳过 fonts/logs/.bak
nyai info <pkg> 读 app.json 显示 name/version/exec/size/routes/services/depends/description
nyai search [pattern] 扫索引列出可装包(含版本/描述)
nyai list-upgradable 索引版本 > 已装版本的包
nyai index <dir> 为 <dir> 生成 Packages 索引(读每个 .mpk 的 app.json 提取 version/description,算 SHA256/Size/Filename)
nyai export <pkg> [out.mpk] 打包 MeowAPPs//(不含 data/cache)→ <pkg>.mpk
nyai scan [dir] / nyai upgrade <pkg> [file.mpk] 列出 .mpk / 升级(保留数据)
nyai --help 帮助;退出码 0=成功 / 非0=错误

源配置 MeowOS/opt/nyai/sources.list(卡;无此文件则默认本地源 MeowOS/opt/repo):

# 每行一个源,支持多源与 # 注释
local /usr/data/mnt/sd_0/MeowOS/opt/repo                  # 本地目录
http http://10.0.0.1/nyai                                  # http(s) 源(url 下挂 Packages)
deb [trusted=yes] /usr/data/mnt/sd_0/MeowOS/opt/repo       # apt 风格别名
file:///usr/data/mnt/sd_0/MeowOS/opt/repo                  # file:// 等价本地

索引 Packages(nyai index <dir> 生成,行式、空行分隔记录):

Package: simtest          # 包名 = .mpk 文件名茎 = app.json.package
Version: 1.2.3            # 来自该 .mpk 内 app.json
Filename: simtest.mpk
Size: 388                 # 字节
SHA256: 3fea0b73…         # 可选;有则 install 时校验
Depends: depapp           # 可选,逗号/空格分隔,递归安装
Description: A simulated app

与 launcher 一致:install/export 复用 M2.12 meow_pkg_install/export 同套语义(tar.gz、gzip 管道、包名=文件名茎、同名先备份、校验 app.json、数据 init);exFAT 无符号链接,数据目录由 MeowOS 经 MEOW_DATA_DIR/CACHE_DIR 注入,CLI 只建目录不建链接。

3.7 音频通路与媒体键(2026-08-31 实测定案,覆盖 §5 旧推论)

  • MeowOS 常驻模式(boot 直进,hiby_player 未运行)声卡空闲:真机实测 fuser /dev/snd/pcmC0D0p /dev/snd/controlC0 无任何占用、pidof hiby_player 不存在——MeowAPP 可直接 open hw:0,0 出声(audio.output 设置键即此);无混音,后开者失败,音频焦点=先占先得(媒体会话/MPRIS 落地后再规范抢占语义)。
  • 会话覆盖模式(HibyOS 运行中热键唤起):SIGSTOP 冻结播放器,声卡仍被其持有,App 无声(§5 旧结论,仅适用该模式)。
  • 物理媒体键路由:MeowOS 与 HibyOS 不同时运行,不存在「转发给 hiby」问题;MeowOS 界面下播放/上下曲 = 全局媒体控制,路由给最近活跃媒体会话(媒体会话/MPRIS 落地后接线),无会话时「抓住」不动作。

3.8 版本号规则(2026-08-31 定案,对齐 Android)

  • app.json 增加 version_code(纯数字,单调递增,安装/升级比较的唯一依据);version 仅作显示字符串(versionName 角色,可为 1.2.3-beta 等任意形态)。
  • 防降级比较只看 version_code:新版 code < 已装 → 拒装;相等 → 允许重装;code 缺失 → 拒装(提示缺 version_code)。
  • nyai index 的 Packages 索引同步记录 Version-Code。