Android Audio和耳机,蓝牙耳机等音频外设

本文详细介绍了在Android系统中,音频服务如何处理蓝牙、A2DP、SCO和有线耳机的连接状态。通过AudioManager接口、BroadcastReceiver监听以及AudioService内部的处理逻辑,展示了设备连接、状态上报、音量设置和强制使用策略的实现。同时,分析了音频策略管理器AudioPolicyManager在设备连接状态改变时如何维护可用设备列表并选择合适的输出设备。
摘要由CSDN通过智能技术生成

做音频开发,避免不了和蓝牙打交道,尤其是做系统开发,又是不同的模块,所以很多问题很难界定。因而,了解一下蓝牙,对整体的系统架的理解会更加完善。同样的,有线耳机和A2DP在Audio的处理有很多相似处。

蓝牙连接处理

Audio这边提提供了多种方式和蓝牙进行交互~

广播接收

AudioService定义了AudioServiceBroadcastReceiver,会接收蓝牙的广播:

        // Register for device connection intent broadcasts.
        IntentFilter intentFilter =
                new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
        intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
        ... ...
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        ... ...
        intentFilter.addAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
        intentFilter.addAction(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);

AudioManager接口

public void startBluetoothSco()
public void stopBluetoothSco()
public boolean isBluetoothScoOn()
public void setBluetoothScoOn(boolean on)
public boolean isBluetoothA2dpOn()
public void startBluetoothScoVirtualCall()
public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device)
public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)

Listener监听

这里主要是监听BluetoothProfile.ServiceListener,监听服务断开和打开

    /**
     * An interface for notifying BluetoothProfile IPC clients when they have
     * been connected or disconnected to the service.
     */
    public interface ServiceListener {
        /**
         * Called to notify the client when the proxy object has been
         * connected to the service.
         * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or
         *                  {@link #A2DP}
         * @param proxy - One of {@link BluetoothHealth}, {@link BluetoothHeadset} or
         *                {@link BluetoothA2dp}
         */
        public void onServiceConnected(int profile, BluetoothProfile proxy);

        /**
         * Called to notify the client that this proxy object has been
         * disconnected from the service.
         * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or
         *                  {@link #A2DP}
         */
        public void onServiceDisconnected(int profile);
    }

蓝牙耳机和AudioService的交互

这里用来测试的蓝牙耳机是Philips SHB5800。连接时,会上报A2DP和SCO两个profile的连接事件。

蓝牙的状态

蓝牙的状态,主要有如下几个状态,定义在BluetoothProfile中:

* frameworks/base/core/java/android/bluetooth/BluetoothProfile.java

    /** The profile is in disconnected state */
    public static final int STATE_DISCONNECTED  = 0;
    /** The profile is in connecting state */
    public static final int STATE_CONNECTING    = 1;
    /** The profile is in connected state */
    public static final int STATE_CONNECTED     = 2;
    /** The profile is in disconnecting state */
    public static final int STATE_DISCONNECTING = 3;


A2DP给AudioService上报连接状态

A2DP是通过AudioManager接口和Audio模块进行交互的,通过setBluetoothA2dpDeviceConnectionState接口,Bluetooth会调两次,上报两次状态。

第一次,状态为state = 1STATE_CONNECTING 连接中状态:

04-28 09:21:38.653 27417 27468 D AudioManager: setBluetoothA2dpDeviceConnectionState: state = 1

第二次,状态为state = 2STATE_CONNECTED 已连接状态:

04-28 09:21:42.168 27417 27468 D AudioManager: setBluetoothA2dpDeviceConnectionState: state = 2

两次状态上报,都是Bluetooth应用上报的,调用栈如下:

04-28 09:21:38.653 27417 27468 W System.err:    at android.media.AudioManager.setBluetoothA2dpDeviceConnectionState(AudioManager.java:3698)
04-28 09:21:38.653 27417 27468 W System.err:    at com.android.bluetooth.a2dp.A2dpStateMachine.broadcastConnectionState(A2dpStateMachine.java:952)
04-28 09:21:38.653 27417 27468 W System.err:    at com.android.bluetooth.a2dp.A2dpStateMachine.-wrap4(Unknown Source:0)
04-28 09:21:38.654 27417 27468 W System.err:    at com.android.bluetooth.a2dp.A2dpStateMachine$Disconnected.processMessage(A2dpStateMachine.java:304)
04-28 09:21:38.654 27417 27468 W System.err:    at com.android.internal.util.StateMachine$SmHandler.processMsg(StateMachine.java:992)
04-28 09:21:38.654 27417 27468 W System.err:    at com.android.internal.util.StateMachine$SmHandler.handleMessage(StateMachine.java:809)
04-28 09:21:38.654 27417 27468 W System.err:    at android.os.Handler.dispatchMessage(Handler.java:106)
04-28 09:21:38.654 27417 27468 W System.err:    at android.os.Looper.loop(Looper.java:175)
04-28 09:21:38.655 27417 27468 W System.err:    at android.os.HandlerThread.run(HandlerThread.java:65)

AudioService的setBluetoothA2dpDeviceConnectionState实现如下:

* frameworks/base/services/core/java/com/android/server/audio/AudioService.java

    public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)
    {
        if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
            return 0;
        }
        return setBluetoothA2dpDeviceConnectionStateInt(
                device, state, profile, AudioSystem.DEVICE_NONE);
    }

setBluetoothA2dpDeviceConnectionStateInt只处理A2DP和A2DP_SINK:

    public int setBluetoothA2dpDeviceConnectionStateInt(
            BluetoothDevice device, int state, int profile, int musicDevice)
    {
        int delay;
        if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
            throw new IllegalArgumentException("invalid profile " + profile);
        }
        synchronized (mConnectedDevices) {
            if (profile == BluetoothProfile.A2DP) {
                int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
                delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                        intState, musicDevice);
            } else {
                delay = 0;
            }

            queueMsgUnderWakeLock(mAudioHandler,
                    (profile == BluetoothProfile.A2DP ?
                        MSG_SET_A2DP_SINK_CONNECTION_STATE : MSG_SET_A2DP_SRC_CONNECTION_STATE),
                    state,
                    0 /* arg2 unused */,
                    device,
                    delay);
        }
        return delay;
    }

对A2DP,MSG_SET_A2DP_SINK_CONNECTION_STATE处理如下:

                case MSG_SET_A2DP_SINK_CONNECTION_STATE:
                    onSetA2dpSinkConnectionState((BluetoothDevice)msg.obj, msg.arg1);
                    mAudioEventWakeLock.release();
                    break;

具体处理在onSetA2dpSinkConnectionState中处理:

    private void onSetA2dpSinkConnectionState(BluetoothDevice btDevice, int state)
    {
        ... ...

        synchronized (mConnectedDevices) {
            final String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                                                 btDevice.getAddress());
            final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
            boolean isConnected = deviceSpec != null;

            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                if (btDevice.isBluetoothDock()) {
                    if (state == BluetoothProfile.STATE_DISCONNECTED) {
                        // introduction of a delay for transient disconnections of docks when
                        // power is rapidly turned off/on, this message will be canceled if
                        // we reconnect the dock under a preset delay
                        makeA2dpDeviceUnavailableLater(address, BTA2DP_DOCK_TIMEOUT_MILLIS);
                        // the next time isConnected is evaluated, it will be false for the dock
                    }
                } else {
                    makeA2dpDeviceUnavailableNow(address);
                }
                synchronized (mCurAudioRoutes) {
                    if (mCurAudioRoutes.bluetoothName != null) {
                        mCurAudioRoutes.bluetoothName = null;
                        sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
                                SENDMSG_NOOP, 0, 0, null, 0);
                    }
                }
            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                if (btDevice.isBluetoothDock()) {
                    // this could be a reconnection after a transient disconnection
                    cancelA2dpDeviceTimeout();
                    mDockAddress = address;
                } else {
                    // this could be a connection of another A2DP device before the timeout of
                    // a dock: cancel the dock timeout, and make the dock unavailable now
                    if(hasScheduledA2dpDockTimeout()) {
                        cancelA2dpDeviceTimeout();
                        makeA2dpDeviceUnavailableNow(mDockAddress);
                    }
                }
                makeA2dpDeviceAvailable(address, btDevice.getName(),
                        "onSetA2dpSinkConnectionState");
                synchronized (mCurAudioRoutes) {
                    String name = btDevice.getAliasName();
                    if (!TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
                        mCurAudioRoutes.bluetoothName = name;
                        sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
                                SENDMSG_NOOP, 0, 0, null, 0);
                    }
                }
            }
        }
    }
  • state 为 1,STATE_CONNECTING时,什么也没有做,走不进调节判断中去。
  • state 为 12,STATE_CONNECTED时,走到else if,关键的是调到了makeA2dpDeviceAvailable

makeA2dpDeviceAvailable 函数如下:

    private void makeA2dpDeviceAvailable(String address, String name, String eventSource) {
        // enable A2DP before notifying A2DP connection to avoid unnecessary processing in
        // audio policy manager
        VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
        sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, streamState, 0);
        setBluetoothA2dpOnInt(true, eventSource);
        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
        // Reset A2DP suspend state each time a new sink is connected
        AudioSystem.setParameters("A2dpSuspended=false");
        mConnectedDevices.put(
                makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address),
                new DeviceListSpec(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
                                   address));
        sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, null, 0);
    }

makeA2dpDeviceAvailable 时,主要做了以下几件事:

  • 发送消息MSG_SET_DEVICE_VOLUME, 设置新的A2DP的音量,各种类型的音量都会设置

04-28 11:01:47.274   823  1101 D AudioService: applyDeviceVolumemStreamType:3,device:128
04-28 11:01:47.275   823  1101 D AudioService: applyDeviceVolumemStreamType:10,device:128
04-28 11:01:47.277   823  1101 D AudioService: applyDeviceVolumemStreamType:2,device:128
04-28 11:01:47.277   823  1101 D AudioService: applyDeviceVolumemStreamType:8,device:128
04-28 11:01:47.280   823  1101 D AudioService: applyDeviceVolumemStreamType:1,device:128
  • A2DP打开,setBluetoothA2dpOnInt,默认Media用A2DP

    public void setBluetoothA2dpOnInt(boolean on, String eventSource) {
        synchronized (mBluetoothA2dpEnabledLock) {
            mBluetoothA2dpEnabled = on;
            mAudioHandler.removeMessages(MSG_SET_FORCE_BT_A2DP_USE);
            setForceUseInt_SyncDevices(AudioSystem.FOR_MEDIA,
                    mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
                            eventSource);
        }
    }

强制使用A2DP

    private void setForceUseInt_SyncDevices(int usage, int config, String eventSource) {
        if (usage == AudioSystem.FOR_MEDIA) {
            sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
                    SENDMSG_NOOP, 0, 0, null, 0);
        }
        mForceUseLogger.log(new ForceUseEvent(usage, config, eventSource));
        if(LOGD){
          Log.i(TAG,new StringBuilder("setForceUse(")
            .append(AudioSystem.forceUseUsageToString(usage))
            .append(", ").append(AudioSystem.forceUseConfigToString(config))
            .append(") due to ").append(eventSource).toString());
        }
        AudioSystem.setForceUse(usage, config);
    }

强制使用A2DP通过,AudioSystem的setForceUse接口完成的。

  • 给native报A2DP连接的状态,通过AudioSystem的setDeviceConnectionState接口完成

  • 将连接上的A2DP添加到mConnectedDevices中,以DeviceListSpec进行封装

回到onSetA2dpSinkConnectionState函数,一般会上报新的Audio线路MSG_REPORT_NEW_ROUTES。

SCO给AudioService上报连接状态

相比A2DP的接口调用,SCO是通过Receiver监听的。AudioServiceBroadcastReceiver在收到ACTION_CONNECTION_STATE_CHANGED时,Audio将同SCO的状态。A2DP只能做音频输出,而SCO可以做音频输出也可以做音频的输入。
输出: AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET(0x20)
输入: AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET (0x8)

大概的栈如下:

04-28 14:13:19.599   805   805 W System.err:    at com.android.server.audio.AudioService.handleDeviceConnection(AudioService.java:5604)
04-28 14:13:19.599   805   805 W System.err:    at com.android.server.audio.AudioService.setBtScoDeviceConnectionState(AudioService.java:3481)
04-28 14:13:19.599   805   805 W System.err:    at com.android.server.audio.AudioService$AudioServiceBroadcastReceiver.onReceive(AudioService.java:5971)

ACTION_CONNECTION_STATE_CHANGED是从Bluetooth发出来的。

    private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            ... ...
            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
                state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
                                               BluetoothProfile.STATE_DISCONNECTED);
                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                setBtScoDeviceConnectionState(btDevice, state);
            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {

setBtScoDeviceConnectionState中,将处理SCO的input和output。

    void setBtScoDeviceConnectionState(BluetoothDevice btDevice, int state) {
        ... ...

        // 类型转换
        String address = btDevice.getAddress();
        BluetoothClass btClass = btDevice.getBluetoothClass();
        int outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
        int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
        if (btClass != null) {
            switch (btClass.getDeviceClass()) {
            case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
            case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
                outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
                break;
            case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
                outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
                break;
            }
        }

        // 处理连接,outDevice和inDevice
        boolean connected = (state == BluetoothProfile.STATE_CONNECTED);

        String btDeviceName =  btDevice.getName();
        boolean success =
            handleDeviceConnection(connected, outDevice, address, btDeviceName) &&
            handleDeviceConnection(connected, inDevice, address, btDeviceName);

        // 只要headset是连着的,就不断开
        if ((state == BluetoothProfile.STATE_DISCONNECTED ||
            state == BluetoothProfile.STATE_DISCONNECTING) &&
            mBluetoothHeadset != null &&
            mBluetoothHeadset.getAudioState(btDevice) == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
            Log.w(TAG, "SCO connected through another device, returning");
            return;
        }

        // 赋值给mBluetoothHeadsetDevice  ***BUG? 貌似有Bug,两个设备的时候怎么处理?***
        synchronized (mScoClients) {
            if (connected) {
                mBluetoothHeadsetDevice = btDevice;
            } else {
                mBluetoothHeadsetDevice = null;
                resetBluetoothSco();
            }
        }
    }

setBtScoDeviceConnectionState大部分处理在handleDeviceConnection中完成

    private boolean handleDeviceConnection(boolean connect, int device, String address,
            String deviceName) {
        synchronized (mConnectedDevices) {
            String deviceKey = makeDeviceListKey(device, address);
            DeviceListSpec deviceSpec = mConnectedDevices.get(deviceKey);
            boolean isConnected = deviceSpec != null;
            if (connect && !isConnected) { // connect现在的状态,isConnected原来的状态
                final int res = AudioSystem.setDeviceConnectionState(device,
                        AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName);
                if (res != AudioSystem.AUDIO_STATUS_OK) {
                    Slog.e(TAG, "not connecting device 0x" + Integer.toHexString(device) +
                            " due to command error " + res );
                    return false;
                }
                mConnectedDevices.put(deviceKey, new DeviceListSpec(device, deviceName, address));
                sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
                        device, 0, null, 0);
                return true;
            } else if (!connect && isConnected) {
                AudioSystem.setDeviceConnectionState(device,
                        AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName);
                // always remove even if disconnection failed
                mConnectedDevices.remove(deviceKey);
                return true;
            }
        }
        return false;
    }
  • 连接时,通过AudioSystem的setDeviceConnectionState接口,同步到native层。注意这里的参数为AudioSystem.DEVICE_STATE_AVAILABLE
  • SCO的设备同样添加到mConnectedDevices中
  • 如果是unmute的设备,通过MSG_ACCESSORY_PLUG_MEDIA_UNMUTE,恢复音量

    private void onAccessoryPlugMediaUnmute(int newDevice) {
        if (DEBUG_VOL) {
            Log.i(TAG, String.format("onAccessoryPlugMediaUnmute newDevice=%d [%s]",
                    newDevice, AudioSystem.getOutputDeviceName(newDevice)));
        }
        synchronized (mConnectedDevices) {
            if (mNm.getZenMode() != Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
                    && (newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0
                    && mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
                    && mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
                    && (newDevice & AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0)
            {
                mStreamStates[AudioSystem.STREAM_MUSIC].mute(false);
            }
        }
    }

有线耳机的连接状态

耳机的连接和Bluetooth的连接很类似,耳机可以做输出也可以做为输入:
输出: AUDIO_DEVICE_OUT_WIRED_HEADSET(0x4)
输入: AUDIO_DEVICE_IN_WIRED_HEADSET (0x10)

有线耳机基本都是通过AudioManager的setWiredDeviceConnectionState接口同步的状态。从WiredAccessoryManager上报的状态:

04-28 15:40:05.859   992   992 W System.err:    at android.media.AudioManager.setWiredDeviceConnectionState(AudioManager.java:3670)
04-28 15:40:05.859   992   992 W System.err:    at com.android.server.WiredAccessoryManager.setDeviceStateLocked(WiredAccessoryManager.java:293)
04-28 15:40:05.859   992   992 W System.err:    at com.android.server.WiredAccessoryManager.setDevicesState(WiredAccessoryManager.java:249)
04-28 15:40:05.859   992   992 W System.err:    at com.android.server.WiredAccessoryManager.-wrap1(Unknown Source:0)
04-28 15:40:05.859   992   992 W System.err:    at com.android.server.WiredAccessoryManager$1.handleMessage(WiredAccessoryManager.java:232)
04-28 15:40:05.859   992   992 W System.err:    at android.os.Handler.dispatchMessage(Handler.java:106)

后续的流程也是通过handleDeviceConnection函数处理的,前面我们已经分析过。

04-28 15:16:51.860   805  1097 W System.err:    at com.android.server.audio.AudioService.handleDeviceConnection(AudioService.java:5604)
04-28 15:16:51.860   805  1097 W System.err:    at com.android.server.audio.AudioService.onSetWiredDeviceConnectionState(AudioService.java:5830

Audio添加音频设备

不管是A2DP,SCO,还是有线耳机,都会通过AudioSystem的setDeviceConnectionState函数,将配件的状态转给底层。下面我来看看,底层是怎么处理的。

AudioPolicy处理音频设备状态

setDeviceConnectionState是一个native函数,通过JNI,将调到了native的AudioSystem中。

status_t AudioSystem::setDeviceConnectionState(audio_devices_t device,
                                               audio_policy_dev_state_t state,
                                               const char *device_address,
                                               const char *device_name)
{
    ... ...

    return aps->setDeviceConnectionState(device, state, address, name);
}

最终是在AudioPolicyManager.cpp中处理的:

status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
                                                      audio_policy_dev_state_t state,
                                                      const char *device_address,
                                                      const char *device_name)
{
  return setDeviceConnectionStateInt(device, state, device_address, device_name);
}

AudioPolicyManager用两个DeviceVector,分别管理系统中的可用的音频输入输出设备。

        DeviceVector  mAvailableOutputDevices; // all available output devices
        DeviceVector  mAvailableInputDevices;  // all available input devices

在setDeviceConnectionStateInt函数中,对设备的状态进行处理,根据状态维护mAvailableOutputDevicesmAvailableInputDevices

status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
                                                         audio_policy_dev_state_t state,
                                                         const char *device_address,
                                                         const char *device_name)
{
    // connect/disconnect only 1 device at a time
    if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE;

    sp<DeviceDescriptor> devDesc =
            mHwModules.getDeviceDescriptor(device, device_address, device_name);

先获取状态变化的设备getDeviceDescriptor。getDeviceDescriptor是从hw_mode中去获取驱动支持的设备。

下面,分分别来看输入,输入的设备的状态:

状态值:

* system/media/audio/include/system/audio_policy.h

typedef enum {
    AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
    AUDIO_POLICY_DEVICE_STATE_AVAILABLE,

    AUDIO_POLICY_DEVICE_STATE_CNT,
    AUDIO_POLICY_DEVICE_STATE_MAX = AUDIO_POLICY_DEVICE_STATE_CNT - 1,
} audio_policy_dev_state_t;

音频输出设备处理

输出设备,用mAvailableOutputDevices维护,主要是连接和断开时的处理。听筒,Speaker等手机自带的设备,一般只有启动的时候才会处理一下,其他时间都是不需要处理的。所以主要是还是我们所说的,蓝牙耳机,有线耳机等。

  • 音频设备连接

status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
                                                         audio_policy_dev_state_t state,
                                                         const char *device_address,
                                                         const char *device_name)
{
    ... ...

    // handle output devices
    if (audio_is_output_device(device)) {
        SortedVector <audio_io_handle_t> outputs;

        ssize_t index = mAvailableOutputDevices.indexOf(devDesc);

        mPreviousOutputs = mOutputs;
        switch (state)
        {
        // handle output device connection
        case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: {
            ... ...

            // register new device as available
            index = mAvailableOutputDevices.add(devDesc);
            if (index >= 0) {
                sp<HwModule> module = mHwModules.getModuleForDevice(device);
                if (module == 0) {
                    ALOGD("setDeviceConnectionState() could not find HW module for device %08x",
                          device);
                    mAvailableOutputDevices.remove(devDesc);
                    return INVALID_OPERATION;
                }
                mAvailableOutputDevices[index]->attach(module);
            } else {
                return NO_MEMORY;
            }

            // Before checking outputs, broadcast connect event to allow HAL to retrieve dynamic
            // parameters on newly connected devices (instead of opening the outputs...)
            broadcastDeviceConnectionState(device, state, devDesc->mAddress);

            if (checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress) != NO_ERROR) {
                mAvailableOutputDevices.remove(devDesc);

                broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
                                               devDesc->mAddress);
                return INVALID_OPERATION;
            }
            // Propagate device availability to Engine
            mEngine->setDeviceConnectionState(devDesc, state);

            ... ...

            } break;

输出设备连接时,主要做以下几件事:

  1. 将新连接的音频设备,添加到mAvailableOutputDevices中,且让设备和具体的HwModule关联。
  2. 广播设备已连接状态,broadcastDeviceConnectionState
  3. checkOutputsForDevice,确认设备是连接是否正确
  4. 将状态传给mEngine
  • 音频设备断开

status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
                                                         audio_policy_dev_state_t state,
                                                         const char *device_address,
                                                         const char *device_name)
{
    ... ...

    // handle output devices
    if (audio_is_output_device(device)) {
        .... ....
        // handle output device disconnection
        case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: {
            ... ...

            // Send Disconnect to HALs
            broadcastDeviceConnectionState(device, state, devDesc->mAddress);

            // remove device from available output devices
            mAvailableOutputDevices.remove(devDesc);

            checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress);

            // Propagate device availability to Engine
            mEngine->setDeviceConnectionState(devDesc, state);
            } break;

        default:
            ALOGE("setDeviceConnectionState() invalid state: %x", state);
            return BAD_VALUE;
        }

断开时,将设备从mAvailableOutputDevices中删掉,其他处理和连接时类似。注意这里的checkOutputsForDevice,这里将更新输出设备mOutputs

设备更新完成后,就需要更新策略了,切换通路,确保音频的输出通路可用且正确。

status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
                                                         audio_policy_dev_state_t state,
                                                         const char *device_address,
                                                         const char *device_name)
{
    ... ...

    // handle output devices
    if (audio_is_output_device(device)) {
        .... ....
        // checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP
        // output is suspended before any tracks are moved to it
        checkA2dpSuspend();
        checkOutputForAllStrategies();
        // outputs must be closed after checkOutputForAllStrategies() is executed
        if (!outputs.isEmpty()) {
            for (size_t i = 0; i < outputs.size(); i++) {
                sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
                // close unused outputs after device disconnection or direct outputs that have been
                // opened by checkOutputsForDevice() to query dynamic parameters
                if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) ||
                        (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) &&
                         (desc->mDirectOpenCount == 0))) {
                    closeOutput(outputs[i]);
                }
            }
            // check again after closing A2DP output to reset mA2dpSuspended if needed
            checkA2dpSuspend();
        }

        updateDevicesAndOutputs();
        if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
            audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/);
            updateCallRouting(newDevice);
        }
        for (size_t i = 0; i < mOutputs.size(); i++) {
            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
            if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) {
                audio_devices_t newDevice = getNewOutputDevice(desc, true /*fromCache*/);
                // do not force device change on duplicated output because if device is 0, it will
                // also force a device 0 for the two outputs it is duplicated to which may override
                // a valid device selection on those outputs.
                bool force = !desc->isDuplicated()
                        && (!device_distinguishes_on_address(device)
                                // always force when disconnecting (a non-duplicated device)
                                || (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
                newDevice = mpAudioPolicyMTKInterface->fm_correctDeviceFromSetDeviceConnectionStateInt(desc, newDevice, force);
                setOutputDevice(desc, newDevice, force, 0);
            }
        }

        if (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
            cleanUpForDevice(devDesc);
        }

        mpClientInterface->onAudioPortListUpdate();
        return NO_ERROR;
    }  // end if is output device

这里主要做了以下几件事:

  1. 阻塞式的检查A2DP的状态 checkA2dpSuspend。
  2. 检查所以策略的输出 checkOutputForAllStrategies
  3. 更新输出设备 updateDevicesAndOutputs
  4. 更新电话通路 updateCallRouting
  5. 设置输出设备 setOutputDevice,包括音量等。
  6. 回调给Client,音频断开更新 onAudioPortListUpdate。

音频输入设备处理

status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
                                                         audio_policy_dev_state_t state,
                                                         const char *device_address,
                                                         const char *device_name)
{
    ... ...
    // handle input devices
    if (audio_is_input_device(device)) {
        SortedVector <audio_io_handle_t> inputs;

        ssize_t index = mAvailableInputDevices.indexOf(devDesc);
        switch (state)
        {
        // handle input device connection
        case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: {
           ... ...

            // Before checking intputs, broadcast connect event to allow HAL to retrieve dynamic
            // parameters on newly connected devices (instead of opening the inputs...)
            broadcastDeviceConnectionState(device, state, devDesc->mAddress);

            if (checkInputsForDevice(devDesc, state, inputs, devDesc->mAddress) != NO_ERROR) {
                broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
                                               devDesc->mAddress);
                return INVALID_OPERATION;
            }

            index = mAvailableInputDevices.add(devDesc);
            if (index >= 0) {
                mAvailableInputDevices[index]->attach(module);
            } else {
                return NO_MEMORY;
            }

            // Propagate device availability to Engine
            mEngine->setDeviceConnectionState(devDesc, state);
        } break;

        // handle input device disconnection
        case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: {
            ... ...

            // Set Disconnect to HALs
            broadcastDeviceConnectionState(device, state, devDesc->mAddress);

            checkInputsForDevice(devDesc, state, inputs, devDesc->mAddress);
            mAvailableInputDevices.remove(devDesc);

            // Propagate device availability to Engine
            mEngine->setDeviceConnectionState(devDesc, state);
        } break;

        default:
            ALOGE("setDeviceConnectionState() invalid state: %x", state);
            return BAD_VALUE;
        }

        closeAllInputs();
        // As the input device list can impact the output device selection, update
        // getDeviceForStrategy() cache
        updateDevicesAndOutputs();

        if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
            audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/);
            updateCallRouting(newDevice);
        }

        if (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
            cleanUpForDevice(devDesc);
        }

        mpClientInterface->onAudioPortListUpdate();
        return NO_ERROR;
    } // end if is input device

    ALOGW("setDeviceConnectionState() invalid device: %x", device);
    return BAD_VALUE;
}

音频输入设备的连接和断开处理,和输出设备的类似,这里就不多说,这里维护的是mAvailableInputDevices。

选择输出设备

播放音频时,将选择具体的音频设备~~

getDevicesForStream
getDeviceForStrategy

这里选择的设备就是我们添加到mAvailableOutputDevices中的可用的输出设备。

录音时,也为输入source选择设备。

getDeviceForInputSource

这里选择的设备就是我们添加到mAvailableInputDevices中的可用的输入设备。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值