1 /* 2 * Copyright (C) 2009 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; 18 19 import static android.bluetooth.BluetoothUtils.getSyncTimeout; 20 21 import android.annotation.IntDef; 22 import android.annotation.IntRange; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SdkConstant; 27 import android.annotation.SdkConstant.SdkConstantType; 28 import android.annotation.SuppressLint; 29 import android.annotation.SystemApi; 30 import android.app.compat.CompatChanges; 31 import android.bluetooth.annotations.RequiresBluetoothConnectPermission; 32 import android.bluetooth.annotations.RequiresBluetoothLocationPermission; 33 import android.bluetooth.annotations.RequiresBluetoothScanPermission; 34 import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; 35 import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; 36 import android.companion.AssociationRequest; 37 import android.compat.annotation.ChangeId; 38 import android.compat.annotation.EnabledSince; 39 import android.compat.annotation.UnsupportedAppUsage; 40 import android.content.AttributionSource; 41 import android.content.Context; 42 import android.os.Build; 43 import android.os.Handler; 44 import android.os.IpcDataCache; 45 import android.os.Parcel; 46 import android.os.ParcelUuid; 47 import android.os.Parcelable; 48 import android.os.Process; 49 import android.os.RemoteException; 50 import android.util.Log; 51 import android.util.Pair; 52 53 import com.android.modules.utils.SynchronousResultReceiver; 54 55 import java.io.IOException; 56 import java.io.UnsupportedEncodingException; 57 import java.lang.annotation.Retention; 58 import java.lang.annotation.RetentionPolicy; 59 import java.util.List; 60 import java.util.UUID; 61 import java.util.concurrent.TimeoutException; 62 63 /** 64 * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you 65 * create a connection with the respective device or query information about 66 * it, such as the name, address, class, and bonding state. 67 * 68 * <p>This class is really just a thin wrapper for a Bluetooth hardware 69 * address. Objects of this class are immutable. Operations on this class 70 * are performed on the remote Bluetooth hardware address, using the 71 * {@link BluetoothAdapter} that was used to create this {@link 72 * BluetoothDevice}. 73 * 74 * <p>To get a {@link BluetoothDevice}, use 75 * {@link BluetoothAdapter#getRemoteDevice(String) 76 * BluetoothAdapter.getRemoteDevice(String)} to create one representing a device 77 * of a known MAC address (which you can get through device discovery with 78 * {@link BluetoothAdapter}) or get one from the set of bonded devices 79 * returned by {@link BluetoothAdapter#getBondedDevices() 80 * BluetoothAdapter.getBondedDevices()}. You can then open a 81 * {@link BluetoothSocket} for communication with the remote device, using 82 * {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using 83 * {@link #createL2capChannel(int)} over Bluetooth LE. 84 * 85 * <div class="special reference"> 86 * <h3>Developer Guides</h3> 87 * <p> 88 * For more information about using Bluetooth, read the <a href= 89 * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer 90 * guide. 91 * </p> 92 * </div> 93 * 94 * {@see BluetoothAdapter} 95 * {@see BluetoothSocket} 96 */ 97 public final class BluetoothDevice implements Parcelable, Attributable { 98 private static final String TAG = "BluetoothDevice"; 99 private static final boolean DBG = false; 100 101 /** 102 * Connection state bitmask as returned by getConnectionState. 103 */ 104 private static final int CONNECTION_STATE_DISCONNECTED = 0; 105 private static final int CONNECTION_STATE_CONNECTED = 1; 106 private static final int CONNECTION_STATE_ENCRYPTED_BREDR = 2; 107 private static final int CONNECTION_STATE_ENCRYPTED_LE = 4; 108 109 /** 110 * Sentinel error value for this class. Guaranteed to not equal any other 111 * integer constant in this class. Provided as a convenience for functions 112 * that require a sentinel error value, for example: 113 * <p><code>Intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 114 * BluetoothDevice.ERROR)</code> 115 */ 116 public static final int ERROR = Integer.MIN_VALUE; 117 118 /** 119 * Broadcast Action: Remote device discovered. 120 * <p>Sent when a remote device is found during discovery. 121 * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link 122 * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or 123 * {@link #EXTRA_RSSI} and/or {@link #EXTRA_IS_COORDINATED_SET_MEMBER} if they are available. 124 */ 125 // TODO: Change API to not broadcast RSSI if not available (incoming connection) 126 @RequiresLegacyBluetoothPermission 127 @RequiresBluetoothScanPermission 128 @RequiresBluetoothLocationPermission 129 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) 130 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 131 public static final String ACTION_FOUND = 132 "android.bluetooth.device.action.FOUND"; 133 134 /** 135 * Broadcast Action: Bluetooth class of a remote device has changed. 136 * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link 137 * #EXTRA_CLASS}. 138 * {@see BluetoothClass} 139 */ 140 @RequiresLegacyBluetoothPermission 141 @RequiresBluetoothConnectPermission 142 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 143 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 144 public static final String ACTION_CLASS_CHANGED = 145 "android.bluetooth.device.action.CLASS_CHANGED"; 146 147 /** 148 * Broadcast Action: Indicates a low level (ACL) connection has been 149 * established with a remote device. 150 * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link #EXTRA_TRANSPORT}. 151 * <p>ACL connections are managed automatically by the Android Bluetooth 152 * stack. 153 */ 154 @RequiresLegacyBluetoothPermission 155 @RequiresBluetoothConnectPermission 156 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 157 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 158 public static final String ACTION_ACL_CONNECTED = 159 "android.bluetooth.device.action.ACL_CONNECTED"; 160 161 /** 162 * Broadcast Action: Indicates that a low level (ACL) disconnection has 163 * been requested for a remote device, and it will soon be disconnected. 164 * <p>This is useful for graceful disconnection. Applications should use 165 * this intent as a hint to immediately terminate higher level connections 166 * (RFCOMM, L2CAP, or profile connections) to the remote device. 167 * <p>Always contains the extra field {@link #EXTRA_DEVICE}. 168 */ 169 @RequiresLegacyBluetoothPermission 170 @RequiresBluetoothConnectPermission 171 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 172 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 173 public static final String ACTION_ACL_DISCONNECT_REQUESTED = 174 "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED"; 175 176 /** 177 * Broadcast Action: Indicates a low level (ACL) disconnection from a 178 * remote device. 179 * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link #EXTRA_TRANSPORT}. 180 * <p>ACL connections are managed automatically by the Android Bluetooth 181 * stack. 182 */ 183 @RequiresLegacyBluetoothPermission 184 @RequiresBluetoothConnectPermission 185 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 186 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 187 public static final String ACTION_ACL_DISCONNECTED = 188 "android.bluetooth.device.action.ACL_DISCONNECTED"; 189 190 /** 191 * Broadcast Action: Indicates the friendly name of a remote device has 192 * been retrieved for the first time, or changed since the last retrieval. 193 * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link 194 * #EXTRA_NAME}. 195 */ 196 @RequiresLegacyBluetoothPermission 197 @RequiresBluetoothConnectPermission 198 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 199 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 200 public static final String ACTION_NAME_CHANGED = 201 "android.bluetooth.device.action.NAME_CHANGED"; 202 203 /** 204 * Broadcast Action: Indicates the alias of a remote device has been 205 * changed. 206 * <p>Always contains the extra field {@link #EXTRA_DEVICE}. 207 */ 208 @SuppressLint("ActionValue") 209 @RequiresLegacyBluetoothPermission 210 @RequiresBluetoothConnectPermission 211 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 212 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 213 public static final String ACTION_ALIAS_CHANGED = 214 "android.bluetooth.device.action.ALIAS_CHANGED"; 215 216 /** 217 * Broadcast Action: Indicates a change in the bond state of a remote 218 * device. For example, if a device is bonded (paired). 219 * <p>Always contains the extra fields {@link #EXTRA_DEVICE}, {@link 220 * #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}. 221 */ 222 // Note: When EXTRA_BOND_STATE is BOND_NONE then this will also 223 // contain a hidden extra field EXTRA_UNBOND_REASON with the result code. 224 @RequiresLegacyBluetoothPermission 225 @RequiresBluetoothConnectPermission 226 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 227 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 228 public static final String ACTION_BOND_STATE_CHANGED = 229 "android.bluetooth.device.action.BOND_STATE_CHANGED"; 230 231 /** 232 * Broadcast Action: Indicates the battery level of a remote device has 233 * been retrieved for the first time, or changed since the last retrieval 234 * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link 235 * #EXTRA_BATTERY_LEVEL}. 236 * 237 * @hide 238 */ 239 @SystemApi 240 @RequiresLegacyBluetoothPermission 241 @RequiresBluetoothConnectPermission 242 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 243 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 244 @SuppressLint("ActionValue") 245 public static final String ACTION_BATTERY_LEVEL_CHANGED = 246 "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED"; 247 248 /** 249 * Broadcast Action: Indicates the audio buffer size should be switched 250 * between a low latency buffer size and a higher and larger latency buffer size. 251 * Only registered receivers will receive this intent. 252 * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link 253 * #EXTRA_LOW_LATENCY_BUFFER_SIZE}. 254 * 255 * @hide 256 */ 257 @SuppressLint("ActionValue") 258 @RequiresBluetoothConnectPermission 259 @RequiresPermission(allOf = { 260 android.Manifest.permission.BLUETOOTH_CONNECT, 261 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 262 }) 263 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 264 @SystemApi 265 public static final String ACTION_SWITCH_BUFFER_SIZE = 266 "android.bluetooth.device.action.SWITCH_BUFFER_SIZE"; 267 268 /** 269 * Used as an Integer extra field in {@link #ACTION_BATTERY_LEVEL_CHANGED} 270 * intent. It contains the most recently retrieved battery level information 271 * ranging from 0% to 100% for a remote device, {@link #BATTERY_LEVEL_UNKNOWN} 272 * when the valid is unknown or there is an error, {@link #BATTERY_LEVEL_BLUETOOTH_OFF} when the 273 * bluetooth is off 274 * 275 * @hide 276 */ 277 @SuppressLint("ActionValue") 278 @SystemApi 279 public static final String EXTRA_BATTERY_LEVEL = 280 "android.bluetooth.device.extra.BATTERY_LEVEL"; 281 282 /** 283 * Used as the unknown value for {@link #EXTRA_BATTERY_LEVEL} and {@link #getBatteryLevel()} 284 * 285 * @hide 286 */ 287 @SystemApi 288 public static final int BATTERY_LEVEL_UNKNOWN = -1; 289 290 /** 291 * Used as an error value for {@link #getBatteryLevel()} to represent bluetooth is off 292 * 293 * @hide 294 */ 295 @SystemApi 296 public static final int BATTERY_LEVEL_BLUETOOTH_OFF = -100; 297 298 /** 299 * Used as a Parcelable {@link BluetoothDevice} extra field in every intent 300 * broadcast by this class. It contains the {@link BluetoothDevice} that 301 * the intent applies to. 302 */ 303 public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE"; 304 305 /** 306 * Used as a String extra field in {@link #ACTION_NAME_CHANGED} and {@link 307 * #ACTION_FOUND} intents. It contains the friendly Bluetooth name. 308 */ 309 public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME"; 310 311 /** 312 * Used as a Parcelable {@link BluetoothQualityReport} extra field in 313 * {@link #ACTION_REMOTE_ISSUE_OCCURRED} intent. It contains the {@link BluetoothQualityReport}. 314 * @hide 315 */ 316 public static final String EXTRA_BQR = "android.bluetooth.qti.extra.EXTRA_BQR"; 317 318 /** 319 * Used as an optional short extra field in {@link #ACTION_FOUND} intents. 320 * Contains the RSSI value of the remote device as reported by the 321 * Bluetooth hardware. 322 */ 323 public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"; 324 325 /** 326 * Used as a boolean extra field in {@link #ACTION_FOUND} intents. 327 * It contains the information if device is discovered as member of a coordinated set or not. 328 * Pairing with device that belongs to a set would trigger pairing with the rest of set members. 329 * See Bluetooth CSIP specification for more details. 330 */ 331 public static final String EXTRA_IS_COORDINATED_SET_MEMBER = 332 "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER"; 333 334 /** 335 * Used as a Parcelable {@link BluetoothClass} extra field in {@link 336 * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents. 337 */ 338 public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS"; 339 340 /** 341 * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. 342 * Contains the bond state of the remote device. 343 * <p>Possible values are: 344 * {@link #BOND_NONE}, 345 * {@link #BOND_BONDING}, 346 * {@link #BOND_BONDED}. 347 */ 348 public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE"; 349 /** 350 * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. 351 * Contains the previous bond state of the remote device. 352 * <p>Possible values are: 353 * {@link #BOND_NONE}, 354 * {@link #BOND_BONDING}, 355 * {@link #BOND_BONDED}. 356 */ 357 public static final String EXTRA_PREVIOUS_BOND_STATE = 358 "android.bluetooth.device.extra.PREVIOUS_BOND_STATE"; 359 360 /** 361 * Used as a boolean extra field to indicate if audio buffer size is low latency or not 362 * 363 * @hide 364 */ 365 @SuppressLint("ActionValue") 366 @SystemApi 367 public static final String EXTRA_LOW_LATENCY_BUFFER_SIZE = 368 "android.bluetooth.device.extra.LOW_LATENCY_BUFFER_SIZE"; 369 370 /** 371 * Indicates the remote device is not bonded (paired). 372 * <p>There is no shared link key with the remote device, so communication 373 * (if it is allowed at all) will be unauthenticated and unencrypted. 374 */ 375 public static final int BOND_NONE = 10; 376 /** 377 * Indicates bonding (pairing) is in progress with the remote device. 378 */ 379 public static final int BOND_BONDING = 11; 380 /** 381 * Indicates the remote device is bonded (paired). 382 * <p>A shared link keys exists locally for the remote device, so 383 * communication can be authenticated and encrypted. 384 * <p><i>Being bonded (paired) with a remote device does not necessarily 385 * mean the device is currently connected. It just means that the pending 386 * procedure was completed at some earlier time, and the link key is still 387 * stored locally, ready to use on the next connection. 388 * </i> 389 */ 390 public static final int BOND_BONDED = 12; 391 392 /** 393 * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} intents for unbond reason. 394 * Possible value are : 395 * - {@link #UNBOND_REASON_AUTH_FAILED} 396 * - {@link #UNBOND_REASON_AUTH_REJECTED} 397 * - {@link #UNBOND_REASON_AUTH_CANCELED} 398 * - {@link #UNBOND_REASON_REMOTE_DEVICE_DOWN} 399 * - {@link #UNBOND_REASON_DISCOVERY_IN_PROGRESS} 400 * - {@link #UNBOND_REASON_AUTH_TIMEOUT} 401 * - {@link #UNBOND_REASON_REPEATED_ATTEMPTS} 402 * - {@link #UNBOND_REASON_REMOTE_AUTH_CANCELED} 403 * - {@link #UNBOND_REASON_REMOVED} 404 * 405 * Note: Can be added as a hidden extra field for {@link #ACTION_BOND_STATE_CHANGED} when the 406 * {@link #EXTRA_BOND_STATE} is {@link #BOND_NONE} 407 * 408 * @hide 409 */ 410 @SystemApi 411 @SuppressLint("ActionValue") 412 public static final String EXTRA_UNBOND_REASON = "android.bluetooth.device.extra.REASON"; 413 414 /** 415 * Use {@link EXTRA_UNBOND_REASON} instead 416 * @hide 417 */ 418 @UnsupportedAppUsage 419 public static final String EXTRA_REASON = EXTRA_UNBOND_REASON; 420 421 422 /** 423 * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} 424 * intents to indicate pairing method used. Possible values are: 425 * {@link #PAIRING_VARIANT_PIN}, 426 * {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION}, 427 */ 428 public static final String EXTRA_PAIRING_VARIANT = 429 "android.bluetooth.device.extra.PAIRING_VARIANT"; 430 431 /** 432 * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} 433 * intents as the value of passkey. 434 * The Bluetooth Passkey is a 6-digit numerical value represented as integer value 435 * in the range 0x00000000 – 0x000F423F (000000 to 999999). 436 */ 437 public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY"; 438 439 /** 440 * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} 441 * intents as the location of initiator. Possible value are: 442 * {@link #EXTRA_PAIRING_INITIATOR_FOREGROUND}, 443 * {@link #EXTRA_PAIRING_INITIATOR_BACKGROUND}, 444 * 445 * @hide 446 */ 447 @SystemApi 448 @SuppressLint("ActionValue") 449 public static final String EXTRA_PAIRING_INITIATOR = 450 "android.bluetooth.device.extra.PAIRING_INITIATOR"; 451 452 /** 453 * Bluetooth pairing initiator, Foreground App 454 * @hide 455 */ 456 @SystemApi 457 public static final int EXTRA_PAIRING_INITIATOR_FOREGROUND = 1; 458 459 /** 460 * Bluetooth pairing initiator, Background 461 * @hide 462 */ 463 @SystemApi 464 public static final int EXTRA_PAIRING_INITIATOR_BACKGROUND = 2; 465 466 /** 467 * Bluetooth device type, Unknown 468 */ 469 public static final int DEVICE_TYPE_UNKNOWN = 0; 470 471 /** 472 * Bluetooth device type, Classic - BR/EDR devices 473 */ 474 public static final int DEVICE_TYPE_CLASSIC = 1; 475 476 /** 477 * Bluetooth device type, Low Energy - LE-only 478 */ 479 public static final int DEVICE_TYPE_LE = 2; 480 481 /** 482 * Bluetooth device type, Dual Mode - BR/EDR/LE 483 */ 484 public static final int DEVICE_TYPE_DUAL = 3; 485 486 487 /** @hide */ 488 @RequiresBluetoothConnectPermission 489 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 490 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 491 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 492 public static final String ACTION_SDP_RECORD = 493 "android.bluetooth.device.action.SDP_RECORD"; 494 495 /** @hide */ 496 @IntDef(prefix = "METADATA_", value = { 497 METADATA_MANUFACTURER_NAME, 498 METADATA_MODEL_NAME, 499 METADATA_SOFTWARE_VERSION, 500 METADATA_HARDWARE_VERSION, 501 METADATA_COMPANION_APP, 502 METADATA_MAIN_ICON, 503 METADATA_IS_UNTETHERED_HEADSET, 504 METADATA_UNTETHERED_LEFT_ICON, 505 METADATA_UNTETHERED_RIGHT_ICON, 506 METADATA_UNTETHERED_CASE_ICON, 507 METADATA_UNTETHERED_LEFT_BATTERY, 508 METADATA_UNTETHERED_RIGHT_BATTERY, 509 METADATA_UNTETHERED_CASE_BATTERY, 510 METADATA_UNTETHERED_LEFT_CHARGING, 511 METADATA_UNTETHERED_RIGHT_CHARGING, 512 METADATA_UNTETHERED_CASE_CHARGING, 513 METADATA_ENHANCED_SETTINGS_UI_URI, 514 METADATA_DEVICE_TYPE, 515 METADATA_MAIN_BATTERY, 516 METADATA_MAIN_CHARGING, 517 METADATA_MAIN_LOW_BATTERY_THRESHOLD, 518 METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD, 519 METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD, 520 METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD, 521 METADATA_SPATIAL_AUDIO, 522 METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, 523 METADATA_LE_AUDIO, 524 METADATA_GMCS_CCCD, 525 METADATA_GTBS_CCCD}) 526 @Retention(RetentionPolicy.SOURCE) 527 public @interface MetadataKey{} 528 529 /** 530 * Maximum length of a metadata entry, this is to avoid exploding Bluetooth 531 * disk usage 532 * @hide 533 */ 534 @SystemApi 535 public static final int METADATA_MAX_LENGTH = 2048; 536 537 /** 538 * Manufacturer name of this Bluetooth device 539 * Data type should be {@String} as {@link Byte} array. 540 * @hide 541 */ 542 @SystemApi 543 public static final int METADATA_MANUFACTURER_NAME = 0; 544 545 /** 546 * Model name of this Bluetooth device 547 * Data type should be {@String} as {@link Byte} array. 548 * @hide 549 */ 550 @SystemApi 551 public static final int METADATA_MODEL_NAME = 1; 552 553 /** 554 * Software version of this Bluetooth device 555 * Data type should be {@String} as {@link Byte} array. 556 * @hide 557 */ 558 @SystemApi 559 public static final int METADATA_SOFTWARE_VERSION = 2; 560 561 /** 562 * Hardware version of this Bluetooth device 563 * Data type should be {@String} as {@link Byte} array. 564 * @hide 565 */ 566 @SystemApi 567 public static final int METADATA_HARDWARE_VERSION = 3; 568 569 /** 570 * Package name of the companion app, if any 571 * Data type should be {@String} as {@link Byte} array. 572 * @hide 573 */ 574 @SystemApi 575 public static final int METADATA_COMPANION_APP = 4; 576 577 /** 578 * URI to the main icon shown on the settings UI 579 * Data type should be {@link Byte} array. 580 * @hide 581 */ 582 @SystemApi 583 public static final int METADATA_MAIN_ICON = 5; 584 585 /** 586 * Whether this device is an untethered headset with left, right and case 587 * Data type should be {@String} as {@link Byte} array. 588 * @hide 589 */ 590 @SystemApi 591 public static final int METADATA_IS_UNTETHERED_HEADSET = 6; 592 593 /** 594 * URI to icon of the left headset 595 * Data type should be {@link Byte} array. 596 * @hide 597 */ 598 @SystemApi 599 public static final int METADATA_UNTETHERED_LEFT_ICON = 7; 600 601 /** 602 * URI to icon of the right headset 603 * Data type should be {@link Byte} array. 604 * @hide 605 */ 606 @SystemApi 607 public static final int METADATA_UNTETHERED_RIGHT_ICON = 8; 608 609 /** 610 * URI to icon of the headset charging case 611 * Data type should be {@link Byte} array. 612 * @hide 613 */ 614 @SystemApi 615 public static final int METADATA_UNTETHERED_CASE_ICON = 9; 616 617 /** 618 * Battery level of left headset 619 * Data type should be {@String} 0-100 as {@link Byte} array, otherwise 620 * as invalid. 621 * @hide 622 */ 623 @SystemApi 624 public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10; 625 626 /** 627 * Battery level of rigth headset 628 * Data type should be {@String} 0-100 as {@link Byte} array, otherwise 629 * as invalid. 630 * @hide 631 */ 632 @SystemApi 633 public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11; 634 635 /** 636 * Battery level of the headset charging case 637 * Data type should be {@String} 0-100 as {@link Byte} array, otherwise 638 * as invalid. 639 * @hide 640 */ 641 @SystemApi 642 public static final int METADATA_UNTETHERED_CASE_BATTERY = 12; 643 644 /** 645 * Whether the left headset is charging 646 * Data type should be {@String} as {@link Byte} array. 647 * @hide 648 */ 649 @SystemApi 650 public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13; 651 652 /** 653 * Whether the right headset is charging 654 * Data type should be {@String} as {@link Byte} array. 655 * @hide 656 */ 657 @SystemApi 658 public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14; 659 660 /** 661 * Whether the headset charging case is charging 662 * Data type should be {@String} as {@link Byte} array. 663 * @hide 664 */ 665 @SystemApi 666 public static final int METADATA_UNTETHERED_CASE_CHARGING = 15; 667 668 /** 669 * URI to the enhanced settings UI slice 670 * Data type should be {@String} as {@link Byte} array, null means 671 * the UI does not exist. 672 * @hide 673 */ 674 @SystemApi 675 public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; 676 677 /** 678 * @hide 679 */ 680 public static final String COMPANION_TYPE_PRIMARY = "COMPANION_PRIMARY"; 681 682 /** 683 * @hide 684 */ 685 public static final String COMPANION_TYPE_SECONDARY = "COMPANION_SECONDARY"; 686 687 /** 688 * @hide 689 */ 690 public static final String COMPANION_TYPE_NONE = "COMPANION_NONE"; 691 692 /** 693 * Type of the Bluetooth device, must be within the list of 694 * BluetoothDevice.DEVICE_TYPE_* 695 * Data type should be {@String} as {@link Byte} array. 696 * @hide 697 */ 698 @SystemApi 699 public static final int METADATA_DEVICE_TYPE = 17; 700 701 /** 702 * Battery level of the Bluetooth device, use when the Bluetooth device 703 * does not support HFP battery indicator. 704 * Data type should be {@String} as {@link Byte} array. 705 * @hide 706 */ 707 @SystemApi 708 public static final int METADATA_MAIN_BATTERY = 18; 709 710 /** 711 * Whether the device is charging. 712 * Data type should be {@String} as {@link Byte} array. 713 * @hide 714 */ 715 @SystemApi 716 public static final int METADATA_MAIN_CHARGING = 19; 717 718 /** 719 * The battery threshold of the Bluetooth device to show low battery icon. 720 * Data type should be {@String} as {@link Byte} array. 721 * @hide 722 */ 723 @SystemApi 724 public static final int METADATA_MAIN_LOW_BATTERY_THRESHOLD = 20; 725 726 /** 727 * The battery threshold of the left headset to show low battery icon. 728 * Data type should be {@String} as {@link Byte} array. 729 * @hide 730 */ 731 @SystemApi 732 public static final int METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD = 21; 733 734 /** 735 * The battery threshold of the right headset to show low battery icon. 736 * Data type should be {@String} as {@link Byte} array. 737 * @hide 738 */ 739 @SystemApi 740 public static final int METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD = 22; 741 742 /** 743 * The battery threshold of the case to show low battery icon. 744 * Data type should be {@String} as {@link Byte} array. 745 * @hide 746 */ 747 @SystemApi 748 public static final int METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD = 23; 749 750 751 /** 752 * The metadata of the audio spatial data. 753 * Data type should be {@link Byte} array. 754 * @hide 755 */ 756 public static final int METADATA_SPATIAL_AUDIO = 24; 757 758 /** 759 * The metadata of the Fast Pair for any custmized feature. 760 * Data type should be {@link Byte} array. 761 * @hide 762 */ 763 public static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25; 764 765 /** 766 * The metadata of the Fast Pair for LE Audio capable devices. 767 * Data type should be {@link Byte} array. 768 * @hide 769 */ 770 @SystemApi 771 public static final int METADATA_LE_AUDIO = 26; 772 773 /** 774 * The UUIDs (16-bit) of registered to CCC characteristics from Media Control services. 775 * Data type should be {@link Byte} array. 776 * @hide 777 */ 778 public static final int METADATA_GMCS_CCCD = 27; 779 780 /** 781 * The UUIDs (16-bit) of registered to CCC characteristics from Telephony Bearer service. 782 * Data type should be {@link Byte} array. 783 * @hide 784 */ 785 public static final int METADATA_GTBS_CCCD = 28; 786 787 private static final int METADATA_MAX_KEY = METADATA_GTBS_CCCD; 788 789 /** 790 * Device type which is used in METADATA_DEVICE_TYPE 791 * Indicates this Bluetooth device is a standard Bluetooth accessory or 792 * not listed in METADATA_DEVICE_TYPE_*. 793 * @hide 794 */ 795 @SystemApi 796 public static final String DEVICE_TYPE_DEFAULT = "Default"; 797 798 /** 799 * Device type which is used in METADATA_DEVICE_TYPE 800 * Indicates this Bluetooth device is a watch. 801 * @hide 802 */ 803 @SystemApi 804 public static final String DEVICE_TYPE_WATCH = "Watch"; 805 806 /** 807 * Device type which is used in METADATA_DEVICE_TYPE 808 * Indicates this Bluetooth device is an untethered headset. 809 * @hide 810 */ 811 @SystemApi 812 public static final String DEVICE_TYPE_UNTETHERED_HEADSET = "Untethered Headset"; 813 814 /** 815 * Device type which is used in METADATA_DEVICE_TYPE 816 * Indicates this Bluetooth device is a stylus. 817 * @hide 818 */ 819 @SystemApi 820 public static final String DEVICE_TYPE_STYLUS = "Stylus"; 821 822 /** 823 * Broadcast Action: This intent is used to broadcast the {@link UUID} 824 * wrapped as a {@link android.os.ParcelUuid} of the remote device after it 825 * has been fetched. This intent is sent only when the UUIDs of the remote 826 * device are requested to be fetched using Service Discovery Protocol 827 * <p> Always contains the extra field {@link #EXTRA_DEVICE} 828 * <p> Always contains the extra field {@link #EXTRA_UUID} 829 */ 830 @RequiresLegacyBluetoothAdminPermission 831 @RequiresBluetoothConnectPermission 832 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 833 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 834 public static final String ACTION_UUID = 835 "android.bluetooth.device.action.UUID"; 836 837 /** @hide */ 838 @RequiresBluetoothConnectPermission 839 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 840 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 841 public static final String ACTION_MAS_INSTANCE = 842 "android.bluetooth.device.action.MAS_INSTANCE"; 843 844 /** 845 * Broadcast Action: Indicates a failure to retrieve the name of a remote 846 * device. 847 * <p>Always contains the extra field {@link #EXTRA_DEVICE}. 848 * 849 * @hide 850 */ 851 //TODO: is this actually useful? 852 @RequiresLegacyBluetoothPermission 853 @RequiresBluetoothConnectPermission 854 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 855 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 856 public static final String ACTION_NAME_FAILED = 857 "android.bluetooth.device.action.NAME_FAILED"; 858 859 /** 860 * Broadcast Action: This intent is used to broadcast PAIRING REQUEST 861 */ 862 @RequiresLegacyBluetoothAdminPermission 863 @RequiresBluetoothConnectPermission 864 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 865 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 866 public static final String ACTION_PAIRING_REQUEST = 867 "android.bluetooth.device.action.PAIRING_REQUEST"; 868 869 /** 870 * Starting from {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, 871 * the return value of {@link BluetoothDevice#toString()} has changed 872 * to improve privacy. 873 */ 874 @ChangeId 875 @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 876 private static final long CHANGE_TO_STRING_REDACTED = 265103382L; 877 878 /** 879 * Broadcast Action: This intent is used to broadcast PAIRING CANCEL 880 * 881 * @hide 882 */ 883 @SystemApi 884 @RequiresBluetoothConnectPermission 885 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 886 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 887 @SuppressLint("ActionValue") 888 public static final String ACTION_PAIRING_CANCEL = 889 "android.bluetooth.device.action.PAIRING_CANCEL"; 890 891 /** 892 * Broadcast Action: This intent is used to broadcast CONNECTION ACCESS REQUEST 893 * 894 * This action will trigger a prompt for the user to accept or deny giving the 895 * permission for this device. Permissions can be specified with 896 * {@link #EXTRA_ACCESS_REQUEST_TYPE}. 897 * 898 * The reply will be an {@link #ACTION_CONNECTION_ACCESS_REPLY} sent to the specified 899 * {@link #EXTRA_PACKAGE_NAME} and {@link #EXTRA_CLASS_NAME}. 900 * 901 * This action can be cancelled with {@link #ACTION_CONNECTION_ACCESS_CANCEL}. 902 * 903 * @hide 904 */ 905 @SystemApi 906 @RequiresBluetoothConnectPermission 907 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 908 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 909 @SuppressLint("ActionValue") 910 public static final String ACTION_CONNECTION_ACCESS_REQUEST = 911 "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST"; 912 913 /** 914 * Broadcast Action: This intent is used to broadcast CONNECTION ACCESS REPLY 915 * 916 * This action is the reply from {@link #ACTION_CONNECTION_ACCESS_REQUEST} 917 * that is sent to the specified {@link #EXTRA_PACKAGE_NAME} 918 * and {@link #EXTRA_CLASS_NAME}. 919 * 920 * See the extra fields {@link #EXTRA_CONNECTION_ACCESS_RESULT} and 921 * {@link #EXTRA_ALWAYS_ALLOWED} for possible results. 922 * 923 * @hide 924 */ 925 @SystemApi 926 @RequiresBluetoothConnectPermission 927 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 928 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 929 @SuppressLint("ActionValue") 930 public static final String ACTION_CONNECTION_ACCESS_REPLY = 931 "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY"; 932 933 /** 934 * Broadcast Action: This intent is used to broadcast CONNECTION ACCESS CANCEL 935 * 936 * @hide 937 */ 938 @SystemApi 939 @RequiresBluetoothConnectPermission 940 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 941 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 942 @SuppressLint("ActionValue") 943 public static final String ACTION_CONNECTION_ACCESS_CANCEL = 944 "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL"; 945 946 /** 947 * Intent to broadcast silence mode changed. 948 * Alway contains the extra field {@link #EXTRA_DEVICE} 949 * 950 * @hide 951 */ 952 @RequiresBluetoothConnectPermission 953 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 954 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 955 @SystemApi 956 public static final String ACTION_SILENCE_MODE_CHANGED = 957 "android.bluetooth.device.action.SILENCE_MODE_CHANGED"; 958 959 /** 960 * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST}. 961 * 962 * Possible values are {@link #REQUEST_TYPE_PROFILE_CONNECTION}, 963 * {@link #REQUEST_TYPE_PHONEBOOK_ACCESS}, {@link #REQUEST_TYPE_MESSAGE_ACCESS} 964 * and {@link #REQUEST_TYPE_SIM_ACCESS} 965 * 966 * @hide 967 */ 968 @SystemApi 969 @SuppressLint("ActionValue") 970 public static final String EXTRA_ACCESS_REQUEST_TYPE = 971 "android.bluetooth.device.extra.ACCESS_REQUEST_TYPE"; 972 973 /** @hide */ 974 @SystemApi 975 public static final int REQUEST_TYPE_PROFILE_CONNECTION = 1; 976 977 /** @hide */ 978 @SystemApi 979 public static final int REQUEST_TYPE_PHONEBOOK_ACCESS = 2; 980 981 /** @hide */ 982 @SystemApi 983 public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3; 984 985 /** @hide */ 986 @SystemApi 987 public static final int REQUEST_TYPE_SIM_ACCESS = 4; 988 989 /** 990 * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents, 991 * Contains package name to return reply intent to. 992 * 993 * @hide 994 */ 995 public static final String EXTRA_PACKAGE_NAME = "android.bluetooth.device.extra.PACKAGE_NAME"; 996 997 /** 998 * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents, 999 * Contains class name to return reply intent to. 1000 * 1001 * @hide 1002 */ 1003 public static final String EXTRA_CLASS_NAME = "android.bluetooth.device.extra.CLASS_NAME"; 1004 1005 /** 1006 * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent. 1007 * 1008 * Possible values are {@link #CONNECTION_ACCESS_YES} and {@link #CONNECTION_ACCESS_NO}. 1009 * 1010 * @hide 1011 */ 1012 @SystemApi 1013 @SuppressLint("ActionValue") 1014 public static final String EXTRA_CONNECTION_ACCESS_RESULT = 1015 "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT"; 1016 1017 /** @hide */ 1018 @SystemApi 1019 public static final int CONNECTION_ACCESS_YES = 1; 1020 1021 /** @hide */ 1022 @SystemApi 1023 public static final int CONNECTION_ACCESS_NO = 2; 1024 1025 /** 1026 * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intents, 1027 * Contains boolean to indicate if the allowed response is once-for-all so that 1028 * next request will be granted without asking user again. 1029 * 1030 * @hide 1031 */ 1032 @SystemApi 1033 @SuppressLint("ActionValue") 1034 public static final String EXTRA_ALWAYS_ALLOWED = 1035 "android.bluetooth.device.extra.ALWAYS_ALLOWED"; 1036 1037 /** 1038 * A bond attempt succeeded 1039 * 1040 * @hide 1041 */ 1042 public static final int BOND_SUCCESS = 0; 1043 1044 /** 1045 * A bond attempt failed because pins did not match, or remote device did 1046 * not respond to pin request in time 1047 * 1048 * @hide 1049 */ 1050 @SystemApi 1051 public static final int UNBOND_REASON_AUTH_FAILED = 1; 1052 1053 /** 1054 * A bond attempt failed because the other side explicitly rejected 1055 * bonding 1056 * 1057 * @hide 1058 */ 1059 @SystemApi 1060 public static final int UNBOND_REASON_AUTH_REJECTED = 2; 1061 1062 /** 1063 * A bond attempt failed because we canceled the bonding process 1064 * 1065 * @hide 1066 */ 1067 @SystemApi 1068 public static final int UNBOND_REASON_AUTH_CANCELED = 3; 1069 1070 /** 1071 * A bond attempt failed because we could not contact the remote device 1072 * 1073 * @hide 1074 */ 1075 @SystemApi 1076 public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; 1077 1078 /** 1079 * A bond attempt failed because a discovery is in progress 1080 * 1081 * @hide 1082 */ 1083 @SystemApi 1084 public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; 1085 1086 /** 1087 * A bond attempt failed because of authentication timeout 1088 * 1089 * @hide 1090 */ 1091 @SystemApi 1092 public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; 1093 1094 /** 1095 * A bond attempt failed because of repeated attempts 1096 * 1097 * @hide 1098 */ 1099 @SystemApi 1100 public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; 1101 1102 /** 1103 * A bond attempt failed because we received an Authentication Cancel 1104 * by remote end 1105 * 1106 * @hide 1107 */ 1108 @SystemApi 1109 public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; 1110 1111 /** 1112 * An existing bond was explicitly revoked 1113 * 1114 * @hide 1115 */ 1116 @SystemApi 1117 public static final int UNBOND_REASON_REMOVED = 9; 1118 1119 /** 1120 * The user will be prompted to enter a pin or 1121 * an app will enter a pin for user. 1122 */ 1123 public static final int PAIRING_VARIANT_PIN = 0; 1124 1125 /** 1126 * The user will be prompted to enter a passkey 1127 * 1128 * @hide 1129 */ 1130 @SystemApi 1131 public static final int PAIRING_VARIANT_PASSKEY = 1; 1132 1133 /** 1134 * The user will be prompted to confirm the passkey displayed on the screen or 1135 * an app will confirm the passkey for the user. 1136 */ 1137 public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; 1138 1139 /** 1140 * The user will be prompted to accept or deny the incoming pairing request 1141 * 1142 * @hide 1143 */ 1144 @SystemApi 1145 public static final int PAIRING_VARIANT_CONSENT = 3; 1146 1147 /** 1148 * The user will be prompted to enter the passkey displayed on remote device 1149 * This is used for Bluetooth 2.1 pairing. 1150 * 1151 * @hide 1152 */ 1153 @SystemApi 1154 public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; 1155 1156 /** 1157 * The user will be prompted to enter the PIN displayed on remote device. 1158 * This is used for Bluetooth 2.0 pairing. 1159 * 1160 * @hide 1161 */ 1162 @SystemApi 1163 public static final int PAIRING_VARIANT_DISPLAY_PIN = 5; 1164 1165 /** 1166 * The user will be prompted to accept or deny the OOB pairing request. 1167 * This is used for Bluetooth 2.1 secure simple pairing. 1168 * 1169 * @hide 1170 */ 1171 @SystemApi 1172 public static final int PAIRING_VARIANT_OOB_CONSENT = 6; 1173 1174 /** 1175 * The user will be prompted to enter a 16 digit pin or 1176 * an app will enter a 16 digit pin for user. 1177 * 1178 * @hide 1179 */ 1180 @SystemApi 1181 public static final int PAIRING_VARIANT_PIN_16_DIGITS = 7; 1182 1183 /** 1184 * Used as an extra field in {@link #ACTION_UUID} intents, 1185 * Contains the {@link android.os.ParcelUuid}s of the remote device which 1186 * is a parcelable version of {@link UUID}. 1187 * A {@code null} EXTRA_UUID indicates a timeout. 1188 */ 1189 public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; 1190 1191 /** @hide */ 1192 public static final String EXTRA_SDP_RECORD = 1193 "android.bluetooth.device.extra.SDP_RECORD"; 1194 1195 /** @hide */ 1196 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1197 public static final String EXTRA_SDP_SEARCH_STATUS = 1198 "android.bluetooth.device.extra.SDP_SEARCH_STATUS"; 1199 1200 /** @hide */ 1201 @IntDef(prefix = "ACCESS_", value = {ACCESS_UNKNOWN, 1202 ACCESS_ALLOWED, ACCESS_REJECTED}) 1203 @Retention(RetentionPolicy.SOURCE) 1204 public @interface AccessPermission{} 1205 1206 /** 1207 * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, 1208 * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. 1209 * 1210 * @hide 1211 */ 1212 @SystemApi 1213 public static final int ACCESS_UNKNOWN = 0; 1214 1215 /** 1216 * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, 1217 * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. 1218 * 1219 * @hide 1220 */ 1221 @SystemApi 1222 public static final int ACCESS_ALLOWED = 1; 1223 1224 /** 1225 * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, 1226 * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. 1227 * 1228 * @hide 1229 */ 1230 @SystemApi 1231 public static final int ACCESS_REJECTED = 2; 1232 1233 /** @hide */ 1234 @Retention(RetentionPolicy.SOURCE) 1235 @IntDef( 1236 prefix = { "TRANSPORT_" }, 1237 value = { 1238 TRANSPORT_AUTO, 1239 TRANSPORT_BREDR, 1240 TRANSPORT_LE, 1241 } 1242 ) 1243 public @interface Transport {} 1244 1245 /** 1246 * No preference of physical transport for GATT connections to remote dual-mode devices 1247 */ 1248 public static final int TRANSPORT_AUTO = 0; 1249 1250 /** 1251 * Constant representing the BR/EDR transport. 1252 */ 1253 public static final int TRANSPORT_BREDR = 1; 1254 1255 /** 1256 * Constant representing the Bluetooth Low Energy (BLE) Transport. 1257 */ 1258 public static final int TRANSPORT_LE = 2; 1259 1260 /** 1261 * Bluetooth LE 1M PHY. Used to refer to LE 1M Physical Channel for advertising, scanning or 1262 * connection. 1263 */ 1264 public static final int PHY_LE_1M = 1; 1265 1266 /** 1267 * Bluetooth LE 2M PHY. Used to refer to LE 2M Physical Channel for advertising, scanning or 1268 * connection. 1269 */ 1270 public static final int PHY_LE_2M = 2; 1271 1272 /** 1273 * Bluetooth LE Coded PHY. Used to refer to LE Coded Physical Channel for advertising, scanning 1274 * or connection. 1275 */ 1276 public static final int PHY_LE_CODED = 3; 1277 1278 /** 1279 * Bluetooth LE 1M PHY mask. Used to specify LE 1M Physical Channel as one of many available 1280 * options in a bitmask. 1281 */ 1282 public static final int PHY_LE_1M_MASK = 1; 1283 1284 /** 1285 * Bluetooth LE 2M PHY mask. Used to specify LE 2M Physical Channel as one of many available 1286 * options in a bitmask. 1287 */ 1288 public static final int PHY_LE_2M_MASK = 2; 1289 1290 /** 1291 * Bluetooth LE Coded PHY mask. Used to specify LE Coded Physical Channel as one of many 1292 * available options in a bitmask. 1293 */ 1294 public static final int PHY_LE_CODED_MASK = 4; 1295 1296 /** 1297 * No preferred coding when transmitting on the LE Coded PHY. 1298 */ 1299 public static final int PHY_OPTION_NO_PREFERRED = 0; 1300 1301 /** 1302 * Prefer the S=2 coding to be used when transmitting on the LE Coded PHY. 1303 */ 1304 public static final int PHY_OPTION_S2 = 1; 1305 1306 /** 1307 * Prefer the S=8 coding to be used when transmitting on the LE Coded PHY. 1308 */ 1309 public static final int PHY_OPTION_S8 = 2; 1310 1311 1312 /** @hide */ 1313 public static final String EXTRA_MAS_INSTANCE = 1314 "android.bluetooth.device.extra.MAS_INSTANCE"; 1315 1316 /** 1317 * Used as an int extra field in {@link #ACTION_ACL_CONNECTED} and 1318 * {@link #ACTION_ACL_DISCONNECTED} intents to indicate which transport is connected. 1319 * Possible values are: {@link #TRANSPORT_BREDR} and {@link #TRANSPORT_LE}. 1320 */ 1321 @SuppressLint("ActionValue") 1322 public static final String EXTRA_TRANSPORT = "android.bluetooth.device.extra.TRANSPORT"; 1323 1324 /** @hide */ 1325 @Retention(RetentionPolicy.SOURCE) 1326 @IntDef( 1327 prefix = { "ADDRESS_TYPE_" }, 1328 value = { 1329 ADDRESS_TYPE_PUBLIC, 1330 ADDRESS_TYPE_RANDOM, 1331 ADDRESS_TYPE_UNKNOWN, 1332 } 1333 ) 1334 public @interface AddressType {} 1335 1336 /** Hardware MAC Address of the device */ 1337 public static final int ADDRESS_TYPE_PUBLIC = 0; 1338 /** Address is either resolvable, non-resolvable or static. */ 1339 public static final int ADDRESS_TYPE_RANDOM = 1; 1340 /** Address type is unknown or unavailable **/ 1341 public static final int ADDRESS_TYPE_UNKNOWN = 0xFFFF; 1342 1343 private static final String NULL_MAC_ADDRESS = "00:00:00:00:00:00"; 1344 1345 private final String mAddress; 1346 @AddressType private final int mAddressType; 1347 1348 private static boolean sIsLogRedactionFlagSynced = false; 1349 private static boolean sIsLogRedactionEnabled = true; 1350 1351 private AttributionSource mAttributionSource; 1352 getService()1353 static IBluetooth getService() { 1354 return BluetoothAdapter.getDefaultAdapter().getBluetoothService(); 1355 } 1356 1357 /** 1358 * Create a new BluetoothDevice. 1359 * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", 1360 * and is validated in this constructor. 1361 * 1362 * @param address valid Bluetooth MAC address 1363 * @param addressType valid address type 1364 * @throws RuntimeException Bluetooth is not available on this platform 1365 * @throws IllegalArgumentException address or addressType is invalid 1366 * @hide 1367 */ BluetoothDevice(String address, int addressType)1368 /*package*/ BluetoothDevice(String address, int addressType) { 1369 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1370 throw new IllegalArgumentException(address + " is not a valid Bluetooth address"); 1371 } 1372 1373 if (addressType != ADDRESS_TYPE_PUBLIC && addressType != ADDRESS_TYPE_RANDOM) { 1374 throw new IllegalArgumentException(addressType + " is not a Bluetooth address type"); 1375 } 1376 1377 mAddress = address; 1378 mAddressType = addressType; 1379 mAttributionSource = AttributionSource.myAttributionSource(); 1380 } 1381 1382 /** 1383 * Create a new BluetoothDevice. 1384 * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", 1385 * and is validated in this constructor. 1386 * 1387 * @param address valid Bluetooth MAC address 1388 * @throws RuntimeException Bluetooth is not available on this platform 1389 * @throws IllegalArgumentException address is invalid 1390 * @hide 1391 */ 1392 @UnsupportedAppUsage BluetoothDevice(String address)1393 /*package*/ BluetoothDevice(String address) { 1394 this(address, ADDRESS_TYPE_PUBLIC); 1395 } 1396 1397 /** 1398 * Create a new BluetoothDevice. 1399 * 1400 * @param in valid parcel 1401 * @throws RuntimeException Bluetooth is not available on this platform 1402 * @throws IllegalArgumentException address is invalid 1403 * @hide 1404 */ 1405 @UnsupportedAppUsage BluetoothDevice(Parcel in)1406 /*package*/ BluetoothDevice(Parcel in) { 1407 this(in.readString(), in.readInt()); 1408 } 1409 1410 /** {@hide} */ setAttributionSource(@onNull AttributionSource attributionSource)1411 public void setAttributionSource(@NonNull AttributionSource attributionSource) { 1412 mAttributionSource = attributionSource; 1413 } 1414 1415 /** 1416 * Method should never be used anywhere. Only exception is from {@link Intent} 1417 * Used to set the device current attribution source 1418 * 1419 * @param attributionSource The associated {@link AttributionSource} for this device in this 1420 * process 1421 * @hide 1422 */ 1423 @SystemApi 1424 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) prepareToEnterProcess(@onNull AttributionSource attributionSource)1425 public void prepareToEnterProcess(@NonNull AttributionSource attributionSource) { 1426 setAttributionSource(attributionSource); 1427 } 1428 1429 @Override equals(@ullable Object o)1430 public boolean equals(@Nullable Object o) { 1431 if (o instanceof BluetoothDevice) { 1432 return mAddress.equals(((BluetoothDevice) o).getAddress()); 1433 } 1434 return false; 1435 } 1436 1437 @Override hashCode()1438 public int hashCode() { 1439 return mAddress.hashCode(); 1440 } 1441 1442 /** 1443 * Returns a string representation of this BluetoothDevice. 1444 * <p> For apps targeting {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} 1445 * (API level 34) or higher, this returns the MAC address of the device redacted 1446 * by replacing the hexadecimal digits of leftmost 4 bytes (in big endian order) 1447 * with "XX", e.g., "XX:XX:XX:XX:12:34". For apps targeting earlier versions, 1448 * the MAC address is returned without redaction. 1449 * 1450 * Warning: The return value of {@link #toString()} may change in the future. 1451 * It is intended to be used in logging statements. Thus apps should never rely 1452 * on the return value of {@link #toString()} in their logic. Always use other 1453 * appropriate APIs instead (e.g., use {@link #getAddress()} to get the MAC address). 1454 * 1455 * @return string representation of this BluetoothDevice 1456 */ 1457 @Override toString()1458 public String toString() { 1459 if (!CompatChanges.isChangeEnabled(CHANGE_TO_STRING_REDACTED)) { 1460 return mAddress; 1461 } 1462 return toStringForLogging(); 1463 } 1464 shouldLogBeRedacted()1465 private static boolean shouldLogBeRedacted() { 1466 boolean defaultValue = true; 1467 if (!sIsLogRedactionFlagSynced) { 1468 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 1469 if (adapter == null || !adapter.isEnabled()) { 1470 return defaultValue; 1471 } 1472 IBluetooth service = adapter.getBluetoothService(); 1473 1474 if (service == null) { 1475 Log.e(TAG, "Bluetooth service is not enabled"); 1476 return defaultValue; 1477 } 1478 1479 try { 1480 sIsLogRedactionEnabled = service.isLogRedactionEnabled(); 1481 sIsLogRedactionFlagSynced = true; 1482 } catch (RemoteException e) { 1483 // by default, set to true 1484 Log.e(TAG, "Failed to call IBluetooth.isLogRedactionEnabled" 1485 + e.toString() + "\n" 1486 + Log.getStackTraceString(new Throwable())); 1487 return true; 1488 } 1489 } 1490 return sIsLogRedactionEnabled; 1491 } 1492 1493 /** 1494 * Returns a string representation of this BluetoothDevice for logging. 1495 * So far, this function only returns hardware address. 1496 * If more information is needed, add it here 1497 * 1498 * @return string representation of this BluetoothDevice used for logging 1499 * @hide 1500 */ toStringForLogging()1501 public String toStringForLogging() { 1502 return getAddressForLogging(); 1503 } 1504 1505 @Override describeContents()1506 public int describeContents() { 1507 return 0; 1508 } 1509 1510 public static final @NonNull Creator<BluetoothDevice> CREATOR = new Creator<>() { 1511 public BluetoothDevice createFromParcel(Parcel in) { 1512 return new BluetoothDevice(in); 1513 } 1514 1515 public BluetoothDevice[] newArray(int size) { 1516 return new BluetoothDevice[size]; 1517 } 1518 }; 1519 1520 @Override writeToParcel(Parcel out, int flags)1521 public void writeToParcel(Parcel out, int flags) { 1522 out.writeString(mAddress); 1523 out.writeInt(mAddressType); 1524 } 1525 1526 /** 1527 * Returns the hardware address of this BluetoothDevice. 1528 * <p> For example, "00:11:22:AA:BB:CC". 1529 * 1530 * @return Bluetooth hardware address as string 1531 */ getAddress()1532 public String getAddress() { 1533 if (DBG) Log.d(TAG, "getAddress: mAddress=" + getAddressForLogging()); 1534 return mAddress; 1535 } 1536 1537 /** 1538 * Returns the address type of this BluetoothDevice. 1539 * 1540 * @return Bluetooth address type 1541 * @hide 1542 */ getAddressType()1543 public int getAddressType() { 1544 if (DBG) Log.d(TAG, "mAddressType: " + mAddressType); 1545 return mAddressType; 1546 } 1547 1548 /** 1549 * Returns the anonymized hardware address of this BluetoothDevice. The first three octets 1550 * will be suppressed for anonymization. 1551 * <p> For example, "XX:XX:XX:AA:BB:CC". 1552 * 1553 * @return Anonymized bluetooth hardware address as string 1554 * @hide 1555 */ 1556 @SystemApi 1557 @NonNull 1558 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) getAnonymizedAddress()1559 public String getAnonymizedAddress() { 1560 return BluetoothUtils.toAnonymizedAddress(mAddress); 1561 } 1562 1563 /** 1564 * Returns string representation of the hardware address of this BluetoothDevice 1565 * for logging purpose. Depending on the build type and device config, 1566 * this function returns either full address string (returned by getAddress), 1567 * or a redacted string with the leftmost 4 bytes shown as 'xx', 1568 * <p> For example, "xx:xx:xx:xx:aa:bb". 1569 * This function is intended to avoid leaking full address in logs. 1570 * 1571 * @return string representation of the hardware address for logging 1572 * @hide 1573 */ getAddressForLogging()1574 public String getAddressForLogging() { 1575 if (shouldLogBeRedacted()) { 1576 return getAnonymizedAddress(); 1577 } 1578 return mAddress; 1579 } 1580 1581 /** 1582 * Returns the identity address of this BluetoothDevice. 1583 * <p> For example, "00:11:22:AA:BB:CC". 1584 * 1585 * @return Bluetooth identity address as a string 1586 * @hide 1587 */ 1588 @SystemApi 1589 @RequiresBluetoothConnectPermission 1590 @RequiresPermission(allOf = { 1591 android.Manifest.permission.BLUETOOTH_CONNECT, 1592 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 1593 }) getIdentityAddress()1594 public @Nullable String getIdentityAddress() { 1595 if (DBG) log("getIdentityAddress()"); 1596 final IBluetooth service = getService(); 1597 final String defaultValue = null; 1598 if (service == null || !isBluetoothEnabled()) { 1599 Log.e(TAG, "BT not enabled. Cannot get identity address"); 1600 } else { 1601 try { 1602 final SynchronousResultReceiver<String> recv = SynchronousResultReceiver.get(); 1603 service.getIdentityAddress(mAddress, recv); 1604 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 1605 } catch (RemoteException | TimeoutException e) { 1606 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1607 } 1608 } 1609 return defaultValue; 1610 } 1611 1612 /** 1613 * Get the friendly Bluetooth name of the remote device. 1614 * 1615 * <p>The local adapter will automatically retrieve remote names when 1616 * performing a device scan, and will cache them. This method just returns 1617 * the name for this device from the cache. 1618 * 1619 * @return the Bluetooth name, or null if there was a problem. 1620 */ 1621 @RequiresLegacyBluetoothPermission 1622 @RequiresBluetoothConnectPermission 1623 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getName()1624 public String getName() { 1625 if (DBG) log("getName()"); 1626 final IBluetooth service = getService(); 1627 final String defaultValue = null; 1628 if (service == null || !isBluetoothEnabled()) { 1629 Log.e(TAG, "BT not enabled. Cannot get Remote Device name"); 1630 if (DBG) log(Log.getStackTraceString(new Throwable())); 1631 } else { 1632 try { 1633 final SynchronousResultReceiver<String> recv = SynchronousResultReceiver.get(); 1634 service.getRemoteName(this, mAttributionSource, recv); 1635 String name = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 1636 if (name != null) { 1637 // remove whitespace characters from the name 1638 return name 1639 .replace('\t', ' ') 1640 .replace('\n', ' ') 1641 .replace('\r', ' '); 1642 } 1643 } catch (RemoteException | TimeoutException e) { 1644 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1645 } 1646 } 1647 return defaultValue; 1648 } 1649 1650 /** 1651 * Get the Bluetooth device type of the remote device. 1652 * 1653 * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} {@link 1654 * #DEVICE_TYPE_DUAL}. {@link #DEVICE_TYPE_UNKNOWN} if it's not available 1655 */ 1656 @RequiresLegacyBluetoothPermission 1657 @RequiresBluetoothConnectPermission 1658 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getType()1659 public int getType() { 1660 if (DBG) log("getType()"); 1661 final IBluetooth service = getService(); 1662 final int defaultValue = DEVICE_TYPE_UNKNOWN; 1663 if (service == null || !isBluetoothEnabled()) { 1664 Log.e(TAG, "BT not enabled. Cannot get Remote Device type"); 1665 if (DBG) log(Log.getStackTraceString(new Throwable())); 1666 } else { 1667 try { 1668 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 1669 service.getRemoteType(this, mAttributionSource, recv); 1670 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 1671 } catch (RemoteException | TimeoutException e) { 1672 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1673 } 1674 } 1675 return defaultValue; 1676 } 1677 1678 /** 1679 * Get the locally modifiable name (alias) of the remote Bluetooth device. 1680 * 1681 * @return the Bluetooth alias, the friendly device name if no alias, or 1682 * null if there was a problem 1683 */ 1684 @Nullable 1685 @RequiresLegacyBluetoothPermission 1686 @RequiresBluetoothConnectPermission 1687 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getAlias()1688 public String getAlias() { 1689 if (DBG) log("getAlias()"); 1690 final IBluetooth service = getService(); 1691 final String defaultValue = null; 1692 if (service == null || !isBluetoothEnabled()) { 1693 Log.e(TAG, "BT not enabled. Cannot get Remote Device Alias"); 1694 if (DBG) log(Log.getStackTraceString(new Throwable())); 1695 } else { 1696 try { 1697 final SynchronousResultReceiver<String> recv = SynchronousResultReceiver.get(); 1698 service.getRemoteAlias(this, mAttributionSource, recv); 1699 String alias = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 1700 if (alias == null) { 1701 return getName(); 1702 } 1703 return alias 1704 .replace('\t', ' ') 1705 .replace('\n', ' ') 1706 .replace('\r', ' '); 1707 } catch (RemoteException | TimeoutException e) { 1708 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1709 } 1710 } 1711 return defaultValue; 1712 } 1713 1714 /** @hide */ 1715 @Retention(RetentionPolicy.SOURCE) 1716 @IntDef(value = { 1717 BluetoothStatusCodes.SUCCESS, 1718 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 1719 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, 1720 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, 1721 BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED 1722 }) 1723 public @interface SetAliasReturnValues{} 1724 1725 /** 1726 * Sets the locally modifiable name (alias) of the remote Bluetooth device. This method 1727 * overwrites the previously stored alias. The new alias is saved in local 1728 * storage so that the change is preserved over power cycles. 1729 * 1730 * <p>This method requires the calling app to be associated with Companion Device Manager (see 1731 * {@link android.companion.CompanionDeviceManager#associate(AssociationRequest, 1732 * android.companion.CompanionDeviceManager.Callback, Handler)}) and have the 1733 * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission. Alternatively, if the 1734 * caller has the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can 1735 * bypass the Companion Device Manager association requirement as well as other permission 1736 * requirements. 1737 * 1738 * @param alias is the new locally modifiable name for the remote Bluetooth device which must 1739 * be the empty string. If null, we clear the alias. 1740 * @return whether the alias was successfully changed 1741 * @throws IllegalArgumentException if the alias is the empty string 1742 */ 1743 @RequiresLegacyBluetoothPermission 1744 @RequiresBluetoothConnectPermission 1745 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) setAlias(@ullable String alias)1746 public @SetAliasReturnValues int setAlias(@Nullable String alias) { 1747 if (alias != null && alias.isEmpty()) { 1748 throw new IllegalArgumentException("alias cannot be the empty string"); 1749 } 1750 if (DBG) log("setAlias(" + alias + ")"); 1751 final IBluetooth service = getService(); 1752 final int defaultValue = BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 1753 if (service == null || !isBluetoothEnabled()) { 1754 Log.e(TAG, "BT not enabled. Cannot set Remote Device name"); 1755 if (DBG) log(Log.getStackTraceString(new Throwable())); 1756 } else { 1757 try { 1758 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 1759 service.setRemoteAlias(this, alias, mAttributionSource, recv); 1760 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 1761 } catch (TimeoutException e) { 1762 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1763 } catch (RemoteException e) { 1764 Log.e(TAG, "", e); 1765 throw e.rethrowFromSystemServer(); 1766 } 1767 } 1768 return defaultValue; 1769 } 1770 1771 /** 1772 * Get the most recent identified battery level of this Bluetooth device 1773 * 1774 * @return Battery level in percents from 0 to 100, {@link #BATTERY_LEVEL_BLUETOOTH_OFF} if 1775 * Bluetooth is disabled or {@link #BATTERY_LEVEL_UNKNOWN} if device is disconnected, or does 1776 * not have any battery reporting service, or return value is invalid 1777 * @hide 1778 */ 1779 @SystemApi 1780 @RequiresLegacyBluetoothPermission 1781 @RequiresBluetoothConnectPermission 1782 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getBatteryLevel()1783 public @IntRange(from = -100, to = 100) int getBatteryLevel() { 1784 if (DBG) log("getBatteryLevel()"); 1785 final IBluetooth service = getService(); 1786 final int defaultValue = BATTERY_LEVEL_BLUETOOTH_OFF; 1787 if (service == null || !isBluetoothEnabled()) { 1788 Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level"); 1789 if (DBG) log(Log.getStackTraceString(new Throwable())); 1790 } else { 1791 try { 1792 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 1793 service.getBatteryLevel(this, mAttributionSource, recv); 1794 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 1795 } catch (RemoteException | TimeoutException e) { 1796 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1797 } 1798 } 1799 return defaultValue; 1800 } 1801 1802 /** 1803 * Start the bonding (pairing) process with the remote device. 1804 * <p>This is an asynchronous call, it will return immediately. Register 1805 * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when 1806 * the bonding process completes, and its result. 1807 * <p>Android system services will handle the necessary user interactions 1808 * to confirm and complete the bonding process. 1809 * 1810 * @return false on immediate error, true if bonding will begin 1811 */ 1812 @RequiresLegacyBluetoothAdminPermission 1813 @RequiresBluetoothConnectPermission 1814 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) createBond()1815 public boolean createBond() { 1816 return createBond(TRANSPORT_AUTO); 1817 } 1818 1819 /** 1820 * Start the bonding (pairing) process with the remote device using the 1821 * specified transport. 1822 * 1823 * <p>This is an asynchronous call, it will return immediately. Register 1824 * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when 1825 * the bonding process completes, and its result. 1826 * <p>Android system services will handle the necessary user interactions 1827 * to confirm and complete the bonding process. 1828 * 1829 * @param transport The transport to use for the pairing procedure. 1830 * @return false on immediate error, true if bonding will begin 1831 * @throws IllegalArgumentException if an invalid transport was specified 1832 * @hide 1833 */ 1834 @SystemApi 1835 @RequiresLegacyBluetoothAdminPermission 1836 @RequiresBluetoothConnectPermission 1837 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) createBond(int transport)1838 public boolean createBond(int transport) { 1839 return createBondInternal(transport, null, null); 1840 } 1841 1842 /** 1843 * Start the bonding (pairing) process with the remote device using the 1844 * Out Of Band mechanism. 1845 * 1846 * <p>This is an asynchronous call, it will return immediately. Register 1847 * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when 1848 * the bonding process completes, and its result. 1849 * 1850 * <p>Android system services will handle the necessary user interactions 1851 * to confirm and complete the bonding process. 1852 * 1853 * <p>There are two possible versions of OOB Data. This data can come in as 1854 * P192 or P256. This is a reference to the cryptography used to generate the key. 1855 * The caller may pass one or both. If both types of data are passed, then the 1856 * P256 data will be preferred, and thus used. 1857 * 1858 * @param transport - Transport to use 1859 * @param remoteP192Data - Out Of Band data (P192) or null 1860 * @param remoteP256Data - Out Of Band data (P256) or null 1861 * @return false on immediate error, true if bonding will begin 1862 * @hide 1863 */ 1864 @SystemApi 1865 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) createBondOutOfBand(int transport, @Nullable OobData remoteP192Data, @Nullable OobData remoteP256Data)1866 public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data, 1867 @Nullable OobData remoteP256Data) { 1868 if (remoteP192Data == null && remoteP256Data == null) { 1869 throw new IllegalArgumentException( 1870 "One or both arguments for the OOB data types are required to not be null." 1871 + " Please use createBond() instead if you do not have OOB data to pass."); 1872 } 1873 return createBondInternal(transport, remoteP192Data, remoteP256Data); 1874 } 1875 1876 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) createBondInternal(int transport, @Nullable OobData remoteP192Data, @Nullable OobData remoteP256Data)1877 private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data, 1878 @Nullable OobData remoteP256Data) { 1879 if (DBG) log("createBondOutOfBand()"); 1880 final IBluetooth service = getService(); 1881 final boolean defaultValue = false; 1882 if (service == null || !isBluetoothEnabled()) { 1883 Log.w(TAG, "BT not enabled, createBondOutOfBand failed"); 1884 if (DBG) log(Log.getStackTraceString(new Throwable())); 1885 } else if (NULL_MAC_ADDRESS.equals(mAddress)) { 1886 Log.e(TAG, "Unable to create bond, invalid address " + mAddress); 1887 } else { 1888 try { 1889 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 1890 service.createBond(this, transport, remoteP192Data, remoteP256Data, 1891 mAttributionSource, recv); 1892 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 1893 } catch (RemoteException | TimeoutException e) { 1894 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1895 } 1896 } 1897 return defaultValue; 1898 } 1899 1900 /** 1901 * Gets whether bonding was initiated locally 1902 * 1903 * @return true if bonding is initiated locally, false otherwise 1904 * 1905 * @hide 1906 */ 1907 @SystemApi 1908 @RequiresLegacyBluetoothPermission 1909 @RequiresBluetoothConnectPermission 1910 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) isBondingInitiatedLocally()1911 public boolean isBondingInitiatedLocally() { 1912 if (DBG) log("isBondingInitiatedLocally()"); 1913 final IBluetooth service = getService(); 1914 final boolean defaultValue = false; 1915 if (service == null || !isBluetoothEnabled()) { 1916 Log.w(TAG, "BT not enabled, isBondingInitiatedLocally failed"); 1917 if (DBG) log(Log.getStackTraceString(new Throwable())); 1918 } else { 1919 try { 1920 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 1921 service.isBondingInitiatedLocally(this, mAttributionSource, recv); 1922 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 1923 } catch (RemoteException | TimeoutException e) { 1924 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1925 } 1926 } 1927 return defaultValue; 1928 } 1929 1930 /** 1931 * Cancel an in-progress bonding request started with {@link #createBond}. 1932 * 1933 * @return true on success, false on error 1934 * @hide 1935 */ 1936 @SystemApi 1937 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) cancelBondProcess()1938 public boolean cancelBondProcess() { 1939 if (DBG) log("cancelBondProcess()"); 1940 final IBluetooth service = getService(); 1941 final boolean defaultValue = false; 1942 if (service == null || !isBluetoothEnabled()) { 1943 Log.e(TAG, "BT not enabled. Cannot cancel Remote Device bond"); 1944 if (DBG) log(Log.getStackTraceString(new Throwable())); 1945 } else { 1946 Log.i(TAG, "cancelBondProcess() for device " + toStringForLogging() 1947 + " called by pid: " + Process.myPid() 1948 + " tid: " + Process.myTid()); 1949 try { 1950 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 1951 service.cancelBondProcess(this, mAttributionSource, recv); 1952 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 1953 } catch (RemoteException | TimeoutException e) { 1954 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1955 } 1956 } 1957 return defaultValue; 1958 } 1959 1960 /** 1961 * Remove bond (pairing) with the remote device. 1962 * <p>Delete the link key associated with the remote device, and 1963 * immediately terminate connections to that device that require 1964 * authentication and encryption. 1965 * 1966 * @return true on success, false on error 1967 * @hide 1968 */ 1969 @SystemApi 1970 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) removeBond()1971 public boolean removeBond() { 1972 if (DBG) log("removeBond()"); 1973 final IBluetooth service = getService(); 1974 final boolean defaultValue = false; 1975 if (service == null || !isBluetoothEnabled()) { 1976 Log.e(TAG, "BT not enabled. Cannot remove Remote Device bond"); 1977 if (DBG) log(Log.getStackTraceString(new Throwable())); 1978 } else { 1979 Log.i(TAG, "removeBond() for device " + toStringForLogging() 1980 + " called by pid: " + Process.myPid() 1981 + " tid: " + Process.myTid()); 1982 try { 1983 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 1984 service.removeBond(this, mAttributionSource, recv); 1985 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 1986 } catch (RemoteException | TimeoutException e) { 1987 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1988 } 1989 } 1990 return defaultValue; 1991 } 1992 1993 /** 1994 * There are several instances of IpcDataCache used in this class. 1995 * BluetoothCache wraps up the common code. All caches are created with a maximum of 1996 * eight entries, and the key is in the bluetooth module. The name is set to the api. 1997 */ 1998 private static class BluetoothCache<Q, R> extends IpcDataCache<Q, R> { BluetoothCache(String api, IpcDataCache.QueryHandler query)1999 BluetoothCache(String api, IpcDataCache.QueryHandler query) { 2000 super(8, IpcDataCache.MODULE_BLUETOOTH, api, api, query); 2001 }}; 2002 2003 /** 2004 * Invalidate a bluetooth cache. This method is just a short-hand wrapper that 2005 * enforces the bluetooth module. 2006 */ invalidateCache(@onNull String api)2007 private static void invalidateCache(@NonNull String api) { 2008 IpcDataCache.invalidateCache(IpcDataCache.MODULE_BLUETOOTH, api); 2009 } 2010 2011 private static final IpcDataCache 2012 .QueryHandler<Pair<IBluetooth, Pair<AttributionSource, BluetoothDevice>>, Integer> 2013 sBluetoothBondQuery = new IpcDataCache.QueryHandler<>() { 2014 @RequiresLegacyBluetoothPermission 2015 @RequiresBluetoothConnectPermission 2016 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 2017 @Override 2018 public Integer apply(Pair<IBluetooth, 2019 Pair<AttributionSource, BluetoothDevice>> pairQuery) { 2020 IBluetooth service = pairQuery.first; 2021 AttributionSource source = pairQuery.second.first; 2022 BluetoothDevice device = pairQuery.second.second; 2023 if (DBG) { 2024 log("getBondState(" + device.toStringForLogging() + ") uncached"); 2025 } 2026 try { 2027 final SynchronousResultReceiver<Integer> recv = 2028 SynchronousResultReceiver.get(); 2029 service.getBondState(device, source, recv); 2030 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(BOND_NONE); 2031 } catch (RemoteException | TimeoutException e) { 2032 throw new RuntimeException(e); 2033 } 2034 } 2035 }; 2036 2037 private static final String GET_BOND_STATE_API = "BluetoothDevice_getBondState"; 2038 2039 private static final 2040 BluetoothCache<Pair<IBluetooth, Pair<AttributionSource, BluetoothDevice>>, Integer> 2041 sBluetoothBondCache = new BluetoothCache<>(GET_BOND_STATE_API, sBluetoothBondQuery); 2042 2043 /** @hide */ disableBluetoothGetBondStateCache()2044 public void disableBluetoothGetBondStateCache() { 2045 sBluetoothBondCache.disableForCurrentProcess(); 2046 } 2047 2048 /** @hide */ invalidateBluetoothGetBondStateCache()2049 public static void invalidateBluetoothGetBondStateCache() { 2050 invalidateCache(GET_BOND_STATE_API); 2051 } 2052 2053 /** 2054 * Get the bond state of the remote device. 2055 * <p>Possible values for the bond state are: 2056 * {@link #BOND_NONE}, 2057 * {@link #BOND_BONDING}, 2058 * {@link #BOND_BONDED}. 2059 * 2060 * @return the bond state 2061 */ 2062 @RequiresLegacyBluetoothPermission 2063 @RequiresBluetoothConnectPermission 2064 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getBondState()2065 public int getBondState() { 2066 if (DBG) log("getBondState(" + toStringForLogging() + ")"); 2067 final IBluetooth service = getService(); 2068 if (service == null) { 2069 Log.e(TAG, "BT not enabled. Cannot get bond state"); 2070 if (DBG) log(Log.getStackTraceString(new Throwable())); 2071 } else { 2072 try { 2073 return sBluetoothBondCache.query( 2074 new Pair<>(service, new Pair<>(mAttributionSource, BluetoothDevice.this))); 2075 } catch (RuntimeException e) { 2076 if (!(e.getCause() instanceof TimeoutException) 2077 && !(e.getCause() instanceof RemoteException)) { 2078 throw e; 2079 } 2080 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2081 } 2082 } 2083 return BOND_NONE; 2084 } 2085 2086 /** 2087 * Checks whether this bluetooth device is associated with CDM and meets the criteria to skip 2088 * the bluetooth pairing dialog because it has been already consented by the CDM prompt. 2089 * 2090 * @return true if we can bond without the dialog, false otherwise 2091 * 2092 * @hide 2093 */ 2094 @SystemApi 2095 @RequiresPermission(allOf = { 2096 android.Manifest.permission.BLUETOOTH_CONNECT, 2097 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2098 }) canBondWithoutDialog()2099 public boolean canBondWithoutDialog() { 2100 if (DBG) log("canBondWithoutDialog, device: " + toStringForLogging()); 2101 final IBluetooth service = getService(); 2102 final boolean defaultValue = false; 2103 if (service == null || !isBluetoothEnabled()) { 2104 Log.e(TAG, "BT not enabled. Cannot check if we can skip pairing dialog"); 2105 if (DBG) log(Log.getStackTraceString(new Throwable())); 2106 } else { 2107 try { 2108 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2109 service.canBondWithoutDialog(this, mAttributionSource, recv); 2110 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2111 } catch (RemoteException | TimeoutException e) { 2112 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2113 } 2114 } 2115 return defaultValue; 2116 } 2117 2118 /** 2119 * Gets the package name of the application that initiate bonding with this device 2120 * 2121 * @return package name of the application, or null of no application initiate bonding with 2122 * this device 2123 * 2124 * @hide 2125 */ 2126 @SystemApi 2127 @Nullable 2128 @RequiresPermission(allOf = { 2129 android.Manifest.permission.BLUETOOTH_CONNECT, 2130 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2131 }) getPackageNameOfBondingApplication()2132 public String getPackageNameOfBondingApplication() { 2133 if (DBG) log("getPackageNameOfBondingApplication()"); 2134 final IBluetooth service = getService(); 2135 final String defaultValue = null; 2136 if (service == null || !isBluetoothEnabled()) { 2137 Log.w(TAG, "BT not enabled, getPackageNameOfBondingApplication failed"); 2138 if (DBG) log(Log.getStackTraceString(new Throwable())); 2139 } else { 2140 try { 2141 final SynchronousResultReceiver<String> recv = SynchronousResultReceiver.get(); 2142 service.getPackageNameOfBondingApplication(this, recv); 2143 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2144 } catch (RemoteException | TimeoutException e) { 2145 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2146 } 2147 } 2148 return defaultValue; 2149 } 2150 2151 /** @hide */ 2152 @Retention(RetentionPolicy.SOURCE) 2153 @IntDef(value = { 2154 BluetoothStatusCodes.SUCCESS, 2155 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 2156 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, 2157 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, 2158 BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED 2159 }) 2160 public @interface ConnectionReturnValues{} 2161 2162 /** 2163 * Connects all user enabled and supported bluetooth profiles between the local and remote 2164 * device. If no profiles are user enabled (e.g. first connection), we connect all supported 2165 * profiles. If the device is not already connected, this will page the device before initiating 2166 * profile connections. Connection is asynchronous and you should listen to each profile's 2167 * broadcast intent ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful. 2168 * For example, to verify a2dp is connected, you would listen for 2169 * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED} 2170 * 2171 * @return whether the messages were successfully sent to try to connect all profiles 2172 * @throws IllegalArgumentException if the device address is invalid 2173 * 2174 * @hide 2175 */ 2176 @SystemApi 2177 @RequiresBluetoothConnectPermission 2178 @RequiresPermission(allOf = { 2179 android.Manifest.permission.BLUETOOTH_CONNECT, 2180 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2181 android.Manifest.permission.MODIFY_PHONE_STATE, 2182 }) connect()2183 public @ConnectionReturnValues int connect() { 2184 if (DBG) log("connect()"); 2185 if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) { 2186 throw new IllegalArgumentException("device cannot have an invalid address"); 2187 } 2188 final IBluetooth service = getService(); 2189 final int defaultValue = BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 2190 if (service == null || !isBluetoothEnabled()) { 2191 Log.e(TAG, "BT not enabled. Cannot connect to remote device."); 2192 if (DBG) log(Log.getStackTraceString(new Throwable())); 2193 } else { 2194 try { 2195 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2196 service.connectAllEnabledProfiles(this, mAttributionSource, recv); 2197 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2198 } catch (TimeoutException e) { 2199 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2200 } catch (RemoteException e) { 2201 Log.e(TAG, "", e); 2202 throw e.rethrowFromSystemServer(); 2203 } 2204 } 2205 return defaultValue; 2206 } 2207 2208 /** 2209 * Disconnects all connected bluetooth profiles between the local and remote device. 2210 * Disconnection is asynchronous, so you should listen to each profile's broadcast intent 2211 * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example, 2212 * to verify a2dp is disconnected, you would listen for 2213 * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}. Once all profiles have disconnected, 2214 * the ACL link should come down and {@link #ACTION_ACL_DISCONNECTED} should be broadcast. 2215 * <p> 2216 * In the rare event that one or more profiles fail to disconnect, call this method again to 2217 * send another request to disconnect each connected profile. 2218 * 2219 * @return whether the messages were successfully sent to try to disconnect all profiles 2220 * @throws IllegalArgumentException if the device address is invalid 2221 * 2222 * @hide 2223 */ 2224 @SystemApi 2225 @RequiresBluetoothConnectPermission 2226 @RequiresPermission(allOf = { 2227 android.Manifest.permission.BLUETOOTH_CONNECT, 2228 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2229 }) disconnect()2230 public @ConnectionReturnValues int disconnect() { 2231 if (DBG) log("disconnect()"); 2232 if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) { 2233 throw new IllegalArgumentException("device cannot have an invalid address"); 2234 } 2235 final IBluetooth service = getService(); 2236 final int defaultValue = BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 2237 if (service == null || !isBluetoothEnabled()) { 2238 Log.e(TAG, "BT not enabled. Cannot disconnect to remote device."); 2239 if (DBG) log(Log.getStackTraceString(new Throwable())); 2240 } else { 2241 try { 2242 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2243 service.disconnectAllEnabledProfiles(this, mAttributionSource, recv); 2244 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2245 } catch (TimeoutException e) { 2246 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2247 } catch (RemoteException e) { 2248 Log.e(TAG, "", e); 2249 throw e.rethrowFromSystemServer(); 2250 } 2251 } 2252 return defaultValue; 2253 } 2254 2255 /** 2256 * Returns whether there is an open connection to this device. 2257 * 2258 * @return True if there is at least one open connection to this device. 2259 * @hide 2260 */ 2261 @SystemApi 2262 @RequiresLegacyBluetoothPermission 2263 @RequiresBluetoothConnectPermission 2264 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) isConnected()2265 public boolean isConnected() { 2266 if (DBG) log("isConnected()"); 2267 final IBluetooth service = getService(); 2268 final int defaultValue = CONNECTION_STATE_DISCONNECTED; 2269 if (service == null || !isBluetoothEnabled()) { 2270 Log.w(TAG, "Proxy not attached to service"); 2271 if (DBG) log(Log.getStackTraceString(new Throwable())); 2272 } else { 2273 try { 2274 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2275 service.getConnectionState(this, mAttributionSource, recv); 2276 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue) 2277 != CONNECTION_STATE_DISCONNECTED; 2278 } catch (RemoteException | TimeoutException e) { 2279 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2280 } 2281 } 2282 // BT is not enabled, we cannot be connected. 2283 return false; 2284 } 2285 2286 /** 2287 * Returns the ACL connection handle associated with an open connection to 2288 * this device on the given transport. 2289 * 2290 * This handle is a unique identifier for the connection while it remains 2291 * active. Refer to the Bluetooth Core Specification Version 5.4 Vol 4 Part E 2292 * Section 5.3.1 Controller Handles for details. 2293 * 2294 * @return the ACL handle, or {@link BluetoothDevice#ERROR} if no connection currently exists on 2295 * the given transport. 2296 * @hide 2297 */ 2298 @SystemApi 2299 @RequiresPermission(allOf = { 2300 android.Manifest.permission.BLUETOOTH_CONNECT, 2301 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2302 }) getConnectionHandle(@ransport int transport)2303 public int getConnectionHandle(@Transport int transport) { 2304 if (DBG) { 2305 log("getConnectionHandle()"); 2306 } 2307 final IBluetooth service = getService(); 2308 if (service == null || !isBluetoothEnabled()) { 2309 Log.w(TAG, "Proxy not attached to service"); 2310 if (DBG) { 2311 log(Log.getStackTraceString(new Throwable())); 2312 } 2313 } else { 2314 try { 2315 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2316 service.getConnectionHandle(this, transport, mAttributionSource, recv); 2317 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(-1); 2318 } catch (RemoteException | TimeoutException e) { 2319 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2320 } 2321 } 2322 // BT is not enabled, we cannot be connected. 2323 return BluetoothDevice.ERROR; 2324 } 2325 2326 /** 2327 * Returns whether there is an open connection to this device 2328 * that has been encrypted. 2329 * 2330 * @return True if there is at least one encrypted connection to this device. 2331 * @hide 2332 */ 2333 @SystemApi 2334 @RequiresLegacyBluetoothPermission 2335 @RequiresBluetoothConnectPermission 2336 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) isEncrypted()2337 public boolean isEncrypted() { 2338 if (DBG) log("isEncrypted()"); 2339 final IBluetooth service = getService(); 2340 final int defaultValue = CONNECTION_STATE_DISCONNECTED; 2341 if (service == null || !isBluetoothEnabled()) { 2342 Log.w(TAG, "Proxy not attached to service"); 2343 if (DBG) log(Log.getStackTraceString(new Throwable())); 2344 } else { 2345 try { 2346 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2347 service.getConnectionState(this, mAttributionSource, recv); 2348 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue) 2349 > CONNECTION_STATE_CONNECTED; 2350 } catch (RemoteException | TimeoutException e) { 2351 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2352 } 2353 } 2354 // BT is not enabled, we cannot be encrypted. 2355 return false; 2356 } 2357 2358 /** 2359 * Get the Bluetooth class of the remote device. 2360 * 2361 * @return Bluetooth class object, or null on error 2362 */ 2363 @RequiresLegacyBluetoothPermission 2364 @RequiresBluetoothConnectPermission 2365 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getBluetoothClass()2366 public BluetoothClass getBluetoothClass() { 2367 if (DBG) log("getBluetoothClass()"); 2368 final IBluetooth service = getService(); 2369 final int defaultValue = 0; 2370 if (service == null || !isBluetoothEnabled()) { 2371 Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class"); 2372 if (DBG) log(Log.getStackTraceString(new Throwable())); 2373 } else { 2374 try { 2375 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2376 service.getRemoteClass(this, mAttributionSource, recv); 2377 int classInt = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2378 if (classInt == BluetoothClass.ERROR) return null; 2379 return new BluetoothClass(classInt); 2380 } catch (RemoteException | TimeoutException e) { 2381 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2382 } 2383 } 2384 return null; 2385 } 2386 2387 /** 2388 * Returns the supported features (UUIDs) of the remote device. 2389 * 2390 * <p>This method does not start a service discovery procedure to retrieve the UUIDs 2391 * from the remote device. Instead, the local cached copy of the service 2392 * UUIDs are returned. 2393 * <p>Use {@link #fetchUuidsWithSdp} if fresh UUIDs are desired. 2394 * 2395 * @return the supported features (UUIDs) of the remote device, or null on error 2396 */ 2397 @RequiresLegacyBluetoothPermission 2398 @RequiresBluetoothConnectPermission 2399 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getUuids()2400 public ParcelUuid[] getUuids() { 2401 if (DBG) log("getUuids()"); 2402 final IBluetooth service = getService(); 2403 final ParcelUuid[] defaultValue = null; 2404 if (service == null || !isBluetoothEnabled()) { 2405 Log.e(TAG, "BT not enabled. Cannot get remote device Uuids"); 2406 if (DBG) log(Log.getStackTraceString(new Throwable())); 2407 } else { 2408 try { 2409 final SynchronousResultReceiver<List<ParcelUuid>> recv = 2410 SynchronousResultReceiver.get(); 2411 service.getRemoteUuids(this, mAttributionSource, recv); 2412 List<ParcelUuid> parcels = recv.awaitResultNoInterrupt(getSyncTimeout()) 2413 .getValue(null); 2414 return parcels != null ? parcels.toArray(new ParcelUuid[parcels.size()]) : null; 2415 } catch (RemoteException | TimeoutException e) { 2416 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2417 } 2418 } 2419 return defaultValue; 2420 } 2421 2422 /** 2423 * Perform a service discovery on the remote device to get the UUIDs supported. 2424 * 2425 * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent, 2426 * with the UUIDs supported by the remote end. If there is an error 2427 * in getting the SDP records or if the process takes a long time, or the device is bonding and 2428 * we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the UUIDs that is 2429 * currently present in the cache. Clients should use the {@link #getUuids} to get UUIDs 2430 * if service discovery is not to be performed. If there is an ongoing bonding process, 2431 * service discovery or device inquiry, the request will be queued. 2432 * 2433 * @return False if the check fails, True if the process of initiating an ACL connection 2434 * to the remote device was started or cached UUIDs will be broadcast. 2435 */ 2436 @RequiresLegacyBluetoothPermission 2437 @RequiresBluetoothConnectPermission 2438 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) fetchUuidsWithSdp()2439 public boolean fetchUuidsWithSdp() { 2440 return fetchUuidsWithSdp(TRANSPORT_AUTO); 2441 } 2442 2443 /** 2444 * Perform a service discovery on the remote device to get the UUIDs supported with the 2445 * specific transport. 2446 * 2447 * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent, 2448 * with the UUIDs supported by the remote end. If there is an error 2449 * in getting the SDP or GATT records or if the process takes a long time, or the device 2450 * is bonding and we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the 2451 * UUIDs that is currently present in the cache. Clients should use the {@link #getUuids} 2452 * to get UUIDs if service discovery is not to be performed. If there is an ongoing bonding 2453 * process, service discovery or device inquiry, the request will be queued. 2454 * 2455 * @param transport - provide type of transport (e.g. LE or Classic). 2456 * @return False if the check fails, True if the process of initiating an ACL connection 2457 * to the remote device was started or cached UUIDs will be broadcast with the specific 2458 * transport. 2459 * 2460 * @hide 2461 */ 2462 @SystemApi 2463 @RequiresPermission(allOf = { 2464 android.Manifest.permission.BLUETOOTH_CONNECT, 2465 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2466 }) fetchUuidsWithSdp(@ransport int transport)2467 public boolean fetchUuidsWithSdp(@Transport int transport) { 2468 if (DBG) log("fetchUuidsWithSdp()"); 2469 final IBluetooth service = getService(); 2470 final boolean defaultValue = false; 2471 if (service == null || !isBluetoothEnabled()) { 2472 Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); 2473 if (DBG) log(Log.getStackTraceString(new Throwable())); 2474 } else { 2475 try { 2476 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2477 service.fetchRemoteUuids(this, transport, mAttributionSource, recv); 2478 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2479 } catch (RemoteException | TimeoutException e) { 2480 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2481 } 2482 } 2483 return defaultValue; 2484 } 2485 2486 /** 2487 * Perform a service discovery on the remote device to get the SDP records associated 2488 * with the specified UUID. 2489 * 2490 * <p>This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent, 2491 * with the SDP records found on the remote end. If there is an error 2492 * in getting the SDP records or if the process takes a long time, 2493 * {@link #ACTION_SDP_RECORD} intent is sent with an status value in 2494 * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0. 2495 * Detailed status error codes can be found by members of the Bluetooth package in 2496 * the AbstractionLayer class. 2497 * <p>The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}. 2498 * The object type will match one of the SdpXxxRecord types, depending on the UUID searched 2499 * for. 2500 * 2501 * @return False if the check fails, True if the process 2502 * of initiating an ACL connection to the remote device 2503 * was started. 2504 */ 2505 /** @hide */ 2506 @RequiresLegacyBluetoothPermission 2507 @RequiresBluetoothConnectPermission 2508 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) sdpSearch(ParcelUuid uuid)2509 public boolean sdpSearch(ParcelUuid uuid) { 2510 if (DBG) log("sdpSearch()"); 2511 final IBluetooth service = getService(); 2512 final boolean defaultValue = false; 2513 if (service == null || !isBluetoothEnabled()) { 2514 Log.e(TAG, "BT not enabled. Cannot query remote device sdp records"); 2515 if (DBG) log(Log.getStackTraceString(new Throwable())); 2516 } else { 2517 try { 2518 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2519 service.sdpSearch(this, uuid, mAttributionSource, recv); 2520 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2521 } catch (RemoteException | TimeoutException e) { 2522 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2523 } 2524 } 2525 return defaultValue; 2526 } 2527 2528 /** 2529 * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} 2530 * 2531 * @return true pin has been set false for error 2532 */ 2533 @RequiresLegacyBluetoothAdminPermission 2534 @RequiresBluetoothConnectPermission 2535 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) setPin(byte[] pin)2536 public boolean setPin(byte[] pin) { 2537 if (DBG) log("setPin()"); 2538 final IBluetooth service = getService(); 2539 final boolean defaultValue = false; 2540 if (service == null || !isBluetoothEnabled()) { 2541 Log.e(TAG, "BT not enabled. Cannot set Remote Device pin"); 2542 if (DBG) log(Log.getStackTraceString(new Throwable())); 2543 } else { 2544 try { 2545 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2546 service.setPin(this, true, pin.length, pin, mAttributionSource, recv); 2547 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2548 } catch (RemoteException | TimeoutException e) { 2549 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2550 } 2551 } 2552 return defaultValue; 2553 } 2554 2555 /** 2556 * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} 2557 * 2558 * @return true pin has been set false for error 2559 * @hide 2560 */ 2561 @SystemApi 2562 @RequiresLegacyBluetoothAdminPermission 2563 @RequiresBluetoothConnectPermission 2564 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) setPin(@onNull String pin)2565 public boolean setPin(@NonNull String pin) { 2566 byte[] pinBytes = convertPinToBytes(pin); 2567 if (pinBytes == null) { 2568 return false; 2569 } 2570 return setPin(pinBytes); 2571 } 2572 2573 /** 2574 * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. 2575 * 2576 * @return true confirmation has been sent out false for error 2577 */ 2578 @RequiresPermission(allOf = { 2579 android.Manifest.permission.BLUETOOTH_CONNECT, 2580 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2581 }) setPairingConfirmation(boolean confirm)2582 public boolean setPairingConfirmation(boolean confirm) { 2583 if (DBG) log("setPairingConfirmation()"); 2584 final IBluetooth service = getService(); 2585 final boolean defaultValue = false; 2586 if (service == null || !isBluetoothEnabled()) { 2587 Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); 2588 if (DBG) log(Log.getStackTraceString(new Throwable())); 2589 } else { 2590 try { 2591 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2592 service.setPairingConfirmation(this, confirm, mAttributionSource, recv); 2593 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2594 } catch (RemoteException | TimeoutException e) { 2595 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2596 } 2597 } 2598 return defaultValue; 2599 } 2600 isBluetoothEnabled()2601 boolean isBluetoothEnabled() { 2602 boolean ret = false; 2603 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 2604 if (adapter != null && adapter.isEnabled()) { 2605 ret = true; 2606 } 2607 return ret; 2608 } 2609 2610 /** 2611 * Gets whether the phonebook access is allowed for this bluetooth device 2612 * 2613 * @return Whether the phonebook access is allowed to this device. Can be {@link 2614 * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. 2615 * @hide 2616 */ 2617 @SystemApi 2618 @RequiresLegacyBluetoothPermission 2619 @RequiresBluetoothConnectPermission 2620 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getPhonebookAccessPermission()2621 public @AccessPermission int getPhonebookAccessPermission() { 2622 if (DBG) log("getPhonebookAccessPermission()"); 2623 final IBluetooth service = getService(); 2624 final int defaultValue = ACCESS_UNKNOWN; 2625 if (service == null || !isBluetoothEnabled()) { 2626 Log.w(TAG, "Proxy not attached to service"); 2627 if (DBG) log(Log.getStackTraceString(new Throwable())); 2628 } else { 2629 try { 2630 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2631 service.getPhonebookAccessPermission(this, mAttributionSource, recv); 2632 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2633 } catch (RemoteException | TimeoutException e) { 2634 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2635 } 2636 } 2637 return defaultValue; 2638 } 2639 2640 /** 2641 * Sets whether the {@link BluetoothDevice} enters silence mode. Audio will not 2642 * be routed to the {@link BluetoothDevice} if set to {@code true}. 2643 * 2644 * When the {@link BluetoothDevice} enters silence mode, and the {@link BluetoothDevice} 2645 * is an active device (for A2DP or HFP), the active device for that profile 2646 * will be set to null. 2647 * If the {@link BluetoothDevice} exits silence mode while the A2DP or HFP 2648 * active device is null, the {@link BluetoothDevice} will be set as the 2649 * active device for that profile. 2650 * If the {@link BluetoothDevice} is disconnected, it exits silence mode. 2651 * If the {@link BluetoothDevice} is set as the active device for A2DP or 2652 * HFP, while silence mode is enabled, then the device will exit silence mode. 2653 * If the {@link BluetoothDevice} is in silence mode, AVRCP position change 2654 * event and HFP AG indicators will be disabled. 2655 * If the {@link BluetoothDevice} is not connected with A2DP or HFP, it cannot 2656 * enter silence mode. 2657 * 2658 * @param silence true to enter silence mode, false to exit 2659 * @return true on success, false on error. 2660 * @throws IllegalStateException if Bluetooth is not turned ON. 2661 * @hide 2662 */ 2663 @SystemApi 2664 @RequiresPermission(allOf = { 2665 android.Manifest.permission.BLUETOOTH_CONNECT, 2666 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2667 }) setSilenceMode(boolean silence)2668 public boolean setSilenceMode(boolean silence) { 2669 if (DBG) log("setSilenceMode()"); 2670 final IBluetooth service = getService(); 2671 final boolean defaultValue = false; 2672 if (service == null || !isBluetoothEnabled()) { 2673 throw new IllegalStateException("Bluetooth is not turned ON"); 2674 } else { 2675 try { 2676 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2677 service.setSilenceMode(this, silence, mAttributionSource, recv); 2678 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2679 } catch (RemoteException | TimeoutException e) { 2680 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2681 } 2682 } 2683 return defaultValue; 2684 } 2685 2686 /** 2687 * Check whether the {@link BluetoothDevice} is in silence mode 2688 * 2689 * @return true on device in silence mode, otherwise false. 2690 * @throws IllegalStateException if Bluetooth is not turned ON. 2691 * @hide 2692 */ 2693 @SystemApi 2694 @RequiresPermission(allOf = { 2695 android.Manifest.permission.BLUETOOTH_CONNECT, 2696 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2697 }) isInSilenceMode()2698 public boolean isInSilenceMode() { 2699 if (DBG) log("isInSilenceMode()"); 2700 final IBluetooth service = getService(); 2701 final boolean defaultValue = false; 2702 if (service == null || !isBluetoothEnabled()) { 2703 throw new IllegalStateException("Bluetooth is not turned ON"); 2704 } else { 2705 try { 2706 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2707 service.getSilenceMode(this, mAttributionSource, recv); 2708 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2709 } catch (RemoteException | TimeoutException e) { 2710 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2711 } 2712 } 2713 return defaultValue; 2714 } 2715 2716 /** 2717 * Sets whether the phonebook access is allowed to this device. 2718 * 2719 * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link 2720 * #ACCESS_REJECTED}. 2721 * @return Whether the value has been successfully set. 2722 * @hide 2723 */ 2724 @SystemApi 2725 @RequiresPermission(allOf = { 2726 android.Manifest.permission.BLUETOOTH_CONNECT, 2727 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2728 }) setPhonebookAccessPermission(@ccessPermission int value)2729 public boolean setPhonebookAccessPermission(@AccessPermission int value) { 2730 if (DBG) log("setPhonebookAccessPermission()"); 2731 final IBluetooth service = getService(); 2732 final boolean defaultValue = false; 2733 if (service == null || !isBluetoothEnabled()) { 2734 Log.w(TAG, "Proxy not attached to service"); 2735 if (DBG) log(Log.getStackTraceString(new Throwable())); 2736 } else { 2737 try { 2738 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2739 service.setPhonebookAccessPermission(this, value, mAttributionSource, recv); 2740 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2741 } catch (RemoteException | TimeoutException e) { 2742 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2743 } 2744 } 2745 return defaultValue; 2746 } 2747 2748 /** 2749 * Gets whether message access is allowed to this bluetooth device 2750 * 2751 * @return Whether the message access is allowed to this device. 2752 * @hide 2753 */ 2754 @SystemApi 2755 @RequiresLegacyBluetoothPermission 2756 @RequiresBluetoothConnectPermission 2757 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getMessageAccessPermission()2758 public @AccessPermission int getMessageAccessPermission() { 2759 if (DBG) log("getMessageAccessPermission()"); 2760 final IBluetooth service = getService(); 2761 final int defaultValue = ACCESS_UNKNOWN; 2762 if (service == null || !isBluetoothEnabled()) { 2763 Log.w(TAG, "Proxy not attached to service"); 2764 if (DBG) log(Log.getStackTraceString(new Throwable())); 2765 } else { 2766 try { 2767 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2768 service.getMessageAccessPermission(this, mAttributionSource, recv); 2769 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2770 } catch (RemoteException | TimeoutException e) { 2771 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2772 } 2773 } 2774 return defaultValue; 2775 } 2776 2777 /** 2778 * Sets whether the message access is allowed to this device. 2779 * 2780 * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded, 2781 * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if 2782 * the permission is not being granted. 2783 * @return Whether the value has been successfully set. 2784 * @hide 2785 */ 2786 @SystemApi 2787 @RequiresPermission(allOf = { 2788 android.Manifest.permission.BLUETOOTH_CONNECT, 2789 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2790 }) setMessageAccessPermission(@ccessPermission int value)2791 public boolean setMessageAccessPermission(@AccessPermission int value) { 2792 // Validates param value is one of the accepted constants 2793 if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) { 2794 throw new IllegalArgumentException(value + "is not a valid AccessPermission value"); 2795 } 2796 if (DBG) log("setMessageAccessPermission()"); 2797 final IBluetooth service = getService(); 2798 final boolean defaultValue = false; 2799 if (service == null || !isBluetoothEnabled()) { 2800 Log.w(TAG, "Proxy not attached to service"); 2801 if (DBG) log(Log.getStackTraceString(new Throwable())); 2802 } else { 2803 try { 2804 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2805 service.setMessageAccessPermission(this, value, mAttributionSource, recv); 2806 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2807 } catch (RemoteException | TimeoutException e) { 2808 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2809 } 2810 } 2811 return defaultValue; 2812 } 2813 2814 /** 2815 * Gets whether sim access is allowed for this bluetooth device 2816 * 2817 * @return Whether the Sim access is allowed to this device. 2818 * @hide 2819 */ 2820 @SystemApi 2821 @RequiresLegacyBluetoothPermission 2822 @RequiresBluetoothConnectPermission 2823 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getSimAccessPermission()2824 public @AccessPermission int getSimAccessPermission() { 2825 if (DBG) log("getSimAccessPermission()"); 2826 final IBluetooth service = getService(); 2827 final int defaultValue = ACCESS_UNKNOWN; 2828 if (service == null || !isBluetoothEnabled()) { 2829 Log.w(TAG, "Proxy not attached to service"); 2830 if (DBG) log(Log.getStackTraceString(new Throwable())); 2831 } else { 2832 try { 2833 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2834 service.getSimAccessPermission(this, mAttributionSource, recv); 2835 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2836 } catch (RemoteException | TimeoutException e) { 2837 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2838 } 2839 } 2840 return defaultValue; 2841 } 2842 2843 /** 2844 * Sets whether the Sim access is allowed to this device. 2845 * 2846 * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded, 2847 * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if 2848 * the permission is not being granted. 2849 * @return Whether the value has been successfully set. 2850 * @hide 2851 */ 2852 @SystemApi 2853 @RequiresPermission(allOf = { 2854 android.Manifest.permission.BLUETOOTH_CONNECT, 2855 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2856 }) setSimAccessPermission(int value)2857 public boolean setSimAccessPermission(int value) { 2858 if (DBG) log("setSimAccessPermission()"); 2859 final IBluetooth service = getService(); 2860 final boolean defaultValue = false; 2861 if (service == null || !isBluetoothEnabled()) { 2862 Log.w(TAG, "Proxy not attached to service"); 2863 if (DBG) log(Log.getStackTraceString(new Throwable())); 2864 } else { 2865 try { 2866 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2867 service.setSimAccessPermission(this, value, mAttributionSource, recv); 2868 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 2869 } catch (RemoteException | TimeoutException e) { 2870 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2871 } 2872 } 2873 return defaultValue; 2874 } 2875 2876 /** 2877 * Create an RFCOMM {@link BluetoothSocket} ready to start a secure 2878 * outgoing connection to this remote device on given channel. 2879 * <p>The remote device will be authenticated and communication on this 2880 * socket will be encrypted. 2881 * <p> Use this socket only if an authenticated socket link is possible. 2882 * Authentication refers to the authentication of the link key to 2883 * prevent person-in-the-middle type of attacks. 2884 * For example, for Bluetooth 2.1 devices, if any of the devices does not 2885 * have an input and output capability or just has the ability to 2886 * display a numeric key, a secure socket connection is not possible. 2887 * In such a case, use {@link createInsecureRfcommSocket}. 2888 * For more details, refer to the Security Model section 5.2 (vol 3) of 2889 * Bluetooth Core Specification version 2.1 + EDR. 2890 * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing 2891 * connection. 2892 * <p>Valid RFCOMM channels are in range 1 to 30. 2893 * 2894 * @param channel RFCOMM channel to connect to 2895 * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection 2896 * @throws IOException on error, for example Bluetooth not available, or insufficient 2897 * permissions 2898 * @hide 2899 */ 2900 @UnsupportedAppUsage 2901 @RequiresLegacyBluetoothPermission 2902 @RequiresBluetoothConnectPermission 2903 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 2904 @SuppressLint("AndroidFrameworkRequiresPermission") createRfcommSocket(int channel)2905 public BluetoothSocket createRfcommSocket(int channel) throws IOException { 2906 if (!isBluetoothEnabled()) { 2907 Log.e(TAG, "Bluetooth is not enabled"); 2908 throw new IOException(); 2909 } 2910 return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel, 2911 null); 2912 } 2913 2914 /** 2915 * Create an L2cap {@link BluetoothSocket} ready to start a secure 2916 * outgoing connection to this remote device on given channel. 2917 * <p>The remote device will be authenticated and communication on this 2918 * socket will be encrypted. 2919 * <p> Use this socket only if an authenticated socket link is possible. 2920 * Authentication refers to the authentication of the link key to 2921 * prevent person-in-the-middle type of attacks. 2922 * For example, for Bluetooth 2.1 devices, if any of the devices does not 2923 * have an input and output capability or just has the ability to 2924 * display a numeric key, a secure socket connection is not possible. 2925 * In such a case, use {@link createInsecureRfcommSocket}. 2926 * For more details, refer to the Security Model section 5.2 (vol 3) of 2927 * Bluetooth Core Specification version 2.1 + EDR. 2928 * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing 2929 * connection. 2930 * <p>Valid L2CAP PSM channels are in range 1 to 2^16. 2931 * 2932 * @param channel L2cap PSM/channel to connect to 2933 * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection 2934 * @throws IOException on error, for example Bluetooth not available, or insufficient 2935 * permissions 2936 * @hide 2937 */ 2938 @RequiresLegacyBluetoothPermission 2939 @RequiresBluetoothConnectPermission 2940 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 2941 @SuppressLint("AndroidFrameworkRequiresPermission") createL2capSocket(int channel)2942 public BluetoothSocket createL2capSocket(int channel) throws IOException { 2943 return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, true, true, this, channel, 2944 null); 2945 } 2946 2947 /** 2948 * Create an L2cap {@link BluetoothSocket} ready to start an insecure 2949 * outgoing connection to this remote device on given channel. 2950 * <p>The remote device will be not authenticated and communication on this 2951 * socket will not be encrypted. 2952 * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing 2953 * connection. 2954 * <p>Valid L2CAP PSM channels are in range 1 to 2^16. 2955 * 2956 * @param channel L2cap PSM/channel to connect to 2957 * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection 2958 * @throws IOException on error, for example Bluetooth not available, or insufficient 2959 * permissions 2960 * @hide 2961 */ 2962 @RequiresLegacyBluetoothPermission 2963 @RequiresBluetoothConnectPermission 2964 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 2965 @SuppressLint("AndroidFrameworkRequiresPermission") createInsecureL2capSocket(int channel)2966 public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException { 2967 return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel, 2968 null); 2969 } 2970 2971 /** 2972 * Create an RFCOMM {@link BluetoothSocket} ready to start a secure 2973 * outgoing connection to this remote device using SDP lookup of uuid. 2974 * <p>This is designed to be used with {@link 2975 * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer 2976 * Bluetooth applications. 2977 * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing 2978 * connection. This will also perform an SDP lookup of the given uuid to 2979 * determine which channel to connect to. 2980 * <p>The remote device will be authenticated and communication on this 2981 * socket will be encrypted. 2982 * <p> Use this socket only if an authenticated socket link is possible. 2983 * Authentication refers to the authentication of the link key to 2984 * prevent person-in-the-middle type of attacks. 2985 * For example, for Bluetooth 2.1 devices, if any of the devices does not 2986 * have an input and output capability or just has the ability to 2987 * display a numeric key, a secure socket connection is not possible. 2988 * In such a case, use {@link #createInsecureRfcommSocketToServiceRecord}. 2989 * For more details, refer to the Security Model section 5.2 (vol 3) of 2990 * Bluetooth Core Specification version 2.1 + EDR. 2991 * <p>Hint: If you are connecting to a Bluetooth serial board then try 2992 * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. 2993 * However if you are connecting to an Android peer then please generate 2994 * your own unique UUID. 2995 * 2996 * @param uuid service record uuid to lookup RFCOMM channel 2997 * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection 2998 * @throws IOException on error, for example Bluetooth not available, or insufficient 2999 * permissions 3000 */ 3001 @RequiresLegacyBluetoothPermission 3002 @RequiresBluetoothConnectPermission 3003 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 3004 @SuppressLint("AndroidFrameworkRequiresPermission") createRfcommSocketToServiceRecord(UUID uuid)3005 public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { 3006 if (!isBluetoothEnabled()) { 3007 Log.e(TAG, "Bluetooth is not enabled"); 3008 throw new IOException(); 3009 } 3010 3011 return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1, 3012 new ParcelUuid(uuid)); 3013 } 3014 3015 /** 3016 * Create an RFCOMM {@link BluetoothSocket} socket ready to start an insecure 3017 * outgoing connection to this remote device using SDP lookup of uuid. 3018 * <p> The communication channel will not have an authenticated link key 3019 * i.e it will be subject to person-in-the-middle attacks. For Bluetooth 2.1 3020 * devices, the link key will be encrypted, as encryption is mandatory. 3021 * For legacy devices (pre Bluetooth 2.1 devices) the link key will 3022 * be not be encrypted. Use {@link #createRfcommSocketToServiceRecord} if an 3023 * encrypted and authenticated communication channel is desired. 3024 * <p>This is designed to be used with {@link 3025 * BluetoothAdapter#listenUsingInsecureRfcommWithServiceRecord} for peer-peer 3026 * Bluetooth applications. 3027 * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing 3028 * connection. This will also perform an SDP lookup of the given uuid to 3029 * determine which channel to connect to. 3030 * <p>The remote device will be authenticated and communication on this 3031 * socket will be encrypted. 3032 * <p>Hint: If you are connecting to a Bluetooth serial board then try 3033 * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. 3034 * However if you are connecting to an Android peer then please generate 3035 * your own unique UUID. 3036 * 3037 * @param uuid service record uuid to lookup RFCOMM channel 3038 * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection 3039 * @throws IOException on error, for example Bluetooth not available, or insufficient 3040 * permissions 3041 */ 3042 @RequiresLegacyBluetoothPermission 3043 @RequiresBluetoothConnectPermission 3044 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 3045 @SuppressLint("AndroidFrameworkRequiresPermission") createInsecureRfcommSocketToServiceRecord(UUID uuid)3046 public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { 3047 if (!isBluetoothEnabled()) { 3048 Log.e(TAG, "Bluetooth is not enabled"); 3049 throw new IOException(); 3050 } 3051 return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1, 3052 new ParcelUuid(uuid)); 3053 } 3054 3055 /** 3056 * Construct an insecure RFCOMM socket ready to start an outgoing 3057 * connection. 3058 * Call #connect on the returned #BluetoothSocket to begin the connection. 3059 * The remote device will not be authenticated and communication on this 3060 * socket will not be encrypted. 3061 * 3062 * @param port remote port 3063 * @return An RFCOMM BluetoothSocket 3064 * @throws IOException On error, for example Bluetooth not available, or insufficient 3065 * permissions. 3066 * @hide 3067 */ 3068 @UnsupportedAppUsage(publicAlternatives = "Use " 3069 + "{@link #createInsecureRfcommSocketToServiceRecord} instead.") 3070 @RequiresLegacyBluetoothAdminPermission 3071 @RequiresBluetoothConnectPermission 3072 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 3073 @SuppressLint("AndroidFrameworkRequiresPermission") createInsecureRfcommSocket(int port)3074 public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { 3075 if (!isBluetoothEnabled()) { 3076 Log.e(TAG, "Bluetooth is not enabled"); 3077 throw new IOException(); 3078 } 3079 return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port, 3080 null); 3081 } 3082 3083 /** 3084 * Construct a SCO socket ready to start an outgoing connection. 3085 * Call #connect on the returned #BluetoothSocket to begin the connection. 3086 * 3087 * @return a SCO BluetoothSocket 3088 * @throws IOException on error, for example Bluetooth not available, or insufficient 3089 * permissions. 3090 * @hide 3091 */ 3092 @UnsupportedAppUsage 3093 @RequiresLegacyBluetoothAdminPermission 3094 @RequiresBluetoothConnectPermission 3095 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 3096 @SuppressLint("AndroidFrameworkRequiresPermission") createScoSocket()3097 public BluetoothSocket createScoSocket() throws IOException { 3098 if (!isBluetoothEnabled()) { 3099 Log.e(TAG, "Bluetooth is not enabled"); 3100 throw new IOException(); 3101 } 3102 return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null); 3103 } 3104 3105 /** 3106 * Check that a pin is valid and convert to byte array. 3107 * 3108 * Bluetooth pin's are 1 to 16 bytes of UTF-8 characters. 3109 * 3110 * @param pin pin as java String 3111 * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin. 3112 * @hide 3113 */ 3114 @UnsupportedAppUsage convertPinToBytes(String pin)3115 public static byte[] convertPinToBytes(String pin) { 3116 if (pin == null) { 3117 return null; 3118 } 3119 byte[] pinBytes; 3120 try { 3121 pinBytes = pin.getBytes("UTF-8"); 3122 } catch (UnsupportedEncodingException uee) { 3123 Log.e(TAG, "UTF-8 not supported?!?"); // this should not happen 3124 return null; 3125 } 3126 if (pinBytes.length <= 0 || pinBytes.length > 16) { 3127 return null; 3128 } 3129 return pinBytes; 3130 } 3131 3132 /** 3133 * Connect to GATT Server hosted by this device. Caller acts as GATT client. 3134 * The callback is used to deliver results to Caller, such as connection status as well 3135 * as any further GATT client operations. 3136 * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct 3137 * GATT client operations. 3138 * 3139 * @param callback GATT callback handler that will receive asynchronous callbacks. 3140 * @param autoConnect Whether to directly connect to the remote device (false) or to 3141 * automatically connect as soon as the remote device becomes available (true). 3142 * @throws IllegalArgumentException if callback is null 3143 */ 3144 @RequiresBluetoothConnectPermission 3145 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)3146 public BluetoothGatt connectGatt(Context context, boolean autoConnect, 3147 BluetoothGattCallback callback) { 3148 return (connectGatt(context, autoConnect, callback, TRANSPORT_AUTO)); 3149 } 3150 3151 /** 3152 * Connect to GATT Server hosted by this device. Caller acts as GATT client. 3153 * The callback is used to deliver results to Caller, such as connection status as well 3154 * as any further GATT client operations. 3155 * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct 3156 * GATT client operations. 3157 * 3158 * @param callback GATT callback handler that will receive asynchronous callbacks. 3159 * @param autoConnect Whether to directly connect to the remote device (false) or to 3160 * automatically connect as soon as the remote device becomes available (true). 3161 * @param transport preferred transport for GATT connections to remote dual-mode devices {@link 3162 * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link 3163 * BluetoothDevice#TRANSPORT_LE} 3164 * @throws IllegalArgumentException if callback is null 3165 */ 3166 @RequiresBluetoothConnectPermission 3167 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport)3168 public BluetoothGatt connectGatt(Context context, boolean autoConnect, 3169 BluetoothGattCallback callback, int transport) { 3170 return (connectGatt(context, autoConnect, callback, transport, PHY_LE_1M_MASK)); 3171 } 3172 3173 /** 3174 * Connect to GATT Server hosted by this device. Caller acts as GATT client. 3175 * The callback is used to deliver results to Caller, such as connection status as well 3176 * as any further GATT client operations. 3177 * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct 3178 * GATT client operations. 3179 * 3180 * @param callback GATT callback handler that will receive asynchronous callbacks. 3181 * @param autoConnect Whether to directly connect to the remote device (false) or to 3182 * automatically connect as soon as the remote device becomes available (true). 3183 * @param transport preferred transport for GATT connections to remote dual-mode devices {@link 3184 * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link 3185 * BluetoothDevice#TRANSPORT_LE} 3186 * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link 3187 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link 3188 * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect} 3189 * is set to true. 3190 * @throws NullPointerException if callback is null 3191 */ 3192 @RequiresBluetoothConnectPermission 3193 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy)3194 public BluetoothGatt connectGatt(Context context, boolean autoConnect, 3195 BluetoothGattCallback callback, int transport, int phy) { 3196 return connectGatt(context, autoConnect, callback, transport, phy, null); 3197 } 3198 3199 /** 3200 * Connect to GATT Server hosted by this device. Caller acts as GATT client. 3201 * The callback is used to deliver results to Caller, such as connection status as well 3202 * as any further GATT client operations. 3203 * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct 3204 * GATT client operations. 3205 * 3206 * @param callback GATT callback handler that will receive asynchronous callbacks. 3207 * @param autoConnect Whether to directly connect to the remote device (false) or to 3208 * automatically connect as soon as the remote device becomes available (true). 3209 * @param transport preferred transport for GATT connections to remote dual-mode devices {@link 3210 * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link 3211 * BluetoothDevice#TRANSPORT_LE} 3212 * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link 3213 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link 3214 * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect} 3215 * is set to true. 3216 * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on 3217 * an un-specified background thread. 3218 * @throws NullPointerException if callback is null 3219 */ 3220 @RequiresBluetoothConnectPermission 3221 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy, Handler handler)3222 public BluetoothGatt connectGatt(Context context, boolean autoConnect, 3223 BluetoothGattCallback callback, int transport, int phy, 3224 Handler handler) { 3225 return connectGatt(context, autoConnect, callback, transport, false, phy, handler); 3226 } 3227 3228 /** 3229 * Connect to GATT Server hosted by this device. Caller acts as GATT client. 3230 * The callback is used to deliver results to Caller, such as connection status as well 3231 * as any further GATT client operations. 3232 * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct 3233 * GATT client operations. 3234 * 3235 * @param callback GATT callback handler that will receive asynchronous callbacks. 3236 * @param autoConnect Whether to directly connect to the remote device (false) or to 3237 * automatically connect as soon as the remote device becomes available (true). 3238 * @param transport preferred transport for GATT connections to remote dual-mode devices {@link 3239 * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link 3240 * BluetoothDevice#TRANSPORT_LE} 3241 * @param opportunistic Whether this GATT client is opportunistic. An opportunistic GATT client 3242 * does not hold a GATT connection. It automatically disconnects when no other GATT connections 3243 * are active for the remote device. 3244 * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link 3245 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link 3246 * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect} 3247 * is set to true. 3248 * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on 3249 * an un-specified background thread. 3250 * @return A BluetoothGatt instance. You can use BluetoothGatt to conduct GATT client 3251 * operations. 3252 * @hide 3253 */ 3254 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 3255 @RequiresBluetoothConnectPermission 3256 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, boolean opportunistic, int phy, Handler handler)3257 public BluetoothGatt connectGatt(Context context, boolean autoConnect, 3258 BluetoothGattCallback callback, int transport, 3259 boolean opportunistic, int phy, Handler handler) { 3260 if (callback == null) { 3261 throw new NullPointerException("callback is null"); 3262 } 3263 3264 // TODO(Bluetooth) check whether platform support BLE 3265 // Do the check here or in GattServer? 3266 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 3267 IBluetoothManager managerService = adapter.getBluetoothManager(); 3268 try { 3269 IBluetoothGatt iGatt = managerService.getBluetoothGatt(); 3270 if (iGatt == null) { 3271 // BLE is not supported 3272 return null; 3273 } else if (NULL_MAC_ADDRESS.equals(mAddress)) { 3274 Log.e(TAG, "Unable to connect gatt, invalid address " + mAddress); 3275 return null; 3276 } 3277 BluetoothGatt gatt = new BluetoothGatt( 3278 iGatt, this, transport, opportunistic, phy, mAttributionSource); 3279 gatt.connect(autoConnect, callback, handler); 3280 return gatt; 3281 } catch (RemoteException e) { 3282 Log.e(TAG, "", e); 3283 } 3284 return null; 3285 } 3286 3287 /** 3288 * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can 3289 * be used to start a secure outgoing connection to the remote device with the same dynamic 3290 * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only. 3291 * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capChannel()} for 3292 * peer-peer Bluetooth applications. 3293 * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection. 3294 * <p>Application using this API is responsible for obtaining PSM value from remote device. 3295 * <p>The remote device will be authenticated and communication on this socket will be 3296 * encrypted. 3297 * <p> Use this socket if an authenticated socket link is possible. Authentication refers 3298 * to the authentication of the link key to prevent person-in-the-middle type of attacks. 3299 * 3300 * @param psm dynamic PSM value from remote device 3301 * @return a CoC #BluetoothSocket ready for an outgoing connection 3302 * @throws IOException on error, for example Bluetooth not available, or insufficient 3303 * permissions 3304 */ 3305 @RequiresLegacyBluetoothPermission 3306 @RequiresBluetoothConnectPermission 3307 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 3308 @SuppressLint("AndroidFrameworkRequiresPermission") createL2capChannel(int psm)3309 public @NonNull BluetoothSocket createL2capChannel(int psm) throws IOException { 3310 if (!isBluetoothEnabled()) { 3311 Log.e(TAG, "createL2capChannel: Bluetooth is not enabled"); 3312 throw new IOException(); 3313 } 3314 if (DBG) Log.d(TAG, "createL2capChannel: psm=" + psm); 3315 return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm, 3316 null); 3317 } 3318 3319 /** 3320 * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can 3321 * be used to start a secure outgoing connection to the remote device with the same dynamic 3322 * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only. 3323 * <p>This is designed to be used with {@link 3324 * BluetoothAdapter#listenUsingInsecureL2capChannel()} for peer-peer Bluetooth applications. 3325 * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection. 3326 * <p>Application using this API is responsible for obtaining PSM value from remote device. 3327 * <p> The communication channel may not have an authenticated link key, i.e. it may be subject 3328 * to person-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and 3329 * authenticated communication channel is possible. 3330 * 3331 * @param psm dynamic PSM value from remote device 3332 * @return a CoC #BluetoothSocket ready for an outgoing connection 3333 * @throws IOException on error, for example Bluetooth not available, or insufficient 3334 * permissions 3335 */ 3336 @RequiresLegacyBluetoothPermission 3337 @RequiresBluetoothConnectPermission 3338 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 3339 @SuppressLint("AndroidFrameworkRequiresPermission") createInsecureL2capChannel(int psm)3340 public @NonNull BluetoothSocket createInsecureL2capChannel(int psm) throws IOException { 3341 if (!isBluetoothEnabled()) { 3342 Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled"); 3343 throw new IOException(); 3344 } 3345 if (DBG) { 3346 Log.d(TAG, "createInsecureL2capChannel: psm=" + psm); 3347 } 3348 return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm, 3349 null); 3350 } 3351 3352 /** 3353 * Set a keyed metadata of this {@link BluetoothDevice} to a 3354 * {@link String} value. 3355 * Only bonded devices's metadata will be persisted across Bluetooth 3356 * restart. 3357 * Metadata will be removed when the device's bond state is moved to 3358 * {@link #BOND_NONE}. 3359 * 3360 * @param key must be within the list of BluetoothDevice.METADATA_* 3361 * @param value a byte array data to set for key. Must be less than 3362 * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length 3363 * @return true on success, false on error 3364 * @hide 3365 */ 3366 @SystemApi 3367 @RequiresPermission(allOf = { 3368 android.Manifest.permission.BLUETOOTH_CONNECT, 3369 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 3370 }) setMetadata(@etadataKey int key, @NonNull byte[] value)3371 public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) { 3372 if (DBG) log("setMetadata()"); 3373 final IBluetooth service = getService(); 3374 final boolean defaultValue = false; 3375 if (service == null || !isBluetoothEnabled()) { 3376 Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata"); 3377 if (DBG) log(Log.getStackTraceString(new Throwable())); 3378 } else if (value.length > METADATA_MAX_LENGTH) { 3379 throw new IllegalArgumentException("value length is " + value.length 3380 + ", should not over " + METADATA_MAX_LENGTH); 3381 } else { 3382 try { 3383 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 3384 service.setMetadata(this, key, value, mAttributionSource, recv); 3385 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 3386 } catch (RemoteException | TimeoutException e) { 3387 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 3388 } 3389 } 3390 return defaultValue; 3391 } 3392 3393 /** 3394 * Get a keyed metadata for this {@link BluetoothDevice} as {@link String} 3395 * 3396 * @param key must be within the list of BluetoothDevice.METADATA_* 3397 * @return Metadata of the key as byte array, null on error or not found 3398 * @hide 3399 */ 3400 @SystemApi 3401 @Nullable 3402 @RequiresPermission(allOf = { 3403 android.Manifest.permission.BLUETOOTH_CONNECT, 3404 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 3405 }) getMetadata(@etadataKey int key)3406 public byte[] getMetadata(@MetadataKey int key) { 3407 if (DBG) log("getMetadata()"); 3408 final IBluetooth service = getService(); 3409 final byte[] defaultValue = null; 3410 if (service == null || !isBluetoothEnabled()) { 3411 Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata"); 3412 if (DBG) log(Log.getStackTraceString(new Throwable())); 3413 } else { 3414 try { 3415 final SynchronousResultReceiver<byte[]> recv = SynchronousResultReceiver.get(); 3416 service.getMetadata(this, key, mAttributionSource, recv); 3417 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 3418 } catch (RemoteException | TimeoutException e) { 3419 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 3420 } 3421 } 3422 return defaultValue; 3423 } 3424 3425 /** 3426 * Get the maxinum metadata key ID. 3427 * 3428 * @return the last supported metadata key 3429 * @hide 3430 */ getMaxMetadataKey()3431 public static @MetadataKey int getMaxMetadataKey() { 3432 return METADATA_MAX_KEY; 3433 } 3434 3435 /** @hide */ 3436 @Retention(RetentionPolicy.SOURCE) 3437 @IntDef( 3438 prefix = { "FEATURE_" }, 3439 value = { 3440 BluetoothStatusCodes.FEATURE_NOT_CONFIGURED, 3441 BluetoothStatusCodes.FEATURE_SUPPORTED, 3442 BluetoothStatusCodes.FEATURE_NOT_SUPPORTED, 3443 } 3444 ) 3445 3446 public @interface AudioPolicyRemoteSupport {} 3447 3448 /** @hide */ 3449 @Retention(RetentionPolicy.SOURCE) 3450 @IntDef(value = { 3451 BluetoothStatusCodes.SUCCESS, 3452 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 3453 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, 3454 BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED, 3455 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, 3456 BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED, 3457 }) 3458 public @interface AudioPolicyReturnValues{} 3459 3460 /** 3461 * Returns whether the audio policy feature is supported by 3462 * both the local and the remote device. 3463 * This is configured during initiating the connection between the devices through 3464 * one of the transport protocols (e.g. HFP Vendor specific protocol). So if the API 3465 * is invoked before this initial configuration is completed, it returns 3466 * {@link BluetoothStatusCodes#FEATURE_NOT_CONFIGURED} to indicate the remote 3467 * device has not yet relayed this information. After the internal configuration, 3468 * the support status will be set to either 3469 * {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} or 3470 * {@link BluetoothStatusCodes#FEATURE_SUPPORTED}. 3471 * The rest of the APIs related to this feature in both {@link BluetoothDevice} 3472 * and {@link BluetoothSinkAudioPolicy} should be invoked only after getting a 3473 * {@link BluetoothStatusCodes#FEATURE_SUPPORTED} response from this API. 3474 * <p>Note that this API is intended to be used by a client device to send these requests 3475 * to the server represented by this BluetoothDevice object. 3476 * 3477 * @return if call audio policy feature is supported by both local and remote 3478 * device or not 3479 * 3480 * @hide 3481 */ 3482 @SystemApi 3483 @RequiresBluetoothConnectPermission 3484 @RequiresPermission(allOf = { 3485 android.Manifest.permission.BLUETOOTH_CONNECT, 3486 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 3487 }) isRequestAudioPolicyAsSinkSupported()3488 public @AudioPolicyRemoteSupport int isRequestAudioPolicyAsSinkSupported() { 3489 if (DBG) log("isRequestAudioPolicyAsSinkSupported()"); 3490 final IBluetooth service = getService(); 3491 final int defaultValue = BluetoothStatusCodes.FEATURE_NOT_CONFIGURED; 3492 if (service == null || !isBluetoothEnabled()) { 3493 Log.e(TAG, "BT not enabled. Cannot retrieve audio policy support status."); 3494 if (DBG) log(Log.getStackTraceString(new Throwable())); 3495 } else { 3496 try { 3497 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 3498 service.isRequestAudioPolicyAsSinkSupported(this, mAttributionSource, recv); 3499 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 3500 } catch (TimeoutException e) { 3501 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 3502 } catch (RemoteException e) { 3503 Log.e(TAG, "", e); 3504 throw e.rethrowFromSystemServer(); 3505 } 3506 } 3507 return defaultValue; 3508 } 3509 3510 /** 3511 * Sets call audio preferences and sends them to the remote device. 3512 * <p>Note that the caller should check if the feature is supported by 3513 * invoking {@link BluetoothDevice#isRequestAudioPolicyAsSinkSupported} first. 3514 * <p>This API will throw an exception if the feature is not supported but still 3515 * invoked. 3516 * <p>Note that this API is intended to be used by a client device to send these requests 3517 * to the server represented by this BluetoothDevice object. 3518 * 3519 * @param policies call audio policy preferences 3520 * @return whether audio policy was requested successfully or not 3521 * 3522 * @hide 3523 */ 3524 @SystemApi 3525 @RequiresBluetoothConnectPermission 3526 @RequiresPermission(allOf = { 3527 android.Manifest.permission.BLUETOOTH_CONNECT, 3528 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 3529 }) requestAudioPolicyAsSink( @onNull BluetoothSinkAudioPolicy policies)3530 public @AudioPolicyReturnValues int requestAudioPolicyAsSink( 3531 @NonNull BluetoothSinkAudioPolicy policies) { 3532 if (DBG) log("requestAudioPolicyAsSink"); 3533 final IBluetooth service = getService(); 3534 final int defaultValue = BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 3535 if (service == null || !isBluetoothEnabled()) { 3536 Log.e(TAG, "Bluetooth is not enabled. Cannot set Audio Policy."); 3537 if (DBG) log(Log.getStackTraceString(new Throwable())); 3538 } else { 3539 try { 3540 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 3541 service.requestAudioPolicyAsSink(this, policies, mAttributionSource, recv); 3542 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 3543 } catch (RemoteException | TimeoutException e) { 3544 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 3545 } 3546 } 3547 return defaultValue; 3548 } 3549 3550 /** 3551 * Gets the call audio preferences for the remote device. 3552 * <p>Note that the caller should check if the feature is supported by 3553 * invoking {@link BluetoothDevice#isRequestAudioPolicyAsSinkSupported} first. 3554 * <p>This API will throw an exception if the feature is not supported but still 3555 * invoked. 3556 * <p>This API will return null if 3557 * 1. The bleutooth service is not started yet, 3558 * 2. It is invoked for a device which is not bonded, or 3559 * 3. The used transport, for example, HFP Client profile is not enabled or 3560 * connected yet. 3561 * <p>Note that this API is intended to be used by a client device to send these requests 3562 * to the server represented by this BluetoothDevice object. 3563 * 3564 * @return call audio policy as {@link BluetoothSinkAudioPolicy} object 3565 * 3566 * @hide 3567 */ 3568 @SystemApi 3569 @RequiresBluetoothConnectPermission 3570 @RequiresPermission(allOf = { 3571 android.Manifest.permission.BLUETOOTH_CONNECT, 3572 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 3573 }) getRequestedAudioPolicyAsSink()3574 public @Nullable BluetoothSinkAudioPolicy getRequestedAudioPolicyAsSink() { 3575 if (DBG) log("getRequestedAudioPolicyAsSink"); 3576 final IBluetooth service = getService(); 3577 if (service == null || !isBluetoothEnabled()) { 3578 Log.e(TAG, "Bluetooth is not enabled. Cannot get Audio Policy."); 3579 if (DBG) log(Log.getStackTraceString(new Throwable())); 3580 } else { 3581 try { 3582 final SynchronousResultReceiver<BluetoothSinkAudioPolicy> 3583 recv = SynchronousResultReceiver.get(); 3584 service.getRequestedAudioPolicyAsSink(this, mAttributionSource, recv); 3585 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 3586 } catch (RemoteException | TimeoutException e) { 3587 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 3588 } 3589 } 3590 return null; 3591 } 3592 3593 /** 3594 * Enable or disable audio low latency for this {@link BluetoothDevice}. 3595 * 3596 * @param allowed true if low latency is allowed, false if low latency is disallowed. 3597 * @return true if the value is successfully set, 3598 * false if there is a error when setting the value. 3599 * @hide 3600 */ 3601 @SystemApi 3602 @RequiresBluetoothConnectPermission 3603 @RequiresPermission(allOf = { 3604 android.Manifest.permission.BLUETOOTH_CONNECT, 3605 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 3606 }) setLowLatencyAudioAllowed(boolean allowed)3607 public boolean setLowLatencyAudioAllowed(boolean allowed) { 3608 if (DBG) log("setLowLatencyAudioAllowed(" + allowed + ")"); 3609 final IBluetooth service = getService(); 3610 final boolean defaultValue = false; 3611 if (service == null || !isBluetoothEnabled()) { 3612 Log.e(TAG, "Bluetooth is not enabled. Cannot allow low latency"); 3613 if (DBG) log(Log.getStackTraceString(new Throwable())); 3614 } else { 3615 try { 3616 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 3617 service.allowLowLatencyAudio(allowed, this, recv); 3618 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 3619 } catch (RemoteException | TimeoutException e) { 3620 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 3621 } 3622 } 3623 return defaultValue; 3624 } 3625 log(String msg)3626 private static void log(String msg) { 3627 Log.d(TAG, msg); 3628 } 3629 } 3630