# 语音协议广播测试脚本 `adb shell am broadcast` 形式的联调脚本集合,覆盖 [可行性评估文档](../../../docs/2026-08-22-语音影视接入可行性评估.md) §6.1 中定义的统一协议(`action=com.pukka.jiecheng.action.VOICE_COMMAND`),用于在没有第三方语音 App 时手工驱动 voice 模块。 > 适用范围:`bash`(Git Bash 即可)。脚本里所有路径用 Unix 风格。 ## 1. 目录结构 ``` test/voice_helper/ ├── lib/ │ └── common.sh # 公共函数:payload 构造 / 广播发送 / 应用级操作 ├── jump_film.sh # 单片跳转 JUMP/FILM ├── jump_category.sh # 分类跳转 JUMP/CATEGORY ├── search.sh # 语音搜索 SEARCH ├── control.sh # 播放控制 CONTROL/ ├── cold_start.sh # 冷启动单片跳转(force-stop + 投递 + 拉起) ├── throttle_test.sh # 2 秒内连续投递同类命令(验证节流) ├── security_test.sh # 非白名单 sourceApp 投递(验证签名校验) ├── listen_callback.sh # 反向广播回调监听(adb logcat) ├── run_all.sh # 顺序执行全部用例 └── README.md ``` ## 2. 协议约定(与文档 §6.1 一致) ``` action: com.pukka.jiecheng.action.VOICE_COMMAND permission: com.pukka.jiecheng.permission.VOICE_COMMAND component: com.pukka.jiecheng/.voice.receiver.VoiceCommandReceiver extras: requestId string 第三方生成的请求 ID,回传时带回 sourceApp string 第三方包名 sourcePlatform string 第三方平台标识(用于选择 VendorAParser/BParser) commandType string JUMP / SEARCH / CONTROL payload string JSON,业务字段 callbackAction string 反向广播 action,由第三方传入 timestamp long 毫秒 ``` `payload` 按 `commandType` 分支: | commandType | payload 字段 | |---|---| | `JUMP` | `jumpType` (`FILM`/`CATEGORY`/`ACTOR`/`DIRECTOR`) + 对应 `*Id`/`*Name`,可带 `autoPlay` | | `SEARCH` | `filmName` / `actorName` / `directorName` / `category` / `unionStyle` / `region` / `releaseDate` | | `CONTROL`| `action` (`PLAY`/`PAUSE`/`FULLSCREEN_ENTER`/`FULLSCREEN_EXIT`/`SEEK_FORWARD`/`SEEK_BACKWARD`/`SEEK_TO`/`PREV`/`NEXT`/`SELECT_INDEX`/`PAGE_NEXT`/`PAGE_PREV`/`GOTO_PAGE`) + `seconds` / `index` / `page` | ## 3. 快速开始 ```bash # 设备已连接 adb 后: cd test/voice_helper # 单片跳转(默认 filmId=film-demo-001 / autoPlay=true) ./jump_film.sh # 自定义参数 ./jump_film.sh film-12345 "我的影片" true # 分类跳转 ./jump_category.sh cat-action "动作片" # 语音搜索 ./search.sh "流浪地球" ./search.sh "流浪地球" "吴京" # 播放控制 ./control.sh PAUSE ./control.sh SEEK_FORWARD seconds=10 ./control.sh SEEK_TO seconds=300 ./control.sh SELECT_INDEX index=3 ./control.sh GOTO_PAGE page=2 # 冷启动(force-stop → 投递 → 拉起 MainActivity) ./cold_start.sh # 2 秒内连续 5 次 PAUSE,验证节流 ./throttle_test.sh PAUSE 5 # 非白名单 sourceApp 投递(验证拦截) ./security_test.sh com.attacker.malicious # 反向广播监听(需 voice 模块按 tag 打印回调日志,tag 默认 VoiceCommand) ./listen_callback.sh # 监听全部 ./listen_callback.sh vt-1756000000-12345 # 只过滤指定 requestId # 一键全量 ./run_all.sh ``` ## 4. 环境变量 | 变量 | 默认值 | 说明 | |---|---|---| | `ACTION_VOICE_COMMAND` | `com.pukka.jiecheng.action.VOICE_COMMAND` | 广播 action | | `PERMISSION_VOICE` | `com.pukka.jiecheng.permission.VOICE_COMMAND` | 协议权限 | | `RECEIVER_COMPONENT` | `com.pukka.jiecheng/.voice.receiver.VoiceCommandReceiver` | 显式组件 | | `MAIN_ACTIVITY_COMPONENT` | `com.pukka.jiecheng/.MainActivity` | 冷启动入口 | | `TEST_APP_PKG` | `com.pukka.jiecheng` | 应用包名 | | `DEFAULT_SOURCE_APP` | `com.test.voicehelper` | 模拟第三方包名 | | `DEFAULT_PLATFORM` | `vendorA` | 模拟平台 | | `DEFAULT_CALLBACK_ACTION`| `com.test.voicehelper.action.VOICE_CALLBACK` | 反向广播 action | | `TEST_REQUEST_PREFIX` | `vt-` | requestId 前缀 | | `SKIP_PERMISSION_CHECK` | `0` | 开发期临时绕开 signature-level permission 校验时置 1 | | `LOG_TAG` | `VoiceCommand` | `listen_callback.sh` 监听的 logcat tag | 示例:自定义第三方包名 ```bash TEST_REQUEST_PREFIX=demo- DEFAULT_SOURCE_APP=com.xiaomi.voice ./jump_film.sh ``` ## 5. 关于 `signature-level permission` 文档 §6.2 规定 Receiver 使用自定义 `signature-level permission`(`com.pukka.jiecheng.permission.VOICE_COMMAND`)。`adb shell` 默认以 `shell` 用户身份投递,**不持有第三方签名权限**,广播会被系统直接丢弃。 联调期三种处理方式: 1. **临时降级(推荐)**:在 `app/build.gradle` 给 `debug` flavor 注入 `manifestPlaceholder`: ```groovy debug { manifestPlaceholders = [voiceProtectionLevel: "normal"] } release { manifestPlaceholders = [voiceProtectionLevel: "signature"] } ``` 然后在 `AndroidManifest.xml`: ```xml ``` 2. **拷贝脚本到端内**:在应用内临时放一个 `BroadcastSender` Activity 直接发广播,绕过 shell 权限。 3. **改 adb 用户**:通过 `adb root` 切到 root 身份,仅在 userdebug 镜像生效。 脚本本身不强制任何一种处理,遇到权限拦截请按上面方式放开。 ## 6. 验证 checklist(对应文档 §12) 联调完成后逐项打勾: - [ ] 应用未启动时第三方冷启动并执行命令(`cold_start.sh`) - [ ] 应用未激活时被守卫拦截,回传 `DEVICE_NOT_ACTIVATED` - [ ] 正在播放时收到搜索 / 分类命令先退出再跳转 - [ ] 2 秒内连续多次控制命令只保留最后一条(`throttle_test.sh`) - [ ] 第三方收到成功 / 失败回传(`listen_callback.sh` 配合 logcat) - [ ] `SELECT_INDEX` / `GOTO_PAGE` 在播放页列表中正确选中 - [ ] 白名单外的 `sourceApp` 被拦截、不回传(`security_test.sh`) ## 7. 调试技巧 ```bash # 查看应用包内注册的 Receiver adb shell dumpsys package com.pukka.jiecheng | grep -i receiver # 查看设备上所有待发广播 adb shell dumpsys activity broadcasts # 查看最近一次 am broadcast 的返回结果(带 requestId 过滤) adb shell am broadcast -a com.pukka.jiecheng.action.VOICE_COMMAND -n ... --es requestId probe # 实时过滤 voice 模块日志 adb logcat -v threadtime VoiceCommand:V VoiceCommandLogger:V "*:S" ```