summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java48
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java35
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java155
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java40
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java84
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java15
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java88
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java26
8 files changed, 456 insertions, 35 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index b4afb7d8cd4c..7374f80fd9db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -444,12 +444,23 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
/**
- * @return {@code true} if {@code cachedBluetoothDevice} is hearing aid device
+ * @return {@code true} if {@code cachedBluetoothDevice} is hearing aid device.
+ * @deprecated use {@link #isHearingDevice() }
+ * // TODO: b/385679160 - Target to deprecate it and replace with #isHearingDevice()
*/
+ @Deprecated
public boolean isHearingAidDevice() {
return mHearingAidInfo != null;
}
+ /**
+ * @return {@code true} if {@code cachedBluetoothDevice} support any of hearing device profile.
+ */
+ public boolean isHearingDevice() {
+ return getProfiles().stream().anyMatch(
+ p -> (p instanceof HearingAidProfile || p instanceof HapClientProfile));
+ }
+
public int getDeviceSide() {
return mHearingAidInfo != null
? mHearingAidInfo.getSide() : HearingAidInfo.DeviceSide.SIDE_INVALID;
@@ -910,12 +921,33 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
}
+ /**
+ * Checks if the device is connected to the specified Bluetooth profile.
+ *
+ * @param profile The Bluetooth profile to check.
+ * @return {@code true} if the device is connected to the profile.
+ */
public boolean isConnectedProfile(LocalBluetoothProfile profile) {
int status = getProfileConnectionState(profile);
return status == BluetoothProfile.STATE_CONNECTED;
}
+ /**
+ * Checks if the device is connected to the Bluetooth profile with the given ID.
+ *
+ * @param profileId The ID of the Bluetooth profile to check.
+ * @return {@code true} if the device is connected to the profile.
+ */
+ public boolean isConnectedProfile(int profileId) {
+ for (LocalBluetoothProfile profile : getProfiles()) {
+ if (profile.getProfileId() == profileId) {
+ return isConnectedProfile(profile);
+ }
+ }
+ return false;
+ }
+
public boolean isBusy() {
synchronized (mProfileLock) {
for (LocalBluetoothProfile profile : mProfiles) {
@@ -1891,13 +1923,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
/**
- * @return {@code true} if {@code cachedBluetoothDevice} is LeAudio hearing aid device
- */
- public boolean isConnectedLeAudioHearingAidDevice() {
- return isConnectedHapClientDevice() && isConnectedLeAudioDevice();
- }
-
- /**
* @return {@code true} if {@code cachedBluetoothDevice} is hearing aid device
*
* The device may be an ASHA hearing aid that supports {@link HearingAidProfile} or a LeAudio
@@ -1908,6 +1933,13 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
/**
+ * @return {@code true} if {@code cachedBluetoothDevice} is LeAudio hearing aid device
+ */
+ public boolean isConnectedLeAudioHearingAidDevice() {
+ return isConnectedHapClientDevice() && isConnectedLeAudioDevice();
+ }
+
+ /**
* @return {@code true} if {@code cachedBluetoothDevice} is LeAudio device
*/
public boolean isConnectedLeAudioDevice() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index b754706fb9a1..69492d5040db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -24,7 +24,10 @@ import android.bluetooth.le.ScanFilter;
import android.content.Context;
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.flags.Flags;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -46,7 +49,7 @@ public class CachedBluetoothDeviceManager {
private final LocalBluetoothManager mBtManager;
@VisibleForTesting
- final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>();
+ final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>();
@VisibleForTesting
HearingAidDeviceManager mHearingAidDeviceManager;
@VisibleForTesting
@@ -192,6 +195,20 @@ public class CachedBluetoothDeviceManager {
}
/**
+ * Notifies the connection status if device is hearing device.
+ *
+ * @param device The {@link CachedBluetoothDevice} need to be hearing device
+ */
+ public synchronized void notifyHearingDevicesConnectionStatusChangedIfNeeded(
+ @NonNull CachedBluetoothDevice device) {
+ if (!device.isHearingDevice()) {
+ return;
+ }
+
+ mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+ }
+
+ /**
* Search for existing sub device {@link CachedBluetoothDevice}.
*
* @param device the address of the Bluetooth device
@@ -388,8 +405,14 @@ public class CachedBluetoothDeviceManager {
/** Handles when the device been set as active/inactive. */
public synchronized void onActiveDeviceChanged(CachedBluetoothDevice cachedBluetoothDevice) {
- if (cachedBluetoothDevice.isHearingAidDevice()) {
+ if (cachedBluetoothDevice == null) {
+ return;
+ }
+ if (cachedBluetoothDevice.isHearingDevice()) {
mHearingAidDeviceManager.onActiveDeviceChanged(cachedBluetoothDevice);
+ if (Flags.hearingDeviceSetConnectionStatusReport()) {
+ mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+ }
}
}
@@ -421,6 +444,14 @@ public class CachedBluetoothDeviceManager {
mainDevice.unpair();
mainDevice.setSubDevice(null);
}
+
+ // TODO: b/386121967 - Should change to use isHearingDevice but mProfile get clear here.
+ // Need to consider where to put this logic when using isHearingDevice()
+ if (device.isHearingAidDevice()) {
+ if (Flags.hearingDeviceSetConnectionStatusReport()) {
+ mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+ }
+ }
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index ad34e837f508..2e9cd30eedc4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -15,7 +15,10 @@
*/
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothDevice.BOND_BONDED;
+
import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHapClient;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
@@ -30,15 +33,22 @@ import android.provider.Settings;
import android.util.FeatureFlagUtils;
import android.util.Log;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.collection.ArraySet;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants.RoutingValue;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
/**
- * HearingAidDeviceManager manages the set of remote HearingAid(ASHA) Bluetooth devices.
+ * HearingAidDeviceManager manages the set of remote bluetooth hearing devices.
*/
public class HearingAidDeviceManager {
private static final String TAG = "HearingAidDeviceManager";
@@ -49,6 +59,10 @@ public class HearingAidDeviceManager {
private final LocalBluetoothManager mBtManager;
private final List<CachedBluetoothDevice> mCachedDevices;
private final HearingAidAudioRoutingHelper mRoutingHelper;
+ @ConnectionStatus
+ private int mDevicesConnectionStatus = ConnectionStatus.NO_DEVICE_BONDED;
+ private boolean mInitialDevicesConnectionStatusUpdate = false;
+
HearingAidDeviceManager(Context context, LocalBluetoothManager localBtManager,
List<CachedBluetoothDevice> CachedDevices) {
mContext = context;
@@ -68,6 +82,145 @@ public class HearingAidDeviceManager {
mRoutingHelper = routingHelper;
}
+ /**
+ * Defines the connection status for hearing devices.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ ConnectionStatus.NO_DEVICE_BONDED,
+ ConnectionStatus.DISCONNECTED,
+ ConnectionStatus.CONNECTED,
+ ConnectionStatus.CONNECTING_OR_DISCONNECTING,
+ ConnectionStatus.ACTIVE
+ })
+ public @interface ConnectionStatus {
+ int NO_DEVICE_BONDED = -1;
+ int DISCONNECTED = 0;
+ int CONNECTED = 1;
+ int CONNECTING_OR_DISCONNECTING = 2;
+ int ACTIVE = 3;
+ }
+
+ /**
+ * Updates the connection status of the hearing devices based on the currently bonded
+ * hearing aid devices.
+ */
+ synchronized void notifyDevicesConnectionStatusChanged() {
+ updateDevicesConnectionStatus();
+ // TODO: b/357882387 - notify connection status changes for the callers
+ }
+
+ private void updateDevicesConnectionStatus() {
+ mInitialDevicesConnectionStatusUpdate = true;
+ // Add all hearing devices including sub and member into a set.
+ Set<CachedBluetoothDevice> allHearingDevices = mCachedDevices.stream()
+ .filter(d -> d.getBondState() == BluetoothDevice.BOND_BONDED
+ && d.isHearingDevice())
+ .flatMap(d -> getAssociatedCachedDevice(d).stream())
+ .collect(Collectors.toSet());
+
+ // Status sequence matters here. If one of the hearing devices is in previous
+ // ConnectionStatus, we will treat whole hearing devices is in this status.
+ // E.g. One of hearing device is in CONNECTED status and another is in DISCONNECTED
+ // status, the hearing devices connection status will notify CONNECTED status.
+ if (isConnectingOrDisconnectingConnectionStatus(allHearingDevices)) {
+ mDevicesConnectionStatus = ConnectionStatus.CONNECTING_OR_DISCONNECTING;
+ } else if (isActiveConnectionStatus(allHearingDevices)) {
+ mDevicesConnectionStatus = ConnectionStatus.ACTIVE;
+ } else if (isConnectedStatus(allHearingDevices)) {
+ mDevicesConnectionStatus = ConnectionStatus.CONNECTED;
+ } else if (isDisconnectedStatus(allHearingDevices)) {
+ mDevicesConnectionStatus = ConnectionStatus.DISCONNECTED;
+ } else {
+ mDevicesConnectionStatus = ConnectionStatus.NO_DEVICE_BONDED;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "updateDevicesConnectionStatus: " + mDevicesConnectionStatus);
+ }
+ }
+
+ /**
+ * @return all the related CachedBluetoothDevices for this device.
+ */
+ @NonNull
+ public Set<CachedBluetoothDevice> getAssociatedCachedDevice(
+ @NonNull CachedBluetoothDevice device) {
+ ArraySet<CachedBluetoothDevice> cachedDeviceSet = new ArraySet<>();
+ cachedDeviceSet.add(device);
+ // Associated device should be added into memberDevice if it support CSIP profile.
+ Set<CachedBluetoothDevice> memberDevices = device.getMemberDevice();
+ if (!memberDevices.isEmpty()) {
+ cachedDeviceSet.addAll(memberDevices);
+ return cachedDeviceSet;
+ }
+ // If not support CSIP profile, it should be ASHA hearing device and added into subDevice.
+ CachedBluetoothDevice subDevice = device.getSubDevice();
+ if (subDevice != null) {
+ cachedDeviceSet.add(subDevice);
+ return cachedDeviceSet;
+ }
+
+ return cachedDeviceSet;
+ }
+
+ private boolean isConnectingOrDisconnectingConnectionStatus(
+ Set<CachedBluetoothDevice> devices) {
+ HearingAidProfile hearingAidProfile = mBtManager.getProfileManager().getHearingAidProfile();
+ HapClientProfile hapClientProfile = mBtManager.getProfileManager().getHapClientProfile();
+
+ for (CachedBluetoothDevice device : devices) {
+ if (hearingAidProfile != null) {
+ int status = device.getProfileConnectionState(hearingAidProfile);
+ if (status == BluetoothProfile.STATE_DISCONNECTING
+ || status == BluetoothProfile.STATE_CONNECTING) {
+ return true;
+ }
+ }
+ if (hapClientProfile != null) {
+ int status = device.getProfileConnectionState(hapClientProfile);
+ if (status == BluetoothProfile.STATE_DISCONNECTING
+ || status == BluetoothProfile.STATE_CONNECTING) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isActiveConnectionStatus(Set<CachedBluetoothDevice> devices) {
+ for (CachedBluetoothDevice device : devices) {
+ if ((device.isActiveDevice(BluetoothProfile.HEARING_AID)
+ && device.isConnectedProfile(BluetoothProfile.HEARING_AID))
+ || (device.isActiveDevice(BluetoothProfile.LE_AUDIO)
+ && device.isConnectedProfile(BluetoothProfile.LE_AUDIO))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isConnectedStatus(Set<CachedBluetoothDevice> devices) {
+ return devices.stream().anyMatch(CachedBluetoothDevice::isConnected);
+ }
+
+ private boolean isDisconnectedStatus(Set<CachedBluetoothDevice> devices) {
+ return devices.stream().anyMatch(
+ d -> (!d.isConnected() && d.getBondState() == BOND_BONDED));
+ }
+
+ /**
+ * Gets the connection status for hearing device set. Will update connection status first if
+ * never updated.
+ */
+ @ConnectionStatus
+ public int getDevicesConnectionStatus() {
+ if (!mInitialDevicesConnectionStatusUpdate) {
+ updateDevicesConnectionStatus();
+ }
+ return mDevicesConnectionStatus;
+ }
+
void initHearingAidDeviceIfNeeded(CachedBluetoothDevice newDevice,
List<ScanFilter> leScanFilters) {
HearingAidInfo info = generateHearingAidInfo(newDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 8dfeb559a8b5..7c24df9e9019 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -47,12 +47,14 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
+import com.android.settingslib.flags.Flags;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -345,11 +347,17 @@ public class LocalBluetoothProfileManager {
oldState == BluetoothProfile.STATE_CONNECTING) {
Log.i(TAG, "Failed to connect " + mProfile + " device");
}
+ final boolean isAshaProfile = getHearingAidProfile() != null
+ && mProfile instanceof HearingAidProfile;
+ final boolean isHapClientProfile = getHapClientProfile() != null
+ && mProfile instanceof HapClientProfile;
+ final boolean isLeAudioProfile = getLeAudioProfile() != null
+ && mProfile instanceof LeAudioProfile;
+ final boolean isHapClientOrLeAudioProfile = isHapClientProfile || isLeAudioProfile;
+ final boolean isCsipProfile = getCsipSetCoordinatorProfile() != null
+ && mProfile instanceof CsipSetCoordinatorProfile;
- if (getHearingAidProfile() != null
- && mProfile instanceof HearingAidProfile
- && (newState == BluetoothProfile.STATE_CONNECTED)) {
-
+ if (isAshaProfile && (newState == BluetoothProfile.STATE_CONNECTED)) {
// Check if the HiSyncID has being initialized
if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice());
@@ -366,11 +374,6 @@ public class LocalBluetoothProfileManager {
HearingAidStatsLogUtils.logHearingAidInfo(cachedDevice);
}
- final boolean isHapClientProfile = getHapClientProfile() != null
- && mProfile instanceof HapClientProfile;
- final boolean isLeAudioProfile = getLeAudioProfile() != null
- && mProfile instanceof LeAudioProfile;
- final boolean isHapClientOrLeAudioProfile = isHapClientProfile || isLeAudioProfile;
if (isHapClientOrLeAudioProfile && newState == BluetoothProfile.STATE_CONNECTED) {
// Checks if both profiles are connected to the device. Hearing aid info need
@@ -385,9 +388,7 @@ public class LocalBluetoothProfileManager {
}
}
- if (getCsipSetCoordinatorProfile() != null
- && mProfile instanceof CsipSetCoordinatorProfile
- && newState == BluetoothProfile.STATE_CONNECTED) {
+ if (isCsipProfile && (newState == BluetoothProfile.STATE_CONNECTED)) {
// Check if the GroupID has being initialized
if (cachedDevice.getGroupId() == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
final Map<Integer, ParcelUuid> groupIdMap = getCsipSetCoordinatorProfile()
@@ -403,6 +404,21 @@ public class LocalBluetoothProfileManager {
}
}
+ // LE_AUDIO, CSIP_SET_COORDINATOR profiles will also impact the connection status
+ // change, e.g. device need to active on LE_AUDIO to become active connection status.
+ final Set<Integer> hearingDeviceConnectionStatusProfileId = Set.of(
+ BluetoothProfile.HEARING_AID,
+ BluetoothProfile.HAP_CLIENT,
+ BluetoothProfile.LE_AUDIO,
+ BluetoothProfile.CSIP_SET_COORDINATOR
+ );
+ if (Flags.hearingDeviceSetConnectionStatusReport()) {
+ if (hearingDeviceConnectionStatusProfileId.contains(mProfile.getProfileId())) {
+ mDeviceManager.notifyHearingDevicesConnectionStatusChangedIfNeeded(
+ cachedDevice);
+ }
+ }
+
cachedDevice.onProfileStateChanged(mProfile, newState);
// Dispatch profile changed after device update
boolean needDispatchProfileConnectionState = true;
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 05f471f62f1d..69e99c616910 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
@@ -33,21 +33,37 @@ import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.os.Parcel;
import android.os.ParcelUuid;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceManagerTest {
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
private final static String DEVICE_NAME_1 = "TestName_1";
private final static String DEVICE_NAME_2 = "TestName_2";
private final static String DEVICE_NAME_3 = "TestName_3";
@@ -82,6 +98,8 @@ public class CachedBluetoothDeviceManagerTest {
@Mock
private HearingAidProfile mHearingAidProfile;
@Mock
+ private HapClientProfile mHapClientProfile;
+ @Mock
private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
@Mock
private BluetoothDevice mDevice1;
@@ -89,12 +107,11 @@ public class CachedBluetoothDeviceManagerTest {
private BluetoothDevice mDevice2;
@Mock
private BluetoothDevice mDevice3;
+ private HearingAidDeviceManager mHearingAidDeviceManager;
private CachedBluetoothDevice mCachedDevice1;
private CachedBluetoothDevice mCachedDevice2;
private CachedBluetoothDevice mCachedDevice3;
private CachedBluetoothDeviceManager mCachedDeviceManager;
- private HearingAidDeviceManager mHearingAidDeviceManager;
- private Context mContext;
private BluetoothClass createBtClass(int deviceClass) {
Parcel p = Parcel.obtain();
@@ -108,8 +125,6 @@ public class CachedBluetoothDeviceManagerTest {
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
when(mDevice3.getAddress()).thenReturn(DEVICE_ADDRESS_3);
@@ -129,13 +144,15 @@ public class CachedBluetoothDeviceManagerTest {
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mPanProfile.isProfileReady()).thenReturn(true);
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
+ when(mHapClientProfile.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);
+ mCachedDeviceManager = spy(
+ new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager));
mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
mCachedDevice3 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3));
@@ -621,12 +638,55 @@ public class CachedBluetoothDeviceManagerTest {
public void onActiveDeviceChanged_validHiSyncId_callExpectedFunction() {
doNothing().when(mHearingAidDeviceManager).onActiveDeviceChanged(any());
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
- CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
- cachedDevice1.setHearingAidInfo(
- new HearingAidInfo.Builder().setHiSyncId(HISYNCID1).build());
+ when(mCachedDevice1.getProfiles()).thenReturn(
+ ImmutableList.of(mHapClientProfile, mHearingAidProfile));
+
+ mCachedDeviceManager.onActiveDeviceChanged(mCachedDevice1);
+
+ verify(mHearingAidDeviceManager).onActiveDeviceChanged(mCachedDevice1);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(
+ com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+ public void onActiveDeviceChanged_hearingDevice_callReportConnectionStatus() {
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mCachedDevice1.getProfiles()).thenReturn(
+ ImmutableList.of(mHapClientProfile, mHearingAidProfile));
+
+ mCachedDeviceManager.onActiveDeviceChanged(mCachedDevice1);
+
+ verify(mHearingAidDeviceManager).notifyDevicesConnectionStatusChanged();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(
+ com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+ public void onDeviceUnpaired_hearingDevice_callReportConnectionStatus() {
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mCachedDevice1.getProfiles()).thenReturn(
+ ImmutableList.of(mHapClientProfile, mHearingAidProfile));
+
+ mCachedDeviceManager.onDeviceUnpaired(mCachedDevice1);
+
+ verify(mHearingAidDeviceManager).notifyDevicesConnectionStatusChanged();
+ }
+
+ @Test
+ public void notifyHearingDevicesConnectionStatusChanged_nonHearingDevice_notCallFunction() {
+ when(mCachedDevice1.getProfiles()).thenReturn(List.of(mA2dpProfile));
+
+ mCachedDeviceManager.notifyHearingDevicesConnectionStatusChangedIfNeeded(mCachedDevice1);
+
+ verify(mHearingAidDeviceManager, never()).notifyDevicesConnectionStatusChanged();
+ }
+
+ @Test
+ public void notifyHearingDevicesConnectionStatusChanged_hearingDeviceProfile_callFunction() {
+ when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile));
- mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1);
+ mCachedDeviceManager.notifyHearingDevicesConnectionStatusChangedIfNeeded(mCachedDevice1);
- verify(mHearingAidDeviceManager).onActiveDeviceChanged(cachedDevice1);
+ verify(mHearingAidDeviceManager).notifyDevicesConnectionStatusChanged();
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 30f8a798b674..d933a1ced8bc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -2074,6 +2074,21 @@ public class CachedBluetoothDeviceTest {
assertThat(mCachedDevice.getConnectionSummary(false)).isNull();
}
+ @Test
+ public void isHearingDevice_supportHearingRelatedProfiles_returnTrue() {
+ when(mCachedDevice.getProfiles()).thenReturn(
+ ImmutableList.of(mHapClientProfile, mHearingAidProfile));
+
+ assertThat(mCachedDevice.isHearingDevice()).isTrue();
+ }
+
+ @Test
+ public void isHearingDevice_supportOnlyLeAudioProfile_returnFalse() {
+ when(mCachedDevice.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile));
+
+ assertThat(mCachedDevice.isHearingDevice()).isFalse();
+ }
+
private void updateProfileStatus(LocalBluetoothProfile profile, int status) {
doReturn(status).when(profile).getConnectionStatus(mDevice);
mCachedDevice.onProfileStateChanged(profile, status);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index 2458c5b2eb6e..f126eaf06503 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -54,6 +54,8 @@ import android.util.FeatureFlagUtils;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager.ConnectionStatus;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -140,6 +142,8 @@ public class HearingAidDeviceManagerTest {
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
when(mLocalProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
when(mLocalProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
+ when(mHapClientProfile.getProfileId()).thenReturn(BluetoothProfile.HAP_CLIENT);
+ when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
when(mAudioStrategy.getAudioAttributesForLegacyStreamType(
AudioManager.STREAM_MUSIC))
.thenReturn((new AudioAttributes.Builder()).build());
@@ -826,6 +830,90 @@ public class HearingAidDeviceManagerTest {
verify(mHapClientProfile).selectPreset(mDevice2, PRESET_INDEX_1);
}
+ @Test
+ public void getAssociatedCachedDevice_existSubDevice_returnSize2() {
+ mCachedDevice1.setSubDevice(mCachedDevice2);
+
+ //including self device
+ assertThat(mHearingAidDeviceManager.getAssociatedCachedDevice(
+ mCachedDevice1).size()).isEqualTo(2);
+ }
+
+ @Test
+ public void getAssociatedCachedDevice_existMemberDevice_returnSize2() {
+ mCachedDevice1.addMemberDevice(mCachedDevice2);
+
+ //including self device
+ assertThat(mHearingAidDeviceManager.getAssociatedCachedDevice(
+ mCachedDevice1).size()).isEqualTo(2);
+ }
+
+ @Test
+ public void notifyDevicesConnectionStatusChanged_connecting_connectingStatus() {
+ when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile));
+ when(mHapClientProfile.getConnectionStatus(mDevice1)).thenReturn(
+ BluetoothProfile.STATE_CONNECTING);
+
+ mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+ mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+ assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo(
+ ConnectionStatus.CONNECTING_OR_DISCONNECTING);
+ }
+
+ @Test
+ public void notifyDevicesConnectionStatusChanged_activeConnectedProfile_activeStatus() {
+ when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile, mLeAudioProfile));
+ when(mLeAudioProfile.getConnectionStatus(mDevice1)).thenReturn(
+ BluetoothProfile.STATE_CONNECTED);
+ when(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true);
+
+ mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+ mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+ assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo(
+ ConnectionStatus.ACTIVE);
+ }
+
+ @Test
+ public void notifyDevicesConnectionStatusChanged_isConnected_connectedStatus() {
+ when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile, mLeAudioProfile));
+ when(mCachedDevice1.isConnected()).thenReturn(true);
+
+ mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+ mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+ assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo(
+ ConnectionStatus.CONNECTED);
+ }
+
+ @Test
+ public void notifyDevicesConnectionStatusChanged_bondedNotConnected_disconnectedStatus() {
+ when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mCachedDevice1.isConnected()).thenReturn(false);
+ when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile));
+ mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+
+ mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+ assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo(
+ ConnectionStatus.DISCONNECTED);
+ }
+
+ @Test
+ public void notifyDevicesConnectionStatusChanged_bondNone_noDeviceBondedStatus() {
+ when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+
+ mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+ assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo(
+ ConnectionStatus.NO_DEVICE_BONDED);
+ }
+
private HearingAidInfo getLeftAshaHearingAidInfo(long hiSyncId) {
return new HearingAidInfo.Builder()
.setAshaDeviceSide(HearingAidInfo.DeviceSide.SIDE_LEFT)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index 6ff90ba4b391..219bfe0c8c53 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -37,10 +37,14 @@ import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.Intent;
import android.os.ParcelUuid;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -56,6 +60,9 @@ import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalBluetoothProfileManagerTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final long HISYNCID = 10;
private static final int GROUP_ID = 1;
@@ -305,6 +312,25 @@ public class LocalBluetoothProfileManagerTest {
verify(mCachedBluetoothDevice).refresh();
}
+ @Test
+ @RequiresFlagsEnabled(
+ com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+ public void stateChangedHandler_hapProfileStateChanged_notifyHearingDevicesConnectionStatus() {
+ mShadowBluetoothAdapter.setSupportedProfiles(generateList(
+ new int[] {BluetoothProfile.HAP_CLIENT}));
+ mProfileManager.updateLocalProfiles();
+
+ mIntent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
+ mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
+ mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
+ mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
+
+ mContext.sendBroadcast(mIntent);
+
+ verify(mDeviceManager).notifyHearingDevicesConnectionStatusChangedIfNeeded(
+ mCachedBluetoothDevice);
+ }
+
private List<Integer> generateList(int[] profiles) {
if (profiles == null) {
return null;