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