1 /* 2 * Copyright (C) 2021 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 android.bluetooth.cts; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; 21 import static android.bluetooth.BluetoothDevice.ACCESS_ALLOWED; 22 import static android.bluetooth.BluetoothDevice.ACCESS_REJECTED; 23 import static android.bluetooth.BluetoothDevice.ACCESS_UNKNOWN; 24 import static android.bluetooth.BluetoothDevice.TRANSPORT_AUTO; 25 import static android.bluetooth.BluetoothDevice.TRANSPORT_BREDR; 26 import static android.bluetooth.BluetoothDevice.TRANSPORT_LE; 27 28 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; 29 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; 30 31 import static com.android.compatibility.common.util.SystemUtil.runShellCommand; 32 33 import static com.google.common.truth.Truth.assertThat; 34 35 import static org.junit.Assert.assertThrows; 36 import static org.junit.Assume.assumeTrue; 37 import static org.mockito.Mockito.any; 38 import static org.mockito.Mockito.mock; 39 import static org.mockito.Mockito.timeout; 40 import static org.mockito.Mockito.verify; 41 42 import android.app.UiAutomation; 43 import android.bluetooth.BluetoothAdapter; 44 import android.bluetooth.BluetoothDevice; 45 import android.bluetooth.BluetoothDevice.BluetoothAddress; 46 import android.bluetooth.BluetoothManager; 47 import android.bluetooth.BluetoothProfile; 48 import android.bluetooth.BluetoothSinkAudioPolicy; 49 import android.bluetooth.BluetoothSocket; 50 import android.bluetooth.BluetoothSocketException; 51 import android.bluetooth.BluetoothStatusCodes; 52 import android.bluetooth.OobData; 53 import android.bluetooth.test_utils.Permissions; 54 import android.content.AttributionSource; 55 import android.content.BroadcastReceiver; 56 import android.content.Context; 57 import android.content.Intent; 58 import android.content.IntentFilter; 59 import android.content.pm.PackageManager; 60 import android.platform.test.annotations.RequiresFlagsEnabled; 61 import android.platform.test.flag.junit.CheckFlagsRule; 62 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 63 64 import androidx.test.ext.junit.runners.AndroidJUnit4; 65 import androidx.test.filters.LargeTest; 66 import androidx.test.platform.app.InstrumentationRegistry; 67 68 import com.android.bluetooth.flags.Flags; 69 import com.android.compatibility.common.util.CddTest; 70 71 import org.hamcrest.Matcher; 72 import org.hamcrest.core.AllOf; 73 import org.junit.After; 74 import org.junit.Before; 75 import org.junit.Rule; 76 import org.junit.Test; 77 import org.junit.runner.RunWith; 78 import org.mockito.hamcrest.MockitoHamcrest; 79 80 import java.io.IOException; 81 import java.io.UnsupportedEncodingException; 82 import java.time.Duration; 83 import java.util.List; 84 import java.util.UUID; 85 86 @RunWith(AndroidJUnit4.class) 87 @LargeTest 88 public class BluetoothDeviceTest { 89 90 private Context mContext; 91 private boolean mHasBluetooth; 92 private boolean mHasCompanionDevice; 93 private BluetoothAdapter mAdapter; 94 private UiAutomation mUiAutomation; 95 96 private final String mFakeDeviceAddress = "00:11:22:AA:BB:CC"; 97 private BluetoothDevice mFakeDevice; 98 private int mFakePsm = 100; 99 private UUID mFakeUuid = UUID.fromString("0000111E-0000-1000-8000-00805F9B34FB"); 100 101 @Rule 102 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 103 104 @Before setUp()105 public void setUp() throws Exception { 106 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 107 108 mHasBluetooth = 109 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH); 110 111 mHasCompanionDevice = 112 mContext.getPackageManager() 113 .hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP); 114 115 if (mHasBluetooth && mHasCompanionDevice) { 116 BluetoothManager manager = mContext.getSystemService(BluetoothManager.class); 117 mAdapter = manager.getAdapter(); 118 mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); 119 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 120 assertThat(BTAdapterUtils.enableAdapter(mAdapter, mContext)).isTrue(); 121 mFakeDevice = mAdapter.getRemoteDevice(mFakeDeviceAddress); 122 } 123 } 124 125 @After tearDown()126 public void tearDown() throws Exception { 127 if (mHasBluetooth && mHasCompanionDevice) { 128 mAdapter = null; 129 mUiAutomation.dropShellPermissionIdentity(); 130 } 131 } 132 133 @Test setAlias_getAlias()134 public void setAlias_getAlias() { 135 // Skip the test if bluetooth or companion device are not present. 136 assumeTrue(mHasBluetooth && mHasCompanionDevice); 137 138 int userId = mContext.getUser().getIdentifier(); 139 String packageName = mContext.getOpPackageName(); 140 141 AttributionSource source = AttributionSource.myAttributionSource(); 142 assertThat(source.getPackageName()).isEqualTo("android.bluetooth.cts"); 143 144 // Verifies that when there is no alias, we return the device name 145 assertThat(mFakeDevice.getAlias()).isNull(); 146 147 assertThrows(IllegalArgumentException.class, () -> mFakeDevice.setAlias("")); 148 149 String testDeviceAlias = "Test Device Alias"; 150 151 // This should throw a SecurityException because there is no CDM association 152 assertThrows( 153 "BluetoothDevice.setAlias without" 154 + " a CDM association or BLUETOOTH_PRIVILEGED permission", 155 SecurityException.class, 156 () -> mFakeDevice.setAlias(testDeviceAlias)); 157 158 runShellCommand( 159 String.format( 160 "cmd companiondevice associate %d %s %s", 161 userId, packageName, mFakeDeviceAddress)); 162 String output = runShellCommand("dumpsys companiondevice"); 163 assertThat(output).contains(packageName); 164 assertThat(output.toLowerCase()).contains(mFakeDeviceAddress.toLowerCase()); 165 166 // Takes time to update the CDM cache, so sleep to ensure the association is cached 167 try { 168 Thread.sleep(1000); 169 } catch (Exception e) { 170 e.printStackTrace(); 171 } 172 173 /* 174 * Device properties don't exist for non-existent BluetoothDevice, so calling setAlias with 175 * permissions should return false 176 */ 177 assertThat(mFakeDevice.setAlias(testDeviceAlias)) 178 .isEqualTo(BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED); 179 runShellCommand( 180 String.format( 181 "cmd companiondevice disassociate %d %s %s", 182 userId, packageName, mFakeDeviceAddress)); 183 184 assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue(); 185 assertThat(mFakeDevice.getAlias()).isNull(); 186 assertThat(mFakeDevice.setAlias(testDeviceAlias)) 187 .isEqualTo(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED); 188 } 189 190 @Test getAddressType()191 public void getAddressType() { 192 // Skip the test if bluetooth or companion device are not present. 193 assumeTrue(mHasBluetooth && mHasCompanionDevice); 194 195 assertThat(mFakeDevice.getAddressType()).isEqualTo(BluetoothDevice.ADDRESS_TYPE_PUBLIC); 196 } 197 198 @Test getIdentityAddress()199 public void getIdentityAddress() { 200 // Skip the test if bluetooth or companion device are not present. 201 assumeTrue(mHasBluetooth && mHasCompanionDevice); 202 203 // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission 204 assertThrows( 205 "No BLUETOOTH_PRIVILEGED permission", 206 SecurityException.class, 207 () -> mFakeDevice.getIdentityAddress()); 208 } 209 210 @Test 211 @RequiresFlagsEnabled(Flags.FLAG_IDENTITY_ADDRESS_TYPE_API) getIdentityAddressWithType()212 public void getIdentityAddressWithType() { 213 // Skip the test if bluetooth or companion device are not present. 214 assumeTrue(mHasBluetooth && mHasCompanionDevice); 215 216 // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission 217 assertThrows( 218 "No BLUETOOTH_PRIVILEGED permission", 219 SecurityException.class, 220 () -> mFakeDevice.getIdentityAddressWithType()); 221 } 222 223 @CddTest(requirements = {"7.4.3/C-2-1"}) 224 @Test 225 @RequiresFlagsEnabled(Flags.FLAG_IDENTITY_ADDRESS_TYPE_API) testBluetoothAddress()226 public void testBluetoothAddress() { 227 int addressType = BluetoothDevice.ADDRESS_TYPE_PUBLIC; 228 BluetoothAddress bluetoothAddress = new BluetoothAddress(mFakeDeviceAddress, addressType); 229 230 assertThat(bluetoothAddress.getAddress()).isEqualTo(mFakeDeviceAddress); 231 assertThat(bluetoothAddress.getAddressType()).isEqualTo(addressType); 232 } 233 234 @Test getConnectionHandle()235 public void getConnectionHandle() { 236 // Skip the test if bluetooth or companion device are not present. 237 assumeTrue(mHasBluetooth && mHasCompanionDevice); 238 239 // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission 240 assertThrows( 241 "No BLUETOOTH_PRIVILEGED permission", 242 SecurityException.class, 243 () -> mFakeDevice.getConnectionHandle(TRANSPORT_LE)); 244 245 // but it should work after we get the permission 246 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED); 247 assertThat(mFakeDevice.getConnectionHandle(TRANSPORT_LE)).isEqualTo(BluetoothDevice.ERROR); 248 } 249 250 @Test getAnonymizedAddress()251 public void getAnonymizedAddress() { 252 // Skip the test if bluetooth or companion device are not present. 253 assumeTrue(mHasBluetooth && mHasCompanionDevice); 254 255 assertThat(mFakeDevice.getAnonymizedAddress()).isEqualTo("XX:XX:XX:XX:BB:CC"); 256 } 257 258 @Test getBatteryLevel()259 public void getBatteryLevel() { 260 // Skip the test if bluetooth or companion device are not present. 261 assumeTrue(mHasBluetooth && mHasCompanionDevice); 262 263 assertThat(mFakeDevice.getBatteryLevel()).isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); 264 265 mUiAutomation.dropShellPermissionIdentity(); 266 assertThrows(SecurityException.class, () -> mFakeDevice.getBatteryLevel()); 267 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 268 269 assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue(); 270 assertThat(mFakeDevice.getBatteryLevel()) 271 .isEqualTo(BluetoothDevice.BATTERY_LEVEL_BLUETOOTH_OFF); 272 } 273 274 @Test isBondingInitiatedLocally()275 public void isBondingInitiatedLocally() { 276 // Skip the test if bluetooth or companion device are not present. 277 assumeTrue(mHasBluetooth && mHasCompanionDevice); 278 279 assertThat(mFakeDevice.isBondingInitiatedLocally()).isFalse(); 280 281 mUiAutomation.dropShellPermissionIdentity(); 282 assertThrows(SecurityException.class, () -> mFakeDevice.isBondingInitiatedLocally()); 283 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 284 285 assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue(); 286 assertThat(mFakeDevice.isBondingInitiatedLocally()).isFalse(); 287 } 288 289 @Test prepareToEnterProcess()290 public void prepareToEnterProcess() { 291 // Skip the test if bluetooth or companion device are not present. 292 assumeTrue(mHasBluetooth && mHasCompanionDevice); 293 294 mFakeDevice.prepareToEnterProcess(null); 295 } 296 297 @Test setPin()298 public void setPin() { 299 // Skip the test if bluetooth or companion device are not present. 300 assumeTrue(mHasBluetooth && mHasCompanionDevice); 301 302 assertThat(mFakeDevice.setPin((String) null)).isFalse(); 303 assertThat(mFakeDevice.setPin("12345678901234567")).isFalse(); // check PIN too big 304 305 assertThat(mFakeDevice.setPin("123456")).isFalse(); // device is not bonding 306 307 mUiAutomation.dropShellPermissionIdentity(); 308 assertThrows(SecurityException.class, () -> mFakeDevice.setPin("123456")); 309 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 310 311 assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue(); 312 assertThat(mFakeDevice.setPin("123456")).isFalse(); 313 } 314 315 @Test connect_disconnect()316 public void connect_disconnect() { 317 // Skip the test if bluetooth or companion device are not present. 318 assumeTrue(mHasBluetooth && mHasCompanionDevice); 319 320 // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission 321 assertThrows(SecurityException.class, () -> mFakeDevice.connect()); 322 assertThrows(SecurityException.class, () -> mFakeDevice.disconnect()); 323 } 324 325 @Test cancelBondProcess()326 public void cancelBondProcess() { 327 // Skip the test if bluetooth or companion device are not present. 328 assumeTrue(mHasBluetooth && mHasCompanionDevice); 329 330 mUiAutomation.dropShellPermissionIdentity(); 331 assertThrows(SecurityException.class, () -> mFakeDevice.cancelBondProcess()); 332 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 333 334 assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue(); 335 assertThat(mFakeDevice.cancelBondProcess()).isFalse(); 336 } 337 338 @Test createBond()339 public void createBond() { 340 // Skip the test if bluetooth or companion device are not present. 341 assumeTrue(mHasBluetooth && mHasCompanionDevice); 342 343 mUiAutomation.dropShellPermissionIdentity(); 344 assertThrows(SecurityException.class, () -> mFakeDevice.createBond(TRANSPORT_AUTO)); 345 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 346 347 assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue(); 348 assertThat(mFakeDevice.createBond(TRANSPORT_AUTO)).isFalse(); 349 } 350 351 @Test createBondOutOfBand()352 public void createBondOutOfBand() { 353 // Skip the test if bluetooth or companion device are not present. 354 assumeTrue(mHasBluetooth && mHasCompanionDevice); 355 356 OobData data = new OobData.ClassicBuilder(new byte[16], new byte[2], new byte[7]).build(); 357 358 assertThrows( 359 IllegalArgumentException.class, 360 () -> mFakeDevice.createBondOutOfBand(TRANSPORT_AUTO, null, null)); 361 362 mUiAutomation.dropShellPermissionIdentity(); 363 assertThrows( 364 SecurityException.class, 365 () -> mFakeDevice.createBondOutOfBand(TRANSPORT_AUTO, data, null)); 366 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 367 } 368 369 @Test getUuids()370 public void getUuids() { 371 // Skip the test if bluetooth or companion device are not present. 372 assumeTrue(mHasBluetooth && mHasCompanionDevice); 373 374 assertThat(mFakeDevice.getUuids()).isNull(); 375 mUiAutomation.dropShellPermissionIdentity(); 376 assertThrows(SecurityException.class, () -> mFakeDevice.getUuids()); 377 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 378 379 assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue(); 380 assertThat(mFakeDevice.getUuids()).isNull(); 381 } 382 383 @Test isEncrypted()384 public void isEncrypted() { 385 // Skip the test if bluetooth or companion device are not present. 386 assumeTrue(mHasBluetooth && mHasCompanionDevice); 387 388 // Device is not connected 389 assertThat(mFakeDevice.isEncrypted()).isFalse(); 390 391 mUiAutomation.dropShellPermissionIdentity(); 392 assertThrows(SecurityException.class, () -> mFakeDevice.isEncrypted()); 393 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 394 395 assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue(); 396 assertThat(mFakeDevice.isEncrypted()).isFalse(); 397 } 398 399 @Test removeBond()400 public void removeBond() { 401 // Skip the test if bluetooth or companion device are not present. 402 assumeTrue(mHasBluetooth && mHasCompanionDevice); 403 404 // Device is not bonded 405 assertThat(mFakeDevice.removeBond()).isFalse(); 406 407 mUiAutomation.dropShellPermissionIdentity(); 408 assertThrows(SecurityException.class, () -> mFakeDevice.removeBond()); 409 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 410 411 assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue(); 412 assertThat(mFakeDevice.removeBond()).isFalse(); 413 } 414 415 @Test setPinByteArray()416 public void setPinByteArray() { 417 // Skip the test if bluetooth or companion device are not present. 418 assumeTrue(mHasBluetooth && mHasCompanionDevice); 419 420 assertThrows(NullPointerException.class, () -> mFakeDevice.setPin((byte[]) null)); 421 422 // check PIN too big 423 assertThat(mFakeDevice.setPin(convertPinToBytes("12345678901234567"))).isFalse(); 424 assertThat(mFakeDevice.setPin(convertPinToBytes("123456"))) 425 .isFalse(); // device is not bonding 426 427 mUiAutomation.dropShellPermissionIdentity(); 428 assertThrows( 429 SecurityException.class, () -> mFakeDevice.setPin(convertPinToBytes("123456"))); 430 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 431 432 assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue(); 433 assertThat(mFakeDevice.setPin(convertPinToBytes("123456"))).isFalse(); 434 } 435 436 @Test connectGatt()437 public void connectGatt() { 438 // Skip the test if bluetooth or companion device are not present. 439 assumeTrue(mHasBluetooth && mHasCompanionDevice); 440 441 assertThrows( 442 NullPointerException.class, 443 () -> 444 mFakeDevice.connectGatt( 445 mContext, 446 false, 447 null, 448 TRANSPORT_AUTO, 449 BluetoothDevice.PHY_LE_1M_MASK)); 450 451 assertThrows( 452 NullPointerException.class, 453 () -> 454 mFakeDevice.connectGatt( 455 mContext, 456 false, 457 null, 458 TRANSPORT_AUTO, 459 BluetoothDevice.PHY_LE_1M_MASK, 460 null)); 461 } 462 463 @Test fetchUuidsWithSdp()464 public void fetchUuidsWithSdp() { 465 // Skip the test if bluetooth or companion device are not present. 466 assumeTrue(mHasBluetooth && mHasCompanionDevice); 467 468 assertThat(mFakeDevice.fetchUuidsWithSdp()).isTrue(); 469 470 // TRANSPORT_AUTO doesn't need BLUETOOTH_PRIVILEGED permission 471 assertThat(mFakeDevice.fetchUuidsWithSdp(TRANSPORT_AUTO)).isTrue(); 472 473 // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission 474 assertThrows(SecurityException.class, () -> mFakeDevice.fetchUuidsWithSdp(TRANSPORT_BREDR)); 475 assertThrows(SecurityException.class, () -> mFakeDevice.fetchUuidsWithSdp(TRANSPORT_LE)); 476 477 assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue(); 478 assertThat(mFakeDevice.fetchUuidsWithSdp(TRANSPORT_AUTO)).isFalse(); 479 } 480 481 @Test messageAccessPermission()482 public void messageAccessPermission() { 483 // Skip the test if bluetooth or companion device are not present 484 // or if MAP is not enabled. 485 assumeTrue( 486 mHasBluetooth 487 && mHasCompanionDevice 488 && TestUtils.isProfileEnabled(BluetoothProfile.MAP)); 489 490 // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission 491 assertThrows( 492 SecurityException.class, 493 () -> mFakeDevice.setMessageAccessPermission(ACCESS_ALLOWED)); 494 assertThrows( 495 SecurityException.class, 496 () -> mFakeDevice.setMessageAccessPermission(ACCESS_UNKNOWN)); 497 assertThrows( 498 SecurityException.class, 499 () -> mFakeDevice.setMessageAccessPermission(ACCESS_REJECTED)); 500 501 TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED); 502 503 // Should be able to set permissions after adopting the BLUETOOTH_PRIVILEGED permission 504 assertThat(mFakeDevice.setMessageAccessPermission(ACCESS_UNKNOWN)).isTrue(); 505 assertThat(mFakeDevice.getMessageAccessPermission()).isEqualTo(ACCESS_UNKNOWN); 506 assertThat(mFakeDevice.setMessageAccessPermission(ACCESS_ALLOWED)).isTrue(); 507 assertThat(mFakeDevice.getMessageAccessPermission()).isEqualTo(ACCESS_ALLOWED); 508 assertThat(mFakeDevice.setMessageAccessPermission(ACCESS_REJECTED)).isTrue(); 509 assertThat(mFakeDevice.getMessageAccessPermission()).isEqualTo(ACCESS_REJECTED); 510 } 511 512 @Test phonebookAccessPermission()513 public void phonebookAccessPermission() { 514 // Skip the test if bluetooth or companion device are not present 515 // or if PBAP is not enabled. 516 assumeTrue( 517 mHasBluetooth 518 && mHasCompanionDevice 519 && TestUtils.isProfileEnabled(BluetoothProfile.PBAP)); 520 521 // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission 522 assertThrows( 523 SecurityException.class, 524 () -> mFakeDevice.setPhonebookAccessPermission(ACCESS_ALLOWED)); 525 assertThrows( 526 SecurityException.class, 527 () -> mFakeDevice.setPhonebookAccessPermission(ACCESS_UNKNOWN)); 528 assertThrows( 529 SecurityException.class, 530 () -> mFakeDevice.setPhonebookAccessPermission(ACCESS_REJECTED)); 531 532 TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED); 533 534 // Should be able to set permissions after adopting the BLUETOOTH_PRIVILEGED permission 535 assertThat(mFakeDevice.setPhonebookAccessPermission(ACCESS_UNKNOWN)).isTrue(); 536 assertThat(mFakeDevice.getPhonebookAccessPermission()).isEqualTo(ACCESS_UNKNOWN); 537 assertThat(mFakeDevice.setPhonebookAccessPermission(ACCESS_ALLOWED)).isTrue(); 538 assertThat(mFakeDevice.getPhonebookAccessPermission()).isEqualTo(ACCESS_ALLOWED); 539 assertThat(mFakeDevice.setPhonebookAccessPermission(ACCESS_REJECTED)).isTrue(); 540 assertThat(mFakeDevice.getPhonebookAccessPermission()).isEqualTo(ACCESS_REJECTED); 541 } 542 543 @Test simAccessPermission()544 public void simAccessPermission() { 545 // Skip the test if bluetooth or companion device are not present 546 // or if SAP is not enabled. 547 assumeTrue( 548 mHasBluetooth 549 && mHasCompanionDevice 550 && TestUtils.isProfileEnabled(BluetoothProfile.SAP)); 551 552 // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission 553 assertThrows( 554 SecurityException.class, () -> mFakeDevice.setSimAccessPermission(ACCESS_ALLOWED)); 555 assertThrows( 556 SecurityException.class, () -> mFakeDevice.setSimAccessPermission(ACCESS_UNKNOWN)); 557 assertThrows( 558 SecurityException.class, () -> mFakeDevice.setSimAccessPermission(ACCESS_REJECTED)); 559 560 TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED); 561 562 // Should be able to set permissions after adopting the BLUETOOTH_PRIVILEGED permission 563 assertThat(mFakeDevice.setSimAccessPermission(ACCESS_UNKNOWN)).isTrue(); 564 assertThat(mFakeDevice.getSimAccessPermission()).isEqualTo(ACCESS_UNKNOWN); 565 assertThat(mFakeDevice.setSimAccessPermission(ACCESS_ALLOWED)).isTrue(); 566 assertThat(mFakeDevice.getSimAccessPermission()).isEqualTo(ACCESS_ALLOWED); 567 assertThat(mFakeDevice.setSimAccessPermission(ACCESS_REJECTED)).isTrue(); 568 assertThat(mFakeDevice.getSimAccessPermission()).isEqualTo(ACCESS_REJECTED); 569 } 570 571 @Test isRequestAudioPolicyAsSinkSupported()572 public void isRequestAudioPolicyAsSinkSupported() { 573 // Skip the test if bluetooth or companion device are not present. 574 assumeTrue(mHasBluetooth && mHasCompanionDevice); 575 576 assertThrows( 577 SecurityException.class, () -> mFakeDevice.isRequestAudioPolicyAsSinkSupported()); 578 579 TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED); 580 581 assertThat(mFakeDevice.isRequestAudioPolicyAsSinkSupported()) 582 .isEqualTo(BluetoothStatusCodes.FEATURE_NOT_CONFIGURED); 583 } 584 585 @Test setGetAudioPolicy()586 public void setGetAudioPolicy() { 587 // Skip the test if bluetooth or companion device are not present. 588 assumeTrue(mHasBluetooth && mHasCompanionDevice); 589 590 BluetoothSinkAudioPolicy demoAudioPolicy = new BluetoothSinkAudioPolicy.Builder().build(); 591 592 // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission 593 assertThrows( 594 SecurityException.class, 595 () -> mFakeDevice.requestAudioPolicyAsSink(demoAudioPolicy)); 596 assertThrows(SecurityException.class, () -> mFakeDevice.getRequestedAudioPolicyAsSink()); 597 598 TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED); 599 600 assertThat(mFakeDevice.requestAudioPolicyAsSink(demoAudioPolicy)) 601 .isEqualTo(BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED); 602 assertThat(mFakeDevice.getRequestedAudioPolicyAsSink()).isNull(); 603 604 BluetoothSinkAudioPolicy newPolicy = 605 new BluetoothSinkAudioPolicy.Builder(demoAudioPolicy) 606 .setCallEstablishPolicy(BluetoothSinkAudioPolicy.POLICY_ALLOWED) 607 .setActiveDevicePolicyAfterConnection( 608 BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED) 609 .setInBandRingtonePolicy(BluetoothSinkAudioPolicy.POLICY_ALLOWED) 610 .build(); 611 612 assertThat(mFakeDevice.requestAudioPolicyAsSink(newPolicy)) 613 .isEqualTo(BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED); 614 assertThat(mFakeDevice.getRequestedAudioPolicyAsSink()).isNull(); 615 616 assertThat(newPolicy.getCallEstablishPolicy()) 617 .isEqualTo(BluetoothSinkAudioPolicy.POLICY_ALLOWED); 618 assertThat(newPolicy.getActiveDevicePolicyAfterConnection()) 619 .isEqualTo(BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED); 620 assertThat(newPolicy.getInBandRingtonePolicy()) 621 .isEqualTo(BluetoothSinkAudioPolicy.POLICY_ALLOWED); 622 } 623 convertPinToBytes(String pin)624 private byte[] convertPinToBytes(String pin) { 625 if (pin == null) { 626 return null; 627 } 628 byte[] pinBytes; 629 try { 630 pinBytes = pin.getBytes("UTF-8"); 631 } catch (UnsupportedEncodingException uee) { 632 return null; 633 } 634 return pinBytes; 635 } 636 637 @Test getPackageNameOfBondingApplication()638 public void getPackageNameOfBondingApplication() { 639 IntentFilter filter = new IntentFilter(); 640 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 641 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 642 BroadcastReceiver mockReceiver = mock(BroadcastReceiver.class); 643 mContext.registerReceiver(mockReceiver, filter); 644 645 // Skip the test if bluetooth or companion device are not present. 646 assumeTrue(mHasBluetooth && mHasCompanionDevice); 647 648 mUiAutomation.dropShellPermissionIdentity(); 649 assertThrows( 650 SecurityException.class, () -> mFakeDevice.getPackageNameOfBondingApplication()); 651 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 652 assertThrows( 653 SecurityException.class, () -> mFakeDevice.getPackageNameOfBondingApplication()); 654 655 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_PRIVILEGED, BLUETOOTH_CONNECT); 656 // Since no application actually start bonding with this device, this should return null 657 assertThat(mFakeDevice.getPackageNameOfBondingApplication()).isNull(); 658 659 mFakeDevice.createBond(); 660 assertThat(mFakeDevice.getPackageNameOfBondingApplication()) 661 .isEqualTo(mContext.getPackageName()); 662 verifyIntentReceived( 663 mockReceiver, 664 Duration.ofSeconds(5), 665 hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), 666 hasExtra(BluetoothDevice.EXTRA_DEVICE, mFakeDevice), 667 hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING)); 668 669 // Clean up create bond 670 // Either cancel the bonding process or remove bond 671 mFakeDevice.cancelBondProcess(); 672 mFakeDevice.removeBond(); 673 verifyIntentReceived( 674 mockReceiver, 675 Duration.ofSeconds(5), 676 hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), 677 hasExtra(BluetoothDevice.EXTRA_DEVICE, mFakeDevice), 678 hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE)); 679 } 680 681 @Test setActiveAudioDevicePolicy_getActiveAudioDevicePolicy()682 public void setActiveAudioDevicePolicy_getActiveAudioDevicePolicy() { 683 if (!mHasBluetooth || !mHasCompanionDevice) { 684 // Skip the test if bluetooth or companion device are not present. 685 return; 686 } 687 String deviceAddress = "00:11:22:AA:AA:AA"; 688 BluetoothDevice device = mAdapter.getRemoteDevice(deviceAddress); 689 690 // This should throw a SecurityException because no BLUETOOTH_CONNECT permission 691 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_PRIVILEGED); 692 assertThrows( 693 SecurityException.class, 694 () -> 695 device.setActiveAudioDevicePolicy( 696 BluetoothDevice 697 .ACTIVE_AUDIO_DEVICE_POLICY_ALL_PROFILES_INACTIVE_UPON_CONNECTION)); 698 assertThrows(SecurityException.class, () -> device.getActiveAudioDevicePolicy()); 699 700 // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission 701 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); 702 assertThrows( 703 SecurityException.class, 704 () -> 705 device.setActiveAudioDevicePolicy( 706 BluetoothDevice 707 .ACTIVE_AUDIO_DEVICE_POLICY_ALL_PROFILES_INACTIVE_UPON_CONNECTION)); 708 assertThrows(SecurityException.class, () -> device.getActiveAudioDevicePolicy()); 709 710 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED); 711 712 assertThat(device.getActiveAudioDevicePolicy()) 713 .isEqualTo(BluetoothDevice.ACTIVE_AUDIO_DEVICE_POLICY_DEFAULT); 714 assertThat( 715 device.setActiveAudioDevicePolicy( 716 BluetoothDevice 717 .ACTIVE_AUDIO_DEVICE_POLICY_ALL_PROFILES_INACTIVE_UPON_CONNECTION)) 718 .isEqualTo(BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED); 719 } 720 721 @RequiresFlagsEnabled(Flags.FLAG_BT_SOCKET_API_L2CAP_CID) 722 @Test getL2capChannel()723 public void getL2capChannel() throws IOException { 724 // Skip the test if bluetooth or companion device are not present. 725 assumeTrue(mHasBluetooth && mHasCompanionDevice); 726 727 BluetoothSocket l2capSocket = mFakeDevice.createInsecureL2capChannel(mFakePsm); 728 BluetoothSocket rfcommSocket = 729 mFakeDevice.createInsecureRfcommSocketToServiceRecord(mFakeUuid); 730 731 mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED); 732 733 // This should throw a BluetoothSocketException because it is not L2CAP socket 734 assertThrows( 735 "Unknown L2CAP socket", 736 BluetoothSocketException.class, 737 () -> rfcommSocket.getL2capLocalChannelId()); 738 assertThrows( 739 "Unknown L2CAP socket", 740 BluetoothSocketException.class, 741 () -> rfcommSocket.getL2capRemoteChannelId()); 742 743 // This should throw a BluetoothSocketException because L2CAP socket is not connected 744 assertThrows( 745 "Socket closed", 746 BluetoothSocketException.class, 747 () -> l2capSocket.getL2capLocalChannelId()); 748 assertThrows( 749 "Socket closed", 750 BluetoothSocketException.class, 751 () -> l2capSocket.getL2capRemoteChannelId()); 752 } 753 754 @RequiresFlagsEnabled(Flags.FLAG_METADATA_API_MICROPHONE_FOR_CALL_ENABLED) 755 @Test setMicrophonePreferredForCalls_isMicrophonePreferredForCalls()756 public void setMicrophonePreferredForCalls_isMicrophonePreferredForCalls() { 757 // Skip the test if bluetooth or companion device are not present. 758 assumeTrue(mHasBluetooth && mHasCompanionDevice); 759 760 // Use alternate address to prevent another test from having unwanted consequences here 761 mFakeDevice = mAdapter.getRemoteDevice("AB:11:22:AA:BB:CC"); 762 Permissions.enforceEachPermissions( 763 () -> mFakeDevice.setMicrophonePreferredForCalls(false), 764 List.of(BLUETOOTH_PRIVILEGED, BLUETOOTH_CONNECT)); 765 Permissions.enforceEachPermissions( 766 () -> mFakeDevice.isMicrophonePreferredForCalls(), 767 List.of(BLUETOOTH_PRIVILEGED, BLUETOOTH_CONNECT)); 768 769 // default value should be true 770 try (var p = Permissions.withPermissions(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED)) { 771 assertThat(mFakeDevice.isMicrophonePreferredForCalls()).isTrue(); 772 assertThat(mFakeDevice.setMicrophonePreferredForCalls(true)) 773 .isEqualTo(BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED); 774 } 775 } 776 verifyIntentReceived( BroadcastReceiver receiver, Duration timeout, Matcher<Intent>... matchers)777 private void verifyIntentReceived( 778 BroadcastReceiver receiver, Duration timeout, Matcher<Intent>... matchers) { 779 verify(receiver, timeout(timeout.toMillis())) 780 .onReceive(any(), MockitoHamcrest.argThat(AllOf.allOf(matchers))); 781 } 782 } 783