音频策略定制:厂商定制接口、策略XML配置、动态策略加载、多产品线适配方案
音频策略定制,说白了就是让Android音频系统听懂你产品的「方言」。
我刚开始接触这块时,以为策略就是改改XML里的几个数字。后来才发现,真正的定制远不止这些——你要面对的是不同硬件平台、不同产品形态、甚至不同市场的合规要求。今天我就把这几年踩过的坑、总结的经验,一次性讲清楚。
一、厂商定制接口:系统留给你的「后门」
Android的音频策略框架,其实留了不少扩展点。我个人习惯把这些接口分成三类:
- 策略引擎接口:AudioPolicyManager的派生与重写
- 策略规则接口:动态策略规则的注入点
- 策略配置接口:XML解析器的扩展点
先看最核心的——如何重写AudioPolicyManager。我记得第一次做这个时,翻源码翻到凌晨三点。
// 厂商自定义策略管理器
class VendorAudioPolicyManager : public AudioPolicyManager {
public:
// 重写设备选择逻辑
audio_devices_t getDeviceForStrategy(routing_strategy strategy,
bool fromCache) override {
// 先走默认逻辑
audio_devices_t device = AudioPolicyManager::getDeviceForStrategy(strategy, fromCache);
// 再根据产品需求调整
if (strategy == STRATEGY_MEDIA && isSpeakerMode()) {
// 我遇到过某些平板产品,媒体播放必须强制走扬声器
device = AUDIO_DEVICE_OUT_SPEAKER;
}
return device;
}
// 重写音量曲线
status_t checkAndSetVolume(audio_stream_type_t stream,
float volume,
audio_io_handle_t output) override {
// 定制音量映射逻辑
float mappedVolume = mapVolumeForProduct(stream, volume);
return AudioPolicyManager::checkAndSetVolume(stream, mappedVolume, output);
}
};
这里有个关键点:不要直接修改AOSP源码。你想想看,每次Google发安全补丁,你都要重新merge,那得多痛苦?正确的做法是通过继承和组合,把定制逻辑隔离在厂商模块里。
二、策略XML配置:从看懂到改对
audio_policy_configuration.xml,这个文件我看了不下百遍。它的结构其实很清晰:
| 配置层级 | 作用 | 常见定制点 |
|---|---|---|
| globalConfiguration | 全局策略开关 | 强制设备路由、音量曲线类型 |
| modules | 音频设备模块定义 | 增加新设备类型、修改采样率 |
| devicePorts | 物理设备端口 | 设备别名、地址映射 |
| mixPorts | 混音器端口 | 通道数、格式、采样率 |
| routes | 路由规则 | 设备间连接关系 |
| profiles | 音频Profile | 编解码器配置、延迟参数 |
举个例子,我曾经需要为一个车载产品增加「后排娱乐」音频通道。做法是这样的:
<!-- 在devicePorts中增加新设备 -->
<devicePort tagName="Rear Entertainment" type="AUDIO_DEVICE_OUT_REAR" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</devicePort>
<!-- 在routes中建立连接 -->
<route type="mix" sink="Rear Entertainment">
<source src="mixport_rear"/>
</route>
嗯,这里要注意:XML中的设备类型必须与HAL层定义的枚举值一致。我见过有人把AUDIO_DEVICE_OUT_REAR写成了AUDIO_DEVICE_OUT_REAR_SEAT,结果设备死活不认。
三、动态策略加载:运行时切换的魔法
静态XML配置有个硬伤——产品运行时可能需要切换策略。比如手机插上游戏手柄,音频路由要变;车载从驾驶模式切换到休息模式,音效也要变。
Android 12之后,系统提供了动态策略加载机制。核心思路是:
- 定义多个策略配置文件,放在/vendor/etc/audio_policy/目录下
- 通过属性或服务触发策略切换
- AudioPolicyManager重新解析配置并生效
我参与过一个项目,需要根据蓝牙设备类型切换策略。代码大致是这样:
// 动态加载策略
void AudioPolicyManager::loadDynamicPolicy(const char* policyName) {
// 构建配置文件路径
char path[256];
snprintf(path, sizeof(path), "/vendor/etc/audio_policy/%s.xml", policyName);
// 解析新配置
sp<AudioPolicyConfig> newConfig = new AudioPolicyConfig();
status_t status = newConfig->loadFromXml(path);
if (status == NO_ERROR) {
// 替换当前配置
Mutex::Autolock _l(mLock);
mConfig = newConfig;
// 重新初始化路由
initializeDeviceSelection();
// 通知HAL层更新
mpClientInterface->setParameters(AUDIO_PARAMETER_KEY_POLICY_CHANGED, "true");
}
}
四、多产品线适配方案:一套代码,多款产品
大厂通常有多个产品线——手机、平板、车载、IoT。每个产品的音频需求都不一样。怎么用一套代码搞定?
我总结了一套「三层适配」方案:
- 第一层:产品标识层——通过ro.product.audio.features属性区分产品
- 第二层:策略配置层——每个产品有自己的XML配置目录
- 第三层:运行时适配层——根据产品标识动态加载不同策略
目录结构建议这样组织:
/vendor/etc/audio_policy/
├── common/ # 公共配置
│ ├── audio_policy_configuration_base.xml
│ └── audio_policy_volumes.xml
├── phone/ # 手机产品线
│ ├── audio_policy_configuration.xml
│ └── audio_policy_engine.xml
├── tablet/ # 平板产品线
│ ├── audio_policy_configuration.xml
│ └── audio_policy_engine.xml
└── vehicle/ # 车载产品线
├── audio_policy_configuration.xml
├── audio_policy_engine.xml
└── audio_policy_routing.xml
在代码中,通过读取产品属性来决定加载哪个目录:
// 获取产品音频特性
const char* productFeature = property_get("ro.product.audio.features", "default");
// 根据特性选择策略目录
const char* policyDir;
if (strcmp(productFeature, "phone") == 0) {
policyDir = "/vendor/etc/audio_policy/phone/";
} else if (strcmp(productFeature, "tablet") == 0) {
policyDir = "/vendor/etc/audio_policy/tablet/";
} else if (strcmp(productFeature, "vehicle") == 0) {
policyDir = "/vendor/etc/audio_policy/vehicle/";
} else {
policyDir = "/vendor/etc/audio_policy/common/";
}
// 加载策略
loadPolicyFromDirectory(policyDir);
五、知识体系总览
下面这张图,是我梳理的音频策略定制整体架构。你看完应该能有个全局认识:
这张图把整个知识体系串起来了。从上到下,从接口到配置,再到运行时和多产品线适配。你想想看,是不是每个环节都有对应的定制手段?
音频策略定制,说到底就是「理解规则、利用规则、扩展规则」。系统给的接口和配置方式,足够覆盖绝大多数产品需求。关键是要理清哪些该改配置,哪些该改代码,哪些该留给运行时决策。
好了,这一章的内容就到这里。希望这些实战经验能帮你少走弯路。