Loading android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +40 −0 Original line number Diff line number Diff line Loading @@ -1041,6 +1041,23 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac return false; } @GuardedBy("mLock") private boolean areSameGroupMembers(BluetoothDevice firstDevice, BluetoothDevice secondDevice) { if (!Flags.admFixDisconnectOfSetMember()) { /* This function shall return false without the fix flag. */ 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); } /** * TODO: This method can return true when a fallback device for an unrelated profile is found. * Take disconnected profile as an argument, and find the exact fallback device. Also, split Loading Loading @@ -1083,6 +1100,13 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac setLeAudioActiveDevice(null, hasFallbackDevice); } else { Log.d(TAG, "Found a LE hearing aid fallback device: " + device); if (areSameGroupMembers(recentlyRemovedDevice, device)) { Log.d( TAG, "Do nothing, removed device belong to the same group as the" + " fallback device."); return true; } setLeHearingAidActiveDevice(device); setHearingAidActiveDevice(null, hasFallbackDevice); setA2dpActiveDevice(null, hasFallbackDevice); Loading Loading @@ -1152,6 +1176,14 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac setHearingAidActiveDevice(null, true); } else { Log.d(TAG, "Found a LE audio fallback device: " + device); if (areSameGroupMembers(recentlyRemovedDevice, device)) { Log.d( TAG, "Do nothing, removed device belong to the same group as the fallback" + " device."); return true; } if (!setLeAudioActiveDevice(device)) { return false; } Loading Loading @@ -1181,6 +1213,14 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac setHearingAidActiveDevice(null, true); } else { Log.d(TAG, "Found a LE audio fallback device: " + device); if (areSameGroupMembers(recentlyRemovedDevice, device)) { Log.d( TAG, "Do nothing, removed device belong to the same group as the fallback" + " device."); return true; } setLeAudioActiveDevice(device); if (!Utils.isDualModeAudioEnabled()) { setA2dpActiveDevice(null, true); Loading android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java +160 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; Loading Loading @@ -67,6 +68,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; Loading @@ -87,6 +89,7 @@ public class ActiveDeviceManagerTest { private BluetoothDevice mHearingAidDevice; private BluetoothDevice mLeAudioDevice; private BluetoothDevice mLeAudioDevice2; private BluetoothDevice mLeAudioDevice3; private BluetoothDevice mLeHearingAidDevice; private BluetoothDevice mSecondaryAudioDevice; private BluetoothDevice mDualModeAudioDevice; Loading Loading @@ -146,6 +149,7 @@ public class ActiveDeviceManagerTest { mSecondaryAudioDevice = TestUtils.getTestDevice(mAdapter, 6); mDualModeAudioDevice = TestUtils.getTestDevice(mAdapter, 7); mLeAudioDevice2 = TestUtils.getTestDevice(mAdapter, 8); mLeAudioDevice3 = TestUtils.getTestDevice(mAdapter, 9); mDeviceConnectionStack = new ArrayList<>(); mMostRecentDevice = null; mOriginalDualModeAudioState = Utils.isDualModeAudioEnabled(); Loading @@ -161,6 +165,7 @@ public class ActiveDeviceManagerTest { when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice); when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice2); when(mLeAudioService.getLeadDevice(mLeAudioDevice3)).thenReturn(mLeAudioDevice3); when(mLeAudioService.getLeadDevice(mDualModeAudioDevice)).thenReturn(mDualModeAudioDevice); when(mLeAudioService.getLeadDevice(mLeHearingAidDevice)).thenReturn(mLeHearingAidDevice); Loading Loading @@ -839,6 +844,161 @@ public class ActiveDeviceManagerTest { verify(mLeAudioService).setActiveDevice(mLeAudioDevice); } /** * One LE Audio set, containing two buds, is connected. When one device got disconnected * fallback device should not be set to true active device to fallback device. */ @Test @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER) public void leAudioSecondDeviceDisconnected_noFallbackDeviceActive_ModeNormal() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); InOrder order = inOrder(mLeAudioService); int groupId = 1; List<BluetoothDevice> groupDevices = List.of(mLeAudioDevice, mLeAudioDevice2); when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice); leAudioConnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice); leAudioConnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); when(mLeAudioService.getGroupId(any())).thenReturn(groupId); when(mLeAudioService.getGroupDevices(groupId)).thenReturn(groupDevices); leAudioDisconnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); } /** * One LE Audio set, containing two buds, is connected. When one device got disconnected * fallback device should not be set to true active device to fallback device. */ @Test @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER) public void leAudioSecondDeviceDisconnected_noFallbackDeviceActive_ModeInCall() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); InOrder order = inOrder(mLeAudioService); int groupId = 1; List<BluetoothDevice> groupDevices = List.of(mLeAudioDevice, mLeAudioDevice2); when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice); leAudioConnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice); leAudioConnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); when(mLeAudioService.getGroupId(any())).thenReturn(groupId); when(mLeAudioService.getGroupDevices(groupId)).thenReturn(groupDevices); leAudioDisconnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); } /** * One LE Audio set, containing two buds, is connected. When one device got disconnected * fallback device should not be set to true active device to fallback device. */ @Test @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER) public void twoLeAudioSets_OneSetDisconnected_FallbackToAnotherOne_ModeNormal() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); InOrder order = inOrder(mLeAudioService); int groupId = 1; List<BluetoothDevice> groupDevices = List.of(mLeAudioDevice, mLeAudioDevice2); when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice); when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(groupId); when(mLeAudioService.getGroupId(mLeAudioDevice2)).thenReturn(groupId); when(mLeAudioService.getGroupDevices(groupId)).thenReturn(groupDevices); int groupId2 = 2; List<BluetoothDevice> groupDevicesId2 = List.of(mLeAudioDevice3); when(mLeAudioService.getGroupId(mLeAudioDevice3)).thenReturn(groupId2); when(mLeAudioService.getGroupDevices(groupId2)).thenReturn(groupDevicesId2); leAudioConnected(mLeAudioDevice3); mTestLooper.dispatchAll(); order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice3); leAudioConnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService).setActiveDevice(mLeAudioDevice); leAudioConnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice2); leAudioDisconnected(mLeAudioDevice2); mTestLooper.dispatchAll(); // Should not encrease a number of this call. order.verify(mLeAudioService, never()).setActiveDevice(any()); leAudioDisconnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice3); } /** * One LE Audio set, containing two buds, is connected. When one device got disconnected * fallback device should not be set to true active device to fallback device. */ @Test @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER) public void twoLeAudioSets_OneSetDisconnected_FallbackToAnotherOne_ModeInCall() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); InOrder order = inOrder(mLeAudioService); int groupId = 1; List<BluetoothDevice> groupDevices = List.of(mLeAudioDevice, mLeAudioDevice2); when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice); when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(groupId); when(mLeAudioService.getGroupId(mLeAudioDevice2)).thenReturn(groupId); when(mLeAudioService.getGroupDevices(groupId)).thenReturn(groupDevices); int groupId2 = 2; List<BluetoothDevice> groupDevicesId2 = List.of(mLeAudioDevice3); when(mLeAudioService.getGroupId(mLeAudioDevice3)).thenReturn(groupId2); when(mLeAudioService.getGroupDevices(groupId2)).thenReturn(groupDevicesId2); leAudioConnected(mLeAudioDevice3); mTestLooper.dispatchAll(); order.verify(mLeAudioService).setActiveDevice(mLeAudioDevice3); leAudioConnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService).setActiveDevice(mLeAudioDevice); leAudioConnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); leAudioDisconnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); leAudioDisconnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice3); } /** A combo (A2DP + Headset) device is connected. Then an LE Audio is connected. */ @Test public void leAudioActive_clearA2dpAndHeadsetActive() { Loading Loading
android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +40 −0 Original line number Diff line number Diff line Loading @@ -1041,6 +1041,23 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac return false; } @GuardedBy("mLock") private boolean areSameGroupMembers(BluetoothDevice firstDevice, BluetoothDevice secondDevice) { if (!Flags.admFixDisconnectOfSetMember()) { /* This function shall return false without the fix flag. */ 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); } /** * TODO: This method can return true when a fallback device for an unrelated profile is found. * Take disconnected profile as an argument, and find the exact fallback device. Also, split Loading Loading @@ -1083,6 +1100,13 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac setLeAudioActiveDevice(null, hasFallbackDevice); } else { Log.d(TAG, "Found a LE hearing aid fallback device: " + device); if (areSameGroupMembers(recentlyRemovedDevice, device)) { Log.d( TAG, "Do nothing, removed device belong to the same group as the" + " fallback device."); return true; } setLeHearingAidActiveDevice(device); setHearingAidActiveDevice(null, hasFallbackDevice); setA2dpActiveDevice(null, hasFallbackDevice); Loading Loading @@ -1152,6 +1176,14 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac setHearingAidActiveDevice(null, true); } else { Log.d(TAG, "Found a LE audio fallback device: " + device); if (areSameGroupMembers(recentlyRemovedDevice, device)) { Log.d( TAG, "Do nothing, removed device belong to the same group as the fallback" + " device."); return true; } if (!setLeAudioActiveDevice(device)) { return false; } Loading Loading @@ -1181,6 +1213,14 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac setHearingAidActiveDevice(null, true); } else { Log.d(TAG, "Found a LE audio fallback device: " + device); if (areSameGroupMembers(recentlyRemovedDevice, device)) { Log.d( TAG, "Do nothing, removed device belong to the same group as the fallback" + " device."); return true; } setLeAudioActiveDevice(device); if (!Utils.isDualModeAudioEnabled()) { setA2dpActiveDevice(null, true); Loading
android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java +160 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; Loading Loading @@ -67,6 +68,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; Loading @@ -87,6 +89,7 @@ public class ActiveDeviceManagerTest { private BluetoothDevice mHearingAidDevice; private BluetoothDevice mLeAudioDevice; private BluetoothDevice mLeAudioDevice2; private BluetoothDevice mLeAudioDevice3; private BluetoothDevice mLeHearingAidDevice; private BluetoothDevice mSecondaryAudioDevice; private BluetoothDevice mDualModeAudioDevice; Loading Loading @@ -146,6 +149,7 @@ public class ActiveDeviceManagerTest { mSecondaryAudioDevice = TestUtils.getTestDevice(mAdapter, 6); mDualModeAudioDevice = TestUtils.getTestDevice(mAdapter, 7); mLeAudioDevice2 = TestUtils.getTestDevice(mAdapter, 8); mLeAudioDevice3 = TestUtils.getTestDevice(mAdapter, 9); mDeviceConnectionStack = new ArrayList<>(); mMostRecentDevice = null; mOriginalDualModeAudioState = Utils.isDualModeAudioEnabled(); Loading @@ -161,6 +165,7 @@ public class ActiveDeviceManagerTest { when(mLeAudioService.getLeadDevice(mLeAudioDevice)).thenReturn(mLeAudioDevice); when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice2); when(mLeAudioService.getLeadDevice(mLeAudioDevice3)).thenReturn(mLeAudioDevice3); when(mLeAudioService.getLeadDevice(mDualModeAudioDevice)).thenReturn(mDualModeAudioDevice); when(mLeAudioService.getLeadDevice(mLeHearingAidDevice)).thenReturn(mLeHearingAidDevice); Loading Loading @@ -839,6 +844,161 @@ public class ActiveDeviceManagerTest { verify(mLeAudioService).setActiveDevice(mLeAudioDevice); } /** * One LE Audio set, containing two buds, is connected. When one device got disconnected * fallback device should not be set to true active device to fallback device. */ @Test @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER) public void leAudioSecondDeviceDisconnected_noFallbackDeviceActive_ModeNormal() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); InOrder order = inOrder(mLeAudioService); int groupId = 1; List<BluetoothDevice> groupDevices = List.of(mLeAudioDevice, mLeAudioDevice2); when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice); leAudioConnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice); leAudioConnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); when(mLeAudioService.getGroupId(any())).thenReturn(groupId); when(mLeAudioService.getGroupDevices(groupId)).thenReturn(groupDevices); leAudioDisconnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); } /** * One LE Audio set, containing two buds, is connected. When one device got disconnected * fallback device should not be set to true active device to fallback device. */ @Test @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER) public void leAudioSecondDeviceDisconnected_noFallbackDeviceActive_ModeInCall() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); InOrder order = inOrder(mLeAudioService); int groupId = 1; List<BluetoothDevice> groupDevices = List.of(mLeAudioDevice, mLeAudioDevice2); when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice); leAudioConnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice); leAudioConnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); when(mLeAudioService.getGroupId(any())).thenReturn(groupId); when(mLeAudioService.getGroupDevices(groupId)).thenReturn(groupDevices); leAudioDisconnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); } /** * One LE Audio set, containing two buds, is connected. When one device got disconnected * fallback device should not be set to true active device to fallback device. */ @Test @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER) public void twoLeAudioSets_OneSetDisconnected_FallbackToAnotherOne_ModeNormal() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL); InOrder order = inOrder(mLeAudioService); int groupId = 1; List<BluetoothDevice> groupDevices = List.of(mLeAudioDevice, mLeAudioDevice2); when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice); when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(groupId); when(mLeAudioService.getGroupId(mLeAudioDevice2)).thenReturn(groupId); when(mLeAudioService.getGroupDevices(groupId)).thenReturn(groupDevices); int groupId2 = 2; List<BluetoothDevice> groupDevicesId2 = List.of(mLeAudioDevice3); when(mLeAudioService.getGroupId(mLeAudioDevice3)).thenReturn(groupId2); when(mLeAudioService.getGroupDevices(groupId2)).thenReturn(groupDevicesId2); leAudioConnected(mLeAudioDevice3); mTestLooper.dispatchAll(); order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice3); leAudioConnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService).setActiveDevice(mLeAudioDevice); leAudioConnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice2); leAudioDisconnected(mLeAudioDevice2); mTestLooper.dispatchAll(); // Should not encrease a number of this call. order.verify(mLeAudioService, never()).setActiveDevice(any()); leAudioDisconnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice3); } /** * One LE Audio set, containing two buds, is connected. When one device got disconnected * fallback device should not be set to true active device to fallback device. */ @Test @EnableFlags(Flags.FLAG_ADM_FIX_DISCONNECT_OF_SET_MEMBER) public void twoLeAudioSets_OneSetDisconnected_FallbackToAnotherOne_ModeInCall() { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); InOrder order = inOrder(mLeAudioService); int groupId = 1; List<BluetoothDevice> groupDevices = List.of(mLeAudioDevice, mLeAudioDevice2); when(mLeAudioService.getLeadDevice(mLeAudioDevice2)).thenReturn(mLeAudioDevice); when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(groupId); when(mLeAudioService.getGroupId(mLeAudioDevice2)).thenReturn(groupId); when(mLeAudioService.getGroupDevices(groupId)).thenReturn(groupDevices); int groupId2 = 2; List<BluetoothDevice> groupDevicesId2 = List.of(mLeAudioDevice3); when(mLeAudioService.getGroupId(mLeAudioDevice3)).thenReturn(groupId2); when(mLeAudioService.getGroupDevices(groupId2)).thenReturn(groupDevicesId2); leAudioConnected(mLeAudioDevice3); mTestLooper.dispatchAll(); order.verify(mLeAudioService).setActiveDevice(mLeAudioDevice3); leAudioConnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService).setActiveDevice(mLeAudioDevice); leAudioConnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); leAudioDisconnected(mLeAudioDevice2); mTestLooper.dispatchAll(); order.verify(mLeAudioService, never()).setActiveDevice(any()); leAudioDisconnected(mLeAudioDevice); mTestLooper.dispatchAll(); order.verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice3); } /** A combo (A2DP + Headset) device is connected. Then an LE Audio is connected. */ @Test public void leAudioActive_clearA2dpAndHeadsetActive() { Loading