diff options
2 files changed, 127 insertions, 0 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index f7f06739d4b2..09b1eafe4d78 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.BluetoothDevice; 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 android.util.Pair; 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.R; 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 class BluetoothUtils { 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 @@ public class BluetoothUtils { 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 f7ec80b041e9..475a6d65a845 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 @@ package com.android.settingslib.bluetooth; 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 static org.mockito.Mockito.when; 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 @@ public class BluetoothUtilsTest { 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 @@ public class BluetoothUtilsTest { 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 @@ public class BluetoothUtilsTest { 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); + } } |