Android音频系列(09)-AudioPolicyManager代码解析

目录

[1. 简述](#1. 简述)

[2 类结构说明](#2 类结构说明)

[3. 初始化流程](#3. 初始化流程)

[4. 设备连接管理](#4. 设备连接管理)

[5. 输出路由策略](#5. 输出路由策略)

[1) 获取输出 (getOutputForAttr)](#1) 获取输出 (getOutputForAttr))

[2) Engine 选设备逻辑 (getDevicesForStrategyInt)](#2) Engine 选设备逻辑 (getDevicesForStrategyInt))

[6. 输入路由策略](#6. 输入路由策略)

[1) getInputForAttr](#1) getInputForAttr)

[2) getDeviceForInputSource](#2) getDeviceForInputSource)

[7. 动态策略与 Mix](#7. 动态策略与 Mix)

[8. 总结](#8. 总结)


深入剖析 Android AudioPolicyManager (后面简称APM) 及其策略引擎 (Engine) 的内部运作机制、核心数据结构与关键流程。

本文基于 Android 源码,重点关注设备管理、路由策略选择及其与 Engine 的交互。

1. 简述

AudioPolicyManager (APM) 是 Android 音频系统的策略中心:

  • 设备管理:维护系统所有可用输入输出设备的状态。
  • 策略路由:决定特定音频流(Stream)或属性(Attributes)应路由到哪个设备。
  • 资源控制:管理音频输入输出并发、音量计算。

Engine 是 APM 的策略决策模块(Strategy Decision Module):

  • 定义产品策略 (Product Strategy):将 Audio Attributes 映射到 Strategy。
  • 设备选择算法:根据当前 Use Case(如通话、媒体、铃声)和强制配置(Force Use),为每个 Strategy 计算最佳输出设备。

这种分离设计使得 APM 框架可以保持通用,而具体的路由策略(如插入蓝牙耳机等等)可以通过替换 Engine 实现或配置。

2 类结构说明

APM 管理着由于音频策略配置文件audio_policy_configuration.xml解析出的对象模型。

图1 APM与Engine简单图

如图1,APM与Engine是一种策略模式,更详细的关系如图2所示。

图2 APM与Engine

HwModule: 对应 HAL 模块(如 primary、 a2dp、usb)。包含其支持的 IOProfile。

IOProfile: 描述了 HAL 能提供的输入输出能力(采样率、格式、通道、支持的设备)。它是静态配置的。

SwAudioOutputDescriptor: 描述了一个实际打开的输出流(Stream)。它关联到一个具体的 IOProfile 和当前路由的 DeviceDescriptor。

DeviceDescriptor: 运行时设备对象,包含地址、类型、能力等。

3. 初始化流程

初始化阶段,APM 会加载 XML 配置文件,构建 HwModule 和 IOProfile 及其支持的 DeviceDescriptor 集合。

1)Factory 创建: AudioPolicyService 调用 createAudioPolicyManager。

2) Config 解析: APM 构造函数中解析 audio_policy_configuration.xml,填充 mHwModules。

3)Engine 初始化: Engine 加载自己的策略配置(如有),建立 Attributes 到 Strategy 的映射表。

4)打开 Output: APM 遍历 HwModule,预先打开那些 Attached(非动态)的 Output Profile(如 Speaker/Earpiece 对应的 Output)。

4. 设备连接管理

当设备插入(如有线耳机、蓝牙耳机)时,流程如图3所示:

图3

checkOutputsForDevice 逻辑如下:

当新设备连接时,APM 会检查所有活动输出,询问 Engine:"这个 Output 现在应该去哪个设备?"

如果 Engine 返回的新设备就是刚连接的设备(或者包含它),APM 就会执行路由切换(Tear down patch > Create new patch)。

5. 输出路由策略

这是 APM 最核心的部分:App 请求播放 -> 决定使用哪个 Output -> 路由到哪个 Device。

1) 获取输出 (getOutputForAttr)

当 App 创建 AudioTrack 时, AudioTrack调用AudioFlinger::createTrack, 然后会调用getOutputForAttr函数:

cpp 复制代码
status_t AudioFlinger::createTrack(const media::CreateTrackRequest& _input,
                                   media::CreateTrackResponse& _output)
{
      ......

  // Aptiv Audio
    lStatus = AudioSystem::getOutputForAttr(&localAttr, &output.outputId, sessionId, 
                 &streamType,adjAttributionSource, &input.config, input.flags,
                 &output.selectedDeviceId, &portId, &secondaryOutputs);
       ......
}

后续流程如图4所示:

图4 getOutputForAttr流程

2) Engine 选设备逻辑 (getDevicesForStrategyInt)

在 Engine.cpp 中,getDevicesForStrategyInt 是决策核心。以下是 STRATEGY_MEDIA 为例简单说明下:

  • 强制配置检查: getForceUse(FOR_MEDIA)。如果是 FORCE_SPEAKER,直接选 Speaker。
  • 外部设备优先: 检查 A2DP, Wired Headset, USB Device。getLastRemovableMediaDevices() 会返回最后连接的外部设备。
  • 默认设备: 如果没有外部设备,退回默认(Speaker 或 Earpiece)。
  • 特殊规则:

STRATEGY_PHONE (通话):优先蓝牙 SCO,然后 Earpiece。

STRATEGY_SONIFICATION (铃声):同时从 Speaker 和 Headset 出声(Dual Routing)。

6. 输入路由策略

输入路由即决定录音来源选择哪个麦克风或输入设备。

1) getInputForAttr

图5 getInputForAttr流程

2) getDeviceForInputSource

Engine::getDeviceForInputSource 处理输入源映射:

VOICE_COMMUNICATION: 如果在通话中,可能强制使用与输出设备匹配的输入(如蓝牙耳机麦克风)。

DEFAULT/MIC: 优先蓝牙 A2DP/SCO(如果支持),其次有线耳机麦克风,最后是内置 Mic。

7. 动态策略与 Mix

除了传统的基于 Stream Type 的策略,Android 还支持 Dynamic Policy (Audio Mix),常用于:

车机系统(Car Audio):将特定 uid 的导航声音路由到特定 BUS 设备。

投屏/录屏:捕获特定内容的音频。

相关类:AudioPolicyMix。Engine 在计算设备时,会优先检查是否有 AudioPolicyMix 匹配当前 Attributes,如果有,则强制路由到对应的 Mix 输出,绕过默认策略。

8. 总结

Android 音频策略框架是一个分层清晰的系统:

1) 静态配置层 (HwModule/IOProfile):定义硬件能力底座。

2) 动态状态层 (DeviceDescriptor/SwAudioOutputDescriptor):维护运行时状态。

3) 策略决策层 (Engine):getDevicesForStrategyInt 是"大脑",根据优先级规则输出目标设备。

4) 执行层 (APM):根据 Engine 的决策,操作 AudioPolicyClient 完成物理连接。

理解 Engine 的 getDevicesForStrategyInt 和 APM 的 setDeviceConnectionState 是掌握 Android 音频路由切换的关键。

更多的内容,敬请关注,待分解。

相关推荐
summerkissyou19875 小时前
Android Handler:机制、原理与示例
android
哈哈浩丶5 小时前
安卓系统全流程启动
android·linux·驱动开发
summerkissyou19877 小时前
Android-Audio-MediaPlayer-播放-流程
android·audio
mjhcsp7 小时前
C++ 后缀平衡树解析
android·java·c++
没有bug.的程序员8 小时前
Gradle 构建优化深度探秘:从 Java 核心到底层 Android 物理性能压榨实战指南
android·java·开发语言·分布式·缓存·gradle
奔跑吧 android11 小时前
【车载Audio】【AudioHal 08】【高通音频架构】【SA8295P 音频资源管理器 (ResourceManager) 决策逻辑深度解析 】
音视频·sa8295p·audiohal·高通音频架构·音频资源管理器
linux_cfan13 小时前
拒绝“黑屏”与“哑剧”:Web视频播放器UX体验与自动播放选型指南 (2026版)
前端·javascript·音视频·html5·ux
lljss202014 小时前
MediaPad 10 Link S10-201wa(安卓4.1.2) 安装vlc
android
黄昏晓x14 小时前
C++----异常
android·java·c++
aningxiaoxixi14 小时前
Android Audio 广播之 ACTION_AUDIO_BECOMING_NOISY
android·java·python