android audio 之 Engine

一. 场景定义(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_ 的协作关系
协作流程
  1. 策略决定默认路由
    STRATEGY_PHONE 默认选择听筒或耳机设备。
  2. 强制路由覆盖策略
    如果通过 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 函数作用
  1. 参数校验 :检查传入的 usage(场景类型)和 config(强制配置)是否合法。
  2. 路由控制:根据场景和配置,强制修改音频设备的默认路由行为(如通话时强制使用扬声器)。
  3. 错误处理 :对非法参数返回错误码 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 关键点说明
  1. 参数校验严格

    • 每个场景(usage)仅允许特定的 config 值,非法组合会触发 ALOGW 警告并返回 BAD_VALUE
    • 例如,AUDIO_POLICY_FORCE_FOR_COMMUNICATION 不允许 AUDIO_POLICY_FORCE_HEADPHONES 配置。
  2. 场景与配置的对应关系

    场景(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 主要逻辑包括:
  1. 策略匹配:针对不同策略(如PHONE、SONIFICATION、MEDIA等)采用不同的设备选择规则。
  2. 设备过滤:根据策略需求排除不合适的设备(如通话时禁用A2DP设备)。
  3. 优先级排序:按设备类型优先级选择设备(如优先选择蓝牙SCO设备用于通话)。
  4. 特殊处理:如安全扬声器、远程音频子混音等场景的特殊逻辑。
  5. 回退机制:若无匹配设备,回退到默认设备。
  6. 强制选择: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 关键点说明
  1. 设备选择优先级:每个策略有明确的设备类型优先级列表,例如电话策略优先选择蓝牙SCO设备。
  2. 动态适应:根据系统状态(如通话中、强制使用模式)动态调整设备选择。
  3. 安全机制:如安全扬声器(SPEAKER_SAFE)在特定场景下自动替换普通扬声器。
  4. 调试支持:通过ALOGV日志输出最终选择的设备类型,便于问题追踪。
相关推荐
小高00713 分钟前
🚀React 更新界面全流程:从 setState 到 像素上屏
前端·react.js·面试
万少19 分钟前
HarmonyOS 读取系统相册图片并预览
前端·harmonyos·客户端
Wgllss21 分钟前
完整案例:Kotlin+Compose+Multiplatform跨平台之桌面端实现(二)
android·架构·android jetpack
林太白23 分钟前
CSS长度单位px、rem、em、vh、vw
前端·javascript·css
王者鳜錸26 分钟前
VUE+SPRINGBOOT从0-1打造前后端-前后台系统-登录实现
前端·vue.js·spring boot
oioihoii40 分钟前
CRT调试堆检测:从原理到实战的资源泄漏排查指南
开发语言·前端·c++·c
不如吃茶去1 小时前
开源推荐:LocalSqueeze - 隐私优先的本地图片压缩工具
前端·react.js·electron
anyup1 小时前
uView Pro 正式开源!70+ Vue3 组件重构全记录,助力 uni-app 组件生态,你会选择吗?
前端·架构·uni-app
一点一木1 小时前
PromptPilot 与豆包新模型:从图片到视频,解锁 AI 新玩法
前端·人工智能