Add CachedBluetoothDeviceManager test case for csip set member device logic
The patch contains two topic.
1. Add test case for csip set member device add/remove logic
2. Fix clearNonBondedDevices issue. Remove object from set as iterating, it would cause ConcurrentModificationException, make a copy as iterating to achieve the safe remove.
Bug: 205507889
Bug: 150670922
Bug: 178981521
Test: make -j50 RunSettingsLibRoboTests ROBOTEST_FILTER=CachedBluetoothDeviceManagerTest
Change-Id: I3fa3fd9a9e13e011ce2ce01a8142511c86ccbf1c
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index a602866..3debd9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -230,9 +230,10 @@
private void clearNonBondedSubDevices() {
for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
- final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+ Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
if (!memberDevices.isEmpty()) {
- for (CachedBluetoothDevice memberDevice : memberDevices) {
+ for (Object it : memberDevices.toArray()) {
+ CachedBluetoothDevice memberDevice = (CachedBluetoothDevice) it;
// Member device exists and it is not bonded
if (memberDevice.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
cachedDevice.removeMemberDevice(memberDevice);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 2c4f57f..d53a3e8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -26,7 +26,9 @@
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothUuid;
import android.content.Context;
+import android.os.ParcelUuid;
import org.junit.Before;
import org.junit.Test;
@@ -37,6 +39,8 @@
import org.robolectric.RuntimeEnvironment;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceManagerTest {
@@ -51,6 +55,10 @@
private final static String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33";
private final static long HISYNCID1 = 10;
private final static long HISYNCID2 = 11;
+ private final static Map<Integer, ParcelUuid> CAP_GROUP1 =
+ Map.of(1, BluetoothUuid.CAP);
+ private final static Map<Integer, ParcelUuid> CAP_GROUP2 =
+ Map.of(2, BluetoothUuid.CAP);
private final BluetoothClass DEVICE_CLASS_1 =
new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
private final BluetoothClass DEVICE_CLASS_2 =
@@ -70,6 +78,8 @@
@Mock
private HearingAidProfile mHearingAidProfile;
@Mock
+ private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
+ @Mock
private BluetoothDevice mDevice1;
@Mock
private BluetoothDevice mDevice2;
@@ -105,8 +115,12 @@
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mPanProfile.isProfileReady()).thenReturn(true);
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
+ when(mCsipSetCoordinatorProfile.isProfileReady())
+ .thenReturn(true);
doAnswer((invocation) -> mHearingAidProfile).
when(mLocalProfileManager).getHearingAidProfile();
+ doAnswer((invocation) -> mCsipSetCoordinatorProfile)
+ .when(mLocalProfileManager).getCsipSetCoordinatorProfile();
mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
@@ -298,7 +312,7 @@
* Test to verify OnDeviceUnpaired() for main hearing Aid device unpair.
*/
@Test
- public void onDeviceUnpaired_unpairMainDevice() {
+ public void onDeviceUnpaired_unpairHearingAidMainDevice() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -398,4 +412,153 @@
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isTrue();
}
+
+ /**
+ * Test to verify getMemberDevice(), new device has the same group id.
+ */
+ @Test
+ public void addDevice_sameGroupId_validMemberDevice() {
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice1);
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice2);
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice3);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+ CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3);
+
+ assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice2);
+ assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice3);
+ }
+
+ /**
+ * Test to verify getMemberDevice(), new device has the different group id.
+ */
+ @Test
+ public void addDevice_differentGroupId_validMemberDevice() {
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice1);
+ doAnswer((invocation) -> CAP_GROUP2).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice2);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+
+ assertThat(cachedDevice1.getMemberDevice()).isEmpty();
+ }
+
+ /**
+ * Test to verify addDevice(), new device has the same group id.
+ */
+ @Test
+ public void addDevice_sameGroupId_validCachedDevices_mainDevicesAdded_memberDevicesNotAdded() {
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice1);
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice2);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+
+ Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).contains(cachedDevice1);
+ assertThat(devices).doesNotContain(cachedDevice2);
+ }
+
+ /**
+ * Test to verify addDevice(), new device has the different group id.
+ */
+ @Test
+ public void addDevice_differentGroupId_validCachedDevices_bothAdded() {
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice1);
+ doAnswer((invocation) -> CAP_GROUP2).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice2);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+
+ Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).contains(cachedDevice1);
+ assertThat(devices).contains(cachedDevice2);
+ }
+
+ /**
+ * Test to verify clearNonBondedDevices() for csip set member device.
+ */
+ @Test
+ public void clearNonBondedDevices_nonBondedMemberDevice() {
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ when(mDevice3.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+ CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3);
+ cachedDevice1.setMemberDevice(cachedDevice2);
+ cachedDevice1.setMemberDevice(cachedDevice3);
+
+ assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice2);
+ assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice3);
+ mCachedDeviceManager.clearNonBondedDevices();
+
+ assertThat(cachedDevice1.getMemberDevice().contains(cachedDevice2)).isFalse();
+ assertThat(cachedDevice1.getMemberDevice().contains(cachedDevice3)).isTrue();
+ }
+
+ /**
+ * Test to verify OnDeviceUnpaired() for csip device unpair.
+ */
+ @Test
+ public void onDeviceUnpaired_unpairCsipMainDevice() {
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+ cachedDevice1.setGroupId(1);
+ cachedDevice2.setGroupId(1);
+ cachedDevice1.setMemberDevice(cachedDevice2);
+
+ // Call onDeviceUnpaired for the one in mCachedDevices.
+ mCachedDeviceManager.onDeviceUnpaired(cachedDevice1);
+ verify(mDevice2).removeBond();
+ }
+
+ /**
+ * Test to verify OnDeviceUnpaired() for csip device unpair.
+ */
+ @Test
+ public void onDeviceUnpaired_unpairCsipSubDevice() {
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+ cachedDevice1.setGroupId(1);
+ cachedDevice2.setGroupId(1);
+ cachedDevice1.setMemberDevice(cachedDevice2);
+
+ // Call onDeviceUnpaired for the one in mCachedDevices.
+ mCachedDeviceManager.onDeviceUnpaired(cachedDevice2);
+ verify(mDevice1).removeBond();
+ }
+
+ /**
+ * Test to verify isSubDevice_validSubDevice().
+ */
+ @Test
+ public void isSubDevice_validMemberDevice() {
+ doReturn(CAP_GROUP1).when(mCsipSetCoordinatorProfile).getGroupUuidMapByDevice(mDevice1);
+ mCachedDeviceManager.addDevice(mDevice1);
+
+ // Both device are not sub device in default value.
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isFalse();
+
+ // Add Device-2 as device with Device-1 with the same group id, and add Device-3 with
+ // the different group id.
+ doReturn(CAP_GROUP1).when(mCsipSetCoordinatorProfile).getGroupUuidMapByDevice(mDevice2);
+ doReturn(CAP_GROUP2).when(mCsipSetCoordinatorProfile).getGroupUuidMapByDevice(mDevice3);
+ mCachedDeviceManager.addDevice(mDevice2);
+ mCachedDeviceManager.addDevice(mDevice3);
+
+ // Verify Device-2 is sub device, but Device-1, and Device-3 is not.
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isTrue();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice3)).isFalse();
+ }
}