AudioSystem,AudioPlicyManagerBase修改设置音频通路

开发平台,安卓4.4。
最近一项目中客户有一需求:提供一接口,设置音频输出设备:听筒或者扬声器。设置后所有的音频都从同一输出设备中输出。
而通话默许走的是听筒,播放音乐等走的是扬声器,这该怎么办呢?
通过设置AudioManager.setMode和AudioManager.setSpeakerphoneOn两个方法所说也可以实现,但是其中逻辑有些复杂,于是放弃了。
下面用的方法非常简单:
我们知道AudioPolicyManagerBase.cpp是管理音频策略的,所以就直接修改它的策略就行了。

先讲几个概念

安卓系统的音频设置比较复杂,不同的音频流走的哪个输出设备之间对应着几种策略

安卓系统音频流分为如下几种:

AudioManager.java中就是引用的AudioSystem.java中定义的音频流类型

//frameworks/base/media/java/android/media/AudioSystem.java
   /*
    * If these are modified, please also update Settings.System.VOLUME_SETTINGS
    * and attrs.xml and AudioManager.java.
    */
   /* The audio stream for phone calls */
   public static final int STREAM_VOICE_CALL = 0;
   /* The audio stream for system sounds */
   public static final int STREAM_SYSTEM = 1;
   /* The audio stream for the phone ring and message alerts */
   public static final int STREAM_RING = 2;
   /* The audio stream for music playback */
   public static final int STREAM_MUSIC = 3;
   /* The audio stream for alarms */
   public static final int STREAM_ALARM = 4;
   /* The audio stream for notifications */
   public static final int STREAM_NOTIFICATION = 5;
   /* @hide The audio stream for phone calls when connected on bluetooth */
   public static final int STREAM_BLUETOOTH_SCO = 6;
   /* @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
   public static final int STREAM_SYSTEM_ENFORCED = 7;
   /* @hide The audio stream for DTMF tones */
   public static final int STREAM_DTMF = 8;
   /* @hide The audio stream for text to speech (TTS) */
   public static final int STREAM_TTS = 9;
   // modified for FM start
   // add STREAM_FM
   /* @hide The audio stream for FM */
   public static final int STREAM_FM = 10;
   // modified for FM end
   /**

音频流的作用:
a. 设备选择,比如MUSIC类型不插耳机只从扬声器出来,手上耳机从耳机出来。来电时RING类型会从扬声器和耳机同时出来
b. 音量控制,不同流类型的音量级不同,例如MUSIC有15个音量级,而STREAM_VOICE_CALL 只有7个音量级,且最低为1

声音模式

也就是AudioManager.setMode(mode)的这个mode值。看下它的定义:

//frameworks/base/media/java/android/media/AudioSystem.java
    /* modes for setPhoneState, must match AudioSystem.h audio_mode */
//这些定义同样在AudioSystem.h中有,且必须是对应的
    public static final int MODE_INVALID            = -2;
    public static final int MODE_CURRENT            = -1;
    public static final int MODE_NORMAL             = 0;
    public static final int MODE_RINGTONE           = 1;
    public static final int MODE_IN_CALL            = 2;
    public static final int MODE_IN_COMMUNICATION   = 3;
    public static final int NUM_MODES               = 4;

AudioSystem区分Phone的状态,与Phone的硬件结构是有关系的,看一张图:


AudioSystem,AudioPlicyManagerBase修改设置音频通路_第1张图片
image.png
  • 系统有一个音频DSP,声音的输入输出都由它来处理(蓝牙除外)。它处理完的数字信号,需要通过D/A转换后送到输出设备上,如扬声器,耳机,听筒等。
  • 系统有一个应用处理核心AP(Application Processor),可以理解为我们的CPU,还有一个基带处理核心BP(Baseband Processor),与通信相关。
  • AP和BP都能向DSP发送数据,硬件链路上互不干扰,但是如果AP和BP同时往DSP送数据,这时就会出现通话声和音乐声音混杂。所以在打电话时将由AP上的Phone程序,主动设置Audio系统的mode,比如设置为MODE_IN_CALL,在这种模式下把MUSIC的音量减小。

强制使用及配置

通话时可以设置强制使用扬声器,就是这个意思。
可以强制使用的设备有:

//frameworks/base/media/java/android/media/AudioSystem.java
    // device categories config for setForceUse, must match audio_policy_forced_cfg_t
    public static final int FORCE_NONE = 0;
    public static final int FORCE_SPEAKER = 1;
    public static final int FORCE_HEADPHONES = 2;
    public static final int FORCE_BT_SCO = 3;
    public static final int FORCE_BT_A2DP = 4;
    public static final int FORCE_WIRED_ACCESSORY = 5;
    public static final int FORCE_BT_CAR_DOCK = 6;
    public static final int FORCE_BT_DESK_DOCK = 7;
    public static final int FORCE_ANALOG_DOCK = 8;
    public static final int FORCE_DIGITAL_DOCK = 9;
    public static final int FORCE_NO_BT_A2DP = 10;
    public static final int FORCE_SYSTEM_ENFORCED = 11;
    public static final int FORCE_HDMI_SYSTEM_AUDIO_ENFORCED = 12;
    public static final int FORCE_ENCODED_SURROUND_NEVER = 13;
    public static final int FORCE_ENCODED_SURROUND_ALWAYS = 14;
    public static final int NUM_FORCE_CONFIG = 15;
    public static final int FORCE_DEFAULT = FORCE_NONE;

那么使用的时机是什么呢?所以还有定义了在什么情况下强制使用:

//frameworks/base/media/java/android/media/AudioSystem.java
   // usage for setForceUse, must match audio_policy_force_use_t
    public static final int FOR_COMMUNICATION = 0;
    public static final int FOR_MEDIA = 1;
    public static final int FOR_RECORD = 2;
    public static final int FOR_DOCK = 3;
    public static final int FOR_SYSTEM = 4;
    public static final int FOR_HDMI_SYSTEM_AUDIO = 5;
    public static final int FOR_ENCODED_SURROUND = 6;
    private static final int NUM_FORCE_USE = 7;

输出设备的定义

//frameworks/base/media/java/android/media/AudioSystem.java
    // output devices, be sure to update AudioManager.java also
    public static final int DEVICE_OUT_EARPIECE = 0x1;
    public static final int DEVICE_OUT_SPEAKER = 0x2;
    public static final int DEVICE_OUT_WIRED_HEADSET = 0x4;
    public static final int DEVICE_OUT_WIRED_HEADPHONE = 0x8;
    public static final int DEVICE_OUT_BLUETOOTH_SCO = 0x10;
    public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20;
    public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40;
    public static final int DEVICE_OUT_BLUETOOTH_A2DP = 0x80;
    public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
    public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
    public static final int DEVICE_OUT_AUX_DIGITAL = 0x400;
    public static final int DEVICE_OUT_HDMI = DEVICE_OUT_AUX_DIGITAL;
    public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800;
    public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000;
    public static final int DEVICE_OUT_USB_ACCESSORY = 0x2000;
    public static final int DEVICE_OUT_USB_DEVICE = 0x4000;
    public static final int DEVICE_OUT_REMOTE_SUBMIX = 0x8000;
    public static final int DEVICE_OUT_TELEPHONY_TX = 0x10000;
    public static final int DEVICE_OUT_LINE = 0x20000;
    public static final int DEVICE_OUT_HDMI_ARC = 0x40000;
    public static final int DEVICE_OUT_SPDIF = 0x80000;
    public static final int DEVICE_OUT_FM = 0x100000;
    public static final int DEVICE_OUT_AUX_LINE = 0x200000;
    public static final int DEVICE_OUT_SPEAKER_SAFE = 0x400000;
    public static final int DEVICE_OUT_IP = 0x800000;
    public static final int DEVICE_OUT_BUS = 0x1000000;
    public static final int DEVICE_OUT_PROXY = 0x2000000;
    public static final int DEVICE_OUT_USB_HEADSET = 0x4000000;

安卓系统的音频路由策略有如下几种

那么音频流和Phone的模式以及输出设备之间是如何协调的呢,就需要为不同的流和输出设备之间制定一定的规则

  //AudioPolicyManagerBase.h中定义了如下几种路由策略
        enum routing_strategy {
            STRATEGY_MEDIA,
            STRATEGY_PHONE,
            STRATEGY_SONIFICATION,
            STRATEGY_SONIFICATION_RESPECTFUL,
            STRATEGY_DTMF,
            STRATEGY_ENFORCED_AUDIBLE,
            STRATEGY_FM, //modified for FM
            NUM_STRATEGIES
        };

再放一张图说明一下它们之间的关系:


AudioSystem,AudioPlicyManagerBase修改设置音频通路_第2张图片
image.png

分析上图中策略的代码实现

获取音频策略的方法,为了方便起见,我只贴出了STRATEGY_PHONE和STRATEGY_MEDIA,其它的是类似的:

audio_devices_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache){
    uint32_t device = AUDIO_DEVICE_NONE;

    switch (strategy) {
    case STRATEGY_PHONE:
        //对于phone的策略,首先考虑强制使用的设备,如果强制使用的设备不可用再考虑其它可用设备
        switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {
        case AudioSystem::FORCE_BT_SCO:
            if (!isInCall() || strategy != STRATEGY_DTMF) {
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
                if (device) break;
            }
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
            if (device) break;
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
            if (device) break;
            // if SCO device is requested but no SCO device is available, fall back to default case
            // FALL THROUGH

        case AudioSystem::FORCE_SPEAKER:
            // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
            // A2DP speaker when forcing to speaker output
            if (mHasA2dp && !isInCall() &&
                    (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) &&
                    (getA2dpOutput() != 0) && !mA2dpSuspended) {
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
                if (device) break;
            }
            if (mPhoneState != AudioSystem::MODE_IN_CALL) {
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
                if (device) break;
            }
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER;
            if (device) break;
            device = mDefaultOutputDevice;
            if (device == AUDIO_DEVICE_NONE) {
                ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER");
            }
            break;

        default:    // FORCE_NONE,没有定义强制使用
            // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
            if (mHasA2dp && !isInCall() &&
                    (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) &&
                    (getA2dpOutput() != 0) && !mA2dpSuspended) {
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
                if (device) break;
            }
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
            if (device) break;
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADSET;
            if (device) break;
            if (mPhoneState != AudioSystem::MODE_IN_CALL) {
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
                if (device) break;
            }
            //如果没有定义强制使用,默认为EARPIECE
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_EARPIECE;
            if (device) break;
            device = mDefaultOutputDevice;
            if (device == AUDIO_DEVICE_NONE) {
                ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE");
            }
            break;
        }
    break;

    case STRATEGY_MEDIA: {
        uint32_t device2 = AUDIO_DEVICE_NONE;
        if (strategy != STRATEGY_SONIFICATION) {
            // no sonification on remote submix (e.g. WFD)
            device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
        }
        if (strategy == STRATEGY_MEDIA || strategy == STRATEGY_ENFORCED_AUDIBLE) {
            switch (mForceUse[AudioSystem::FOR_MEDIA]) {
            case AudioSystem::FORCE_SPEAKER:
                ALOGD("geting device of force_speaker");
                if (device2 == AUDIO_DEVICE_NONE) {
                    device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER;
                }
                //强制使用SPEAKER
                 device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER;
              default://如果没有定义,默认改为听筒
              ALOGI("bianjb--getDeviceForStrategy() STRATEGY_MEDIA-aaa");
              device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_EARPIECE;
              break;

            }
        }
        if ((device2 == AUDIO_DEVICE_NONE) &&
                mHasA2dp && (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) &&
                (getA2dpOutput() != 0) && !mA2dpSuspended) {
            device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
            if (device2 == AUDIO_DEVICE_NONE) {
                device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
            }
            if (device2 == AUDIO_DEVICE_NONE) {
                device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
            }
        }
        switch (mForceUse[AudioSystem::FOR_FM]) {
            case AudioSystem::FORCE_SPEAKER:
                ALOGI("geting device of force_speaker :STRATEGY_FM");
                if (device2 == AUDIO_DEVICE_NONE) {
                    device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER;
                }
        }
        if (device2 == AUDIO_DEVICE_NONE) {
            device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
        }
        if (device2 == AUDIO_DEVICE_NONE) {
            device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADSET;
        }
        if (device2 == AUDIO_DEVICE_NONE) {
            device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY;
        }
        if (device2 == AUDIO_DEVICE_NONE) {
            device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE;
        }
        if (device2 == AUDIO_DEVICE_NONE) {
            device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
        }
        if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) {
            // no sonification on aux digital (e.g. HDMI)
            device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL;
        }
        if ((device2 == AUDIO_DEVICE_NONE) &&
                (mForceUse[AudioSystem::FOR_DOCK] == AudioSystem::FORCE_ANALOG_DOCK)) {
            device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
        }
        if (device2 == AUDIO_DEVICE_NONE) {
            device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER;
        }

        // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or
        // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise
        device |= device2;
        if (device) break;
        device = mDefaultOutputDevice;
        if (device == AUDIO_DEVICE_NONE) {
            ALOGE("getDeviceForStrategy() no device found for STRATEGY_MEDIA");
        }
        } break;
    default:
        ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
        break;
    }

接下来就是设置强制用了代码了

在自己的应用中做如下设置,当然必须为系统应用,否则还是不能调用的:

if(TextUtils.equals("1", ssamValue)){
    AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER);
    AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_SPEAKER);
    Log.d(TAG, "set force use speaker");
    replyMsg(getResString(R.string.ok));
}else if(TextUtils.equals("0", ssamValue)){
    AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
    AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE);
    Log.d(TAG, "set force use headphone");
    replyMsg(getResString(R.string.ok));
}

写的不是很详细,有什么问题欢迎交流!

你可能感兴趣的:(AudioSystem,AudioPlicyManagerBase修改设置音频通路)