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

Commit f9bc5e5b authored by android-build-prod (mdb)'s avatar android-build-prod (mdb) Committed by android-build-merger
Browse files

Merge "Newly connected A2DP/HFP device will become the Active device" am: 7b9aaafd am: 6cc71873

am: ebd84b19

Change-Id: Ica1e2ff987ec182273ada821d7efa1e0f50d0ef2
parents 01fa746f ebd84b19
Loading
Loading
Loading
Loading
+68 −88
Original line number Original line Diff line number Diff line
@@ -42,7 +42,7 @@ import java.util.Objects;


/**
/**
 * The active device manager is responsible for keeping track of the
 * The active device manager is responsible for keeping track of the
 * connected A2DP/HFP/AVRCP devices and select which device is
 * connected A2DP/HFP/AVRCP/HearingAid devices and select which device is
 * active (for each profile).
 * active (for each profile).
 *
 *
 * Current policy (subject to change):
 * Current policy (subject to change):
@@ -52,36 +52,30 @@ import java.util.Objects;
 *    devices is more than one, the rules below will apply.
 *    devices is more than one, the rules below will apply.
 * 2) The selected A2DP active device is the one used for AVRCP as well.
 * 2) The selected A2DP active device is the one used for AVRCP as well.
 * 3) The HFP active device might be different from the A2DP active device.
 * 3) The HFP active device might be different from the A2DP active device.
 * 4) The Active Device Manager always listens for
 * 4) The Active Device Manager always listens for ACTION_ACTIVE_DEVICE_CHANGED
 *    ACTION_ACTIVE_DEVICE_CHANGED broadcasts for each profile:
 *    broadcasts for each profile:
 *    - BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED for A2DP
 *    - BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED for A2DP
 *    - BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED for HFP
 *    - BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED for HFP
 *    - BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED for HearingAid
 *    If such broadcast is received (e.g., triggered indirectly by user
 *    If such broadcast is received (e.g., triggered indirectly by user
 *    action on the UI), the device in the received broacast is marked
 *    action on the UI), the device in the received broacast is marked
 *    as the current active device for that profile.
 *    as the current active device for that profile.
 * 5) If there are no connected devices (e.g., during startup, or after all
 * 5) If there is a HearingAid active device, then A2DP and HFP active devices
 *    must be set to null (i.e., A2DP and HFP cannot have active devices).
 *    The reason is because A2DP or HFP cannot be used together with HearingAid.
 * 6) If there are no connected devices (e.g., during startup, or after all
 *    devices have been disconnected, the active device per profile
 *    devices have been disconnected, the active device per profile
 *    (either A2DP or HFP) is selected as follows:
 *    (A2DP/HFP/HearingAid) is selected as follows:
 * 5.1) The first connected device (for either A2DP or HFP) is immediately
 * 6.1) The last connected HearingAid device is selected as active.
 *      selected as active for that profile. Assume the first connected device
 *      If there is an active A2DP or HFP device, those must be set to null.
 *      is for A2DP.
 * 6.2) The last connected A2DP or HFP device is selected as active.
 * 5.2) A timer is started: if the same device is connected for the other
 *      However, if there is an active HearingAid device, then the
 *      profile as well (HFP in this example) while the timer is running,
 *      A2DP or HFP active device is not set (must remain null).
 *      and there is no active HFP device yet, that device is selected as
 * 7) If the currently active device (per profile) is disconnected, the
 *      active for HFP as well. The purpose is to select by default the same
 *      device as active for both profiles.
 * 5.3) While the timer is running, all other HFP connected devices are
 *      listed locally, but none of those devices is selected as active.
 * 5.4) While the timer is running, if ACTION_ACTIVE_DEVICE_CHANGED broadcast
 *      is received for HFP, the device contained in the broadcast is
 *      marked as active.
 * 5.5) If the timer expires and no HFP device has been selected as active,
 *      the first HFP connected device is selected as active.
 * 6) If the currently active device (per profile) is disconnected, the
 *    Active Device Manager just marks that the profile has no active device,
 *    Active Device Manager just marks that the profile has no active device,
 *    but does not attempt to select a new one. Currently, the expectation is
 *    but does not attempt to select a new one. Currently, the expectation is
 *    that the user will explicitly select the new active device.
 *    that the user will explicitly select the new active device.
 * 7) If there is already an active device, and the corresponding
 * 8) If there is already an active device, and the corresponding
 *    ACTION_ACTIVE_DEVICE_CHANGED broadcast is received, the device
 *    ACTION_ACTIVE_DEVICE_CHANGED broadcast is received, the device
 *    contained in the broadcast is marked as active. However, if
 *    contained in the broadcast is marked as active. However, if
 *    the contained device is null, the corresponding profile is marked
 *    the contained device is null, the corresponding profile is marked
@@ -93,15 +87,11 @@ class ActiveDeviceManager {


    // Message types for the handler
    // Message types for the handler
    private static final int MESSAGE_ADAPTER_ACTION_STATE_CHANGED = 1;
    private static final int MESSAGE_ADAPTER_ACTION_STATE_CHANGED = 1;
    private static final int MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT = 2;
    private static final int MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED = 2;
    private static final int MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED = 3;
    private static final int MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED = 3;
    private static final int MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED = 4;
    private static final int MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED = 4;
    private static final int MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED = 5;
    private static final int MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED = 5;
    private static final int MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED = 6;
    private static final int MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED = 6;
    private static final int MESSAGE_HEARING_AID_ACTION_ACTIVE_DEVICE_CHANGED = 7;

    // Timeouts
    private static final int SELECT_ACTIVE_DEVICE_TIMEOUT_MS = 6000; // 6s


    private final AdapterService mAdapterService;
    private final AdapterService mAdapterService;
    private final ServiceFactory mFactory;
    private final ServiceFactory mFactory;
@@ -155,8 +145,8 @@ class ActiveDeviceManager {
        }
        }
    };
    };


    class ActivePoliceManagerHandler extends Handler {
    class ActiveDeviceManagerHandler extends Handler {
        ActivePoliceManagerHandler(Looper looper) {
        ActiveDeviceManagerHandler(Looper looper) {
            super(looper);
            super(looper);
        }
        }


@@ -176,28 +166,16 @@ class ActiveDeviceManager {
                }
                }
                break;
                break;


                case MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT: {
                    if (DBG) {
                        Log.d(TAG, "handleMessage(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT)");
                    }
                    // Set the first connected device as active
                    if ((mA2dpActiveDevice == null) && !mA2dpConnectedDevices.isEmpty()
                            && mHearingAidActiveDevice == null) {
                        setA2dpActiveDevice(mA2dpConnectedDevices.get(0));
                    }
                    if ((mHfpActiveDevice == null) && !mHfpConnectedDevices.isEmpty()
                            && mHearingAidActiveDevice == null) {
                        setHfpActiveDevice(mHfpConnectedDevices.get(0));
                    }
                }
                break;

                case MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED: {
                case MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED: {
                    Intent intent = (Intent) msg.obj;
                    Intent intent = (Intent) msg.obj;
                    BluetoothDevice device =
                    BluetoothDevice device =
                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
                    int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
                    int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
                    int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
                    if (prevState == nextState) {
                        // Nothing has changed
                        break;
                    }
                    if (nextState == BluetoothProfile.STATE_CONNECTED) {
                    if (nextState == BluetoothProfile.STATE_CONNECTED) {
                        // Device connected
                        // Device connected
                        if (DBG) {
                        if (DBG) {
@@ -206,34 +184,17 @@ class ActiveDeviceManager {
                                    + "device " + device + " connected");
                                    + "device " + device + " connected");
                        }
                        }
                        if (mA2dpConnectedDevices.contains(device)) {
                        if (mA2dpConnectedDevices.contains(device)) {
                            break;
                            break;      // The device is already connected
                        }
                        }
                        if (!hasConnectedClassicDevices() && mHearingAidActiveDevice == null) {
                            // First connected device: select it as active and start the timer
                        mA2dpConnectedDevices.add(device);
                        mA2dpConnectedDevices.add(device);
                            Message m = obtainMessage(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT);
                        if (mHearingAidActiveDevice == null) {
                            sendMessageDelayed(m, SELECT_ACTIVE_DEVICE_TIMEOUT_MS);
                            // New connected device: select it as active
                            setA2dpActiveDevice(device);
                            setA2dpActiveDevice(device);
                            break;
                            break;
                        }
                        }
                        mA2dpConnectedDevices.add(device);
                        // Check whether the active device for the other profile is same
                        if ((mA2dpActiveDevice == null) && matchesActiveDevice(device)
                                && mHearingAidActiveDevice == null) {
                            setA2dpActiveDevice(device);
                            break;
                        }
                        // Check whether the active device selection timer is not running
                        if ((mA2dpActiveDevice == null)
                                && !hasMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT)
                                && mHearingAidActiveDevice == null) {
                            setA2dpActiveDevice(mA2dpConnectedDevices.get(0));
                            break;
                        }
                        break;
                        break;
                    }
                    }
                    if ((prevState == BluetoothProfile.STATE_CONNECTED)
                    if (prevState == BluetoothProfile.STATE_CONNECTED) {
                            && (nextState != prevState)) {
                        // Device disconnected
                        // Device disconnected
                        if (DBG) {
                        if (DBG) {
                            Log.d(TAG,
                            Log.d(TAG,
@@ -256,7 +217,6 @@ class ActiveDeviceManager {
                        Log.d(TAG, "handleMessage(MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED): "
                        Log.d(TAG, "handleMessage(MESSAGE_A2DP_ACTION_ACTIVE_DEVICE_CHANGED): "
                                + "device= " + device);
                                + "device= " + device);
                    }
                    }
                    removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT);
                    if (device != null && !Objects.equals(mA2dpActiveDevice, device)) {
                    if (device != null && !Objects.equals(mA2dpActiveDevice, device)) {
                        setHearingAidActiveDevice(null);
                        setHearingAidActiveDevice(null);
                    }
                    }
@@ -271,8 +231,40 @@ class ActiveDeviceManager {
                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
                    int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
                    int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
                    int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
                    // TODO: Copy the corresponding logic from the processing of
                    if (prevState == nextState) {
                    // message MESSAGE_A2DP_ACTION_CONNECTION_STATE_CHANGED
                        // Nothing has changed
                        break;
                    }
                    if (nextState == BluetoothProfile.STATE_CONNECTED) {
                        // Device connected
                        if (DBG) {
                            Log.d(TAG,
                                    "handleMessage(MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED): "
                                    + "device " + device + " connected");
                        }
                        if (mHfpConnectedDevices.contains(device)) {
                            break;      // The device is already connected
                        }
                        mHfpConnectedDevices.add(device);
                        if (mHearingAidActiveDevice == null) {
                            // New connected device: select it as active
                            setHfpActiveDevice(device);
                            break;
                        }
                        break;
                    }
                    if (prevState == BluetoothProfile.STATE_CONNECTED) {
                        // Device disconnected
                        if (DBG) {
                            Log.d(TAG,
                                    "handleMessage(MESSAGE_HFP_ACTION_CONNECTION_STATE_CHANGED): "
                                    + "device " + device + " disconnected");
                        }
                        mHfpConnectedDevices.remove(device);
                        if (Objects.equals(mHfpActiveDevice, device)) {
                            setHfpActiveDevice(null);
                        }
                    }
                }
                }
                break;
                break;


@@ -284,7 +276,6 @@ class ActiveDeviceManager {
                        Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED): "
                        Log.d(TAG, "handleMessage(MESSAGE_HFP_ACTION_ACTIVE_DEVICE_CHANGED): "
                                + "device= " + device);
                                + "device= " + device);
                    }
                    }
                    removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT);
                    if (device != null && !Objects.equals(mHfpActiveDevice, device)) {
                    if (device != null && !Objects.equals(mHfpActiveDevice, device)) {
                        setHearingAidActiveDevice(null);
                        setHearingAidActiveDevice(null);
                    }
                    }
@@ -301,7 +292,6 @@ class ActiveDeviceManager {
                        Log.d(TAG, "handleMessage(MESSAGE_HA_ACTION_ACTIVE_DEVICE_CHANGED): "
                        Log.d(TAG, "handleMessage(MESSAGE_HA_ACTION_ACTIVE_DEVICE_CHANGED): "
                                + "device= " + device);
                                + "device= " + device);
                    }
                    }
                    removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT);
                    // Just assign locally the new value
                    // Just assign locally the new value
                    mHearingAidActiveDevice = device;
                    mHearingAidActiveDevice = device;
                    if (device != null) {
                    if (device != null) {
@@ -326,7 +316,7 @@ class ActiveDeviceManager {


        mHandlerThread = new HandlerThread("BluetoothActiveDeviceManager");
        mHandlerThread = new HandlerThread("BluetoothActiveDeviceManager");
        mHandlerThread.start();
        mHandlerThread.start();
        mHandler = new ActivePoliceManagerHandler(mHandlerThread.getLooper());
        mHandler = new ActiveDeviceManagerHandler(mHandlerThread.getLooper());


        IntentFilter filter = new IntentFilter();
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
@@ -393,23 +383,13 @@ class ActiveDeviceManager {
        mHearingAidActiveDevice = device;
        mHearingAidActiveDevice = device;
    }
    }


    private boolean hasConnectedClassicDevices() {
        return (!mA2dpConnectedDevices.isEmpty() || !mHfpConnectedDevices.isEmpty());
    }

    private boolean matchesActiveDevice(BluetoothDevice device) {
        return (Objects.equals(mA2dpActiveDevice, device)
                || Objects.equals(mHfpActiveDevice, device));
    }

    private void resetState() {
    private void resetState() {
        if (mHandler != null) {
            mHandler.removeMessages(MESSAGE_SELECT_ACTICE_DEVICE_TIMEOUT);
        }
        mA2dpConnectedDevices.clear();
        mA2dpConnectedDevices.clear();
        mA2dpActiveDevice = null;
        mA2dpActiveDevice = null;


        mHfpConnectedDevices.clear();
        mHfpConnectedDevices.clear();
        mHfpActiveDevice = null;
        mHfpActiveDevice = null;

        mHearingAidActiveDevice = null;
    }
    }
}
}
+0 −3
Original line number Original line Diff line number Diff line
@@ -1156,9 +1156,6 @@ public class HeadsetService extends ProfileService {
                            stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR,
                            stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR,
                                    0));
                                    0));
                }
                }
                if (mActiveDevice == null) {
                    setActiveDevice(device);
                }
                MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET);
                MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET);
            }
            }
            if (fromState == BluetoothProfile.STATE_CONNECTED
            if (fromState == BluetoothProfile.STATE_CONNECTED
+0 −1
Original line number Original line Diff line number Diff line
@@ -206,7 +206,6 @@ public class HeadsetServiceAndStateMachineTest {
        Assert.assertEquals(Collections.singletonList(mCurrentDevice),
        Assert.assertEquals(Collections.singletonList(mCurrentDevice),
                mHeadsetService.getDevicesMatchingConnectionStates(
                mHeadsetService.getDevicesMatchingConnectionStates(
                        new int[]{BluetoothProfile.STATE_CONNECTED}));
                        new int[]{BluetoothProfile.STATE_CONNECTED}));
        Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice());
    }
    }


    /**
    /**
+10 −12
Original line number Original line Diff line number Diff line
@@ -354,7 +354,8 @@ public class HeadsetServiceTest {
                mHeadsetService.getConnectedDevices());
                mHeadsetService.getConnectedDevices());
        mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice,
        mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice,
                BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
                BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
        // Test connect audio, the first connected device should be the default active device
        // Test connect audio - set the device first as the active device
        Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice));
        Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice));
        Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice));
        verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO,
        verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO,
                mCurrentDevice);
                mCurrentDevice);
@@ -418,16 +419,11 @@ public class HeadsetServiceTest {
            Assert.assertThat(mHeadsetService.getConnectedDevices(),
            Assert.assertThat(mHeadsetService.getConnectedDevices(),
                    Matchers.containsInAnyOrder(connectedDevices.toArray()));
                    Matchers.containsInAnyOrder(connectedDevices.toArray()));
            // Try to connect audio
            // Try to connect audio
            if (i == 0) {
            // Should fail
                // Should only succeed with the first device
                Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice));
            } else {
                // Should fail for other devices
            Assert.assertFalse(mHeadsetService.connectAudio(mCurrentDevice));
            Assert.assertFalse(mHeadsetService.connectAudio(mCurrentDevice));
            // Should succeed after setActiveDevice()
            // Should succeed after setActiveDevice()
            Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice));
            Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice));
            Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice));
            Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice));
            }
            verify(mStateMachines.get(mCurrentDevice)).sendMessage(
            verify(mStateMachines.get(mCurrentDevice)).sendMessage(
                    HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice);
                    HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice);
            // Put device to audio connecting state
            // Put device to audio connecting state
@@ -500,7 +496,8 @@ public class HeadsetServiceTest {
            // Try to connect audio
            // Try to connect audio
            BluetoothDevice firstDevice = connectedDevices.get(0);
            BluetoothDevice firstDevice = connectedDevices.get(0);
            BluetoothDevice secondDevice = connectedDevices.get(1);
            BluetoothDevice secondDevice = connectedDevices.get(1);
            // First device is the default device
            // Set the first device as the active device
            Assert.assertTrue(mHeadsetService.setActiveDevice(firstDevice));
            Assert.assertTrue(mHeadsetService.connectAudio(firstDevice));
            Assert.assertTrue(mHeadsetService.connectAudio(firstDevice));
            verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO,
            verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO,
                    firstDevice);
                    firstDevice);
@@ -572,6 +569,7 @@ public class HeadsetServiceTest {
        }
        }
        // Try to connect audio
        // Try to connect audio
        BluetoothDevice firstDevice = connectedDevices.get(0);
        BluetoothDevice firstDevice = connectedDevices.get(0);
        Assert.assertTrue(mHeadsetService.setActiveDevice(firstDevice));
        Assert.assertTrue(mHeadsetService.connectAudio());
        Assert.assertTrue(mHeadsetService.connectAudio());
        verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO,
        verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO,
                firstDevice);
                firstDevice);