RK3288外接USB麦克风在钉钉/ZOOM无法录音的排查与修复
拿着 RK3288 的板子做音视频会议产品第一件让我头疼的事就是外接 USB 麦克风在钉钉和 ZOOM 里录不了音。我一开始以为是硬件兼容性换了三支不同型号的 USB 麦克风、两根 OTG 线甚至加了独立供电的 HUB问题还是复现。真正怪的是同一台设备用系统相机录像、自带录音机录音都有声音一打开钉钉或者 ZOOM 开会对方就什么都听不到。这种“原生能录、三方不能录”不是玄学而是 Android 音频权限与路由策略在中间夹了几道工序。Android 5.1 和 7.1 虽然只差两个大版本但录音权限模型、音频策略配置格式和处理 USB 音频的框架层都有明显区别踩的坑也不一样。这篇文章就从我实际排查的路径出发把这类问题的根因、日志定位和修复方法完整讲一遍适合做 RK3288 方案、以及做各类 ARM 主板 Android 音视频会议的开发者参考。1. 现象定位原生能录三方不能录问题卡在硬件还是中间件1.1 先把“不能录音”拆成三种现场接到反馈之后不要急着下结论先让现场把现象的颗粒度描述清楚。我遇到过的情况大致分成三类现象常见原因应用一直提示“麦克风权限”未授权或者弹窗一闪而过系统权限管理/运行时权限没有正确触发应用界面麦克风状态正常但对方听不到任何声音录音源没有从 USB 麦克风取到数据或者取到的是空数据应用直接报错、闪退或提示音频设备打开失败AudioRecord 打开失败通常是路由或采样率不匹配我这边遇到的是第二种界面看起来一切正常会议也进来了但就是没有录音数据。这种表面“正常”的情况比直接报错更难追因为问题往往不在 App 本身而在系统音频策略层。1.2 为什么系统能录、三方不能录很多人第一反应是“USB 麦克风坏了”或者“主板 USB 供电不足”。如果系统相机和录音机都能录说明 ALSA 层已经拿到了 USB 音频数据HAL 和驱动的通路是通的问题大概率出在 Android 上层怎么给第三方应用分配输入设备。关键点在于录音源AudioSource不同。系统自带录音机大多使用普通的 MIC 源或者走“默认输入设备”这条路而钉钉、ZOOM 这类音视频应用为了尽量减小回音和延迟通常使用VOICE_COMMUNICATION这类通信录音源。Android 的 AudioPolicyManager 对不同录音源会走不同路由决策如果板子上的音频 HAL 只把内置麦克风挂到了通信源的路由上USB 麦克风就只能出现在“默认录音设备”这条路上于是出现“系统能录、三方不能录”的分裂现象。1.3 这一步最值得先做的一件事如果你也遇到类似问题先别扎进配置文件和代码里先抓两个 dumpsysadb shell dumpsys media.audio_flinger | grep -A 30 RecordThread adb shell dumpsys audio | grep -i -E input|usb|devicedumpsys media.audio_flinger能看到当前音频框架里活跃的录音线程、采样率和输入设备dumpsys audio能看到 USB 设备有没有被系统识别成可用的音频设备。这两份日志能帮你判断问题是出在 HAL 层没有上报 USB 设备还是上报了但路由没有走到第三方应用。2. Android 5.1 和 7.1 的权限暗坑同一套代码在两个系统的表现完全不同2.1 5.1 是安装时授权7.1 是运行时授权Android 5.1 还在用安装时权限模型应用安装的时候只要声明了RECORD_AUDIO安装完成后权限就默认有了。Android 7.1 已经引入运行时权限应用不仅要声明在首次使用麦克风时还要弹窗让用户确认。很多 RK3288 定制 ROM 为了省空间把系统自带的权限弹窗界面裁剪掉了或者由于设备没有在设置里打开“录音”相关权限入口用户压根没法手动授权。结果就是钉钉、ZOOM 这类 targetSdk 版本较高的应用在 7.1 板子上出现奇怪的静音问题。更迷惑的是系统录音机作为系统应用有时候会被授予特权权限不弹窗也能录于是又形成“系统能录、三方不能录”的假象。2.2 用命令快速验证权限不要靠肉眼判断状态栏图标直接通过 adb 查询真实授权# 查看目标应用的运行时权限状态 adb shell dumpsys package com.example.voipapp | grep -A 10 runtime permissions # 强行授予录音权限 adb shell pm grant com.example.voipapp android.permission.RECORD_AUDIO # 通过 appops 强制放行录音操作7.1 可用 adb shell appops set com.example.voipapp RECORD_AUDIO allow如果权限真的是缺失的logcat 里会出现类似E AudioRecord: Could not open audio record device: Permission denied W PackageManager: Not granting permission android.permission.RECORD_AUDIO to package ...看到这种日志问题就已经明确了一半先把应用权限理顺再去折腾路由。权限问题虽然低级但在定制 ROM 上出现的频率相当高尤其是 7.1 的设备。2.3 权限正常还是不能录继续往下走还有一种情况权限已经给了弹窗也出现了用户点了允许但录音依然没有声音。这种情况就会把矛头指向下一层USB 音频设备没有被 Android 音频策略正确路由。我在实际项目里有近乎一半的“不能录音”问题最终是在音频策略配置里解决的不是权限。3. 抓路由证据用 dumpsys 和 logcat 找出 USB MIC 到底有没有进入 Android 音频框架3.1 ALSA 层先确认 USB 声卡已经枚举不管上层怎么折腾硬件枚举是一切的前提。插入 USB 麦克风之后先确认 ALSA 这边是不是真的出现了新的声卡adb shell cat /proc/asound/cards adb shell cat /proc/asound/pcm正常情况下会多出一个类似USB Audio的声卡比如card2。如果这里根本看不到 USB 声卡那问题在 USB 驱动和硬件供电层面和 Android 权限、路由都没关系。常见原因包括OTG 口供电不足、UAC 协议不被芯片支持、USB HUB 没带独立供电。如果这里能看到声卡再接着看 Android 音频框架有没有感知到这个新设备adb shell dumpsys media.audio_flinger | grep -i -E usb|input adb shell dumpsys audio | grep -i -E usb|device如果 dumpsys 结果里完全没有 USB 设备相关条目说明 AudioService 没有收到“USB 音频设备插入”的事件通知这通常是UsbAlsaManager或 HAL 的audio_device上报逻辑被裁剪导致的。这种情况改 XML 是没有用的要先让 framework 感知到 USB 设备。3.2 AudioPolicyManager 的路由决策过程Android 的录音路由不是“哪个 App 申请就给它接哪个设备”而是由 AudioPolicyManager 根据录音源、应用 UID、当前可用设备、策略文件里的 route 配置综合决定。第三方案里常见的调用路径是App 打开 AudioRecord - 指定 source 为 VOICE_COMMUNICATION - AudioPolicyManager 根据 source 找默认输入设备 - 查 audio_policy_configuration.xml 里的 route - HAL 打开对应声卡采集数据如果getDeviceForInputSource()最终返回的是AUDIO_DEVICE_IN_BUILTIN_MIC说明策略把通信录音源固定指向了内置麦克风USB 设备根本没有进入可选路由表。系统录音机因为走的是另一条默认设备路线所以不受影响。3.3 logcat 里需要捕捉的关键行抓取日志时建议做一次清理然后只保留音频相关输出adb logcat -c adb logcat -v time | grep -Ei AudioPolicyManager|AudioFlinger|AudioRecord|VOICE_COMMUNICATION|USB_DEVICE /tmp/audio_route.log然后在板子上打开钉钉或 ZOOM进入一场会议让对方发起通话。结束之后查看日志重点关注日志关键词含义getInputForAttr()应用请求的录音源、UID、属性getDeviceForInputSource()策略层最终选择的输入设备openInputStream()HAL 层打开输入流USB_DEVICE或Built-in Mic实际被选用的设备类型如果日志里从头到尾都是Built-in Mic而 USB 声卡已经被 ALSA 枚举那问题就锁定在策略文件或 HAL 的路由实现上。接下来就可以动手改配置了。4. 修改音频策略让 USB 输入源进入第三方应用的可路由列表4.1 先分清你的板子用哪套配置RK3288 的 5.1 和 7.1 方案音频策略文件的格式并不统一。有些 5.1 源码还沿用老的audio_policy.conf而 7.1 基本都会用audio_policy_configuration.xml。改之前先确认adb shell find /system/etc -maxdepth 1 -name audio_policy* adb shell ls -l /vendor/etc/audio_policy* /odm/etc/audio_policy* 2/dev/null看到audio_policy_configuration.xml就以 XML 为准如果只有audio_policy.conf就改 conf。不要照着 7.1 的 XML 直接往 5.1 上套我曾经见过有人把 XML 塞到只认 conf 的板子上AudioPolicyService 直接启动不了系统整个没有声音。4.2 7.1 的 XML 修改示范以常见 RK3288 Android 7.1 的audio_policy_configuration.xml为例一个思路是把 USB Device 挂到 primary 模块的 route 里让它成为通信录音源可以选择的设备mixPort nameprimary input rolesource profile name formatAUDIO_FORMAT_PCM_16_BIT samplingRates8000,11025,16000,22050,44100,48000 channelMasksAUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO/ /mixPort devicePort tagNameBuilt-In Mic typeAUDIO_DEVICE_IN_BUILTIN_MIC rolesource profile name formatAUDIO_FORMAT_PCM_16_BIT samplingRates8000,11025,16000,22050,44100,48000 channelMasksAUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO/ /devicePort devicePort tagNameUSB Device typeAUDIO_DEVICE_IN_USB_DEVICE rolesource profile name formatAUDIO_FORMAT_PCM_16_BIT samplingRates8000,11025,16000,22050,44100,48000 channelMasksAUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO/ /devicePort route typemix sinkprimary input sourcesBuilt-In Mic,USB Device/注意三个细节type 必须是AUDIO_DEVICE_IN_USB_DEVICE不能写错tagName 和 route 里 sources 的引用字符串要完全一致samplingRates 和 channelMasks 要尽量覆盖应用可能的请求值否则策略层会拒绝打开。有些 SDK 默认的策略文件里USB 是在单独一个usbmodule 里定义的这时只需要在 primary 模块的 devicePorts 里补上同一个 tagName 的 USB Device并在 primary input 的 route 里加上该 source让通信录音源能够走到 USB 设备上。4.3 5.1 老式 conf 的改法如果 5.1 板子用的是audio_policy.conf改动思路一样只是格式不同。通常在audio_hw_modules里给 primary 的 inputs 增加对 USB 设备的支持audio_hw_modules { primary { inputs { primary { sampling_rates 8000|11025|16000|22050|44100|48000 channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO formats AUDIO_FORMAT_PCM_16_BIT devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_USB_DEVICE } } } }同时要在 routes 里确认AUDIO_DEVICE_IN_USB_DEVICE被允许路由到primary input。很多老的 conf 里 input 路由只写了AUDIO_DEVICE_IN_BUILTIN_MIC这是最直接的根因。改完后重启生效不需要重新编译整个镜像。4.4 修改后的生效与系统验证配置放回系统后最稳妥的方式是重启整机。如果不想重启可以在 userdebug 版本上重启 keyboard 相关 service实际有效的做法是adb shell killall audioserver这条命令会让 AudioFlinger 自动拉起并重读策略配置比stop start影响面小。执行完以后重新跑一轮录音验证先测系统录音机再测钉钉/ZOOM。很多人改完配置急着测钉钉结果发现没效果其实系统录音机在这时候也会因为新配置而出现变化先测它能帮你快速判断配置有没有被正确加载。5. 策略文件改完还是不行继续排查采样率、声道和 HAL 兜底5.1 采样率不匹配导致 AudioRecord 打不开策略文件里写了 USB 设备但应用中依然没有声音这种情况也不少见。根因往往出在采样率上。USB 麦克风自带的固件通常固定支持 48kHz而钉钉、ZOOM 这类应用在 VOICE_COMMUNICATION 场景下经常请求 16kHz 或 44.1kHz。Android 框架层理论上能处理重采样但很多 RK3288 方案的 vendor HAL 实现并不完整一旦策略层发现应用请求的采样率在 HAL 上报的 profile 里不存在就会直接拒绝打开输入流表现就是应用不报错但采集不到数据。解决办法是把audio_policy_configuration.xml里的 samplingRates 配置尽量放宽覆盖8000,11025,16000,22050,44100,48000。改完后再抓一次日志确认应用请求的采样率和最终 HAL 打开的采样率是否一致。5.2 麦克风是立体声应用请求单声道同样会翻车另一个容易踩的坑是声道数。很多 USB 会议麦克风默认是双声道AUDIO_CHANNEL_IN_STEREO但钉钉/ZOOM 的底层录音可能只请求单声道。如果 HAL 没有做降混或者策略配置里只声明了 MONO那么声卡无法满足上层请求同样会失败。至少要在配置里同时声明channelMasksAUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO如果 HAL 本身不能把立体声转成单声道通常需要在 HAL 的录制路径里做一次混音或者通过 ALSA 的 route 选择麦克风的主声道采集。这个属于底层的 micro-tuning调试时可借助 tinymix 查看当前 PCM 通道设置adb shell tinymix | grep -i -E mic|pcm|channel5.3 HAL 层强制路由的兜底做法如果策略文件、采样率、声道都调整完了getDeviceForInputSource()还是返回内置麦克风就得考虑 HAL 层兜底了。这种场景常见于一些“老 SDK 新外设”的板子框架层怎么配置都不认最后只能在 HAL 里做硬编码。做法大致是在 HAL 的open_input_stream或start_input_stream里判断当前使用的输入声卡设备。如果检测到 USB 声卡已经枚举就让实际的录音 PCM 使用 USB 声卡的 card/device同时在 HAL 里完成采样率匹配、声道降混。这里要提醒一点HAL 兜底属于项目级私有修改升级、换内核、换底包都可能被冲掉。改完一定要留好 patch 和变更记录不然三个月后自己都忘了这个坑是怎么填的。6. 最终验证清单与几个容易复发的坑6.1 回归验证的完整顺序修完之后我习惯按下面这个顺序回归一步都不会跳插上 USB 麦克风执行cat /proc/asound/cards确认声卡枚举成功。执行dumpsys media.audio_flinger | grep -i usb确认 Android 音频框架感知到 USB 设备。打开系统录音机录制 10 秒确认有波形。打开系统相机录制一段视频确认录到的声音清晰。打开钉钉发起音视频会议确认对方能听到声音。打开 ZOOM重复验证一轮。拔掉 USB 麦克风再验证内置麦克风是否恢复正常。重新插入 USB 麦克风验证热插拔后录音是否依然正常。只要中间任何一步挂了就能快速缩小范围。比如系统录音机挂了说明配置或 HAL 还有问题不用急着去怪 App。6.2 容易复发的三个坑第一配置文件 tagName 和 type 的大小写、空格必须严格匹配。Android 音频策略解析器对字符串是很敏感的多一个空格或者少一个引号devicePort 就可能加载失败但系统不会给出明显报错只会沉默地走默认路由。第二整机固件升级后/system分区被覆盖所有手动改动都会丢失。如果产品已经量产一定要把修改落到源码里而不是只在开发板上 adb push 一份。第三不要忽略 USB 麦克风自身的物理静音开关。我踩过一次最尴尬的坑路由、权限全部正常最后发现是麦克风上有个实体静音键压在桌面边缘。硬件问题虽然低级但在没有 os 工具输出时它是最容易浪费半天时间的存在。6.3 一个实际的小技巧我现在拿到“外接 USB 麦克风不能录音”的反馈时第一句话不是问型号而是先问系统录音机有没有声音。这个问题能直接帮我跳过至少一半的排查流程。如果系统录音机能录问题大概率在上层路由和策略如果系统录音机也录不了先查 ALSA 枚举、USB 供电和 UAC 协议而不是去抓 App 日志。把这套顺序固化下来遇到 RK3288 这类板子的问题基本半小时内能定位到根因剩下就是按配置修改来收尾了。