Loading android/app/src/com/android/bluetooth/Utils.java +30 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.app.BroadcastOptions; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.companion.AssociationInfo; import android.companion.CompanionDeviceManager; Loading Loading @@ -72,6 +73,7 @@ import androidx.annotation.VisibleForTesting; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.flags.Flags; import org.xmlpull.v1.XmlPullParser; Loading Loading @@ -168,6 +170,34 @@ public final class Utils { isScoManagedByAudioEnabled = enabled; } /** * Checks CoD and metadata to determine if the device is a watch * * @param service Adapter service * @param device the remote device * @return {@code true} if it's a watch, {@code false} otherwise */ public static boolean isWatch( @NonNull AdapterService service, @NonNull BluetoothDevice device) { // Check CoD BluetoothClass deviceClass = new BluetoothClass(service.getRemoteClass(device)); if (deviceClass.getDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) { return true; } // Check metadata DatabaseManager mDbManager = service.getDatabase(); byte[] deviceType = mDbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE); if (deviceType == null) { return false; } String deviceTypeStr = new String(deviceType); if (deviceTypeStr.equals(BluetoothDevice.DEVICE_TYPE_WATCH)) { return true; } return false; } /** * Only exposed for testing, do not invoke this method outside of tests. * Loading android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +1 −28 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.bluetooth.btservice; import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothLeAudio; Loading Loading @@ -361,7 +360,7 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac if (mDbManager.getProfileConnectionPolicy(device, BluetoothProfile.A2DP) != BluetoothProfile.CONNECTION_POLICY_ALLOWED || mAudioManager.getMode() != AudioManager.MODE_NORMAL) { if (isWatch(device)) { if (Utils.isWatch(mAdapterService, device)) { Log.i(TAG, "Do not set hfp active for watch device " + device); return; } Loading Loading @@ -1347,32 +1346,6 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac return BluetoothHearingAid.HI_SYNC_ID_INVALID; } /** * Checks CoD and metadata to determine if the device is a watch * * @param device the remote device * @return {@code true} if it's a watch, {@code false} otherwise */ private boolean isWatch(BluetoothDevice device) { // Check CoD BluetoothClass deviceClass = new BluetoothClass(mAdapterService.getRemoteClass(device)); if (deviceClass.getDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) { return true; } // Check metadata byte[] deviceType = mDbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE); if (deviceType == null) { return false; } String deviceTypeStr = new String(deviceType); if (deviceTypeStr.equals(BluetoothDevice.DEVICE_TYPE_WATCH)) { return true; } return false; } /** * Checks if le audio broadcasting is ON * Loading android/app/src/com/android/bluetooth/hfp/HeadsetService.java +1 −11 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; Loading Loading @@ -2644,16 +2643,7 @@ public class HeadsetService extends ProfileService { List<BluetoothDevice> fallbackCandidates = getConnectedDevices(); List<BluetoothDevice> uninterestedCandidates = new ArrayList<>(); for (BluetoothDevice device : fallbackCandidates) { byte[] deviceType = dbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE); BluetoothClass deviceClass = new BluetoothClass( mAdapterService.getRemoteDevices().getBluetoothClass(device)); if ((deviceClass != null && deviceClass.getMajorDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) || (deviceType != null && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) { if (Utils.isWatch(mAdapterService, device)) { uninterestedCandidates.add(device); } } Loading android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java +22 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; Loading Loading @@ -1236,6 +1237,27 @@ public class HeadsetServiceTest { .containsExactly(deviceRegular); } @Test public void testGetFallbackCandidates_HasWatchDeviceWithCod() { BluetoothDevice deviceWatch = TestUtils.getTestDevice(mAdapter, 0); BluetoothDevice deviceRegular = TestUtils.getTestDevice(mAdapter, 1); // Make deviceWatch as watch with COD when(mAdapterService.getRemoteClass(deviceWatch)) .thenReturn(BluetoothClass.Device.WEARABLE_WRIST_WATCH); when(mAdapterService.getRemoteClass(deviceRegular)) .thenReturn(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE); // Has a connected watch device addConnectedDeviceHelper(deviceWatch); assertThat(mHeadsetService.getFallbackCandidates(mDatabaseManager)).isEmpty(); // Two connected devices with one watch addConnectedDeviceHelper(deviceRegular); assertThat(mHeadsetService.getFallbackCandidates(mDatabaseManager)) .containsExactly(deviceRegular); } @Test public void testConnectDeviceNotAllowedInbandRingPolicy_InbandRingStatus() { when(mDatabaseManager.getProfileConnectionPolicy( Loading Loading
android/app/src/com/android/bluetooth/Utils.java +30 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.app.BroadcastOptions; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.companion.AssociationInfo; import android.companion.CompanionDeviceManager; Loading Loading @@ -72,6 +73,7 @@ import androidx.annotation.VisibleForTesting; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.flags.Flags; import org.xmlpull.v1.XmlPullParser; Loading Loading @@ -168,6 +170,34 @@ public final class Utils { isScoManagedByAudioEnabled = enabled; } /** * Checks CoD and metadata to determine if the device is a watch * * @param service Adapter service * @param device the remote device * @return {@code true} if it's a watch, {@code false} otherwise */ public static boolean isWatch( @NonNull AdapterService service, @NonNull BluetoothDevice device) { // Check CoD BluetoothClass deviceClass = new BluetoothClass(service.getRemoteClass(device)); if (deviceClass.getDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) { return true; } // Check metadata DatabaseManager mDbManager = service.getDatabase(); byte[] deviceType = mDbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE); if (deviceType == null) { return false; } String deviceTypeStr = new String(deviceType); if (deviceTypeStr.equals(BluetoothDevice.DEVICE_TYPE_WATCH)) { return true; } return false; } /** * Only exposed for testing, do not invoke this method outside of tests. * Loading
android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +1 −28 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.bluetooth.btservice; import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothLeAudio; Loading Loading @@ -361,7 +360,7 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac if (mDbManager.getProfileConnectionPolicy(device, BluetoothProfile.A2DP) != BluetoothProfile.CONNECTION_POLICY_ALLOWED || mAudioManager.getMode() != AudioManager.MODE_NORMAL) { if (isWatch(device)) { if (Utils.isWatch(mAdapterService, device)) { Log.i(TAG, "Do not set hfp active for watch device " + device); return; } Loading Loading @@ -1347,32 +1346,6 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac return BluetoothHearingAid.HI_SYNC_ID_INVALID; } /** * Checks CoD and metadata to determine if the device is a watch * * @param device the remote device * @return {@code true} if it's a watch, {@code false} otherwise */ private boolean isWatch(BluetoothDevice device) { // Check CoD BluetoothClass deviceClass = new BluetoothClass(mAdapterService.getRemoteClass(device)); if (deviceClass.getDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) { return true; } // Check metadata byte[] deviceType = mDbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE); if (deviceType == null) { return false; } String deviceTypeStr = new String(deviceType); if (deviceTypeStr.equals(BluetoothDevice.DEVICE_TYPE_WATCH)) { return true; } return false; } /** * Checks if le audio broadcasting is ON * Loading
android/app/src/com/android/bluetooth/hfp/HeadsetService.java +1 −11 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; Loading Loading @@ -2644,16 +2643,7 @@ public class HeadsetService extends ProfileService { List<BluetoothDevice> fallbackCandidates = getConnectedDevices(); List<BluetoothDevice> uninterestedCandidates = new ArrayList<>(); for (BluetoothDevice device : fallbackCandidates) { byte[] deviceType = dbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE); BluetoothClass deviceClass = new BluetoothClass( mAdapterService.getRemoteDevices().getBluetoothClass(device)); if ((deviceClass != null && deviceClass.getMajorDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) || (deviceType != null && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) { if (Utils.isWatch(mAdapterService, device)) { uninterestedCandidates.add(device); } } Loading
android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java +22 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; Loading Loading @@ -1236,6 +1237,27 @@ public class HeadsetServiceTest { .containsExactly(deviceRegular); } @Test public void testGetFallbackCandidates_HasWatchDeviceWithCod() { BluetoothDevice deviceWatch = TestUtils.getTestDevice(mAdapter, 0); BluetoothDevice deviceRegular = TestUtils.getTestDevice(mAdapter, 1); // Make deviceWatch as watch with COD when(mAdapterService.getRemoteClass(deviceWatch)) .thenReturn(BluetoothClass.Device.WEARABLE_WRIST_WATCH); when(mAdapterService.getRemoteClass(deviceRegular)) .thenReturn(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE); // Has a connected watch device addConnectedDeviceHelper(deviceWatch); assertThat(mHeadsetService.getFallbackCandidates(mDatabaseManager)).isEmpty(); // Two connected devices with one watch addConnectedDeviceHelper(deviceRegular); assertThat(mHeadsetService.getFallbackCandidates(mDatabaseManager)) .containsExactly(deviceRegular); } @Test public void testConnectDeviceNotAllowedInbandRingPolicy_InbandRingStatus() { when(mDatabaseManager.getProfileConnectionPolicy( Loading