Loading android/app/src/com/android/bluetooth/btservice/PhonePolicy.java +108 −48 Original line number Diff line number Diff line Loading @@ -96,8 +96,10 @@ class PhonePolicy { private static final int MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED = 5; private static final int MESSAGE_DEVICE_CONNECTED = 6; private static final String PREFER_LE_AUDIO_ONLY_MODE = @VisibleForTesting static final String PREFER_LE_AUDIO_ONLY_MODE = "persist.bluetooth.prefer_le_audio_only_mode"; @VisibleForTesting static final String AUTO_CONNECT_PROFILES_PROPERTY = "bluetooth.auto_connect_profiles.enabled"; // Timeouts @VisibleForTesting static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s Loading @@ -110,7 +112,8 @@ class PhonePolicy { private final HashSet<BluetoothDevice> mA2dpRetrySet = new HashSet<>(); private final HashSet<BluetoothDevice> mConnectOtherProfilesDeviceSet = new HashSet<>(); private Boolean mPreferLeAudioOnlyMode = false; @VisibleForTesting boolean mPreferLeAudioOnlyMode; @VisibleForTesting boolean mAutoConnectProfilesSupported; // Broadcast receiver for all changes to states of various profiles private final BroadcastReceiver mReceiver = new BroadcastReceiver() { Loading Loading @@ -291,6 +294,8 @@ class PhonePolicy { mFactory = factory; mHandler = new PhonePolicyHandler(service.getMainLooper()); mPreferLeAudioOnlyMode = SystemProperties.getBoolean(PREFER_LE_AUDIO_ONLY_MODE, true); mAutoConnectProfilesSupported = SystemProperties.getBoolean( AUTO_CONNECT_PROFILES_PROPERTY, false); } // Policy implementation, all functions MUST be private Loading @@ -311,75 +316,104 @@ class PhonePolicy { BassClientService bcService = mFactory.getBassClientService(); BatteryService batteryService = mFactory.getBatteryService(); boolean isLeAudioProfileAllowed = false; if ((leAudioService != null) && Utils.arrayContains(uuids, BluetoothUuid.LE_AUDIO) && (leAudioService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { isLeAudioProfileAllowed = true; } // Set profile priorities only for the profiles discovered on the remote device. // This avoids needless auto-connect attempts to profiles non-existent on the remote device if ((hidService != null) && (Utils.arrayContains(uuids, BluetoothUuid.HID) || Utils.arrayContains(uuids, BluetoothUuid.HOGP)) && ( hidService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { if (mAutoConnectProfilesSupported) { hidService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HID_HOST, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } // If we do not have a stored priority for HFP/A2DP (all roles) then default to on. if ((headsetService != null) && ((Utils.arrayContains(uuids, BluetoothUuid.HSP) || Utils.arrayContains(uuids, BluetoothUuid.HFP)) && ( headsetService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN))) { if (mPreferLeAudioOnlyMode && isLeAudioProfileAllowed) { debugLog("clear hfp profile priority for the le audio dual mode device " + device); mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } else { if (mAutoConnectProfilesSupported) { headsetService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } } if ((a2dpService != null) && (Utils.arrayContains(uuids, BluetoothUuid.A2DP_SINK) || Utils.arrayContains(uuids, BluetoothUuid.ADV_AUDIO_DIST)) && ( a2dpService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { if (mPreferLeAudioOnlyMode && isLeAudioProfileAllowed) { debugLog("clear a2dp profile priority for the le audio dual mode device " + device); mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } else { if (mAutoConnectProfilesSupported) { a2dpService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } } // CSIP should be connected prior than LE Audio if ((csipSetCooridnatorService != null) && (Utils.arrayContains(uuids, BluetoothUuid.COORDINATED_SET)) && (csipSetCooridnatorService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { if (mAutoConnectProfilesSupported) { csipSetCooridnatorService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.CSIP_SET_COORDINATOR, BluetoothProfile.CONNECTION_POLICY_ALLOWED); BluetoothProfile.CSIP_SET_COORDINATOR, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } // If we do not have a stored priority for HFP/A2DP (all roles) then default to on. if ((panService != null) && (Utils.arrayContains(uuids, BluetoothUuid.PANU) && ( panService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN) && mAdapterService.getResources() .getBoolean(R.bool.config_bluetooth_pan_enable_autoconnect))) { if (mAutoConnectProfilesSupported) { panService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.PAN, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } boolean isLeAudioProfileAllowed = false; if ((leAudioService != null) && Utils.arrayContains(uuids, BluetoothUuid.LE_AUDIO) && (leAudioService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { if (isLeAudioProfileAllowed) { debugLog("setting le audio profile priority for device " + device); isLeAudioProfileAllowed = true; if (mAutoConnectProfilesSupported) { leAudioService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO, BluetoothProfile.CONNECTION_POLICY_ALLOWED); if (mPreferLeAudioOnlyMode) { if (mAdapterService.getDatabase() .getProfileConnectionPolicy(device, BluetoothProfile.A2DP) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { debugLog("clear a2dp profile priority for the le audio dual mode device " + device); mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } if (mAdapterService.getDatabase() .getProfileConnectionPolicy(device, BluetoothProfile.HEADSET) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { debugLog("clear hfp profile priority for the le audio dual mode device " + device); mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } } } Loading @@ -390,8 +424,14 @@ class PhonePolicy { debugLog("LE Audio preferred over ASHA for device " + device); } else { debugLog("setting hearing aid profile priority for device " + device); if (mAutoConnectProfilesSupported) { hearingAidService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HEARING_AID, BluetoothProfile.CONNECTION_POLICY_ALLOWED); BluetoothProfile.HEARING_AID, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } } Loading @@ -399,34 +439,54 @@ class PhonePolicy { BluetoothUuid.VOLUME_CONTROL) && (volumeControlService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { debugLog("setting volume control profile priority for device " + device); if (mAutoConnectProfilesSupported) { volumeControlService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL, BluetoothProfile.CONNECTION_POLICY_ALLOWED); BluetoothProfile.VOLUME_CONTROL, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } if ((hapClientService != null) && Utils.arrayContains(uuids, BluetoothUuid.HAS) && (hapClientService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { debugLog("setting hearing access profile priority for device " + device); if (mAutoConnectProfilesSupported) { hapClientService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } if ((bcService != null) && Utils.arrayContains(uuids, BluetoothUuid.BASS) && (bcService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { debugLog("setting broadcast assistant profile priority for device " + device); if (mAutoConnectProfilesSupported) { bcService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } if ((batteryService != null) && Utils.arrayContains(uuids, BluetoothUuid.BATTERY) && (batteryService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { debugLog("setting battery profile priority for device " + device); if (mAutoConnectProfilesSupported) { batteryService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.BATTERY, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } } @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) private void processProfileStateChanged(BluetoothDevice device, int profileId, int nextState, Loading android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java +114 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.bluetooth.TestUtils; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.hfp.HeadsetService; import com.android.bluetooth.le_audio.LeAudioService; import org.junit.After; import org.junit.Before; Loading Loading @@ -67,6 +68,8 @@ public class PhonePolicyTest { @Mock private ServiceFactory mServiceFactory; @Mock private HeadsetService mHeadsetService; @Mock private A2dpService mA2dpService; @Mock private LeAudioService mLeAudioService; @Mock private DatabaseManager mDatabaseManager; @Before Loading @@ -83,6 +86,7 @@ public class PhonePolicyTest { // Setup the mocked factory to return mocked services doReturn(mHeadsetService).when(mServiceFactory).getHeadsetService(); doReturn(mA2dpService).when(mServiceFactory).getA2dpService(); doReturn(mLeAudioService).when(mServiceFactory).getLeAudioService(); // Start handler thread for this test mHandlerThread = new HandlerThread("PhonePolicyTestHandlerThread"); mHandlerThread.start(); Loading Loading @@ -111,6 +115,8 @@ public class PhonePolicyTest { */ @Test public void testProcessInitProfilePriorities() { mPhonePolicy.mAutoConnectProfilesSupported = false; BluetoothDevice device = getTestDevice(mAdapter, 0); // Mock the HeadsetService to return unknown connection policy when(mHeadsetService.getConnectionPolicy(device)) Loading Loading @@ -140,6 +146,114 @@ public class PhonePolicyTest { BluetoothProfile.CONNECTION_POLICY_ALLOWED); } @Test public void testProcessInitProfilePriorities_WithAutoConnect() { mPhonePolicy.mAutoConnectProfilesSupported = true; BluetoothDevice device = getTestDevice(mAdapter, 0); // Mock the HeadsetService to return unknown connection policy when(mHeadsetService.getConnectionPolicy(device)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); // Mock the A2DP service to return undefined unknown connection policy when(mA2dpService.getConnectionPolicy(device)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); // Inject an event for UUIDs updated for a remote device with only HFP enabled Intent intent = new Intent(BluetoothDevice.ACTION_UUID); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); ParcelUuid[] uuids = new ParcelUuid[2]; uuids[0] = BluetoothUuid.HFP; uuids[1] = BluetoothUuid.A2DP_SINK; intent.putExtra(BluetoothDevice.EXTRA_UUID, uuids); mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); // Check auto connect verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } @Test public void testProcessInitProfilePriorities_LeAudio() { BluetoothDevice device = getTestDevice(mAdapter, 0); // Auto connect to LE audio but disallow HFP and A2DP processInitProfilePriorities_LeAudioHelper(true, true); verify(mLeAudioService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); // Does not auto connect and disallow HFP and A2DP to be connected processInitProfilePriorities_LeAudioHelper(true, false); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)) .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)) .setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); // Auto connect to LE audio, HFP, A2DP processInitProfilePriorities_LeAudioHelper(false, true); verify(mLeAudioService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); // Does not auto connect and allow HFP and A2DP to be connected processInitProfilePriorities_LeAudioHelper(false, false); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)) .setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } private void processInitProfilePriorities_LeAudioHelper( boolean preferLeOnly, boolean autoConnect) { mPhonePolicy.mPreferLeAudioOnlyMode = preferLeOnly; mPhonePolicy.mAutoConnectProfilesSupported = autoConnect; BluetoothDevice device = getTestDevice(mAdapter, 0); // Mock the HFP, A2DP and LE audio services to return unknown connection policy when(mHeadsetService.getConnectionPolicy(device)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); when(mA2dpService.getConnectionPolicy(device)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); when(mLeAudioService.getConnectionPolicy(device)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); // Inject an event for UUIDs updated for a remote device with only HFP enabled Intent intent = new Intent(BluetoothDevice.ACTION_UUID); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); ParcelUuid[] uuids = new ParcelUuid[3]; uuids[0] = BluetoothUuid.HFP; uuids[1] = BluetoothUuid.A2DP_SINK; uuids[2] = BluetoothUuid.LE_AUDIO; intent.putExtra(BluetoothDevice.EXTRA_UUID, uuids); mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); } /** * Test that when the adapter is turned ON then we call autoconnect on devices that have HFP and * A2DP enabled. NOTE that the assumption is that we have already done the pairing previously Loading Loading
android/app/src/com/android/bluetooth/btservice/PhonePolicy.java +108 −48 Original line number Diff line number Diff line Loading @@ -96,8 +96,10 @@ class PhonePolicy { private static final int MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED = 5; private static final int MESSAGE_DEVICE_CONNECTED = 6; private static final String PREFER_LE_AUDIO_ONLY_MODE = @VisibleForTesting static final String PREFER_LE_AUDIO_ONLY_MODE = "persist.bluetooth.prefer_le_audio_only_mode"; @VisibleForTesting static final String AUTO_CONNECT_PROFILES_PROPERTY = "bluetooth.auto_connect_profiles.enabled"; // Timeouts @VisibleForTesting static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s Loading @@ -110,7 +112,8 @@ class PhonePolicy { private final HashSet<BluetoothDevice> mA2dpRetrySet = new HashSet<>(); private final HashSet<BluetoothDevice> mConnectOtherProfilesDeviceSet = new HashSet<>(); private Boolean mPreferLeAudioOnlyMode = false; @VisibleForTesting boolean mPreferLeAudioOnlyMode; @VisibleForTesting boolean mAutoConnectProfilesSupported; // Broadcast receiver for all changes to states of various profiles private final BroadcastReceiver mReceiver = new BroadcastReceiver() { Loading Loading @@ -291,6 +294,8 @@ class PhonePolicy { mFactory = factory; mHandler = new PhonePolicyHandler(service.getMainLooper()); mPreferLeAudioOnlyMode = SystemProperties.getBoolean(PREFER_LE_AUDIO_ONLY_MODE, true); mAutoConnectProfilesSupported = SystemProperties.getBoolean( AUTO_CONNECT_PROFILES_PROPERTY, false); } // Policy implementation, all functions MUST be private Loading @@ -311,75 +316,104 @@ class PhonePolicy { BassClientService bcService = mFactory.getBassClientService(); BatteryService batteryService = mFactory.getBatteryService(); boolean isLeAudioProfileAllowed = false; if ((leAudioService != null) && Utils.arrayContains(uuids, BluetoothUuid.LE_AUDIO) && (leAudioService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { isLeAudioProfileAllowed = true; } // Set profile priorities only for the profiles discovered on the remote device. // This avoids needless auto-connect attempts to profiles non-existent on the remote device if ((hidService != null) && (Utils.arrayContains(uuids, BluetoothUuid.HID) || Utils.arrayContains(uuids, BluetoothUuid.HOGP)) && ( hidService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { if (mAutoConnectProfilesSupported) { hidService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HID_HOST, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } // If we do not have a stored priority for HFP/A2DP (all roles) then default to on. if ((headsetService != null) && ((Utils.arrayContains(uuids, BluetoothUuid.HSP) || Utils.arrayContains(uuids, BluetoothUuid.HFP)) && ( headsetService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN))) { if (mPreferLeAudioOnlyMode && isLeAudioProfileAllowed) { debugLog("clear hfp profile priority for the le audio dual mode device " + device); mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } else { if (mAutoConnectProfilesSupported) { headsetService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } } if ((a2dpService != null) && (Utils.arrayContains(uuids, BluetoothUuid.A2DP_SINK) || Utils.arrayContains(uuids, BluetoothUuid.ADV_AUDIO_DIST)) && ( a2dpService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { if (mPreferLeAudioOnlyMode && isLeAudioProfileAllowed) { debugLog("clear a2dp profile priority for the le audio dual mode device " + device); mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } else { if (mAutoConnectProfilesSupported) { a2dpService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } } // CSIP should be connected prior than LE Audio if ((csipSetCooridnatorService != null) && (Utils.arrayContains(uuids, BluetoothUuid.COORDINATED_SET)) && (csipSetCooridnatorService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { if (mAutoConnectProfilesSupported) { csipSetCooridnatorService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.CSIP_SET_COORDINATOR, BluetoothProfile.CONNECTION_POLICY_ALLOWED); BluetoothProfile.CSIP_SET_COORDINATOR, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } // If we do not have a stored priority for HFP/A2DP (all roles) then default to on. if ((panService != null) && (Utils.arrayContains(uuids, BluetoothUuid.PANU) && ( panService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN) && mAdapterService.getResources() .getBoolean(R.bool.config_bluetooth_pan_enable_autoconnect))) { if (mAutoConnectProfilesSupported) { panService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.PAN, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } boolean isLeAudioProfileAllowed = false; if ((leAudioService != null) && Utils.arrayContains(uuids, BluetoothUuid.LE_AUDIO) && (leAudioService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { if (isLeAudioProfileAllowed) { debugLog("setting le audio profile priority for device " + device); isLeAudioProfileAllowed = true; if (mAutoConnectProfilesSupported) { leAudioService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO, BluetoothProfile.CONNECTION_POLICY_ALLOWED); if (mPreferLeAudioOnlyMode) { if (mAdapterService.getDatabase() .getProfileConnectionPolicy(device, BluetoothProfile.A2DP) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { debugLog("clear a2dp profile priority for the le audio dual mode device " + device); mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } if (mAdapterService.getDatabase() .getProfileConnectionPolicy(device, BluetoothProfile.HEADSET) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { debugLog("clear hfp profile priority for the le audio dual mode device " + device); mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } } } Loading @@ -390,8 +424,14 @@ class PhonePolicy { debugLog("LE Audio preferred over ASHA for device " + device); } else { debugLog("setting hearing aid profile priority for device " + device); if (mAutoConnectProfilesSupported) { hearingAidService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HEARING_AID, BluetoothProfile.CONNECTION_POLICY_ALLOWED); BluetoothProfile.HEARING_AID, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } } Loading @@ -399,34 +439,54 @@ class PhonePolicy { BluetoothUuid.VOLUME_CONTROL) && (volumeControlService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { debugLog("setting volume control profile priority for device " + device); if (mAutoConnectProfilesSupported) { volumeControlService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL, BluetoothProfile.CONNECTION_POLICY_ALLOWED); BluetoothProfile.VOLUME_CONTROL, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } if ((hapClientService != null) && Utils.arrayContains(uuids, BluetoothUuid.HAS) && (hapClientService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { debugLog("setting hearing access profile priority for device " + device); if (mAutoConnectProfilesSupported) { hapClientService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } if ((bcService != null) && Utils.arrayContains(uuids, BluetoothUuid.BASS) && (bcService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { debugLog("setting broadcast assistant profile priority for device " + device); if (mAutoConnectProfilesSupported) { bcService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } if ((batteryService != null) && Utils.arrayContains(uuids, BluetoothUuid.BATTERY) && (batteryService.getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) { debugLog("setting battery profile priority for device " + device); if (mAutoConnectProfilesSupported) { batteryService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } else { mAdapterService.getDatabase().setProfileConnectionPolicy(device, BluetoothProfile.BATTERY, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } } } @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) private void processProfileStateChanged(BluetoothDevice device, int profileId, int nextState, Loading
android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java +114 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.bluetooth.TestUtils; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.hfp.HeadsetService; import com.android.bluetooth.le_audio.LeAudioService; import org.junit.After; import org.junit.Before; Loading Loading @@ -67,6 +68,8 @@ public class PhonePolicyTest { @Mock private ServiceFactory mServiceFactory; @Mock private HeadsetService mHeadsetService; @Mock private A2dpService mA2dpService; @Mock private LeAudioService mLeAudioService; @Mock private DatabaseManager mDatabaseManager; @Before Loading @@ -83,6 +86,7 @@ public class PhonePolicyTest { // Setup the mocked factory to return mocked services doReturn(mHeadsetService).when(mServiceFactory).getHeadsetService(); doReturn(mA2dpService).when(mServiceFactory).getA2dpService(); doReturn(mLeAudioService).when(mServiceFactory).getLeAudioService(); // Start handler thread for this test mHandlerThread = new HandlerThread("PhonePolicyTestHandlerThread"); mHandlerThread.start(); Loading Loading @@ -111,6 +115,8 @@ public class PhonePolicyTest { */ @Test public void testProcessInitProfilePriorities() { mPhonePolicy.mAutoConnectProfilesSupported = false; BluetoothDevice device = getTestDevice(mAdapter, 0); // Mock the HeadsetService to return unknown connection policy when(mHeadsetService.getConnectionPolicy(device)) Loading Loading @@ -140,6 +146,114 @@ public class PhonePolicyTest { BluetoothProfile.CONNECTION_POLICY_ALLOWED); } @Test public void testProcessInitProfilePriorities_WithAutoConnect() { mPhonePolicy.mAutoConnectProfilesSupported = true; BluetoothDevice device = getTestDevice(mAdapter, 0); // Mock the HeadsetService to return unknown connection policy when(mHeadsetService.getConnectionPolicy(device)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); // Mock the A2DP service to return undefined unknown connection policy when(mA2dpService.getConnectionPolicy(device)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); // Inject an event for UUIDs updated for a remote device with only HFP enabled Intent intent = new Intent(BluetoothDevice.ACTION_UUID); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); ParcelUuid[] uuids = new ParcelUuid[2]; uuids[0] = BluetoothUuid.HFP; uuids[1] = BluetoothUuid.A2DP_SINK; intent.putExtra(BluetoothDevice.EXTRA_UUID, uuids); mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); // Check auto connect verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } @Test public void testProcessInitProfilePriorities_LeAudio() { BluetoothDevice device = getTestDevice(mAdapter, 0); // Auto connect to LE audio but disallow HFP and A2DP processInitProfilePriorities_LeAudioHelper(true, true); verify(mLeAudioService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); // Does not auto connect and disallow HFP and A2DP to be connected processInitProfilePriorities_LeAudioHelper(true, false); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)) .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)) .setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); // Auto connect to LE audio, HFP, A2DP processInitProfilePriorities_LeAudioHelper(false, true); verify(mLeAudioService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); // Does not auto connect and allow HFP and A2DP to be connected processInitProfilePriorities_LeAudioHelper(false, false); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)) .setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setProfileConnectionPolicy(device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_ALLOWED); verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } private void processInitProfilePriorities_LeAudioHelper( boolean preferLeOnly, boolean autoConnect) { mPhonePolicy.mPreferLeAudioOnlyMode = preferLeOnly; mPhonePolicy.mAutoConnectProfilesSupported = autoConnect; BluetoothDevice device = getTestDevice(mAdapter, 0); // Mock the HFP, A2DP and LE audio services to return unknown connection policy when(mHeadsetService.getConnectionPolicy(device)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); when(mA2dpService.getConnectionPolicy(device)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); when(mLeAudioService.getConnectionPolicy(device)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); // Inject an event for UUIDs updated for a remote device with only HFP enabled Intent intent = new Intent(BluetoothDevice.ACTION_UUID); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); ParcelUuid[] uuids = new ParcelUuid[3]; uuids[0] = BluetoothUuid.HFP; uuids[1] = BluetoothUuid.A2DP_SINK; uuids[2] = BluetoothUuid.LE_AUDIO; intent.putExtra(BluetoothDevice.EXTRA_UUID, uuids); mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); } /** * Test that when the adapter is turned ON then we call autoconnect on devices that have HFP and * A2DP enabled. NOTE that the assumption is that we have already done the pairing previously Loading