1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.btservice; 18 19 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; 20 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; 21 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 22 import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; 23 import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; 24 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; 25 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING; 26 27 import static com.android.bluetooth.TestUtils.MockitoRule; 28 import static com.android.bluetooth.TestUtils.getTestDevice; 29 30 import static org.mockito.Mockito.any; 31 import static org.mockito.Mockito.anyBoolean; 32 import static org.mockito.Mockito.anyInt; 33 import static org.mockito.Mockito.clearInvocations; 34 import static org.mockito.Mockito.doReturn; 35 import static org.mockito.Mockito.eq; 36 import static org.mockito.Mockito.inOrder; 37 import static org.mockito.Mockito.never; 38 import static org.mockito.Mockito.times; 39 import static org.mockito.Mockito.verify; 40 import static org.mockito.Mockito.when; 41 42 import android.bluetooth.BluetoothAdapter; 43 import android.bluetooth.BluetoothCsipSetCoordinator; 44 import android.bluetooth.BluetoothDevice; 45 import android.bluetooth.BluetoothProfile; 46 import android.bluetooth.BluetoothUuid; 47 import android.os.ParcelUuid; 48 import android.platform.test.annotations.DisableFlags; 49 import android.platform.test.annotations.EnableFlags; 50 import android.platform.test.flag.junit.SetFlagsRule; 51 52 import androidx.room.Room; 53 import androidx.test.filters.MediumTest; 54 import androidx.test.platform.app.InstrumentationRegistry; 55 import androidx.test.runner.AndroidJUnit4; 56 57 import com.android.bluetooth.TestLooper; 58 import com.android.bluetooth.TestUtils; 59 import com.android.bluetooth.Utils; 60 import com.android.bluetooth.a2dp.A2dpService; 61 import com.android.bluetooth.btservice.storage.DatabaseManager; 62 import com.android.bluetooth.btservice.storage.MetadataDatabase; 63 import com.android.bluetooth.csip.CsipSetCoordinatorService; 64 import com.android.bluetooth.flags.Flags; 65 import com.android.bluetooth.hap.HapClientService; 66 import com.android.bluetooth.hearingaid.HearingAidService; 67 import com.android.bluetooth.hfp.HeadsetService; 68 import com.android.bluetooth.le_audio.LeAudioService; 69 import com.android.bluetooth.util.SystemProperties; 70 71 import org.junit.After; 72 import org.junit.Before; 73 import org.junit.Rule; 74 import org.junit.Test; 75 import org.junit.runner.RunWith; 76 import org.mockito.InOrder; 77 import org.mockito.Mock; 78 79 import java.util.ArrayList; 80 import java.util.Collections; 81 import java.util.List; 82 83 /** Test cases for {@link PhonePolicy}. */ 84 @MediumTest 85 @RunWith(AndroidJUnit4.class) 86 public class PhonePolicyTest { 87 @Rule public final MockitoRule mMockitoRule = new MockitoRule(); 88 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 89 90 @Mock private AdapterService mAdapterService; 91 @Mock private ServiceFactory mServiceFactory; 92 @Mock private HeadsetService mHeadsetService; 93 @Mock private A2dpService mA2dpService; 94 @Mock private LeAudioService mLeAudioService; 95 @Mock private DatabaseManager mDatabaseManager; 96 @Mock private CsipSetCoordinatorService mCsipSetCoordinatorService; 97 @Mock private HearingAidService mHearingAidService; 98 @Mock private HapClientService mHapClientService; 99 @Mock private SystemProperties.MockableSystemProperties mProperties; 100 101 private static final int MAX_CONNECTED_AUDIO_DEVICES = 5; 102 103 private final BluetoothDevice mDevice = getTestDevice(0); 104 private final BluetoothDevice mDevice2 = getTestDevice(1); 105 private PhonePolicy mPhonePolicy; 106 private boolean mOriginalDualModeState; 107 private TestLooper mLooper; 108 109 private List<BluetoothDevice> mLeAudioAllowedConnectionPolicyList; 110 111 @Before setUp()112 public void setUp() throws Exception { 113 mLeAudioAllowedConnectionPolicyList = new ArrayList<>(); 114 mLooper = new TestLooper(); 115 116 // Stub A2DP and HFP 117 doReturn(true).when(mHeadsetService).connect(any()); 118 doReturn(true).when(mA2dpService).connect(any()); 119 120 doReturn(BluetoothAdapter.STATE_ON).when(mAdapterService).getState(); 121 doReturn(MAX_CONNECTED_AUDIO_DEVICES).when(mAdapterService).getMaxConnectedAudioDevices(); 122 doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); 123 // Setup the mocked factory to return mocked services 124 doReturn(mHeadsetService).when(mServiceFactory).getHeadsetService(); 125 doReturn(mA2dpService).when(mServiceFactory).getA2dpService(); 126 doReturn(mLeAudioService).when(mServiceFactory).getLeAudioService(); 127 doReturn(mCsipSetCoordinatorService).when(mServiceFactory).getCsipSetCoordinatorService(); 128 doReturn(mHearingAidService).when(mServiceFactory).getHearingAidService(); 129 doReturn(mHapClientService).when(mServiceFactory).getHapClientService(); 130 131 // Most common default 132 doReturn(CONNECTION_POLICY_UNKNOWN).when(mHeadsetService).getConnectionPolicy(any()); 133 doReturn(CONNECTION_POLICY_UNKNOWN).when(mA2dpService).getConnectionPolicy(any()); 134 doReturn(CONNECTION_POLICY_UNKNOWN).when(mLeAudioService).getConnectionPolicy(any()); 135 doReturn(STATE_DISCONNECTED).when(mA2dpService).getConnectionState(any()); 136 doReturn(STATE_DISCONNECTED).when(mHeadsetService).getConnectionState(any()); 137 doReturn(Collections.emptyList()).when(mA2dpService).getConnectedDevices(); 138 doReturn(Collections.emptyList()).when(mHeadsetService).getConnectedDevices(); 139 140 SystemProperties.mProperties = mProperties; 141 142 mPhonePolicy = new PhonePolicy(mAdapterService, mLooper.getLooper(), mServiceFactory); 143 mOriginalDualModeState = Utils.isDualModeAudioEnabled(); 144 } 145 146 @After tearDown()147 public void tearDown() throws Exception { 148 SystemProperties.mProperties = null; 149 Utils.setDualModeAudioStateForTesting(mOriginalDualModeState); 150 } 151 getLeAudioConnectionPolicy(BluetoothDevice dev)152 int getLeAudioConnectionPolicy(BluetoothDevice dev) { 153 if (!mLeAudioAllowedConnectionPolicyList.contains(dev)) { 154 return CONNECTION_POLICY_UNKNOWN; 155 } 156 return CONNECTION_POLICY_ALLOWED; 157 } 158 setLeAudioAllowedConnectionPolicy(BluetoothDevice dev)159 boolean setLeAudioAllowedConnectionPolicy(BluetoothDevice dev) { 160 mLeAudioAllowedConnectionPolicyList.add(dev); 161 return true; 162 } 163 164 /** 165 * Test that when new UUIDs are refreshed for a device then we set the priorities for various 166 * profiles accurately. The following profiles should have ON priorities: A2DP, HFP, HID and PAN 167 */ 168 @Test testProcessInitProfilePriorities()169 public void testProcessInitProfilePriorities() { 170 mPhonePolicy.mAutoConnectProfilesSupported = false; 171 172 ParcelUuid[] uuids = {BluetoothUuid.HFP, BluetoothUuid.A2DP_SINK}; 173 mPhonePolicy.onUuidsDiscovered(mDevice, uuids); 174 175 verify(mDatabaseManager) 176 .setProfileConnectionPolicy( 177 mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_ALLOWED); 178 verify(mDatabaseManager) 179 .setProfileConnectionPolicy( 180 mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_ALLOWED); 181 } 182 processInitProfilePriorities_LeAudioOnlyHelper( int csipGroupId, int groupSize, boolean dualMode, boolean ashaDevice)183 private void processInitProfilePriorities_LeAudioOnlyHelper( 184 int csipGroupId, int groupSize, boolean dualMode, boolean ashaDevice) { 185 Utils.setDualModeAudioStateForTesting(false); 186 mPhonePolicy.mLeAudioEnabledByDefault = true; 187 mPhonePolicy.mAutoConnectProfilesSupported = true; 188 doReturn(false) 189 .when(mProperties) 190 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean()); 191 192 int testedDeviceType = BluetoothDevice.DEVICE_TYPE_LE; 193 if (dualMode) { 194 /* If CSIP mode, use DUAL type only for single device */ 195 testedDeviceType = BluetoothDevice.DEVICE_TYPE_DUAL; 196 } 197 198 List<BluetoothDevice> allConnectedDevices = new ArrayList<>(); 199 for (int i = 0; i < groupSize; i++) { 200 BluetoothDevice device = getTestDevice(i); 201 allConnectedDevices.add(device); 202 } 203 204 doReturn(csipGroupId).when(mCsipSetCoordinatorService).getGroupId(any(), any()); 205 doReturn(groupSize).when(mCsipSetCoordinatorService).getDesiredGroupSize(csipGroupId); 206 207 /* Build list of test UUIDs */ 208 int numOfServices = 1; 209 if (groupSize > 1) { 210 numOfServices++; 211 } 212 if (ashaDevice) { 213 numOfServices++; 214 } 215 ParcelUuid[] uuids = new ParcelUuid[numOfServices]; 216 int iter = 0; 217 uuids[iter++] = BluetoothUuid.LE_AUDIO; 218 if (groupSize > 1) { 219 uuids[iter++] = BluetoothUuid.COORDINATED_SET; 220 } 221 if (ashaDevice) { 222 uuids[iter++] = BluetoothUuid.HEARING_AID; 223 } 224 225 doReturn(uuids).when(mAdapterService).getRemoteUuids(any()); 226 doReturn(true) 227 .when(mAdapterService) 228 .isProfileSupported(any(), eq(BluetoothProfile.LE_AUDIO)); 229 doReturn(ashaDevice) 230 .when(mAdapterService) 231 .isProfileSupported(any(), eq(BluetoothProfile.HEARING_AID)); 232 233 List<BluetoothDevice> connectedDevices = new ArrayList<>(); 234 doReturn(connectedDevices) 235 .when(mCsipSetCoordinatorService) 236 .getGroupDevicesOrdered(csipGroupId); 237 238 for (BluetoothDevice dev : allConnectedDevices) { 239 when(mLeAudioService.setConnectionPolicy(dev, CONNECTION_POLICY_ALLOWED)) 240 .thenAnswer( 241 invocation -> { 242 return setLeAudioAllowedConnectionPolicy(dev); 243 }); 244 when(mLeAudioService.getConnectionPolicy(dev)) 245 .thenAnswer( 246 invocation -> { 247 return getLeAudioConnectionPolicy(dev); 248 }); 249 250 /* First device is always LE only second depends on dualMode */ 251 if (groupSize == 1 || connectedDevices.size() >= 1) { 252 doReturn(testedDeviceType).when(mAdapterService).getRemoteType(dev); 253 } else { 254 doReturn(BluetoothDevice.DEVICE_TYPE_LE).when(mAdapterService).getRemoteType(dev); 255 } 256 257 mPhonePolicy.onUuidsDiscovered(dev, uuids); 258 if (groupSize > 1) { 259 connectedDevices.add(dev); 260 // Simulate CSIP connection 261 mPhonePolicy.profileConnectionStateChanged( 262 BluetoothProfile.CSIP_SET_COORDINATOR, 263 dev, 264 STATE_DISCONNECTED, 265 STATE_CONNECTED); 266 mLooper.dispatchAll(); 267 } 268 } 269 } 270 271 @Test 272 @EnableFlags(Flags.FLAG_LEAUDIO_ALLOW_LEAUDIO_ONLY_DEVICES) testConnectLeAudioOnlyDevices_BandedHeadphones()273 public void testConnectLeAudioOnlyDevices_BandedHeadphones() { 274 // Single device, no CSIP 275 processInitProfilePriorities_LeAudioOnlyHelper( 276 BluetoothCsipSetCoordinator.GROUP_ID_INVALID, 1, false, false); 277 verify(mLeAudioService) 278 .setConnectionPolicy(any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED)); 279 } 280 281 @Test 282 @EnableFlags(Flags.FLAG_LEAUDIO_ALLOW_LEAUDIO_ONLY_DEVICES) testConnectLeAudioOnlyDevices_CsipSet()283 public void testConnectLeAudioOnlyDevices_CsipSet() { 284 // CSIP Le Audio only devices 285 processInitProfilePriorities_LeAudioOnlyHelper(1, 2, false, false); 286 verify(mLeAudioService, times(2)) 287 .setConnectionPolicy(any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED)); 288 } 289 290 @Test 291 @EnableFlags(Flags.FLAG_LEAUDIO_ALLOW_LEAUDIO_ONLY_DEVICES) testConnectLeAudioOnlyDevices_DualModeCsipSet()292 public void testConnectLeAudioOnlyDevices_DualModeCsipSet() { 293 // CSIP Dual mode devices 294 processInitProfilePriorities_LeAudioOnlyHelper(1, 2, true, false); 295 verify(mLeAudioService, never()) 296 .setConnectionPolicy(any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED)); 297 } 298 299 @Test 300 @EnableFlags(Flags.FLAG_LEAUDIO_ALLOW_LEAUDIO_ONLY_DEVICES) testConnectLeAudioOnlyDevices_AshaAndCsipSet()301 public void testConnectLeAudioOnlyDevices_AshaAndCsipSet() { 302 // CSIP Dual mode devices 303 processInitProfilePriorities_LeAudioOnlyHelper(1, 2, false, true); 304 verify(mLeAudioService, never()) 305 .setConnectionPolicy(any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED)); 306 } 307 308 @Test testProcessInitProfilePriorities_WithAutoConnect()309 public void testProcessInitProfilePriorities_WithAutoConnect() { 310 mPhonePolicy.mAutoConnectProfilesSupported = true; 311 312 // Inject an event for UUIDs updated for a remote device with only HFP enabled 313 ParcelUuid[] uuids = new ParcelUuid[2]; 314 uuids[0] = BluetoothUuid.HFP; 315 uuids[1] = BluetoothUuid.A2DP_SINK; 316 mPhonePolicy.onUuidsDiscovered(mDevice, uuids); 317 318 // Check auto connect 319 verify(mA2dpService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED); 320 verify(mHeadsetService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED); 321 } 322 323 @Test testProcessInitProfilePriorities_LeAudioDisabledByDefault()324 public void testProcessInitProfilePriorities_LeAudioDisabledByDefault() { 325 when(mAdapterService.isLeAudioAllowed(mDevice)).thenReturn(true); 326 327 // Auto connect to LE audio, HFP, A2DP 328 processInitProfilePriorities_LeAudioHelper(true, true, false, false); 329 verify(mLeAudioService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED); 330 verify(mA2dpService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED); 331 verify(mHeadsetService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED); 332 333 // Does not auto connect and allow HFP and A2DP to be connected 334 processInitProfilePriorities_LeAudioHelper(true, false, false, false); 335 verify(mDatabaseManager) 336 .setProfileConnectionPolicy( 337 mDevice, BluetoothProfile.LE_AUDIO, CONNECTION_POLICY_ALLOWED); 338 verify(mDatabaseManager) 339 .setProfileConnectionPolicy( 340 mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_ALLOWED); 341 verify(mDatabaseManager) 342 .setProfileConnectionPolicy( 343 mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_ALLOWED); 344 345 // Auto connect to HFP and A2DP but disallow LE Audio 346 processInitProfilePriorities_LeAudioHelper(false, true, false, false); 347 verify(mDatabaseManager) 348 .setProfileConnectionPolicy( 349 mDevice, BluetoothProfile.LE_AUDIO, CONNECTION_POLICY_FORBIDDEN); 350 verify(mA2dpService, times(2)).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED); 351 verify(mHeadsetService, times(2)).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED); 352 353 // Does not auto connect and disallow LE Audio to be connected 354 processInitProfilePriorities_LeAudioHelper(false, false, false, false); 355 verify(mDatabaseManager, times(2)) 356 .setProfileConnectionPolicy( 357 mDevice, BluetoothProfile.LE_AUDIO, CONNECTION_POLICY_FORBIDDEN); 358 verify(mDatabaseManager, times(2)) 359 .setProfileConnectionPolicy( 360 mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_ALLOWED); 361 verify(mDatabaseManager, times(2)) 362 .setProfileConnectionPolicy( 363 mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_ALLOWED); 364 } 365 366 @Test testProcessInitProfilePriorities_LeAudioEnabledByDefault()367 public void testProcessInitProfilePriorities_LeAudioEnabledByDefault() { 368 when(mAdapterService.isLeAudioAllowed(mDevice)).thenReturn(true); 369 370 // Auto connect to LE audio, HFP, A2DP 371 processInitProfilePriorities_LeAudioHelper(true, true, true, false); 372 verify(mLeAudioService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED); 373 verify(mA2dpService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED); 374 verify(mHeadsetService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED); 375 376 // Does not auto connect and allow HFP and A2DP to be connected 377 processInitProfilePriorities_LeAudioHelper(true, false, true, false); 378 verify(mDatabaseManager) 379 .setProfileConnectionPolicy( 380 mDevice, BluetoothProfile.LE_AUDIO, CONNECTION_POLICY_ALLOWED); 381 verify(mDatabaseManager) 382 .setProfileConnectionPolicy( 383 mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_ALLOWED); 384 verify(mDatabaseManager) 385 .setProfileConnectionPolicy( 386 mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_ALLOWED); 387 388 // Auto connect to LE audio but disallow HFP and A2DP 389 processInitProfilePriorities_LeAudioHelper(false, true, true, false); 390 verify(mLeAudioService, times(2)).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED); 391 verify(mDatabaseManager) 392 .setProfileConnectionPolicy( 393 mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_FORBIDDEN); 394 verify(mDatabaseManager) 395 .setProfileConnectionPolicy( 396 mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_FORBIDDEN); 397 398 // Does not auto connect and disallow HFP and A2DP to be connected 399 processInitProfilePriorities_LeAudioHelper(false, false, true, false); 400 verify(mDatabaseManager, times(2)) 401 .setProfileConnectionPolicy( 402 mDevice, BluetoothProfile.LE_AUDIO, CONNECTION_POLICY_ALLOWED); 403 verify(mDatabaseManager, times(2)) 404 .setProfileConnectionPolicy( 405 mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_FORBIDDEN); 406 verify(mDatabaseManager, times(2)) 407 .setProfileConnectionPolicy( 408 mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_FORBIDDEN); 409 } 410 processInitProfilePriorities_LeAudioHelper( boolean dualModeEnabled, boolean autoConnect, boolean leAudioEnabledByDefault, boolean bypassLeAudioAllowlist)411 private void processInitProfilePriorities_LeAudioHelper( 412 boolean dualModeEnabled, 413 boolean autoConnect, 414 boolean leAudioEnabledByDefault, 415 boolean bypassLeAudioAllowlist) { 416 Utils.setDualModeAudioStateForTesting(dualModeEnabled); 417 mPhonePolicy.mLeAudioEnabledByDefault = leAudioEnabledByDefault; 418 mPhonePolicy.mAutoConnectProfilesSupported = autoConnect; 419 doReturn(bypassLeAudioAllowlist) 420 .when(mProperties) 421 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean()); 422 423 // Inject an event for UUIDs updated for a remote device with only HFP enabled 424 ParcelUuid[] uuids = new ParcelUuid[3]; 425 uuids[0] = BluetoothUuid.HFP; 426 uuids[1] = BluetoothUuid.A2DP_SINK; 427 uuids[2] = BluetoothUuid.LE_AUDIO; 428 mPhonePolicy.onUuidsDiscovered(mDevice, uuids); 429 } 430 431 /* In this test we want to check following scenario 432 * 1. First Le Audio set member bonds/connect and user switch LeAudio toggle 433 * 2. Second device connects later, and LeAudio shall be enabled automatically 434 */ 435 @Test testLateConnectOfLeAudioEnabled_DualModeBud()436 public void testLateConnectOfLeAudioEnabled_DualModeBud() { 437 Utils.setDualModeAudioStateForTesting(false); 438 mPhonePolicy.mLeAudioEnabledByDefault = true; 439 mPhonePolicy.mAutoConnectProfilesSupported = true; 440 441 /* Just for the moment, set to true to setup first device */ 442 doReturn(true) 443 .when(mProperties) 444 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean()); 445 446 int csipGroupId = 1; 447 int groupSize = 2; 448 449 List<BluetoothDevice> connectedDevices = new ArrayList<>(); 450 when(mCsipSetCoordinatorService.getDesiredGroupSize(csipGroupId)).thenReturn(groupSize); 451 when(mCsipSetCoordinatorService.getGroupId(any(), any())).thenReturn(csipGroupId); 452 when(mLeAudioService.getGroupId(any())).thenReturn(csipGroupId); 453 when(mCsipSetCoordinatorService.getGroupDevicesOrdered(csipGroupId)) 454 .thenReturn(connectedDevices); 455 456 // Connect first set member 457 connectedDevices.add(mDevice); 458 459 /* Build list of test UUIDs */ 460 ParcelUuid[] uuids = new ParcelUuid[4]; 461 uuids[0] = BluetoothUuid.LE_AUDIO; 462 uuids[1] = BluetoothUuid.COORDINATED_SET; 463 uuids[2] = BluetoothUuid.A2DP_SINK; 464 uuids[3] = BluetoothUuid.HFP; 465 466 // Prepare common handlers 467 doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any()); 468 doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any()); 469 470 when(mLeAudioService.setConnectionPolicy( 471 any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED))) 472 .thenAnswer( 473 invocation -> { 474 return setLeAudioAllowedConnectionPolicy(invocation.getArgument(0)); 475 }); 476 when(mLeAudioService.getConnectionPolicy(any(BluetoothDevice.class))) 477 .thenAnswer( 478 invocation -> { 479 return getLeAudioConnectionPolicy(invocation.getArgument(0)); 480 }); 481 when(mLeAudioService.getGroupDevices(csipGroupId)).thenReturn(connectedDevices); 482 483 when(mAdapterService.getRemoteUuids(any(BluetoothDevice.class))).thenReturn(uuids); 484 when(mAdapterService.isProfileSupported( 485 any(BluetoothDevice.class), eq(BluetoothProfile.HEARING_AID))) 486 .thenReturn(false); 487 when(mAdapterService.isProfileSupported( 488 any(BluetoothDevice.class), eq(BluetoothProfile.LE_AUDIO))) 489 .thenReturn(true); 490 491 /* Always DualMode for test purpose */ 492 when(mAdapterService.getRemoteType(any(BluetoothDevice.class))) 493 .thenReturn(BluetoothDevice.DEVICE_TYPE_DUAL); 494 495 // Inject first devices 496 mPhonePolicy.onUuidsDiscovered(mDevice, uuids); 497 mPhonePolicy.profileConnectionStateChanged( 498 BluetoothProfile.CSIP_SET_COORDINATOR, 499 mDevice, 500 STATE_DISCONNECTED, 501 STATE_CONNECTED); 502 mLooper.dispatchAll(); 503 504 // Verify connection policy is set properly 505 verify(mLeAudioService).setConnectionPolicy(eq(mDevice), eq(CONNECTION_POLICY_ALLOWED)); 506 507 mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, mDevice); 508 mLooper.dispatchAll(); 509 510 verify(mA2dpService).setConnectionPolicy(eq(mDevice), eq(CONNECTION_POLICY_FORBIDDEN)); 511 verify(mHeadsetService).setConnectionPolicy(eq(mDevice), eq(CONNECTION_POLICY_FORBIDDEN)); 512 513 /* Remove bypass and check that second set member will be added*/ 514 doReturn(false) 515 .when(mProperties) 516 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean()); 517 518 // Now connect second device and make sure 519 // Connect first set member 520 connectedDevices.add(mDevice2); 521 522 // Inject second set member connection 523 mPhonePolicy.onUuidsDiscovered(mDevice2, uuids); 524 mPhonePolicy.profileConnectionStateChanged( 525 BluetoothProfile.CSIP_SET_COORDINATOR, 526 mDevice2, 527 STATE_DISCONNECTED, 528 STATE_CONNECTED); 529 mLooper.dispatchAll(); 530 531 // Verify connection policy is set properly 532 verify(mLeAudioService).setConnectionPolicy(eq(mDevice2), eq(CONNECTION_POLICY_ALLOWED)); 533 534 mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, mDevice2); 535 mLooper.dispatchAll(); 536 537 verify(mA2dpService).setConnectionPolicy(eq(mDevice2), eq(CONNECTION_POLICY_FORBIDDEN)); 538 verify(mHeadsetService).setConnectionPolicy(eq(mDevice2), eq(CONNECTION_POLICY_FORBIDDEN)); 539 } 540 541 @Test testLateConnectOfLeAudioEnabled_AshaAndLeAudioBud()542 public void testLateConnectOfLeAudioEnabled_AshaAndLeAudioBud() { 543 Utils.setDualModeAudioStateForTesting(false); 544 mPhonePolicy.mLeAudioEnabledByDefault = true; 545 mPhonePolicy.mAutoConnectProfilesSupported = true; 546 547 /* Just for the moment, set to true to setup first device */ 548 doReturn(true) 549 .when(mProperties) 550 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean()); 551 552 int csipGroupId = 1; 553 int groupSize = 2; 554 555 List<BluetoothDevice> connectedDevices = new ArrayList<>(); 556 when(mCsipSetCoordinatorService.getDesiredGroupSize(csipGroupId)).thenReturn(groupSize); 557 when(mCsipSetCoordinatorService.getGroupId(any(), any())).thenReturn(csipGroupId); 558 when(mLeAudioService.getGroupId(any())).thenReturn(csipGroupId); 559 when(mCsipSetCoordinatorService.getGroupDevicesOrdered(csipGroupId)) 560 .thenReturn(connectedDevices); 561 562 // Connect first set member 563 connectedDevices.add(mDevice); 564 565 /* Build list of test UUIDs */ 566 ParcelUuid[] uuids = new ParcelUuid[4]; 567 uuids[0] = BluetoothUuid.LE_AUDIO; 568 uuids[1] = BluetoothUuid.COORDINATED_SET; 569 uuids[2] = BluetoothUuid.HEARING_AID; 570 uuids[3] = BluetoothUuid.HAS; 571 572 // Prepare common handlers 573 doReturn(CONNECTION_POLICY_ALLOWED).when(mHearingAidService).getConnectionPolicy(any()); 574 575 when(mLeAudioService.setConnectionPolicy( 576 any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED))) 577 .thenAnswer( 578 invocation -> { 579 return setLeAudioAllowedConnectionPolicy(invocation.getArgument(0)); 580 }); 581 when(mLeAudioService.getConnectionPolicy(any(BluetoothDevice.class))) 582 .thenAnswer( 583 invocation -> { 584 return getLeAudioConnectionPolicy(invocation.getArgument(0)); 585 }); 586 when(mLeAudioService.getGroupDevices(csipGroupId)).thenReturn(connectedDevices); 587 588 when(mAdapterService.getRemoteUuids(any(BluetoothDevice.class))).thenReturn(uuids); 589 when(mAdapterService.isProfileSupported( 590 any(BluetoothDevice.class), eq(BluetoothProfile.HEARING_AID))) 591 .thenReturn(false); 592 when(mAdapterService.isProfileSupported( 593 any(BluetoothDevice.class), eq(BluetoothProfile.LE_AUDIO))) 594 .thenReturn(true); 595 596 /* Always DualMode for test purpose */ 597 when(mAdapterService.getRemoteType(any(BluetoothDevice.class))) 598 .thenReturn(BluetoothDevice.DEVICE_TYPE_LE); 599 600 // Inject first devices 601 mPhonePolicy.onUuidsDiscovered(mDevice, uuids); 602 mPhonePolicy.profileConnectionStateChanged( 603 BluetoothProfile.CSIP_SET_COORDINATOR, 604 mDevice, 605 STATE_DISCONNECTED, 606 STATE_CONNECTED); 607 mLooper.dispatchAll(); 608 609 // Verify connection policy is set properly 610 verify(mLeAudioService).setConnectionPolicy(eq(mDevice), eq(CONNECTION_POLICY_ALLOWED)); 611 612 mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, mDevice); 613 mLooper.dispatchAll(); 614 615 verify(mHearingAidService) 616 .setConnectionPolicy(eq(mDevice), eq(CONNECTION_POLICY_FORBIDDEN)); 617 618 /* Remove bypass and check that second set member will be added*/ 619 doReturn(false) 620 .when(mProperties) 621 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean()); 622 623 // Now connect second device and make sure 624 // Connect first set member 625 connectedDevices.add(mDevice2); 626 627 // Inject second set member connection 628 mPhonePolicy.onUuidsDiscovered(mDevice2, uuids); 629 mPhonePolicy.profileConnectionStateChanged( 630 BluetoothProfile.CSIP_SET_COORDINATOR, 631 mDevice2, 632 STATE_DISCONNECTED, 633 STATE_CONNECTED); 634 mLooper.dispatchAll(); 635 636 // Verify connection policy is set properly 637 verify(mLeAudioService).setConnectionPolicy(eq(mDevice2), eq(CONNECTION_POLICY_ALLOWED)); 638 639 mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, mDevice2); 640 mLooper.dispatchAll(); 641 642 verify(mHearingAidService) 643 .setConnectionPolicy(eq(mDevice2), eq(CONNECTION_POLICY_FORBIDDEN)); 644 } 645 646 /** 647 * Test that when the adapter is turned ON then we call autoconnect on devices that have HFP and 648 * A2DP enabled. NOTE that the assumption is that we have already done the pairing previously 649 * and hence the priorities for the device is already set to AUTO_CONNECT over HFP and A2DP (as 650 * part of post pairing process). 651 */ 652 @Test testAdapterOnAutoConnect()653 public void testAdapterOnAutoConnect() { 654 // Return desired values from the mocked object(s) 655 when(mAdapterService.isQuietModeEnabled()).thenReturn(false); 656 657 // Return a list of connection order 658 when(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).thenReturn(mDevice); 659 when(mAdapterService.getBondState(mDevice)).thenReturn(BluetoothDevice.BOND_BONDED); 660 661 doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any()); 662 doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any()); 663 664 // Inject an event that the adapter is turned on. 665 mPhonePolicy.onBluetoothStateChange(BluetoothAdapter.STATE_OFF, BluetoothAdapter.STATE_ON); 666 667 // Check that we got a request to connect over HFP and A2DP 668 verify(mA2dpService).connect(eq(mDevice)); 669 verify(mHeadsetService).connect(eq(mDevice)); 670 } 671 672 /** Test that when an active device is disconnected, we will not auto connect it */ 673 @Test testDisconnectNoAutoConnect()674 public void testDisconnectNoAutoConnect() { 675 // Return desired values from the mocked object(s) 676 when(mAdapterService.isQuietModeEnabled()).thenReturn(false); 677 678 // Return a list of connection order 679 List<BluetoothDevice> connectionOrder = new ArrayList<>(); 680 connectionOrder.add(mDevice); 681 connectionOrder.add(mDevice2); 682 connectionOrder.add(getTestDevice(2)); 683 connectionOrder.add(getTestDevice(3)); 684 685 doReturn(mDevice).when(mDatabaseManager).getMostRecentlyConnectedA2dpDevice(); 686 687 // Make all devices auto connect 688 doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any()); 689 doReturn(CONNECTION_POLICY_FORBIDDEN) 690 .when(mA2dpService) 691 .getConnectionPolicy(eq(connectionOrder.get(3))); 692 693 // Make one of the device active 694 mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.A2DP, connectionOrder.get(0)); 695 mLooper.dispatchAll(); 696 697 // Only calls setConnection on device connectionOrder.get(0) with STATE_CONNECTED 698 verify(mDatabaseManager).setConnection(mDevice, BluetoothProfile.A2DP); 699 verify(mDatabaseManager, never()).setConnection(eq(connectionOrder.get(1)), anyInt()); 700 verify(mDatabaseManager, never()).setConnection(eq(connectionOrder.get(2)), anyInt()); 701 verify(mDatabaseManager, never()).setConnection(eq(connectionOrder.get(3)), anyInt()); 702 703 // Make another device active 704 when(mHeadsetService.getConnectionState(connectionOrder.get(1))) 705 .thenReturn(STATE_CONNECTED); 706 mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.A2DP, connectionOrder.get(1)); 707 mLooper.dispatchAll(); 708 709 // Only calls setConnection on device connectionOrder.get(1) with STATE_CONNECTED 710 verify(mDatabaseManager).setConnection(connectionOrder.get(0), BluetoothProfile.A2DP); 711 verify(mDatabaseManager).setConnection(connectionOrder.get(1), BluetoothProfile.A2DP); 712 verify(mDatabaseManager, never()).setConnection(eq(connectionOrder.get(2)), anyInt()); 713 verify(mDatabaseManager, never()).setConnection(eq(connectionOrder.get(3)), anyInt()); 714 715 // Disconnect a2dp for the device from previous STATE_CONNECTED 716 when(mHeadsetService.getConnectionState(connectionOrder.get(1))) 717 .thenReturn(STATE_DISCONNECTED); 718 mPhonePolicy.profileConnectionStateChanged( 719 BluetoothProfile.A2DP, connectionOrder.get(1), STATE_CONNECTED, STATE_DISCONNECTED); 720 mLooper.dispatchAll(); 721 722 // Verify that we do not call setConnection, nor setDisconnection on disconnect 723 // from previous STATE_CONNECTED 724 verify(mDatabaseManager) 725 .setConnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.A2DP)); 726 verify(mDatabaseManager, never()) 727 .setDisconnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.A2DP)); 728 729 // Disconnect a2dp for the device from previous STATE_DISCONNECTING 730 mPhonePolicy.profileConnectionStateChanged( 731 BluetoothProfile.A2DP, 732 connectionOrder.get(1), 733 STATE_DISCONNECTING, 734 STATE_DISCONNECTED); 735 mLooper.dispatchAll(); 736 737 // Verify that we do not call setConnection, but instead setDisconnection on disconnect 738 verify(mDatabaseManager) 739 .setConnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.A2DP)); 740 verify(mDatabaseManager) 741 .setDisconnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.A2DP)); 742 743 // Make the current active device fail to connect 744 when(mA2dpService.getConnectionState(connectionOrder.get(1))) 745 .thenReturn(STATE_DISCONNECTED); 746 updateProfileConnectionStateHelper( 747 connectionOrder.get(1), 748 BluetoothProfile.HEADSET, 749 STATE_CONNECTING, 750 STATE_DISCONNECTED); 751 mLooper.dispatchAll(); 752 753 // Verify we don't call deleteConnection as that only happens when we disconnect a2dp 754 verify(mDatabaseManager) 755 .setDisconnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.A2DP)); 756 757 // Verify we didn't have any unexpected calls to setConnection or deleteConnection 758 verify(mDatabaseManager, times(2)).setConnection(any(BluetoothDevice.class), anyInt()); 759 verify(mDatabaseManager) 760 .setDisconnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.HEADSET)); 761 } 762 763 /** 764 * Test that we will try to re-connect to a profile on a device if other profile(s) are 765 * connected. This is to add robustness to the connection mechanism 766 */ 767 @Test testReconnectOnPartialConnect()768 public void testReconnectOnPartialConnect() { 769 BluetoothDevice[] bondedDevices = {mDevice}; 770 doReturn(bondedDevices).when(mAdapterService).getBondedDevices(); 771 772 doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any()); 773 doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any()); 774 775 // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP 776 // To enable that we need to make sure that HeadsetService returns the device as list of 777 // connected devices 778 ArrayList<BluetoothDevice> hsConnectedDevices = new ArrayList<>(); 779 hsConnectedDevices.add(mDevice); 780 when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices); 781 // Also the A2DP should say that its not connected for same device 782 when(mA2dpService.getConnectionState(mDevice)).thenReturn(STATE_DISCONNECTED); 783 784 // ACL is connected, lets simulate this. 785 when(mAdapterService.getConnectionState(mDevice)) 786 .thenReturn(BluetoothDevice.CONNECTION_STATE_ENCRYPTED_BREDR); 787 788 // We send a connection successful for one profile since the re-connect *only* works if we 789 // have already connected successfully over one of the profiles 790 updateProfileConnectionStateHelper( 791 mDevice, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED); 792 793 // Check that we get a call to A2DP connect 794 verify(mA2dpService).connect(eq(mDevice)); 795 } 796 797 /** 798 * Test that connectOtherProfile will not trigger any actions when ACL is disconnected. This is 799 * to add robustness to the connection mechanism 800 */ 801 @Test testConnectOtherProfileWhileDeviceIsDisconnected()802 public void testConnectOtherProfileWhileDeviceIsDisconnected() { 803 BluetoothDevice[] bondedDevices = {mDevice}; 804 doReturn(bondedDevices).when(mAdapterService).getBondedDevices(); 805 806 doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any()); 807 doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any()); 808 809 // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP 810 // To enable that we need to make sure that HeadsetService returns the device as list of 811 // connected devices 812 doReturn(List.of(mDevice)).when(mHeadsetService).getConnectedDevices(); 813 814 // ACL is disconnected just after HEADSET profile got connected and connectOtherProfile 815 // was scheduled. Lets simulate this. 816 when(mAdapterService.getConnectionState(mDevice)) 817 .thenReturn(BluetoothDevice.CONNECTION_STATE_DISCONNECTED); 818 819 // We send a connection successful for one profile since the re-connect *only* works if we 820 // have already connected successfully over one of the profiles 821 updateProfileConnectionStateHelper( 822 mDevice, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED); 823 824 // Check that there will be no A2DP connect 825 verify(mA2dpService, never()).connect(eq(mDevice)); 826 } 827 828 /** 829 * Test that we will try to re-connect to a profile on a device next time if a previous attempt 830 * failed partially. This will make sure the connection mechanism still works at next try while 831 * the previous attempt is some profiles connected on a device but some not. 832 */ 833 @Test testReconnectOnPartialConnect_PreviousPartialFail()834 public void testReconnectOnPartialConnect_PreviousPartialFail() { 835 InOrder mInOrder = inOrder(mA2dpService); 836 // ACL is connected, lets simulate this. 837 doReturn(STATE_CONNECTED).when(mAdapterService).getConnectionState(mDevice); 838 doReturn(mDevice).when(mDatabaseManager).getMostRecentlyConnectedA2dpDevice(); 839 doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any()); 840 doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any()); 841 842 // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP 843 // To enable that we need to make sure that HeadsetService returns the device among a list 844 // of connected devices 845 doReturn(List.of(mDevice)).when(mHeadsetService).getConnectedDevices(); 846 847 // We send a connection success event for one profile since the re-connect *only* works if 848 // we have already connected successfully over one of the profiles 849 updateProfileConnectionStateHelper( 850 mDevice, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED); 851 852 // Check that we get a call to A2DP reconnect 853 mInOrder.verify(mA2dpService).connect(mDevice); 854 855 // We send a connection failure event for the attempted profile, and keep the connected 856 // profile connected. 857 updateProfileConnectionStateHelper( 858 mDevice, BluetoothProfile.A2DP, STATE_CONNECTING, STATE_DISCONNECTED); 859 860 // Verify no one changes the priority of the failed profile 861 mInOrder.verify(mA2dpService, never()).setConnectionPolicy(eq(mDevice), anyInt()); 862 863 // Send a connection success event for one profile again without disconnecting all profiles 864 updateProfileConnectionStateHelper( 865 mDevice, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED); 866 867 // Check that we won't get a call to A2DP reconnect again before all profiles disconnected 868 mInOrder.verify(mA2dpService, never()).connect(mDevice); 869 870 // Send a disconnection event for all connected profiles 871 doReturn(Collections.emptyList()).when(mHeadsetService).getConnectedDevices(); 872 updateProfileConnectionStateHelper( 873 mDevice, BluetoothProfile.HEADSET, STATE_CONNECTED, STATE_DISCONNECTED); 874 875 // Send a connection success event for one profile again to trigger re-connect 876 doReturn(List.of(mDevice)).when(mHeadsetService).getConnectedDevices(); 877 updateProfileConnectionStateHelper( 878 mDevice, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED); 879 880 // Check that we get a call to A2DP connect again 881 mInOrder.verify(mA2dpService).connect(mDevice); 882 } 883 884 /** 885 * Test that when the adapter is turned ON then call auto connect on devices that only has HFP 886 * enabled. NOTE that the assumption is that we have already done the pairing previously and 887 * hence the priorities for the device is already set to AUTO_CONNECT over HFP (as part of post 888 * pairing process). 889 */ 890 @Test 891 @DisableFlags(Flags.FLAG_AUTO_CONNECT_ON_MULTIPLE_HFP_WHEN_NO_A2DP_DEVICE) testAutoConnectHfpOnly()892 public void testAutoConnectHfpOnly() { 893 894 // Return desired values from the mocked object(s) 895 doReturn(false).when(mAdapterService).isQuietModeEnabled(); 896 897 MetadataDatabase mDatabase = 898 Room.inMemoryDatabaseBuilder( 899 InstrumentationRegistry.getInstrumentation().getTargetContext(), 900 MetadataDatabase.class) 901 .build(); 902 DatabaseManager db = new DatabaseManager(mAdapterService); 903 doReturn(db).when(mAdapterService).getDatabase(); 904 PhonePolicy phonePolicy = 905 new PhonePolicy(mAdapterService, mLooper.getLooper(), mServiceFactory); 906 907 db.start(mDatabase); 908 TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); 909 910 // Return a device that is HFP only 911 912 db.setConnection(mDevice, BluetoothProfile.HEADSET); 913 doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(eq(mDevice)); 914 915 // wait for all MSG_UPDATE_DATABASE 916 TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); 917 918 phonePolicy.autoConnect(); 919 920 // Check that we got a request to connect over HFP for each device 921 verify(mHeadsetService).connect(eq(mDevice)); 922 } 923 924 @Test 925 @EnableFlags(Flags.FLAG_AUTO_CONNECT_ON_MULTIPLE_HFP_WHEN_NO_A2DP_DEVICE) autoConnect_whenMultiHfp_startConnection()926 public void autoConnect_whenMultiHfp_startConnection() { 927 // Return desired values from the mocked object(s) 928 doReturn(false).when(mAdapterService).isQuietModeEnabled(); 929 930 MetadataDatabase mDatabase = 931 Room.inMemoryDatabaseBuilder( 932 InstrumentationRegistry.getInstrumentation().getTargetContext(), 933 MetadataDatabase.class) 934 .build(); 935 DatabaseManager db = new DatabaseManager(mAdapterService); 936 doReturn(db).when(mAdapterService).getDatabase(); 937 PhonePolicy phonePolicy = 938 new PhonePolicy(mAdapterService, mLooper.getLooper(), mServiceFactory); 939 940 db.start(mDatabase); 941 TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); 942 943 List<BluetoothDevice> devices = 944 List.of(getTestDevice(1), getTestDevice(2), getTestDevice(3)); 945 946 for (BluetoothDevice device : devices) { 947 db.setConnection(device, BluetoothProfile.HEADSET); 948 doReturn(CONNECTION_POLICY_ALLOWED) 949 .when(mHeadsetService) 950 .getConnectionPolicy(eq(device)); 951 } 952 // wait for all MSG_UPDATE_DATABASE 953 TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); 954 955 phonePolicy.autoConnect(); 956 957 // Check that we got a request to connect over HFP for each device 958 for (BluetoothDevice device : devices) { 959 verify(mHeadsetService).connect(eq(device)); 960 } 961 } 962 963 @Test 964 @EnableFlags(Flags.FLAG_AUTO_CONNECT_ON_MULTIPLE_HFP_WHEN_NO_A2DP_DEVICE) autoConnect_whenMultiHfpAndDisconnection_startConnection()965 public void autoConnect_whenMultiHfpAndDisconnection_startConnection() { 966 // Return desired values from the mocked object(s) 967 doReturn(false).when(mAdapterService).isQuietModeEnabled(); 968 969 MetadataDatabase mDatabase = 970 Room.inMemoryDatabaseBuilder( 971 InstrumentationRegistry.getInstrumentation().getTargetContext(), 972 MetadataDatabase.class) 973 .build(); 974 DatabaseManager db = new DatabaseManager(mAdapterService); 975 doReturn(db).when(mAdapterService).getDatabase(); 976 PhonePolicy phonePolicy = 977 new PhonePolicy(mAdapterService, mLooper.getLooper(), mServiceFactory); 978 979 db.start(mDatabase); 980 TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); 981 982 BluetoothDevice deviceToDisconnect = getTestDevice(0); 983 db.setConnection(deviceToDisconnect, BluetoothProfile.HEADSET); 984 doReturn(CONNECTION_POLICY_ALLOWED) 985 .when(mHeadsetService) 986 .getConnectionPolicy(eq(deviceToDisconnect)); 987 988 List<BluetoothDevice> devices = 989 List.of(getTestDevice(1), getTestDevice(2), getTestDevice(3)); 990 991 for (BluetoothDevice device : devices) { 992 db.setConnection(device, BluetoothProfile.HEADSET); 993 doReturn(CONNECTION_POLICY_ALLOWED) 994 .when(mHeadsetService) 995 .getConnectionPolicy(eq(device)); 996 } 997 998 db.setDisconnection(deviceToDisconnect, BluetoothProfile.HEADSET); 999 1000 // wait for all MSG_UPDATE_DATABASE 1001 TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper()); 1002 1003 phonePolicy.autoConnect(); 1004 1005 // Check that we got a request to connect over HFP for each device 1006 for (BluetoothDevice device : devices) { 1007 verify(mHeadsetService).connect(eq(device)); 1008 } 1009 // Except for the device that was manually disconnected 1010 verify(mHeadsetService, never()).connect(eq(deviceToDisconnect)); 1011 } 1012 1013 /** 1014 * Test that a second device will auto-connect if there is already one connected device. 1015 * 1016 * <p>Even though we currently only set one device to be auto connect. The consumer of the auto 1017 * connect property works independently so that we will connect to all devices that are in auto 1018 * connect mode. 1019 */ 1020 @Test testAutoConnectMultipleDevices()1021 public void testAutoConnectMultipleDevices() { 1022 final int kMaxTestDevices = 3; 1023 BluetoothDevice[] testDevices = new BluetoothDevice[kMaxTestDevices]; 1024 ArrayList<BluetoothDevice> hsConnectedDevices = new ArrayList<>(); 1025 ArrayList<BluetoothDevice> a2dpConnectedDevices = new ArrayList<>(); 1026 1027 for (int i = 0; i < kMaxTestDevices; i++) { 1028 BluetoothDevice testDevice = getTestDevice(i); 1029 testDevices[i] = testDevice; 1030 1031 // ACL is connected, lets simulate this. 1032 when(mAdapterService.getConnectionState(testDevice)).thenReturn(STATE_CONNECTED); 1033 1034 // Return PRIORITY_AUTO_CONNECT over HFP and A2DP. This would imply that the profiles 1035 // are auto-connectable. 1036 when(mHeadsetService.getConnectionPolicy(testDevice)) 1037 .thenReturn(CONNECTION_POLICY_ALLOWED); 1038 when(mA2dpService.getConnectionPolicy(testDevice)) 1039 .thenReturn(CONNECTION_POLICY_ALLOWED); 1040 // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP 1041 // To enable that we need to make sure that HeadsetService returns the device as list 1042 // of connected devices. 1043 hsConnectedDevices.add(testDevice); 1044 // Connect A2DP for all devices except the last one 1045 if (i < (kMaxTestDevices - 2)) { 1046 a2dpConnectedDevices.add(testDevice); 1047 } 1048 } 1049 BluetoothDevice a2dpNotConnectedDevice1 = hsConnectedDevices.get(kMaxTestDevices - 1); 1050 BluetoothDevice a2dpNotConnectedDevice2 = hsConnectedDevices.get(kMaxTestDevices - 2); 1051 1052 when(mAdapterService.getBondedDevices()).thenReturn(testDevices); 1053 when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices); 1054 when(mA2dpService.getConnectedDevices()).thenReturn(a2dpConnectedDevices); 1055 // Two of the A2DP devices are not connected 1056 when(mA2dpService.getConnectionState(a2dpNotConnectedDevice1)) 1057 .thenReturn(STATE_DISCONNECTED); 1058 when(mA2dpService.getConnectionState(a2dpNotConnectedDevice2)) 1059 .thenReturn(STATE_DISCONNECTED); 1060 1061 // We send a connection successful for one profile since the re-connect *only* works if we 1062 // have already connected successfully over one of the profiles 1063 updateProfileConnectionStateHelper( 1064 a2dpNotConnectedDevice1, 1065 BluetoothProfile.HEADSET, 1066 STATE_DISCONNECTED, 1067 STATE_CONNECTED); 1068 1069 // We send a connection successful for one profile since the re-connect *only* works if we 1070 // have already connected successfully over one of the profiles 1071 updateProfileConnectionStateHelper( 1072 a2dpNotConnectedDevice2, 1073 BluetoothProfile.HEADSET, 1074 STATE_DISCONNECTED, 1075 STATE_CONNECTED); 1076 1077 // Check that we get a call to A2DP connect 1078 verify(mA2dpService).connect(eq(a2dpNotConnectedDevice1)); 1079 verify(mA2dpService).connect(eq(a2dpNotConnectedDevice2)); 1080 } 1081 1082 /** 1083 * Test that the connection policy of all devices are set as appropriate if there is one 1084 * connected device. - The HFP and A2DP connect priority for connected devices is set to 1085 * BluetoothProfile.PRIORITY_AUTO_CONNECT - The HFP and A2DP connect priority for bonded devices 1086 * is set to CONNECTION_POLICY_ALLOWED 1087 */ 1088 @Test testSetConnectionPolicyMultipleDevices()1089 public void testSetConnectionPolicyMultipleDevices() { 1090 // testDevices[0] - connected for both HFP and A2DP 1091 // testDevices[1] - connected only for HFP - will auto-connect for A2DP 1092 // testDevices[2] - connected only for A2DP - will auto-connect for HFP 1093 // testDevices[3] - not connected 1094 final int kMaxTestDevices = 4; 1095 BluetoothDevice[] testDevices = new BluetoothDevice[kMaxTestDevices]; 1096 ArrayList<BluetoothDevice> hsConnectedDevices = new ArrayList<>(); 1097 ArrayList<BluetoothDevice> a2dpConnectedDevices = new ArrayList<>(); 1098 1099 for (int i = 0; i < kMaxTestDevices; i++) { 1100 BluetoothDevice testDevice = getTestDevice(i); 1101 testDevices[i] = testDevice; 1102 1103 // ACL is connected, lets simulate this. 1104 when(mAdapterService.getConnectionState(testDevices[i])).thenReturn(STATE_CONNECTED); 1105 1106 // Connect HFP and A2DP for each device as appropriate. 1107 // Return PRIORITY_AUTO_CONNECT only for testDevices[0] 1108 if (i == 0) { 1109 hsConnectedDevices.add(testDevice); 1110 a2dpConnectedDevices.add(testDevice); 1111 when(mHeadsetService.getConnectionPolicy(testDevice)) 1112 .thenReturn(CONNECTION_POLICY_ALLOWED); 1113 when(mA2dpService.getConnectionPolicy(testDevice)) 1114 .thenReturn(CONNECTION_POLICY_ALLOWED); 1115 } 1116 if (i == 1) { 1117 hsConnectedDevices.add(testDevice); 1118 when(mHeadsetService.getConnectionPolicy(testDevice)) 1119 .thenReturn(CONNECTION_POLICY_ALLOWED); 1120 when(mA2dpService.getConnectionPolicy(testDevice)) 1121 .thenReturn(CONNECTION_POLICY_ALLOWED); 1122 } 1123 if (i == 2) { 1124 a2dpConnectedDevices.add(testDevice); 1125 when(mHeadsetService.getConnectionPolicy(testDevice)) 1126 .thenReturn(CONNECTION_POLICY_ALLOWED); 1127 when(mA2dpService.getConnectionPolicy(testDevice)) 1128 .thenReturn(CONNECTION_POLICY_ALLOWED); 1129 } 1130 if (i == 3) { 1131 // Device not connected 1132 when(mHeadsetService.getConnectionPolicy(testDevice)) 1133 .thenReturn(CONNECTION_POLICY_ALLOWED); 1134 when(mA2dpService.getConnectionPolicy(testDevice)) 1135 .thenReturn(CONNECTION_POLICY_ALLOWED); 1136 } 1137 } 1138 when(mAdapterService.getBondedDevices()).thenReturn(testDevices); 1139 when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices); 1140 when(mA2dpService.getConnectedDevices()).thenReturn(a2dpConnectedDevices); 1141 // Some of the devices are not connected 1142 // testDevices[0] - connected for both HFP and A2DP 1143 when(mHeadsetService.getConnectionState(testDevices[0])).thenReturn(STATE_CONNECTED); 1144 when(mA2dpService.getConnectionState(testDevices[0])).thenReturn(STATE_CONNECTED); 1145 // testDevices[1] - connected only for HFP - will auto-connect for A2DP 1146 when(mHeadsetService.getConnectionState(testDevices[1])).thenReturn(STATE_CONNECTED); 1147 when(mA2dpService.getConnectionState(testDevices[1])).thenReturn(STATE_DISCONNECTED); 1148 // testDevices[2] - connected only for A2DP - will auto-connect for HFP 1149 when(mHeadsetService.getConnectionState(testDevices[2])).thenReturn(STATE_DISCONNECTED); 1150 when(mA2dpService.getConnectionState(testDevices[2])).thenReturn(STATE_CONNECTED); 1151 // testDevices[3] - not connected 1152 when(mHeadsetService.getConnectionState(testDevices[3])).thenReturn(STATE_DISCONNECTED); 1153 when(mA2dpService.getConnectionState(testDevices[3])).thenReturn(STATE_DISCONNECTED); 1154 1155 // Generate connection state changed for HFP for testDevices[1] and trigger 1156 // auto-connect for A2DP. 1157 updateProfileConnectionStateHelper( 1158 testDevices[1], BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED); 1159 1160 // Check that we get a call to A2DP connect 1161 verify(mA2dpService).connect(eq(testDevices[1])); 1162 1163 // testDevices[1] auto-connect completed for A2DP 1164 a2dpConnectedDevices.add(testDevices[1]); 1165 when(mA2dpService.getConnectedDevices()).thenReturn(a2dpConnectedDevices); 1166 when(mA2dpService.getConnectionState(testDevices[1])).thenReturn(STATE_CONNECTED); 1167 1168 // Check the connect priorities for all devices 1169 // - testDevices[0] - connected for HFP and A2DP: setConnectionPolicy() should not be called 1170 // - testDevices[1] - connection state changed for HFP should no longer trigger auto 1171 // connect priority change since it is now triggered by A2DP active 1172 // device change intent 1173 // - testDevices[2] - connected for A2DP: setConnectionPolicy() should not be called 1174 // - testDevices[3] - not connected for HFP nor A2DP: setConnectionPolicy() should not be 1175 // called 1176 verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[0]), anyInt()); 1177 verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[0]), anyInt()); 1178 verify(mHeadsetService, never()) 1179 .setConnectionPolicy( 1180 eq(testDevices[1]), eq(BluetoothProfile.PRIORITY_AUTO_CONNECT)); 1181 verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[1]), anyInt()); 1182 verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[2]), anyInt()); 1183 verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[2]), anyInt()); 1184 verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[3]), anyInt()); 1185 verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[3]), anyInt()); 1186 clearInvocations(mHeadsetService, mA2dpService); 1187 1188 // Generate connection state changed for A2DP for testDevices[2] and trigger 1189 // auto-connect for HFP. 1190 updateProfileConnectionStateHelper( 1191 testDevices[2], BluetoothProfile.A2DP, STATE_DISCONNECTED, STATE_CONNECTED); 1192 1193 // Check that we get a call to HFP connect 1194 verify(mHeadsetService).connect(eq(testDevices[2])); 1195 1196 // testDevices[2] auto-connect completed for HFP 1197 hsConnectedDevices.add(testDevices[2]); 1198 when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices); 1199 when(mHeadsetService.getConnectionState(testDevices[2])).thenReturn(STATE_CONNECTED); 1200 1201 // Check the connect priorities for all devices 1202 // - testDevices[0] - connected for HFP and A2DP: setConnectionPolicy() should not be called 1203 // - testDevices[1] - connected for HFP and A2DP: setConnectionPolicy() should not be called 1204 // - testDevices[2] - connection state changed for A2DP should no longer trigger auto 1205 // connect priority change since it is now triggered by A2DP 1206 // active device change intent 1207 // - testDevices[3] - not connected for HFP nor A2DP: setConnectionPolicy() should not be 1208 // called 1209 verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[0]), anyInt()); 1210 verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[0]), anyInt()); 1211 verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[1]), anyInt()); 1212 verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[1]), anyInt()); 1213 verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[2]), anyInt()); 1214 verify(mA2dpService, never()) 1215 .setConnectionPolicy( 1216 eq(testDevices[2]), eq(BluetoothProfile.PRIORITY_AUTO_CONNECT)); 1217 verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[3]), anyInt()); 1218 verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[3]), anyInt()); 1219 clearInvocations(mHeadsetService, mA2dpService); 1220 } 1221 1222 /** Test that we will not try to reconnect on a profile if all the connections failed */ 1223 @Test testNoReconnectOnNoConnect()1224 public void testNoReconnectOnNoConnect() { 1225 BluetoothDevice[] bondedDevices = {mDevice}; 1226 doReturn(bondedDevices).when(mAdapterService).getBondedDevices(); 1227 1228 doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any()); 1229 doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any()); 1230 1231 mPhonePolicy.handleAclConnected(mDevice); 1232 mLooper.dispatchAll(); 1233 1234 // Check that we don't get any calls to reconnect 1235 verify(mA2dpService, never()).connect(any()); 1236 verify(mHeadsetService, never()).connect(any()); 1237 } 1238 1239 /** 1240 * Test that we will not try to reconnect on a profile if all the connections failed with 1241 * multiple devices 1242 */ 1243 @Test testNoReconnectOnNoConnect_MultiDevice()1244 public void testNoReconnectOnNoConnect_MultiDevice() { 1245 BluetoothDevice[] bondedDevices = {mDevice, mDevice2}; 1246 doReturn(bondedDevices).when(mAdapterService).getBondedDevices(); 1247 1248 doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any()); 1249 doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any()); 1250 1251 doReturn(List.of(mDevice2)).when(mHeadsetService).getConnectedDevices(); 1252 doReturn(List.of(mDevice2)).when(mA2dpService).getConnectedDevices(); 1253 1254 doReturn(STATE_CONNECTED).when(mA2dpService).getConnectionState(eq(mDevice2)); 1255 doReturn(STATE_CONNECTED).when(mHeadsetService).getConnectionState(eq(mDevice2)); 1256 1257 doReturn(STATE_CONNECTED).when(mAdapterService).getConnectionState(any()); 1258 1259 // We send a connection successful for one profile since the re-connect *only* works if we 1260 // have already connected successfully over one of the profiles 1261 mPhonePolicy.handleAclConnected(mDevice); 1262 mLooper.dispatchAll(); 1263 1264 // Check that we don't get any calls to reconnect 1265 verify(mA2dpService, never()).connect(eq(mDevice)); 1266 verify(mHeadsetService, never()).connect(eq(mDevice)); 1267 } 1268 1269 /** 1270 * Test that we will try to connect to other profiles of a device if it is partially connected 1271 */ 1272 @Test testReconnectOnPartialConnect_MultiDevice()1273 public void testReconnectOnPartialConnect_MultiDevice() { 1274 BluetoothDevice[] bondedDevices = {mDevice, mDevice2}; 1275 doReturn(bondedDevices).when(mAdapterService).getBondedDevices(); 1276 1277 doReturn(List.of(mDevice2)).when(mHeadsetService).getConnectedDevices(); 1278 1279 doReturn(STATE_CONNECTED).when(mAdapterService).getConnectionState(eq(mDevice2)); 1280 doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any()); 1281 1282 // We send a connection successful for one profile since the re-connect *only* works if we 1283 // have already connected successfully over one of the profiles 1284 updateProfileConnectionStateHelper( 1285 mDevice2, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED); 1286 1287 // Check that we do get A2DP call to reconnect, because HEADSET just got connected 1288 verify(mA2dpService).connect(eq(mDevice2)); 1289 } 1290 1291 @Test discoversUuids_whenNullUuids_policyIsNotChanged()1292 public void discoversUuids_whenNullUuids_policyIsNotChanged() { 1293 mPhonePolicy.onUuidsDiscovered(mDevice, null); 1294 1295 verify(mHeadsetService, never()).setConnectionPolicy(any(), anyInt()); 1296 verify(mA2dpService, never()).setConnectionPolicy(any(), anyInt()); 1297 } 1298 updateProfileConnectionStateHelper( BluetoothDevice device, int profileId, int prevState, int nextState)1299 private void updateProfileConnectionStateHelper( 1300 BluetoothDevice device, int profileId, int prevState, int nextState) { 1301 switch (profileId) { 1302 case BluetoothProfile.A2DP -> 1303 doReturn(nextState).when(mA2dpService).getConnectionState(device); 1304 case BluetoothProfile.HEADSET -> 1305 doReturn(nextState).when(mHeadsetService).getConnectionState(device); 1306 } 1307 mPhonePolicy.profileConnectionStateChanged(profileId, device, prevState, nextState); 1308 mLooper.dispatchAll(); 1309 mLooper.moveTimeForward(PhonePolicy.CONNECT_OTHER_PROFILES_TIMEOUT.toMillis()); 1310 mLooper.dispatchAll(); 1311 } 1312 } 1313