Merge "[Hide DCK devices] Implementation to determine check is a Bluetooth device is exclusively managed." into main
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index f7f0673..09b1eaf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -9,6 +9,7 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -24,6 +25,7 @@
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.core.graphics.drawable.IconCompat;
@@ -31,9 +33,12 @@
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.settingslib.widget.AdaptiveOutlineDrawable;
+import com.google.common.collect.ImmutableSet;
+
import java.io.IOException;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -47,6 +52,8 @@
public static final String BT_ADVANCED_HEADER_ENABLED = "bt_advanced_header_enabled";
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
private static final String KEY_HEARABLE_CONTROL_SLICE = "HEARABLE_CONTROL_SLICE_WITH_WIDTH";
+ private static final Set<String> EXCLUSIVE_MANAGERS = ImmutableSet.of(
+ "com.google.android.gms.dck");
private static ErrorListener sErrorListener;
@@ -647,4 +654,66 @@
private static String generateExpressionWithTag(String tag, String value) {
return getTagStart(tag) + value + getTagEnd(tag);
}
+
+ /**
+ * Returns the BluetoothDevice's exclusive manager
+ * ({@link BluetoothDevice.METADATA_EXCLUSIVE_MANAGER} in metadata) if it exists and is in the
+ * given set, otherwise null.
+ */
+ @Nullable
+ private static String getAllowedExclusiveManager(BluetoothDevice bluetoothDevice) {
+ byte[] exclusiveManagerNameBytes = bluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_EXCLUSIVE_MANAGER);
+ if (exclusiveManagerNameBytes == null) {
+ Log.d(TAG, "Bluetooth device " + bluetoothDevice.getName()
+ + " doesn't have exclusive manager");
+ return null;
+ }
+ String exclusiveManagerName = new String(exclusiveManagerNameBytes);
+ return getExclusiveManagers().contains(exclusiveManagerName) ? exclusiveManagerName
+ : null;
+ }
+
+ /**
+ * Checks if given package is installed
+ */
+ private static boolean isPackageInstalled(Context context,
+ String packageName) {
+ PackageManager packageManager = context.getPackageManager();
+ try {
+ packageManager.getPackageInfo(packageName, 0);
+ return true;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.d(TAG, "Package " + packageName + " is not installed");
+ }
+ return false;
+ }
+
+ /**
+ * A BluetoothDevice is exclusively managed if
+ * 1) it has field {@link BluetoothDevice.METADATA_EXCLUSIVE_MANAGER} in metadata.
+ * 2) the exclusive manager app name is in the allowlist.
+ * 3) the exclusive manager app is installed.
+ */
+ public static boolean isExclusivelyManagedBluetoothDevice(@NonNull Context context,
+ @NonNull BluetoothDevice bluetoothDevice) {
+ String exclusiveManagerName = getAllowedExclusiveManager(bluetoothDevice);
+ if (exclusiveManagerName == null) {
+ return false;
+ }
+ if (!isPackageInstalled(context, exclusiveManagerName)) {
+ return false;
+ } else {
+ Log.d(TAG, "Found exclusively managed app " + exclusiveManagerName);
+ return true;
+ }
+ }
+
+ /**
+ * Return the allowlist for exclusive manager names.
+ */
+ @NonNull
+ public static Set<String> getExclusiveManagers() {
+ return EXCLUSIVE_MANAGERS;
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index f7ec80b..475a6d6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -17,6 +17,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -24,6 +26,8 @@
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.Uri;
@@ -49,6 +53,8 @@
private BluetoothDevice mBluetoothDevice;
@Mock
private AudioManager mAudioManager;
+ @Mock
+ private PackageManager mPackageManager;
private Context mContext;
private static final String STRING_METADATA = "string_metadata";
@@ -59,6 +65,7 @@
private static final String CONTROL_METADATA =
"<HEARABLE_CONTROL_SLICE_WITH_WIDTH>" + STRING_METADATA
+ "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>";
+ private static final String FAKE_EXCLUSIVE_MANAGER_NAME = "com.fake.name";
@Before
public void setUp() {
@@ -372,4 +379,55 @@
assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
mAudioManager)).isEqualTo(false);
}
+
+ @Test
+ public void isExclusivelyManagedBluetoothDevice_isNotExclusivelyManaged_returnFalse() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
+ null);
+
+ assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
+ mBluetoothDevice)).isEqualTo(false);
+ }
+
+ @Test
+ public void isExclusivelyManagedBluetoothDevice_isNotInAllowList_returnFalse() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
+ FAKE_EXCLUSIVE_MANAGER_NAME.getBytes());
+
+ assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
+ mBluetoothDevice)).isEqualTo(false);
+ }
+
+ @Test
+ public void isExclusivelyManagedBluetoothDevice_packageNotInstalled_returnFalse()
+ throws Exception {
+ final String exclusiveManagerName =
+ BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
+ FAKE_EXCLUSIVE_MANAGER_NAME);
+
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
+ exclusiveManagerName.getBytes());
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager).getPackageInfo(
+ exclusiveManagerName, 0);
+
+ assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
+ mBluetoothDevice)).isEqualTo(false);
+ }
+
+ @Test
+ public void isExclusivelyManagedBluetoothDevice_isExclusivelyManaged_returnTrue()
+ throws Exception {
+ final String exclusiveManagerName =
+ BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
+ FAKE_EXCLUSIVE_MANAGER_NAME);
+
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
+ exclusiveManagerName.getBytes());
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0);
+
+ assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
+ mBluetoothDevice)).isEqualTo(true);
+ }
}