Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8dd33941 authored by Jack He's avatar Jack He Committed by Pavlin Radoslavov
Browse files

BT-HFP: Update Bluetooth headset state handler to Multi-HFP

* When multiple headset devices are connected at the same time, at most one
  device can be used for SCO audio at any time. This device is called
  Active Device and is indicated by either
  BluetoothHeadset.getActiveDevice() or
  BluetoothHeadset.ACTIVE_DEVICE_CHANGED intent. It can also be set
  through BluetoothHeadset.setActiveDevice(BluetoothDevice) internal API.
* This change let AudioService to listen to ACTIVE_DEVICE_CHANGED intent
  instead of CONNECTION_STATE_CHANGED intent since it is the active
  device that AudioService cares about, not the list of connected
  devices.
* Everytime a new active device is set, AudioService will treat the old
  one (if not null) as disconnected and call disconnection methods in
  audio framework and the new active device is regarded as newly
  connected and connection methods will be called by AudioService.
* When disconnectHeadset() is called, active device will be set to null

Bug: 71875419
Test: compile, connect multiple HFP devices and switch active device
      among them
Change-Id: I148cca079d36a2dfc6a46b8d42ba69821c9c6de3
parent c584d277
Loading
Loading
Loading
Loading
+56 −55
Original line number Diff line number Diff line
@@ -136,7 +136,6 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -768,7 +767,7 @@ public class AudioService extends IAudioService.Stub
        // Register for device connection intent broadcasts.
        IntentFilter intentFilter =
                new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
        intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
        intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
        intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -2929,16 +2928,30 @@ public class AudioService extends IAudioService.Stub
    }

    public void setBluetoothScoOnInt(boolean on, String eventSource) {
        if (DEBUG_DEVICES) {
            Log.d(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
        }
        if (on) {
            // do not accept SCO ON if SCO audio is not connected
            synchronized (mScoClients) {
                if ((mBluetoothHeadset != null) &&
                    (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
                             != BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
                if (mBluetoothHeadset != null) {
                    if (mBluetoothHeadsetDevice == null) {
                        BluetoothDevice activeDevice = mBluetoothHeadset.getActiveDevice();
                        if (activeDevice != null) {
                            // setBtScoActiveDevice() might trigger resetBluetoothSco() which
                            // will call setBluetoothScoOnInt(false, "resetBluetoothSco")
                            setBtScoActiveDevice(activeDevice);
                        }
                    }
                    if (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
                            != BluetoothHeadset.STATE_AUDIO_CONNECTED) {
                        mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
                        Log.w(TAG, "setBluetoothScoOnInt(true) failed because "
                                + mBluetoothHeadsetDevice + " is not in audio connected mode");
                        return;
                    }
                }
            }
            mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
        } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
            mForcedUseForComm = AudioSystem.FORCE_NONE;
@@ -3324,11 +3337,10 @@ public class AudioService extends IAudioService.Stub
        }
    }

    void setBtScoDeviceConnectionState(BluetoothDevice btDevice, int state) {
    private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
        if (btDevice == null) {
            return;
            return true;
        }

        String address = btDevice.getAddress();
        BluetoothClass btClass = btDevice.getBluetoothClass();
        int outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
@@ -3349,37 +3361,36 @@ public class AudioService extends IAudioService.Stub
            address = "";
        }

        boolean connected = (state == BluetoothProfile.STATE_CONNECTED);

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

        if (!success) {
          return;
        boolean result = handleDeviceConnection(isActive, outDevice, address, btDeviceName);
        // handleDeviceConnection() && result to make sure the method get executed
        result = handleDeviceConnection(isActive, inDevice, address, btDeviceName) && result;
        return result;
    }

        /* When one BT headset is disconnected while another BT headset
         * is connected, don't mess with the headset device.
         */
        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;
    void setBtScoActiveDevice(BluetoothDevice btDevice) {
        if (DEBUG_DEVICES) {
            Log.d(TAG, "setBtScoActiveDevice(" + btDevice + ")");
        }

        synchronized (mScoClients) {
            if (connected) {
            final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
            if (!Objects.equals(btDevice, previousActiveDevice)) {
                if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
                    Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device "
                            + previousActiveDevice);
                }
                if (!handleBtScoActiveDeviceChange(btDevice, true)) {
                    Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice);
                    // set mBluetoothHeadsetDevice to null when failing to add new device
                    btDevice = null;
                }
                mBluetoothHeadsetDevice = btDevice;
            } else {
                mBluetoothHeadsetDevice = null;
                if (mBluetoothHeadsetDevice == null) {
                    resetBluetoothSco();
                }
            }
        }
    }

    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
        new BluetoothProfile.ServiceListener() {
@@ -3431,12 +3442,7 @@ public class AudioService extends IAudioService.Stub
                    // Discard timeout message
                    mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
                    mBluetoothHeadset = (BluetoothHeadset) proxy;
                    deviceList = mBluetoothHeadset.getConnectedDevices();
                    if (deviceList.size() > 0) {
                        mBluetoothHeadsetDevice = deviceList.get(0);
                    } else {
                        mBluetoothHeadsetDevice = null;
                    }
                    setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice());
                    // Refresh SCO audio state
                    checkScoAudioState();
                    // Continue pending action if any
@@ -3557,10 +3563,7 @@ public class AudioService extends IAudioService.Stub

    void disconnectHeadset() {
        synchronized (mScoClients) {
            if (mBluetoothHeadsetDevice != null) {
                setBtScoDeviceConnectionState(mBluetoothHeadsetDevice,
                        BluetoothProfile.STATE_DISCONNECTED);
            }
            setBtScoActiveDevice(null);
            mBluetoothHeadset = null;
        }
    }
@@ -5732,11 +5735,9 @@ public class AudioService extends IAudioService.Stub
                    AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
                }
                mDockState = dockState;
            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
                state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
                                               BluetoothProfile.STATE_DISCONNECTED);
            } else if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                setBtScoDeviceConnectionState(btDevice, state);
                setBtScoActiveDevice(btDevice);
            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
                boolean broadcast = false;
                int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;