# Lila Wakeup APK 工程上下文（com.lila.wakeup）

> **本文件用于 Claude Code 插件加载，自动获取项目背景。**
> 本工程是 OrangePi CM5 数字人产品的 KWS 软件唤醒服务（独立 APK），与第三方 Unity 数字人 APP（包名 `com.qy.lila`）通过 Android Broadcast 协议联动。

---

## 一、项目本质（一句话）

基于 **sherpa-onnx** 引擎的 Android 13 KWS（Keyword Spotting / 关键词唤醒）APK，监听用户喊"你好 Lila"等唤醒词后通过 Broadcast 通知数字人 APP 显示数字人 + 接通火山 RTC。

替代原方案的"ESP32 + 离线语音芯片 + USB 串口 SO_WAKEUP1"硬件方案，省 BOM 成本 + 更灵活。

---

## 二、核心元信息（写代码前必读）

| 项 | 值 |
|---|---|
| 本 APK 包名 | `com.lila.wakeup` |
| 数字人 APP 包名 | `com.qy.lila`（已确认，来源 LTY_Project ProjectSettings.asset） |
| 目标系统 | Android 13 (API 33) |
| 目标架构 | arm64-v8a（仅打包此架构，节省 APK 体积） |
| 目标设备 | OrangePi CM5（Rockchip RK3588，rk3588s_s） |
| 协议版本 | **v2.1**（详见上游 `docs/OrangePi_CM5/MD_Document/KWS唤醒协议-V2.md`） |
| KWS 引擎 | sherpa-onnx Zipformer wenetspeech 3.3M（中文，已预编译 native）|
| 集成方式 | **基于 sherpa-onnx 官方 SherpaOnnxKws demo 改造**（不是 Maven 依赖，因 Android arm64-v8a .so + Kotlin Wrapper 在 Maven 上没发布完整包） |
| 当前阶段 | **方案 A（普通 APK）**（量产前升级到方案 B：系统签名 + /system/priv-app/）|

---

## 三、协议规约（v2.1 核心摘要）

### 三个 Broadcast Action（双向 setPackage 强制要求）

| 方向 | Action | 字段 |
|---|---|---|
| **WAKEUP**（本 → APP）| `com.lila.intent.action.WAKEUP` | `keyword: String` / `timestamp: long` / `confidence: float` |
| **KWS_PAUSE**（APP → 本）| `com.lila.intent.action.KWS_PAUSE` | `silence_ms: long`（默认 **3000**，Networking 场景 60000）/ `reason: String` |
| **KWS_RESUME**（APP → 本）| `com.lila.intent.action.KWS_RESUME` | `reason: String` |

### 关键设计约束（不要破坏）

1. **silence_ms 二次保险**：收到 PAUSE 后即使收到 RESUME 也要等到 silence 满才真正 resume —— 防 AI 自我唤醒
2. **2 分钟兜底超时**：APP 漏发 RESUME 时 KWS 自动恢复（防永久哑火）
3. **后验平滑**：连续 N 帧（默认 2）置信度过阈值才发 WAKEUP —— 降误唤醒
4. **AudioRecord 暂停时完全 release**：把麦克风让给 RTC SDK，避免资源冲突
5. **WAKEUP 命中后不自动 PAUSE 自己**：必须由 APP 主动发 PAUSE —— 防漏命中导致的死锁

---

## 四、工程结构（基于 sherpa-onnx demo 改造后）

```
工程根/（原 sherpa-onnx/android/SherpaOnnxKws）
├── CLAUDE.md                              ← 本文件
├── app/
│   ├── build.gradle.kts                   ← 已改：包名 + arm64-v8a only + targetSdk 33
│   └── src/main/
│       ├── AndroidManifest.xml            ← 已改：完整权限 + 4 个组件声明
│       ├── assets/
│       │   └── sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01/
│       │       ├── *.onnx                 ← 原 demo 自带（不动）
│       │       ├── tokens.txt             ← 原 demo 自带（不动）
│       │       └── keywords.txt           ← 已改：5 个 Lila 唤醒词
│       └── java/com/lila/wakeup/          ← 已改：包名 + 9 个新文件
│           ├── Config.kt                  ← 协议常量、阈值、模型路径
│           ├── MainActivity.kt            ← 状态查看 UI + 权限申请（开发期辅助）
│           ├── BootReceiver.kt            ← 开机自启
│           ├── WakeupForegroundService.kt ← 前台 Service 主体
│           ├── kws/
│           │   ├── AudioCapture.kt        ← AudioRecord 16kHz mono PCM16
│           │   ├── KwsEngine.kt           ← sherpa-onnx KWS 封装
│           │   └── KwsStateMachine.kt     ← LISTENING/PAUSED 状态机 + silence_ms
│           ├── protocol/
│           │   ├── BroadcastSender.kt     ← 发 WAKEUP 给 com.qy.lila
│           │   └── ProtocolReceiver.kt    ← 收 PAUSE/RESUME
│           └── （sherpa-onnx 自带的 Wrapper 类保留，包名 com.k2fsa.sherpa.onnx 不动）
└── build.gradle.kts (项目级)              ← 不动
```

### 9 个我们写的 Kotlin 文件代码量

| 文件 | 行数 | 职责 |
|---|---|---|
| Config.kt | 108 | 全局常量 |
| MainActivity.kt | 117 | 状态 UI + 权限申请 |
| BootReceiver.kt | 50 | 开机自启 |
| WakeupForegroundService.kt | 142 | Service 主体 + 通知栏 |
| AudioCapture.kt | 112 | AudioRecord 封装 |
| KwsEngine.kt | 119 | sherpa-onnx 封装 |
| KwsStateMachine.kt | 163 | 状态机 + silence_ms + 兜底 |
| BroadcastSender.kt | 36 | 发 WAKEUP |
| ProtocolReceiver.kt | 63 | 收 PAUSE/RESUME |

---

## 五、关键参考文档（在主仓库 OrangePi_CM5_Project 中）

> **这些文档是工程的"上游契约"**，本工程实现必须与之一致。改协议要先去主仓库改文档。

| 文档 | 路径 | 作用 |
|---|---|---|
| 协议规约 v2.1 | `/Volumes/LinuxDev/OrangePi_CM5_Project/docs/OrangePi_CM5/MD_Document/KWS唤醒协议-V2.md` | 双方对接契约（最权威）|
| 服务设计 | `/Volumes/LinuxDev/OrangePi_CM5_Project/docs/OrangePi_CM5/MD_Document/KWS服务设计.md` | 我侧实现指南 |
| 第三方评估 | `/Volumes/LinuxDev/OrangePi_CM5_Project/docs/OrangePi_CM5/MD_Document/KWS唤醒方案适配评估_Unity.md` | Unity APP 团队的对接评估（含 v2 微调建议）|
| 工程骨架 README | `/Volumes/LinuxDev/OrangePi_CM5_Project/docs/OrangePi_CM5/MD_Document/KWS-APK-工程骨架/README.md` | 9 步实施指南（含路线图、坑提示）|

---

## 六、用户偏好与代码风格（继承自主项目）

- **语言**：所有沟通用中文，代码注释用中文
- **代码风格**：snake_case 变量、UPPER_SNAKE_CASE 常量、_t 后缀类型；Kotlin 这边按 Kotlin 标准（CamelCase 类名 / camelCase 变量），但**注释一律中文**
- **嵌入式原则**：节省 RAM/Flash > 时序优化 > 模块化（虽然这是 Android 但 OrangePi 资源仍宝贵）
- **不主动加冗余校验**（CLAUDE.md 全局规则）
- **破坏性操作先确认**（git push --force / rm 等）
- **优先静态分配**：避免频繁 malloc/free，字符串处理用固定缓冲区

---

## 七、当前任务路线图

| 阶段 | 内容 | 状态 |
|---|---|---|
| 0. 算法验证 | sherpa-onnx 预编译 KWS APK 装到板子，测识别率 | ✅ 已完成（5/8 命中）|
| 1. 工程骨架 | 9 个 Kotlin 类 + Manifest + gradle | ✅ 已完成（在主仓库 KWS-APK-工程骨架/）|
| 2. AS 装齐组件 | API 33 / Build-Tools 33 / NDK 25 / CMake 3.22 | 🔄 进行中 |
| 3. 克隆 sherpa-onnx + 改造 | git clone + Refactor 包名 + 拷骨架文件 | 📋 待做 |
| 4. Build → adb install 到 OrangePi CM5 | 验证唤醒效果 | 📋 待做 |
| 5. 协议联调（与 Unity APP）| 按协议 v2.1 §八 联调清单 | 📋 待 APP 团队 |
| 6. 方案 B 升级 | 系统签名 + 集成进 update.img | 📋 联调通过后 |
| 7. 长稳测试 | 72h 连续运行 | 📋 量产前 |

---

## 八、关键技术决策记录（不要重新讨论）

| 决策 | 结论 | 理由（一句话）|
|---|---|---|
| 用 Maven 依赖还是克隆 demo？ | **克隆 demo** | Maven 上 sherpa-onnx 的 Android arm64-v8a .so + Kotlin Wrapper 不全 |
| 方案 A / B / C 怎么走？ | **A 跑通 → B 升级**（不走 C）| C 改 AOSP system_server 收益不抵成本 |
| silence_ms 默认值 | **3000ms** | 第三方实测 RTC 远端音频回灌 800-1500ms + AI TTS 触发，2000ms 不够 |
| Networking 场景是否唤醒？ | **不允许**（APP 进入发 PAUSE）| 配网中被唤醒会要求 APP 新增响应逻辑 + 决策跳哪里 |
| BroadcastReceiver 与 Service 通信方式 | **Locator 模式** | 比 startService / AIDL 简单，性能好 |
| AudioRecord 暂停时是 stop 还是 release？ | **完全 release** | 释放麦克风给 RTC SDK，避免资源冲突 |

---

## 九、绝对不要做的事

1. ❌ **不要改包名 `com.qy.lila`**：APP 团队已确认，改了协议失效
2. ❌ **不要改 Action 名 `com.lila.intent.action.*`**：双方协议契约
3. ❌ **不要把 sherpa-onnx 源码包名 `com.k2fsa.sherpa.onnx` 改掉**：JNI native 库 binding 的硬编码包名
4. ❌ **不要在前台 Service 里调耗时操作（vTaskDelay 类）阻塞 main looper**：StateMachine 的 timeout 用 Handler.postDelayed
5. ❌ **不要在 AudioCapture 线程里同步调网络 / 文件 IO**：会丢音频帧
6. ❌ **不要禁用 START_STICKY**：Service 异常重启依赖此标志
7. ❌ **不要把 RECORD_AUDIO 申请放在 Service**：必须在 Activity 里申请（Android 限制）

---

## 十、与主项目（OrangePi_CM5_Project）的协作

主项目的 Claude Code 会话负责：
- 协议规约文档（KWS唤醒协议-V2.md）演进
- 服务设计文档（KWS服务设计.md）演进
- 与第三方 APP 团队对接
- 系统侧（kernel / dts / Audio HAL）相关修改
- 整机集成（方案 B 升级）

本工程（sherpa-onnx 改造）的 Claude Code 会话负责：
- 9 个 Kotlin 文件的具体实现
- Gradle / Manifest 配置
- Android Studio Build / Run 调试
- KWS 引擎参数调优（threshold、N 帧平滑）
- 真机测试（与 OrangePi CM5 联调）

**协议改动必须先在主项目改文档，再同步到本工程。**

---

## 十一、典型工作请求示例（让 Claude Code 上手）

新来 Claude Code 插件可以处理的典型任务：

- "帮我改 [`KwsStateMachine.kt`](app/src/main/java/com/lila/wakeup/kws/KwsStateMachine.kt) 的后验平滑窗口从 2 帧改为 3 帧"
- "把 [`build.gradle.kts`](app/build.gradle.kts) 的 minSdk 从 26 改为 24"
- "新增一个 keywords 重载接口，让 [`KwsEngine.kt`](app/src/main/java/com/lila/wakeup/kws/KwsEngine.kt) 支持热更新唤醒词"
- "[`MainActivity.kt`](app/src/main/java/com/lila/wakeup/MainActivity.kt) 状态展示太丑，帮我用 Compose 重写"
- "Build 报错 `Unresolved reference: KeywordSpotter`，怎么改？"
- "帮我加一个 logcat 过滤器，只看 KWS 命中日志"

需要主项目协调的请求（**不要自己改本工程**）：

- "改协议字段名" → 让用户回主项目讨论
- "升级方案 B" → 涉及 SDK 集成，回主项目
- "对接第三方新需求" → 协议要先改

---

## 十二、运行时验证

```bash
# 装到板子
adb install -r -g ./app/build/outputs/apk/debug/app-debug.apk

# 看日志
adb logcat -s KwsService.Svc:* KwsService.Engine:* KwsService.State:* KwsService.Audio:* KwsService.Send:* KwsService.Proto:* KwsService.Boot:*

# 关键日志关键字
[Engine] init done            ← 引擎加载成功
[State] -> LISTENING          ← 进入监听
[KWS] HIT confirmed           ← 命中
[Broadcast] WAKEUP -> com.qy.lila  ← 发广播给 APP

# 测试 APP 端 PAUSE/RESUME（不依赖 Unity APP，用 adb 模拟）
adb shell am broadcast -a com.lila.intent.action.KWS_PAUSE \
  -n com.lila.wakeup/.protocol.ProtocolReceiver \
  --el silence_ms 3000 --es reason "test_pause"

adb shell am broadcast -a com.lila.intent.action.KWS_RESUME \
  -n com.lila.wakeup/.protocol.ProtocolReceiver \
  --es reason "test_resume"
```
