Android Audio和耳機,藍牙耳機等音頻外設

Android Audio和耳機,藍牙耳機等音頻外設

做音頻開發,避免不了和藍牙打交道,尤其是做系統開發,又是不同的模塊,所以很多問題很難界定。因而,瞭解一下藍牙,對整體的系統架的理解會更加完善。同樣的,有線耳機和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中的可用的輸入設備。

這裏起個頭,我們後續再介紹~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章