一. 场景定义(STRATEGY 与 AUDIO_POLICY_FORCE_FOR)
1. STRATEGY_
后缀(音频策略类型)
作用
定义音频流的 逻辑分组策略 ,用于决定不同音频场景(如音乐、通话、铃声)的默认设备路由和混音行为。
(例如:音乐播放用扬声器,通话用听筒。)
常见策略类型
策略名称 | 用途说明 |
---|---|
STRATEGY_MEDIA |
媒体播放(音乐、视频、游戏) |
STRATEGY_PHONE |
通话(语音通话、VoIP) |
STRATEGY_SONIFICATION |
系统提示音(铃声、通知音) |
STRATEGY_SONIFICATION_RESPECTFUL |
避免打断媒体的提示音(如低优先级通知) |
STRATEGY_ACCESSIBILITY |
无障碍功能(如屏幕朗读) |
STRATEGY_DTMF |
拨号键盘音 |
STRATEGY_TRANSMITTED_THROUGH_SPEAKER |
语音传输(如对讲机模式) |
代码中的使用场景
- 通过
getDevicesForStrategyInt()
为策略选择设备。 - 在
Engine::getOutputDevicesForAttributes()
中映射属性(audio_attributes_t
)到策略。
2. AUDIO_POLICY_FORCE_FOR_
后缀(强制路由场景)
作用
强制覆盖特定场景的默认路由行为,无视用户或系统默认设置 。
(例如:即使用户插了耳机,强制让通话走扬声器。)
常见强制场景类型
强制场景名称 | 用途说明 |
---|---|
AUDIO_POLICY_FORCE_FOR_COMMUNICATION |
强制修改通话/VoIP 的设备路由(如免提模式) |
AUDIO_POLICY_FORCE_FOR_MEDIA |
强制修改媒体播放的设备路由(如强制蓝牙输出) |
AUDIO_POLICY_FORCE_FOR_SYSTEM |
强制系统音效的设备路由(如静音模式下仍播放按键音) |
AUDIO_POLICY_FORCE_FOR_DOCK |
强制扩展坞场景的设备路由(如车载模式) |
AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND |
强制环绕声编码输出(如禁用 Dolby) |
代码中的使用场景
- 通过
Engine::setForceUse()
动态修改路由。 - 在
getDevicesForStrategyInt()
中影响策略的最终设备选择。
3. STRATEGY_ 跟 AUDIO_POLICY_FORCE_FOR_ 的协作关系
协作流程
- 策略决定默认路由 :
STRATEGY_PHONE
默认选择听筒或耳机设备。 - 强制路由覆盖策略 :
如果通过setForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION, FORCE_SPEAKER)
,则无视策略默认行为,强制使用扬声器。
关键区别
维度 | STRATEGY_ |
AUDIO_POLICY_FORCE_FOR_ |
---|---|---|
目的 | 逻辑分组,决定默认行为 | 强制覆盖默认行为 |
动态性 | 静态配置(XML/策略表) | 可运行时动态修改(setForceUse ) |
优先级 | 低(可被强制覆盖) | 高(覆盖策略) |
影响范围 | 全局策略 | 特定场景(如仅通话或仅媒体) |
4. 实际案例
场景:强制媒体通过蓝牙输出
cpp
// 1. 默认策略:STRATEGY_MEDIA 可能选择扬声器或耳机
auto devices = engine->getDevicesForStrategy(STRATEGY_MEDIA); // 返回 [AUDIO_DEVICE_OUT_SPEAKER]
// 2. 强制覆盖策略
engine->setForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_BT_A2DP);
// 3. 重新获取设备时,强制生效
devices = engine->getDevicesForStrategy(STRATEGY_MEDIA); // 返回 [AUDIO_DEVICE_OUT_BLUETOOTH_A2DP]
场景:静音模式下强制播放铃声
cpp
// 1. 默认策略:STRATEGY_SONIFICATION 可能因静音被抑制
engine->setForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM, AUDIO_POLICY_FORCE_SYSTEM_ENFORCED);
// 2. 即使静音,铃声仍通过扬声器播放
二. Engine::setForceUse
Engine::setForceUse
是 Android 音频策略引擎中用于 强制设置特定音频场景的设备路由配置 的核心函数。它通过 audio_policy_force_use_t
指定场景(如通话、媒体播放、录音等),并通过 audio_policy_forced_cfg_t
设置强制路由策略(如强制扬声器、强制蓝牙等)。函数会验证输入参数的合法性,并将配置传递给底层引擎实现。
1 函数作用
- 参数校验 :检查传入的
usage
(场景类型)和config
(强制配置)是否合法。 - 路由控制:根据场景和配置,强制修改音频设备的默认路由行为(如通话时强制使用扬声器)。
- 错误处理 :对非法参数返回错误码
BAD_VALUE
,合法参数则调用基类EngineBase::setForceUse
执行实际配置。
2 代码分析
cpp
status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) {
// 根据场景类型(usage)检查配置(config)的合法性
switch(usage) {
// 场景:语音通信(如通话、VoIP)
case AUDIO_POLICY_FORCE_FOR_COMMUNICATION:
// 允许的配置:强制扬声器、强制蓝牙SCO、无强制(默认)
if (config != AUDIO_POLICY_FORCE_SPEAKER &&
config != AUDIO_POLICY_FORCE_BT_SCO &&
config != AUDIO_POLICY_FORCE_NONE) {
ALOGW("setForceUse() invalid config %d for COMMUNICATION", config);
return BAD_VALUE; // 非法配置返回错误
}
break;
// 场景:媒体播放(如音乐、视频)
case AUDIO_POLICY_FORCE_FOR_MEDIA:
// 允许的配置:强制耳机、强制蓝牙A2DP、强制有线设备、模拟/数字码头、无强制等
if (config != AUDIO_POLICY_FORCE_HEADPHONES &&
config != AUDIO_POLICY_FORCE_BT_A2DP &&
config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY &&
config != AUDIO_POLICY_FORCE_ANALOG_DOCK &&
config != AUDIO_POLICY_FORCE_DIGITAL_DOCK &&
config != AUDIO_POLICY_FORCE_NONE &&
config != AUDIO_POLICY_FORCE_NO_BT_A2DP &&
config != AUDIO_POLICY_FORCE_SPEAKER) {
ALOGW("setForceUse() invalid config %d for MEDIA", config);
return BAD_VALUE;
}
break;
// 场景:录音输入
case AUDIO_POLICY_FORCE_FOR_RECORD:
// 允许的配置:强制蓝牙SCO麦克风、强制有线麦克风、无强制
if (config != AUDIO_POLICY_FORCE_BT_SCO &&
config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY &&
config != AUDIO_POLICY_FORCE_NONE) {
ALOGW("setForceUse() invalid config %d for RECORD", config);
return BAD_VALUE;
}
break;
// 场景:外接扩展坞(如车载底座、桌面底座)
case AUDIO_POLICY_FORCE_FOR_DOCK:
// 允许的配置:无强制、车载蓝牙底座、桌面蓝牙底座、有线设备、模拟/数字码头
if (config != AUDIO_POLICY_FORCE_NONE &&
config != AUDIO_POLICY_FORCE_BT_CAR_DOCK &&
config != AUDIO_POLICY_FORCE_BT_DESK_DOCK &&
config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY &&
config != AUDIO_POLICY_FORCE_ANALOG_DOCK &&
config != AUDIO_POLICY_FORCE_DIGITAL_DOCK) {
ALOGW("setForceUse() invalid config %d for DOCK", config);
return BAD_VALUE;
}
break;
// 场景:系统音效(如按键音、锁屏音)
case AUDIO_POLICY_FORCE_FOR_SYSTEM:
// 允许的配置:无强制、强制播放(即使用户静音)
if (config != AUDIO_POLICY_FORCE_NONE &&
config != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
ALOGW("setForceUse() invalid config %d for SYSTEM", config);
return BAD_VALUE;
}
break;
// 场景:HDMI系统音频(如电视ARC/eARC)
case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO:
// 允许的配置:无强制、强制HDMI输出
if (config != AUDIO_POLICY_FORCE_NONE &&
config != AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED) {
ALOGW("setForceUse() invalid config %d for HDMI_SYSTEM_AUDIO", config);
return BAD_VALUE;
}
break;
// 场景:环绕声编码(如Dolby Digital)
case AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND:
// 允许的配置:无强制、禁用环绕声、强制环绕声、手动选择
if (config != AUDIO_POLICY_FORCE_NONE &&
config != AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER &&
config != AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS &&
config != AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL) {
ALOGW("setForceUse() invalid config %d for ENCODED_SURROUND", config);
return BAD_VALUE;
}
break;
// 场景:震动/静音模式下的铃声处理
case AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING:
// 允许的配置:强制蓝牙SCO、强制蓝牙LE、无强制
if (config != AUDIO_POLICY_FORCE_BT_SCO &&
config != AUDIO_POLICY_FORCE_BT_BLE &&
config != AUDIO_POLICY_FORCE_NONE) {
ALOGW("setForceUse() invalid config %d for VIBRATE_RINGING", config);
return BAD_VALUE;
}
break;
// 默认情况:未知场景类型
default:
ALOGW("setForceUse() invalid usage %d", usage);
return BAD_VALUE;
}
// 参数合法,调用基类方法执行实际配置
return EngineBase::setForceUse(usage, config);
}
3 关键点说明
-
参数校验严格:
- 每个场景(
usage
)仅允许特定的config
值,非法组合会触发ALOGW
警告并返回BAD_VALUE
。 - 例如,
AUDIO_POLICY_FORCE_FOR_COMMUNICATION
不允许AUDIO_POLICY_FORCE_HEADPHONES
配置。
- 每个场景(
-
场景与配置的对应关系:
场景(usage) 允许的配置(config) COMMUNICATION
(通话)FORCE_SPEAKER
,FORCE_BT_SCO
,FORCE_NONE
MEDIA
(媒体)FORCE_HEADPHONES
,FORCE_BT_A2DP
,FORCE_WIRED_ACCESSORY
,FORCE_SPEAKER
等VIBRATE_RINGING
(震动铃声)FORCE_BT_SCO
,FORCE_BT_BLE
,FORCE_NONE
4 调用示例
cpp
// 强制通话使用扬声器(免提模式)
engine->setForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION, AUDIO_POLICY_FORCE_SPEAKER);
// 强制媒体音频通过蓝牙A2DP输出
engine->setForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_BT_A2DP);
// 恢复默认路由
engine->setForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE);
三. Engine::getDevicesForStrategyInt
Engine::getDevicesForStrategyInt
是音频策略引擎中根据特定策略选择输出设备的核心函数。它根据传入的音频策略类型(如电话、媒体、铃声等),结合当前可用设备列表和活跃输出状态,筛选出最适合该策略的音频输出设备集合。
1 主要逻辑包括:
- 策略匹配:针对不同策略(如PHONE、SONIFICATION、MEDIA等)采用不同的设备选择规则。
- 设备过滤:根据策略需求排除不合适的设备(如通话时禁用A2DP设备)。
- 优先级排序:按设备类型优先级选择设备(如优先选择蓝牙SCO设备用于通话)。
- 特殊处理:如安全扬声器、远程音频子混音等场景的特殊逻辑。
- 回退机制:若无匹配设备,回退到默认设备。
- 强制选择:getForceUse
cpp
audio_policy_forced_cfg_t EngineBase::getForceUse(audio_policy_force_use_t usage) const;
2 代码分析
cpp
DeviceVector Engine::getDevicesForStrategyInt(
legacy_strategy strategy, // 输入策略类型(如STRATEGY_PHONE)
DeviceVector availableOutputDevices, // 当前可用输出设备列表
const SwAudioOutputCollection &outputs) const // 活跃输出集合(用于状态检查)
{
DeviceVector devices; // 最终返回的设备集合
// 根据策略类型选择设备
switch (strategy) {
// 策略:透传扬声器(仅选择扬声器设备)
case STRATEGY_TRANSMITTED_THROUGH_SPEAKER:
devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
break;
// 策略:电话
case STRATEGY_PHONE: {
// 优先选择最后连接的可移除媒体设备(排除助听器和BLE耳机)
devices = availableOutputDevices.getFirstDevicesFromTypes(
getLastRemovableMediaDevices(GROUP_NONE, {
AUDIO_DEVICE_OUT_HEARING_AID,
AUDIO_DEVICE_OUT_BLE_HEADSET,
AUDIO_DEVICE_OUT_AUX_DIGITAL
}));
if (!devices.isEmpty()) break;
// 次选数字码头耳机、听筒或扬声器
devices = availableOutputDevices.getFirstDevicesFromTypes({
AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET,
AUDIO_DEVICE_OUT_EARPIECE,
AUDIO_DEVICE_OUT_SPEAKER});
} break;
// 策略:提示音/强制 audible(如警报)
case STRATEGY_SONIFICATION:
case STRATEGY_ENFORCED_AUDIBLE:
// 强制系统模式下优先选择数字码头耳机或扬声器
if ((strategy == STRATEGY_SONIFICATION) ||
(getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)) {
devices = availableOutputDevices.getFirstDevicesFromTypes({
AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET,
AUDIO_DEVICE_OUT_SPEAKER});
}
// 如果蓝牙SCO设备可用且被电话策略选中
if (!availableOutputDevices.getDevicesFromTypes(getAudioDeviceOutAllScoSet()).isEmpty()
&& audio_is_bluetooth_out_sco_device(getPreferredDeviceTypeForLegacyStrategy(
availableOutputDevices, STRATEGY_PHONE))) {
DeviceVector devices2 = availableOutputDevices.getFirstDevicesFromTypes({
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT,
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
AUDIO_DEVICE_OUT_BLUETOOTH_SCO});
// 振动模式下仅使用蓝牙SCO设备
if (!((getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)
&& (strategy == STRATEGY_ENFORCED_AUDIBLE))
&& (getForceUse(AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING)
== AUDIO_POLICY_FORCE_BT_SCO)) {
devices = devices2;
break;
}
// 正常模式下同时使用默认设备和蓝牙SCO
if (strategy == STRATEGY_SONIFICATION) {
devices.replaceDevicesByType(
AUDIO_DEVICE_OUT_SPEAKER,
availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER_SAFE));
}
devices.add(devices2);
break;
}
// 类似逻辑处理蓝牙LE Audio设备
if (!availableOutputDevices.getDevicesFromTypes(getAudioDeviceOutAllBleSet()).isEmpty()
&& audio_is_ble_out_device(getPreferredDeviceTypeForLegacyStrategy(
availableOutputDevices, STRATEGY_PHONE))) {
DeviceVector devices2 = availableOutputDevices.getFirstDevicesFromTypes({
AUDIO_DEVICE_OUT_BLE_HEADSET,
AUDIO_DEVICE_OUT_BLE_SPEAKER});
// ...(省略LE Audio处理逻辑,类似SCO)
}
// 对于提示音策略,第二设备使用媒体策略的设备
FALLTHROUGH_INTENDED;
// 策略:媒体/无障碍/DTMF等
case STRATEGY_DTMF:
case STRATEGY_ACCESSIBILITY:
case STRATEGY_SONIFICATION_RESPECTFUL:
case STRATEGY_REROUTING:
case STRATEGY_MEDIA: {
DeviceVector devices2;
if (strategy != STRATEGY_SONIFICATION) {
// 排除远程子混音设备(如WFD)
sp<DeviceDescriptor> remoteSubmix = availableOutputDevices.getDevice(
AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0"), AUDIO_FORMAT_DEFAULT);
if (remoteSubmix != nullptr) devices2.add(remoteSubmix);
}
// 强制媒体输出到扬声器的情况
if (devices2.isEmpty() &&
(getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_SPEAKER)) {
devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
}
// LE音频广播设备选择(需满足无通话等条件)
if (devices2.isEmpty() && !isInCall() &&
(strategy != STRATEGY_PHONE && strategy != STRATEGY_TRANSMITTED_THROUGH_SPEAKER)) {
// ...(检查最高优先级活跃策略)
devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_BLE_BROADCAST);
}
// 选择最后连接的可移除设备(有线/蓝牙A2DP)
if (devices2.isEmpty() && (getLastRemovableMediaDevices().size() > 0)) {
std::vector<audio_devices_t> excludedDevices;
if (strategy == STRATEGY_SONIFICATION) {
excludedDevices.push_back(AUDIO_DEVICE_OUT_AUX_DIGITAL); // 提示音不用AUX数字设备
}
if (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) {
// ...(蓝牙SCO或A2DP设备选择)
} else {
// 仅选择有线设备
devices2 = availableOutputDevices.getFirstDevicesFromTypes(
getLastRemovableMediaDevices(GROUP_WIRED, excludedDevices));
}
}
// 模拟码头设备处理
if (devices2.isEmpty() &&
(getForceUse(AUDIO_POLICY_FORCE_FOR_DOCK) == AUDIO_POLICY_FORCE_ANALOG_DOCK)) {
devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET);
}
// 默认回退到数字码头耳机或扬声器
if (devices2.isEmpty()) {
devices2 = availableOutputDevices.getFirstDevicesFromTypes({
AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET,
AUDIO_DEVICE_OUT_SPEAKER});
}
// 媒体策略额外添加ARC/SPDIF/AUX_LINE设备
DeviceVector devices3;
if (strategy == STRATEGY_MEDIA) {
devices3 = availableOutputDevices.getDevicesFromTypes({
AUDIO_DEVICE_OUT_HDMI_ARC,
AUDIO_DEVICE_OUT_HDMI_EARC,
AUDIO_DEVICE_OUT_SPDIF,
AUDIO_DEVICE_OUT_AUX_LINE});
}
devices2.add(devices3);
devices.add(devices2);
// HDMI系统音频模式时移除扬声器
if ((strategy == STRATEGY_MEDIA) &&
(getForceUse(AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO) ==
AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) {
devices.remove(devices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER));
}
// 安全扬声器处理(当媒体未播放时替换普通扬声器)
bool mediaActiveLocally = outputs.isActiveLocally(...);
bool ringActiveLocally = outputs.isActiveLocally(...);
if (strategy == STRATEGY_SONIFICATION || ringActiveLocally ||
(strategy == STRATEGY_SONIFICATION_RESPECTFUL && !mediaActiveLocally)) {
devices.replaceDevicesByType(
AUDIO_DEVICE_OUT_SPEAKER,
availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER_SAFE));
}
} break;
// 策略:通话辅助(仅选择电话TX设备)
case STRATEGY_CALL_ASSISTANT:
devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_TELEPHONY_TX);
break;
// 空策略(内部处理用)
case STRATEGY_NONE:
break;
default:
ALOGW("%s unknown strategy: %d", __func__, strategy);
break;
}
// 无设备时的回退逻辑
if (devices.isEmpty()) {
ALOGI("%s no device found for strategy %d", __func__, strategy);
sp<DeviceDescriptor> defaultOutputDevice = getApmObserver()->getDefaultOutputDevice();
if (defaultOutputDevice != nullptr) devices.add(defaultOutputDevice);
ALOGE_IF(devices.isEmpty(), "%s no default device defined", __func__);
}
ALOGVV("%s strategy %d, device %s", __func__,
strategy, dumpDeviceTypes(devices.types()).c_str());
return devices;
}
3 关键点说明
- 设备选择优先级:每个策略有明确的设备类型优先级列表,例如电话策略优先选择蓝牙SCO设备。
- 动态适应:根据系统状态(如通话中、强制使用模式)动态调整设备选择。
- 安全机制:如安全扬声器(SPEAKER_SAFE)在特定场景下自动替换普通扬声器。
- 调试支持:通过ALOGV日志输出最终选择的设备类型,便于问题追踪。