11、音频策略调试工具:dumpsys audio命令详解、日志分析技巧(logcat)、策略转储文件解读

调试音频策略,说白了就是跟系统“对账”。你定了一套路由规则,系统到底听没听?听对了没有?

我早年刚接触Android音频时,最头疼的就是策略不生效。明明配置了A2DP优先,结果插上蓝牙耳机,声音还是从扬声器出来。后来我才明白——光靠看代码是没用的,你得学会让系统“开口说话”。

今天要讲的这三个工具,就是让系统开口的手段。dumpsys audio、logcat、还有策略转储文件,它们各有侧重,组合起来用,基本能解决90%的路由问题。

核心思路: 先看dumpsys了解当前状态,再用logcat追踪动态变化,最后用转储文件验证策略配置是否正确。三步走,定位问题不超过5分钟。

11.1 dumpsys audio:系统的“体检报告”

dumpsys audio 是Android系统提供的一个诊断命令。我个人习惯把它叫做“音频系统的快照”。你执行这条命令时,系统会把当前所有音频相关的状态、设备、策略、客户端信息一股脑儿吐出来。

基本用法很简单:

adb shell dumpsys audio

输出内容很长,但别慌。我把它拆成几个关键区块来讲。

11.1.1 设备列表:看看系统认出了哪些硬件

输出开头部分会列出所有已注册的音频设备。你会看到类似这样的信息:

AudioPolicyManager:
  Devices:
    - type: 0x2 (AUDIO_DEVICE_OUT_SPEAKER)
      address: ""
      name: "speaker"
    - type: 0x8 (AUDIO_DEVICE_OUT_WIRED_HEADSET)
      address: ""
      name: "headset"
    - type: 0x80 (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP)
      address: "00:11:22:33:44:55"
      name: "Bluetooth A2DP Sink"

这里要注意的是type字段。它用十六进制表示设备类型。0x2是扬声器,0x8是有线耳机,0x80是蓝牙A2DP。每个设备还有一个address,蓝牙设备会显示MAC地址,USB设备会显示设备路径。

我的经验: 如果某个设备插上后没出现在列表里,说明硬件层就没识别到。这时候别查策略,先去查驱动和HAL层。

11.1.2 策略规则:系统当前在用什么规则

再往下翻,你会看到策略引擎的当前状态:

AudioPolicyManager:
  Engine Config:
    mEngineLib: libaudiopolicymanagerdefault.so
    mPhoneState: 0 (CALL_STATE_IDLE)
    mForceUse: 
      FOR_COMMUNICATION: 0 (FORCE_NONE)
      FOR_MEDIA: 0 (FORCE_NONE)
      FOR_RECORD: 0 (FORCE_NONE)
      FOR_DOCK: 0 (FORCE_NONE)

mForceUse 是重点。它表示当前有没有强制使用某个设备。比如你插了耳机,但声音还是从扬声器出来,可以看看FOR_MEDIA是不是被设成了FORCE_SPEAKER。如果是,那就是有上层应用强制改了策略。

我曾经遇到一个案例:某视频App播放时声音死活不从蓝牙走。一查dumpsys,发现FOR_MEDIA被设成了FORCE_WIRED_ACCESSORY。后来定位到是App自己调了setForceUse接口。嗯,这种问题最坑人。

11.1.3 客户端列表:谁在占用音频

这部分会列出所有正在使用音频的客户端:

AudioFlinger:
  Clients:
    pid: 1234
    package: com.tencent.qqmusic
    tracks:
      - stream_type: 3 (MUSIC)
        device: 0x80 (A2DP)
        flags: 0x100
        sample_rate: 48000
        channel_mask: 0x3 (STEREO)

这里能看到每个App的音频流类型、输出设备、采样率等信息。如果某个App的声音没按预期路由,先看它的device字段是不是对的。

注意: dumpsys audio 的输出是动态的。同一个设备在不同状态下,输出可能完全不同。建议在问题复现时立刻抓取,不要事后补抓。

11.2 logcat日志分析:追踪音频事件的“时间线”

dumpsys 是静态快照,logcat 是动态日志。两者配合,才能还原问题全貌。

音频相关的logcat标签主要有这几个:

标签 用途 过滤命令
AudioPolicyManager 策略引擎决策日志 adb logcat -s AudioPolicyManager
AudioFlinger 音频混音和输出日志 adb logcat -s AudioFlinger
AudioService 上层服务调度日志 adb logcat -s AudioService
audio_hw_primary 硬件抽象层日志 adb logcat -s audio_hw_primary

我个人习惯这样抓:

adb logcat -c  # 先清空旧日志
adb logcat -v time -s AudioPolicyManager AudioFlinger AudioService > audio_log.txt

然后复现问题。等复现完了,按Ctrl+C停止抓取。打开audio_log.txt,按时间线看事件顺序。

11.2.1 关键日志模式:设备插拔

插拔耳机时,你会看到这样的日志:

01-01 12:00:01.123 AudioPolicyManager: setDeviceConnectionState(0x8, CONNECTED)
01-01 12:00:01.124 AudioPolicyManager: checkOutputsForDevice(0x8)
01-01 12:00:01.125 AudioPolicyManager: switchOutputDevice(0x2 -> 0x8)

这三行日志说明:系统检测到耳机插入(0x8),检查了哪些输出可以用,然后把输出设备从扬声器(0x2)切换到了耳机(0x8)。

如果只看到前两行,没有第三行,说明策略引擎认为不需要切换。这时候就要去查策略配置文件了。

11.2.2 避坑指南:日志级别

我曾经被坑过一次:某台设备上蓝牙连上后声音没切过去。抓了logcat,发现AudioPolicyManager的日志很少,根本看不出决策过程。后来才发现,那台设备的系统把日志级别设成了WARN,很多DEBUG级别的日志被过滤掉了。

解决办法:

adb shell setprop persist.log.tag.AudioPolicyManager DEBUG
adb reboot

重启后,所有策略决策的细节日志就都出来了。

11.3 策略转储文件:配置的“源代码”

dumpsys 和 logcat 看的是运行时状态。但有时候,问题出在配置本身。这时候就需要看策略转储文件。

Android 10之后,策略配置通常放在这里:

/vendor/etc/audio_policy_configuration.xml
/vendor/etc/audio_policy_engine_configuration.xml

你可以用adb pull拉下来看:

adb pull /vendor/etc/audio_policy_configuration.xml .

11.3.1 配置文件结构

打开这个XML文件,你会看到这样的结构:

<audioPolicyConfiguration version="1.0">
  <globalConfiguration speaker_drc_enabled="false"/>
  <modules>
    <module name="primary" halVersion="3.0">
      <attachedDevices>
        <item>Speaker</item>
        <item>Wired Headset</item>
      </attachedDevices>
      <devicePorts>
        <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="source">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </devicePort>
      </devicePorts>
      <mixPorts>
        <mixPort name="primary output" role="sink">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </mixPort>
      </mixPorts>
      <routes>
        <route type="mix" sink="primary output" sources="Speaker,Wired Headset"/>
      </routes>
    </module>
  </modules>
</audioPolicyConfiguration>

这里的关键是routes部分。它定义了mixPort和devicePort之间的连接关系。如果某个设备没有出现在routes里,那策略引擎永远不会把音频路由到它。

检查要点:
  • 设备是否在attachedDevices里?不在的话,系统不会主动识别它。
  • 设备是否在routes里?不在的话,策略引擎不会路由到它。
  • 采样率和格式是否匹配?不匹配的话,可能无声或声音异常。

11.3.2 策略引擎配置

另一个文件 audio_policy_engine_configuration.xml 定义了更细粒度的路由规则:

<policyEngineConfiguration>
  <engineSuffix>default</engineSuffix>
  <criterionTypes>
    <criterionType name="Usage">
      <criterion name="USAGE_MEDIA" value="1"/>
      <criterion name="USAGE_VOICE_COMMUNICATION" value="2"/>
    </criterionType>
  </criterionTypes>
  <policy>
    <rules>
      <rule>
        <condition>Usage == USAGE_MEDIA</condition>
        <output>A2DP, Speaker, Wired Headset</output>
      </rule>
    </rules>
  </policy>
</policyEngineConfiguration>

这个文件定义了“当Usage是媒体时,优先使用A2DP,其次Speaker,最后Wired Headset”。如果这个规则写错了,比如把A2DP漏掉了,那蓝牙耳机就永远不会有声音。

11.4 实战:一个完整的调试流程

假设你遇到一个问题:插上蓝牙耳机,音乐App的声音还是从扬声器出来。我会这样排查:

  1. 第一步:dumpsys audio,看蓝牙设备是否在设备列表里。如果不在,查蓝牙连接和HAL层。
  2. 第二步:dumpsys audio,看mForceUse有没有被强制设为扬声器。如果有,查上层应用。
  3. 第三步:抓logcat,看插拔蓝牙时有没有switchOutputDevice的日志。如果没有,查策略配置。
  4. 第四步:拉取audio_policy_configuration.xml,看蓝牙设备是否在routes里。
  5. 第五步:拉取audio_policy_engine_configuration.xml,看媒体路由规则是否包含A2DP。

按这个顺序走,99%的问题都能定位到根因。

我的习惯: 我会把dumpsys audio的输出保存成文件,然后grep关键字段。比如 dumpsys audio | grep -A 5 "Devices:" 只看设备列表。这样比翻几百行输出快得多。

11.5 知识体系图

下面这张图总结了本章的核心逻辑:三个工具分别覆盖了调试的三个层面。

音频策略调试工具体系 dumpsys audio 静态快照 • 设备列表 • 策略规则状态 • 客户端占用情况 • 强制使用标志 logcat 日志 动态时间线 • 设备插拔事件 • 策略决策过程 • 输出切换日志 • 错误/警告信息 策略转储文件 配置源代码 • 设备端口定义 • 路由规则配置 • 采样率/格式 • 引擎策略规则 调试流程:dumpsys → logcat → 转储文件 先看状态,再看过程,最后验证配置 三个工具组合使用,覆盖调试全链路

这三个工具就像医生的听诊器、CT机和化验单。dumpsys audio 告诉你“现在怎么样”,logcat 告诉你“刚才发生了什么”,策略转储文件告诉你“应该是什么样”。三者对照,问题无所遁形。

好了,这一章的内容就到这里。记住我说的:调试音频策略,别靠猜,让数据说话。


公众号:蓝海资料掘金营,微信deep3321