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

@[TOC]

做音频开发,避免不了和蓝牙打交道,尤其是做系统开发,又是不同的模块,所以很多问题很难界定。因而,了解一下蓝牙,对整体的系统架的理解会更加完善。同样的,有线耳机和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中的可用的输入设备。

这里起个头,我们后续再介绍~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 摘要 该配置文件定义了支持高质量音频分发所需的Bluetooth®设备的要求。这些要求以终端用户服务的方式表达,并...
    公子小水阅读 9,397评论 0 4
  • 蓝牙 注:本文翻译自https://developer.android.com/guide/topics/conn...
    RxCode阅读 8,620评论 11 99
  • Android平台支持蓝牙网络协议栈,实现蓝牙设备之间数据的无线传输。本文档描述了怎样利用android平台提供的...
    Camming阅读 3,280评论 0 3
  • 最近项目使用蓝牙,之前并没有接触,还是发现了很多坑,查阅了很多资料,说的迷迷糊糊,今天特查看官方文档。 说下遇到的...
    King9527阅读 1,784评论 0 1
  • 前言 最近在做Android蓝牙这部分内容,所以查阅了很多相关资料,在此总结一下。 基本概念 Bluetooth是...
    猫疏阅读 14,486评论 7 113