AudioPolicyService 笔记

7 阅读6分钟

好的,我们接着深入探讨 audioserver 中的另一位核心成员——AudioPolicyService。它与 AudioFlinger 相辅相成,共同构成了 Android 音频系统的中枢。

🧭 角色定位:音频系统的“决策中心”

如果说 AudioFlinger 是“执行者”,负责音频数据的实际搬运与处理,那么 AudioPolicyService 就是“决策者”,负责回答三个核心问题:

  1. 何时、何地 播放或录制?
  2. 使用哪个设备(扬声器、耳机、蓝牙等)?
  3. 各个音频流之间如何协调(谁可以出声、音量多大)?

它不接触音频数据本身,而是制定规则并下达指令,指挥 AudioFlinger 和硬件抽象层(HAL)完成具体工作。

🚀 启动与架构

AudioPolicyService 同样运行在 audioserver 进程中,由 main_audioserver.cpp 在启动 AudioFlinger 之后创建并注册到 ServiceManager

其核心组件结构如下:

AudioPolicyService (Binder 服务端)
  └── AudioPolicyManager (主要实现逻辑)
        ├── Engine (策略引擎,解析配置、执行路由算法)
        ├── HwModuleCollection (管理音频硬件模块)
        ├── DeviceManager (管理可用设备及连接状态)
        └── Output/Input 管理 (跟踪当前打开的所有音频流)
  • AudioPolicyManager:策略的核心实现类(位于 services/audiopolicy/engine/)。
  • Engine:可插拔的策略算法模块,不同产品(手机、汽车、电视)可替换引擎实现。
  • HAL 接口:通过 Device HALIDevicesFactory 等)获取可用的音频硬件模块(如 primary、a2dp、usb 等)。

📋 核心职责详解

1. 音频路由策略

根据当前系统状态(如通话中、媒体播放)和设备连接情况(有线耳机、蓝牙、HDMI 等),动态决定音频数据的输入/输出设备

  • 输出路由:例如媒体音乐通常优先输出到有线耳机,如果未连接则走扬声器。
  • 输入路由:通话时使用主麦克风,录音时可能使用前麦克风或 USB 麦克风。
  • 策略强制使用setForceUse):例如在车载模式下,强制所有音频输出到蓝牙或 AUX。

2. 音量管理

  • 音量曲线:为每种流类型(媒体、闹钟、通话音量等)定义从静音到最大音量的非线性曲线(响度单位)。
  • 音量步长:系统 UI 按下音量键时增加/减少的步进值。
  • 音量限制:可配置最大音量、安全音量(如欧盟对耳机的限制)。
  • 音量组:某些设备共享同一音量索引(如媒体和导航)。

3. 音频焦点管理(Audio Focus)

注:焦点管理的逻辑主要在 Java 层的 AudioService 中实现,但 AudioPolicyService 负责执行焦点变化引起的底层策略调整,例如对失去焦点的流进行“闪避”(ducking)。

  • 当应用申请或放弃焦点时,AudioService 会通过 Binder 调用 AudioPolicyService 的 notifyAudioFocusChange()
  • AudioPolicyService 据此决定是否降低其他流的音量(闪避)、暂停播放等。

4. 动态策略与配置

  • 静态配置文件/vendor/etc/audio_policy_configuration.xml(或 .conf)定义了系统中的所有音频硬件模块、设备端口、混音端口、路由规则等。
  • 动态策略:支持运行时加载新的策略模块(例如插入 USB 声卡时动态添加对应的路由)。

5. 输入/输出流管理

  • 当应用通过 AudioTrack 请求播放时,AudioService 最终会调用 AudioPolicyService 的 getOutputForAttr()
  • AudioPolicyService 根据属性(流类型、用途、采样率、标志位等)选择或创建一个合适的输出流句柄audio_io_handle_t),并通知 AudioFlinger 打开对应的输出线程。
  • 同样,录音时会调用 getInputForAttr()

🔄 与 AudioFlinger 的协作流程

AudioPolicyService 不直接操作硬件,所有实际工作都通过 AudioFlinger 完成。典型协作模式:

  1. 上层请求播放:应用调用 AudioTrackAudioSystem (native) → AudioPolicyService::getOutputForAttr()
  2. 策略决策:AudioPolicyService 根据请求的属性(例如 USAGE_MEDIA)和设备状态,决定应该使用哪个输出设备(如扬声器)。
  3. 通知 AudioFlinger:调用 AudioFlinger::openOutput() 获取对应设备上的输出流,并返回 audio_io_handle_t
  4. 后续控制:当设备切换(如插入耳机),AudioPolicyService 会调用 AudioFlinger::closeOutput() 关闭原流,再 openOutput() 打开新流,并触发 AudioFlinger 内部的线程切换,保证音频连续(无缝切换)。

🧪 典型工作流程示例:插入有线耳机

  1. 硬件事件:内核检测到耳机插入,通过 uevent 上报。
  2. HAL 通知:音频 HAL 服务(如 audio.primary)调用 AudioPolicyService::setDeviceConnectionState()
  3. 策略重新评估:AudioPolicyService 运行 Engine::updateDeviceSelection(),根据策略规则(如媒体音乐优先输出到有线耳机)决定将当前播放的媒体流切换到耳机。
  4. 执行切换
    • 调用 AudioFlinger::closeOutput() 关闭扬声器输出线程。
    • 调用 AudioFlinger::openOutput() 打开耳机输出线程。
    • 如果有正在播放的 AudioTrack,AudioFlinger 会将其重新路由到新线程(通常通过线程切换或重新创建 Track)。
  5. 上层通知:通过回调通知 AudioService(Java 层),后者发送广播(ACTION_HEADSET_PLUG),应用可据此调整 UI。

📄 配置文件详解

audio_policy_configuration.xml 是策略的“宪法”。典型结构:

<audioPolicyConfiguration>
    <globalConfiguration speaker_drc_enabled="false"/>
    <modules>
        <module name="primary" halVersion="3.0">
            <attachedDevices>
                <item>Speaker</item>
                <item>Built-In Mic</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="Speaker" sources="primary output"/>
                <!-- 其他路由规则 -->
            </routes>
        </module>
        <!-- 其他模块:a2dp、usb 等 -->
    </modules>
</audioPolicyConfiguration>
  • devicePorts:描述物理设备(扬声器、麦克风、耳机插孔等)。
  • mixPorts:描述软件混音端口(即 AudioFlinger 中的输出线程)。
  • routes:定义哪些混音端口可以路由到哪些设备端口。

厂商可以修改此文件来定制路由行为(例如插入耳机后是否同时让扬声器发声,用于外放+耳机双模式)。

🎛️ 主要 Binder 接口(供 AudioService 调用)

方法用途
setDeviceConnectionStateHAL 或系统服务通知设备连接/断开
setPhoneState设置电话状态(通话中、空闲、来电等),影响路由策略
setForceUse强制使用特定场景(如车载、HDMI 等)
getOutputForAttr请求一个输出流句柄用于播放
releaseOutput释放输出流
getInputForAttr请求一个输入流句柄用于录音
startOutput/stopOutput通知策略服务某个流开始/停止使用(用于动态路由优化)
setVolumeIndex设置某流类型的音量索引

🔒 安全与权限

  • 运行在 audioserver 用户组,拥有读取配置文件的权限。
  • Binder 调用会检查调用者的 android.permission.MODIFY_AUDIO_SETTINGS 等权限。
  • 对于录音相关的策略(如 getInputForAttr),会额外检查 RECORD_AUDIO 权限。

🧩 版本演进要点

  • Android 8.0 (Treble):将音频 HAL 与 AudioPolicyService 解耦,通过 HIDL 通信;策略引擎可独立升级。
  • Android 10:引入 AudioPolicyEngine 接口,支持完全自定义策略逻辑。
  • Android 13+:部分接口转为 AIDL,但核心架构不变。

💎 总结

对比项AudioFlingerAudioPolicyService
核心角色执行者(数据平面)决策者(控制平面)
处理对象音频数据策略与事件
关键操作混音、读写 HAL、效果处理路由选择、设备切换、音量策略
运行线程多个播放/录制线程(高负载)少量控制线程(低负载)
依赖 HAL直接调用 StreamOut/In调用 Device HAL 获取信息
配置方式硬编码 + 参数传递配置文件 + 可替换引擎

AudioPolicyService 是 Android 音频系统灵活性和可定制性的基石。它使得手机、汽车、电视等不同形态的设备可以共享同一套音频框架,只需修改策略配置或引擎即可实现差异化的音频行为。理解 AudioPolicyService,是进行音频路由调试、音量曲线定制、多设备场景开发的关键。