这篇写什么?
Soundbar / 带屏音箱上经常同时存在:
- 系统远场唤醒(例如 Google 远场)——后台长期占用麦克风
- 自研语音助手 / 数字人——也要实时听麦、做本地唤醒
若两路都走同一个逻辑输入(常见是 Built-In Mic),策略层往往只允许一路打开:远场占着麦,自研应用一 AudioRecord 就失败,表现为「只有系统能唤醒,自己的助手醒不了」。
本文总结一种工程做法:再挂一路逻辑输入设备(复用 AUDIO_DEVICE_IN_FM_TUNER),让两路都能从物理麦取数,并允许 mic mixPort 双开。
一句话:
问题:远场占 Built-In Mic → 自研助手录不到
做法:新增 FM Tuner 设备端口 → 路由到 built-in mic mixPort
→ maxOpenCount/maxActiveCount = 2
→ 自研走 FM Tuner,系统远场仍走 Built-In Mic
结果:两路可同时录音、各自唤醒

底层 Mic/Ref 怎么采,见 Mic + Ref 做唤醒 / 降噪;硬件麦阵是否干净,见 麦克风阵列音频检查。
一、问题本质:抢的不是「物理麦」,是「策略上的唯一输入」
Android AudioPolicy 眼里,应用录音要选 device / mixPort / source。
Google 远场常年以 Built-In Mic 打开输入;自研助手若也申请同一路径,会遇到:
| 现象 | 原因(常见) |
|---|---|
自研 AudioRecord 失败 / 无数据 |
Built-In Mic 已被远场占用,默认不允许再开一路 |
| 只有杀远场才能录 | 策略上 mic 是「单开」 |
| HAL 其实能出多路数据 | 卡在 policy XML + 路由,不是麦坏了 |
所以要做的是:策略层承认「同一物理麦 → 两个逻辑设备」,并允许 mic 相关 mixPort 同时打开 2 路。
二、方案概览
| 角色 | 逻辑设备 | 典型用途 |
|---|---|---|
| 系统远场 | Built-In Mic | Google 等远场唤醒 |
| 自研助手 / 数字人 | FM Tuner(AUDIO_DEVICE_IN_FM_TUNER) |
本地唤醒 / 数字人录音 |
物理上仍是同一套麦阵(或同一采集后端);HAL 侧把 FM Tuner 接到与 Built-In Mic 相同的数据源即可。
选用 FM_TUNER 是因为它是系统已有的 输入 device 类型,便于在 policy 里扩端口,而不必新造一套私有枚举(具体 HAL 映射按平台适配)。
同时建议:
built-in mic的 mixPort 设置maxOpenCount="2"、maxActiveCount="2"- 路由:
built-in mic←Built-In Mic, FM Tuner - 按需把 Echo Reference 等唤醒相关 profile 统一到 16 kHz / 16-bit,与唤醒链路一致
三、必须改的 AudioPolicy 配置(概念级)
不同产品会拆成 audio_policy_devices.xml、*_sbr.xml 与公共 audio_policy_common_*.xml。改动点如下。
1)设备列表挂上 FM Tuner
在 attachedDevices 与 devicePorts 引用列表中增加:
<item>FM Tuner</item>
Soundbar 专用 policy、公版 policy 若分文件,两边都要加,避免某一机型漏配。
2)声明 devicePort
在公共 devicePorts 中增加源端口,例如:
<devicePort tagName="FM Tuner" type="AUDIO_DEVICE_IN_FM_TUNER" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="16000"
channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
</devicePort>
采样率 / 声道与自研唤醒约定对齐(常用 16 kHz 立体声 16-bit)。
3)允许 mic mixPort 双开
在公共 base 里,给 built-in mic mixPort 加上:
<mixPort name="built-in mic" role="sink"
maxOpenCount="2" maxActiveCount="2">
...
</mixPort>
含义(人话):
- maxOpenCount="2":最多允许打开 2 个与该 port 相关的客户端
- maxActiveCount="2":最多 2 路可同时处于 active
没有这两项,即使声明了 FM Tuner,策略仍可能按「单麦单开」拒掉第二路。
4)路由:两路设备都进 built-in mic
<route type="mix" sink="built-in mic"
sources="Built-In Mic,FM Tuner"/>
这样无论应用选 Built-In Mic 还是 FM Tuner,都会进同一套 mic mix 路径(再由 HAL 决定如何从硬件取数)。
5)Echo Reference 与唤醒采样对齐(可选但常见)
若远场 / AEC 回采也走 policy,可将 echo reference 从例如 48 kHz / 32-bit 收到与唤醒一致的 16 kHz / 16-bit,减少「麦一路 16k、回采一路 48k」的错配。是否改、改到多少,以你们算法与 HAL 为准。
四、Framework:输入源权限策略
在 AudioPolicyInterfaceImpl 获取 input 时,会对 inputSource 做录音权限判断。
引入 FM Tuner 做「第二路麦」后,需要确认:
- 自研应用持有正常的录音权限
- FM Tuner 路径不会被旧逻辑误拦或误放行
实践中常会梳理 AUDIO_SOURCE_FM_TUNER 与 recordingAllowed 的关系,使 第二路在授权应用下可开、未授权不可偷录。具体 diff 随 Android 大版本略有差异,合入时对照当前分支的 getInputForAttr 逻辑回归一遍。
HAL 侧:为 AUDIO_DEVICE_IN_FM_TUNER 提供打开 / 读流实现,数据源与 Built-In Mic 同源(或同源后做轻量拷贝),否则 policy 配通了仍无 PCM。
五、应用怎么用?
| 客户端 | 建议 |
|---|---|
| Google 远场 | 继续原有 Built-In Mic / 系统约定 source |
| 自研助手 / 数字人 | 录音时指定 FM Tuner 设备(或平台封装好的第二路 API) |
| 两边 | 都按 16 kHz(或统一约定)开流,避免各开各的速率 |
验证建议:
- 只开远场 → 系统可唤醒
- 只开自研 → 助手可唤醒
- 两路同时开 → 两边都能持续出数、都能唤醒
- 杀一路不影响另一路(或符合产品定义的降级策略)
六、注意点与边界
- 不是无限多开 —
maxOpenCount=2只保证两路;再加第三家助手要重新评估。 - 功耗与带宽 — 双开意味着采集/拷贝成本上升,注意待机远场场景。
- 隐私 — 第二路同样是麦克风;权限与前台提示要合规。
- HAL 必须真支持双读 — 仅改 XML 不够;底层要能同时服务两个输入客户端。
- 命名别误解 — 这里的 FM Tuner 是 借用设备类型当第二路麦,不是真的在听 FM 电台。
七、改动文件清单(按模块)
| 模块 | 文件(示例路径) | 改什么 |
|---|---|---|
| 产品 policy | audio_policy_devices.xml / *_sbr.xml |
挂上 FM Tuner |
| 公共 policy | audio_policy_common_base.xml |
mic maxOpenCount/maxActiveCount=2;echo 采样按需 |
| 公共 policy | audio_policy_common_devicePorts.xml |
声明 FM Tuner devicePort |
| 公共 policy | audio_policy_common_routes.xml |
Built-In Mic + FM Tuner → built-in mic |
| Framework | AudioPolicyInterfaceImpl.cpp |
FM Tuner 输入源权限/放行逻辑 |
| Audio HAL | 平台实现 | FM Tuner 打开与读数接到真实麦 |
总结
多路语音唤醒要解决的,是 「两个产品功能都要听麦,但 AudioPolicy 默认只给一路 Built-In Mic」。
做法是:第二逻辑设备(FM Tuner)+ mic 双开配额 + 路由合流 + HAL 同源出数。
配通之后,Google 远场与自研助手可以同时录音、各自唤醒,而不必互相「踢麦」。
相关阅读
评论
还没有评论,来做第一个吧
赞同、吐槽、提问都行,评论区开放