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

Commit 6dfae2a6 authored by Jakub Pawłowski's avatar Jakub Pawłowski Committed by Gerrit Code Review
Browse files

Merge "ActiveDeviceManager: Fix handling disconnect of streaming set member" into main

parents 19abc765 fac08f46
Loading
Loading
Loading
Loading
+17 −4
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSinkAudioPolicy;
import android.media.AudioDeviceCallback;
@@ -569,8 +570,8 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
            boolean hasFallbackDevice = false;
            if (Objects.equals(mLeAudioActiveDevice, device)) {
                hasFallbackDevice = setFallbackDeviceActiveLocked(device);
                if (!hasFallbackDevice) {
                    setLeAudioActiveDevice(null, false);
                if (!hasFallbackDevice && !Flags.admFixDisconnectOfSetMember()) {
                    leAudioService.removeActiveDevice(false);
                }
            }
            leAudioService.deviceDisconnected(device, hasFallbackDevice);
@@ -1095,13 +1096,25 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
            return false;
        }

        if (firstDevice == null || secondDevice == null) {
            return false;
        }

        final LeAudioService leAudioService = mFactory.getLeAudioService();
        if (leAudioService == null) {
            Log.e(TAG, "LeAudioService not available");
            return false;
        }

        return leAudioService.getGroupId(firstDevice) == leAudioService.getGroupId(secondDevice);
        int groupIdFirst = leAudioService.getGroupId(firstDevice);
        int groupIdSecond = leAudioService.getGroupId(secondDevice);

        if (groupIdFirst == BluetoothLeAudio.GROUP_ID_INVALID
                || groupIdSecond == BluetoothLeAudio.GROUP_ID_INVALID) {
            return false;
        }

        return groupIdFirst == groupIdSecond;
    }

    /**
@@ -1113,7 +1126,7 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
     */
    @GuardedBy("mLock")
    private boolean setFallbackDeviceActiveLocked(BluetoothDevice recentlyRemovedDevice) {
        Log.d(TAG, "setFallbackDeviceActive");
        Log.d(TAG, "setFallbackDeviceActive, recently removed: " + recentlyRemovedDevice);
        mDbManager = mAdapterService.getDatabase();
        List<BluetoothDevice> connectedHearingAidDevices = new ArrayList<>();
        if (!mHearingAidConnectedDevices.isEmpty()) {
+83 −41
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.media.AudioDeviceInfo;
import android.media.AudioDevicePort;
import android.media.AudioManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
@@ -385,10 +386,13 @@ public class ActiveDeviceManagerTest {
    @Test
    public void headsetRemoveActive_fallbackToLeAudio() {
        when(mHeadsetService.getFallbackDevice()).thenReturn(mHeadsetDevice);
        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);

        InOrder order = inOrder(mLeAudioService);

        leAudioConnected(mLeAudioDevice);
        mTestLooper.dispatchAll();
        verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice);
        order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice);

        headsetConnected(mHeadsetDevice, false);
        mTestLooper.dispatchAll();
@@ -827,7 +831,28 @@ public class ActiveDeviceManagerTest {

    /** One LE Audio is connected and disconnected later. Should then set active device to null. */
    @Test
    @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER)
    public void lastLeAudioDisconnected_clearLeAudioActive() {
        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
        when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice);

        leAudioConnected(mLeAudioDevice);
        mTestLooper.dispatchAll();
        verify(mLeAudioService).setActiveDevice(mLeAudioDevice);

        leAudioDisconnected(mLeAudioDevice);
        mTestLooper.dispatchAll();
        verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
        verify(mLeAudioService).deviceDisconnected(mLeAudioDevice, false);
    }

    /** One LE Audio is connected and disconnected later. Should then set active device to null. */
    @Test
    @DisableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER)
    public void lastLeAudioDisconnected_clearLeAudioActive_NoFixDisconnectFlag() {
        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
        when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice);

        leAudioConnected(mLeAudioDevice);
        mTestLooper.dispatchAll();
        verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
@@ -857,11 +882,16 @@ public class ActiveDeviceManagerTest {
    }

    /**
     * Two LE Audio are connected and the current active is then disconnected. Should then set
     * active device to fallback device.
     * Two LE Audio Sets are connected and the current active Set is disconnected. The other
     * connected LeAudio Set shall become an active device.
     */
    @Test
    public void leAudioSecondDeviceDisconnected_fallbackDeviceActive() {
        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
        when(mLeAudioService.getGroupId(mLeAudioDevice2)).thenReturn(2);
        when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice);
        when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice2);

        leAudioConnected(mLeAudioDevice);
        mTestLooper.dispatchAll();
        verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
@@ -1101,6 +1131,7 @@ public class ActiveDeviceManagerTest {
    @Test
    public void leAudioAndA2dpConnectedThenA2dpDisconnected_fallbackToLeAudio() {
        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);

        leAudioConnected(mLeAudioDevice);
        mTestLooper.dispatchAll();
@@ -1143,13 +1174,13 @@ public class ActiveDeviceManagerTest {
    }

    /**
     * An LE Audio set connected. The active bud disconnected. Set active device returns false
     * indicating an issue (the other bud is also disconnected). Then the active device should be
     * removed and hasFallback should be set to false.
     * An LE Audio set connected. The active bud disconnected. Active device manager should not
     * choose other set member as active device.
     */
    @Test
    public void leAudioSetConnectedThenActiveOneDisconnected_noFallback() {
        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
        when(mLeAudioService.getLeadDevice(any())).thenReturn(mLeAudioDevice);

        leAudioConnected(mLeAudioDevice);
        mTestLooper.dispatchAll();
@@ -1157,71 +1188,76 @@ public class ActiveDeviceManagerTest {

        leAudioConnected(mLeAudioDevice2);
        mTestLooper.dispatchAll();
        verify(mLeAudioService).setActiveDevice(mLeAudioDevice2);

        Mockito.clearInvocations(mLeAudioService);

        // Return false to indicate an issue when setting new active device
        // (e.g. the other device disconnected as well).
        when(mLeAudioService.setActiveDevice(any())).thenReturn(false);
        verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice2);

        leAudioDisconnected(mLeAudioDevice2);
        mTestLooper.dispatchAll();
        verify(mLeAudioService).removeActiveDevice(false);
        verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
        verify(mLeAudioService).deviceDisconnected(mLeAudioDevice2, false);
    }

    /**
     * An LE Audio set connected. The active bud disconnected. Set active device returns true
     * indicating the other bud is going to be the active device. Then the active device should
     * change and hasFallback should be set to true.
     */
    @Test
    public void leAudioSetConnectedThenActiveOneDisconnected_hasFallback() {
    @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER)
    public void leAudioSetConnectedGroupThenDisconnected_noFallback() {
        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);

        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
        when(mLeAudioService.getGroupId(mLeAudioDevice2)).thenReturn(1);
        when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice);
        when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice);

        InOrder order = inOrder(mLeAudioService);

        leAudioConnected(mLeAudioDevice);
        mTestLooper.dispatchAll();
        verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
        order.verify(mLeAudioService).setActiveDevice(mLeAudioDevice);

        leAudioConnected(mLeAudioDevice2);
        mTestLooper.dispatchAll();
        verify(mLeAudioService).setActiveDevice(mLeAudioDevice2);

        Mockito.clearInvocations(mLeAudioService);
        order.verify(mLeAudioService, never()).setActiveDevice(any());

        leAudioDisconnected(mLeAudioDevice2);
        mTestLooper.dispatchAll();
        verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
        verify(mLeAudioService).deviceDisconnected(mLeAudioDevice2, true);
        order.verify(mLeAudioService, never()).setActiveDevice(any());
        order.verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
        order.verify(mLeAudioService).deviceDisconnected(mLeAudioDevice2, false);

        leAudioDisconnected(mLeAudioDevice);
        mTestLooper.dispatchAll();
        order.verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
        order.verify(mLeAudioService).deviceDisconnected(mLeAudioDevice, false);
    }

    @Test
    public void leAudioSetConnectedGroupThenDisconnected_noFallback() {
    @DisableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER)
    public void leAudioSetConnectedGroupThenDisconnected_noFallback_NoFixDisconnectFlag() {
        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);

        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1);
        when(mLeAudioService.getGroupId(mLeAudioDevice2)).thenReturn(1);
        when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice);
        when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice);

        InOrder order = inOrder(mLeAudioService);

        leAudioConnected(mLeAudioDevice);
        mTestLooper.dispatchAll();
        verify(mLeAudioService).setActiveDevice(mLeAudioDevice);

        Mockito.clearInvocations(mLeAudioService);
        order.verify(mLeAudioService).setActiveDevice(mLeAudioDevice);

        when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice);
        leAudioConnected(mLeAudioDevice2);
        mTestLooper.dispatchAll();
        verify(mLeAudioService, never()).setActiveDevice(any());

        Mockito.clearInvocations(mLeAudioService);
        order.verify(mLeAudioService, never()).setActiveDevice(any());

        leAudioDisconnected(mLeAudioDevice2);
        mTestLooper.dispatchAll();
        verify(mLeAudioService, never()).setActiveDevice(any());
        verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
        order.verify(mLeAudioService, never()).setActiveDevice(any());
        order.verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
        order.verify(mLeAudioService).deviceDisconnected(mLeAudioDevice2, false);

        leAudioDisconnected(mLeAudioDevice);
        mTestLooper.dispatchAll();
        verify(mLeAudioService).removeActiveDevice(false);
        verify(mLeAudioService).deviceDisconnected(mLeAudioDevice2, false);
        order.verify(mLeAudioService).removeActiveDevice(false);
        order.verify(mLeAudioService).deviceDisconnected(mLeAudioDevice, false);
    }

    /**
@@ -1255,6 +1291,11 @@ public class ActiveDeviceManagerTest {
    @Test
    @EnableFlags(Flags.FLAG_ADM_VERIFY_ACTIVE_FALLBACK_DEVICE)
    public void sameDeviceAsAshaAndLeAudio_noFallbackOnSwitch() {
        /* Dual mode ASHA/LeAudio device from group 1 */
        when(mLeAudioService.getGroupId(mHearingAidDevice)).thenReturn(1);
        /* Different LeAudio only device from group 2 */
        when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(2);

        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);

        /* Connect first device as ASHA */
@@ -1262,11 +1303,12 @@ public class ActiveDeviceManagerTest {
        mTestLooper.dispatchAll();
        verify(mHearingAidService).setActiveDevice(mHearingAidDevice);

        /* Connect first device as LE Audio */
        leAudioConnected(mHearingAidDevice);
        /* Disconnect ASHA and connect first device as LE Audio */
        hearingAidDisconnected(mHearingAidDevice);
        mTestLooper.dispatchAll();
        verify(mHearingAidService).removeActiveDevice(false);
        verify(mHearingAidService).removeActiveDevice(true /* stop audio */);
        leAudioConnected(mHearingAidDevice);
        mTestLooper.dispatchAll();
        verify(mLeAudioService).setActiveDevice(mHearingAidDevice);

        /* Connect second device as LE Audio. First device is disconnected with fallback to