1 /* 2 * Copyright 2009-2016 The Android Open Source Project 3 * Copyright 2015 Samsung LSI 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package android.bluetooth; 19 20 import static android.bluetooth.BluetoothUtils.getSyncTimeout; 21 22 import static java.util.Objects.requireNonNull; 23 24 import android.annotation.CallbackExecutor; 25 import android.annotation.IntDef; 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.annotation.RequiresNoPermission; 29 import android.annotation.RequiresPermission; 30 import android.annotation.SdkConstant; 31 import android.annotation.SdkConstant.SdkConstantType; 32 import android.annotation.SuppressLint; 33 import android.annotation.SystemApi; 34 import android.app.PendingIntent; 35 import android.bluetooth.BluetoothDevice.AddressType; 36 import android.bluetooth.BluetoothDevice.Transport; 37 import android.bluetooth.BluetoothProfile.ConnectionPolicy; 38 import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; 39 import android.bluetooth.annotations.RequiresBluetoothConnectPermission; 40 import android.bluetooth.annotations.RequiresBluetoothLocationPermission; 41 import android.bluetooth.annotations.RequiresBluetoothScanPermission; 42 import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; 43 import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; 44 import android.bluetooth.le.BluetoothLeAdvertiser; 45 import android.bluetooth.le.BluetoothLeScanner; 46 import android.bluetooth.le.DistanceMeasurementManager; 47 import android.bluetooth.le.PeriodicAdvertisingManager; 48 import android.bluetooth.le.ScanCallback; 49 import android.bluetooth.le.ScanFilter; 50 import android.bluetooth.le.ScanRecord; 51 import android.bluetooth.le.ScanResult; 52 import android.bluetooth.le.ScanSettings; 53 import android.compat.annotation.UnsupportedAppUsage; 54 import android.content.AttributionSource; 55 import android.content.Context; 56 import android.os.Binder; 57 import android.os.BluetoothServiceManager; 58 import android.os.Build; 59 import android.os.Bundle; 60 import android.os.IBinder; 61 import android.os.IpcDataCache; 62 import android.os.ParcelUuid; 63 import android.os.RemoteException; 64 import android.sysprop.BluetoothProperties; 65 import android.util.Log; 66 import android.util.Pair; 67 68 import com.android.internal.annotations.GuardedBy; 69 import com.android.modules.utils.SynchronousResultReceiver; 70 71 import java.io.IOException; 72 import java.lang.annotation.Retention; 73 import java.lang.annotation.RetentionPolicy; 74 import java.time.Duration; 75 import java.util.ArrayList; 76 import java.util.Arrays; 77 import java.util.Collections; 78 import java.util.HashMap; 79 import java.util.HashSet; 80 import java.util.List; 81 import java.util.Locale; 82 import java.util.Map; 83 import java.util.Set; 84 import java.util.UUID; 85 import java.util.WeakHashMap; 86 import java.util.concurrent.Executor; 87 import java.util.concurrent.TimeoutException; 88 import java.util.concurrent.locks.ReentrantReadWriteLock; 89 90 /** 91 * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter} 92 * lets you perform fundamental Bluetooth tasks, such as initiate 93 * device discovery, query a list of bonded (paired) devices, 94 * instantiate a {@link BluetoothDevice} using a known MAC address, and create 95 * a {@link BluetoothServerSocket} to listen for connection requests from other 96 * devices, and start a scan for Bluetooth LE devices. 97 * 98 * <p>To get a {@link BluetoothAdapter} representing the local Bluetooth 99 * adapter, call the {@link BluetoothManager#getAdapter} function on {@link BluetoothManager}. 100 * On JELLY_BEAN_MR1 and below you will need to use the static {@link #getDefaultAdapter} 101 * method instead. 102 * </p><p> 103 * Fundamentally, this is your starting point for all 104 * Bluetooth actions. Once you have the local adapter, you can get a set of 105 * {@link BluetoothDevice} objects representing all paired devices with 106 * {@link #getBondedDevices()}; start device discovery with 107 * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to 108 * listen for incoming RFComm connection requests with {@link 109 * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented 110 * Channels (CoC) connection requests with {@link #listenUsingL2capChannel()}; or start a scan for 111 * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. 112 * </p> 113 * <p>This class is thread safe.</p> 114 * <div class="special reference"> 115 * <h3>Developer Guides</h3> 116 * <p> 117 * For more information about using Bluetooth, read the <a href= 118 * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer 119 * guide. 120 * </p> 121 * </div> 122 * 123 * {@see BluetoothDevice} 124 * {@see BluetoothServerSocket} 125 */ 126 public final class BluetoothAdapter { 127 private static final String TAG = "BluetoothAdapter"; 128 private static final String DESCRIPTOR = "android.bluetooth.BluetoothAdapter"; 129 private static final boolean DBG = true; 130 private static final boolean VDBG = false; 131 132 /** 133 * Default MAC address reported to a client that does not have the 134 * {@link android.Manifest.permission#LOCAL_MAC_ADDRESS} permission. 135 * 136 * 137 * @hide 138 */ 139 public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00"; 140 141 /** 142 * Sentinel error value for this class. Guaranteed to not equal any other 143 * integer constant in this class. Provided as a convenience for functions 144 * that require a sentinel error value, for example: 145 * <p><code>Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 146 * BluetoothAdapter.ERROR)</code> 147 */ 148 public static final int ERROR = Integer.MIN_VALUE; 149 150 /** 151 * Broadcast Action: The state of the local Bluetooth adapter has been 152 * changed. 153 * <p>For example, Bluetooth has been turned on or off. 154 * <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link 155 * #EXTRA_PREVIOUS_STATE} containing the new and old states 156 * respectively. 157 */ 158 @RequiresLegacyBluetoothPermission 159 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String 160 ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED"; 161 162 /** 163 * Used as an int extra field in {@link #ACTION_STATE_CHANGED} 164 * intents to request the current power state. Possible values are: 165 * {@link #STATE_OFF}, 166 * {@link #STATE_TURNING_ON}, 167 * {@link #STATE_ON}, 168 * {@link #STATE_TURNING_OFF}, 169 */ 170 public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE"; 171 /** 172 * Used as an int extra field in {@link #ACTION_STATE_CHANGED} 173 * intents to request the previous power state. Possible values are: 174 * {@link #STATE_OFF}, 175 * {@link #STATE_TURNING_ON}, 176 * {@link #STATE_ON}, 177 * {@link #STATE_TURNING_OFF} 178 */ 179 public static final String EXTRA_PREVIOUS_STATE = 180 "android.bluetooth.adapter.extra.PREVIOUS_STATE"; 181 182 /** @hide */ 183 @IntDef(prefix = { "STATE_" }, value = { 184 STATE_OFF, 185 STATE_TURNING_ON, 186 STATE_ON, 187 STATE_TURNING_OFF, 188 STATE_BLE_TURNING_ON, 189 STATE_BLE_ON, 190 STATE_BLE_TURNING_OFF 191 }) 192 @Retention(RetentionPolicy.SOURCE) 193 public @interface InternalAdapterState {} 194 195 /** @hide */ 196 @IntDef(prefix = { "STATE_" }, value = { 197 STATE_OFF, 198 STATE_TURNING_ON, 199 STATE_ON, 200 STATE_TURNING_OFF, 201 }) 202 @Retention(RetentionPolicy.SOURCE) 203 public @interface AdapterState {} 204 205 /** 206 * Indicates the local Bluetooth adapter is off. 207 */ 208 public static final int STATE_OFF = 10; 209 /** 210 * Indicates the local Bluetooth adapter is turning on. However local 211 * clients should wait for {@link #STATE_ON} before attempting to 212 * use the adapter. 213 */ 214 public static final int STATE_TURNING_ON = 11; 215 /** 216 * Indicates the local Bluetooth adapter is on, and ready for use. 217 */ 218 public static final int STATE_ON = 12; 219 /** 220 * Indicates the local Bluetooth adapter is turning off. Local clients 221 * should immediately attempt graceful disconnection of any remote links. 222 */ 223 public static final int STATE_TURNING_OFF = 13; 224 225 /** 226 * Indicates the local Bluetooth adapter is turning Bluetooth LE mode on. 227 * 228 * @hide 229 */ 230 public static final int STATE_BLE_TURNING_ON = 14; 231 232 /** 233 * Indicates the local Bluetooth adapter is in LE only mode. 234 * 235 * @hide 236 */ 237 @SystemApi 238 public static final int STATE_BLE_ON = 15; 239 240 /** 241 * Indicates the local Bluetooth adapter is turning off LE only mode. 242 * 243 * @hide 244 */ 245 public static final int STATE_BLE_TURNING_OFF = 16; 246 247 /** 248 * UUID of the GATT Read Characteristics for LE_PSM value. 249 * 250 * @hide 251 */ 252 public static final UUID LE_PSM_CHARACTERISTIC_UUID = 253 UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a"); 254 255 /** 256 * Used as an optional extra field for the {@link PendingIntent} provided to {@link 257 * #startRfcommServer(String, UUID, PendingIntent)}. This is useful for when an 258 * application registers multiple RFCOMM listeners, and needs a way to determine which service 259 * record the incoming {@link BluetoothSocket} is using. 260 * 261 * @hide 262 */ 263 @SystemApi 264 @SuppressLint("ActionValue") 265 public static final String EXTRA_RFCOMM_LISTENER_ID = 266 "android.bluetooth.adapter.extra.RFCOMM_LISTENER_ID"; 267 268 /** @hide */ 269 @IntDef(value = { 270 BluetoothStatusCodes.SUCCESS, 271 BluetoothStatusCodes.ERROR_TIMEOUT, 272 BluetoothStatusCodes.RFCOMM_LISTENER_START_FAILED_UUID_IN_USE, 273 BluetoothStatusCodes.RFCOMM_LISTENER_OPERATION_FAILED_NO_MATCHING_SERVICE_RECORD, 274 BluetoothStatusCodes.RFCOMM_LISTENER_OPERATION_FAILED_DIFFERENT_APP, 275 BluetoothStatusCodes.RFCOMM_LISTENER_FAILED_TO_CREATE_SERVER_SOCKET, 276 BluetoothStatusCodes.RFCOMM_LISTENER_FAILED_TO_CLOSE_SERVER_SOCKET, 277 BluetoothStatusCodes.RFCOMM_LISTENER_NO_SOCKET_AVAILABLE, 278 }) 279 @Retention(RetentionPolicy.SOURCE) 280 public @interface RfcommListenerResult {} 281 282 /** 283 * Human-readable string helper for AdapterState and InternalAdapterState 284 * 285 * @hide 286 */ 287 @SystemApi 288 @RequiresNoPermission 289 @NonNull nameForState(@nternalAdapterState int state)290 public static String nameForState(@InternalAdapterState int state) { 291 switch (state) { 292 case STATE_OFF: 293 return "OFF"; 294 case STATE_TURNING_ON: 295 return "TURNING_ON"; 296 case STATE_ON: 297 return "ON"; 298 case STATE_TURNING_OFF: 299 return "TURNING_OFF"; 300 case STATE_BLE_TURNING_ON: 301 return "BLE_TURNING_ON"; 302 case STATE_BLE_ON: 303 return "BLE_ON"; 304 case STATE_BLE_TURNING_OFF: 305 return "BLE_TURNING_OFF"; 306 default: 307 return "?!?!? (" + state + ")"; 308 } 309 } 310 311 /** 312 * Activity Action: Show a system activity that requests discoverable mode. 313 * This activity will also request the user to turn on Bluetooth if it 314 * is not currently enabled. 315 * <p>Discoverable mode is equivalent to {@link 316 * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see 317 * this Bluetooth adapter when they perform a discovery. 318 * <p>For privacy, Android is not discoverable by default. 319 * <p>The sender of this Intent can optionally use extra field {@link 320 * #EXTRA_DISCOVERABLE_DURATION} to request the duration of 321 * discoverability. Currently the default duration is 120 seconds, and 322 * maximum duration is capped at 300 seconds for each request. 323 * <p>Notification of the result of this activity is posted using the 324 * {@link android.app.Activity#onActivityResult} callback. The 325 * <code>resultCode</code> 326 * will be the duration (in seconds) of discoverability or 327 * {@link android.app.Activity#RESULT_CANCELED} if the user rejected 328 * discoverability or an error has occurred. 329 * <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED} 330 * for global notification whenever the scan mode changes. For example, an 331 * application can be notified when the device has ended discoverability. 332 */ 333 @RequiresLegacyBluetoothPermission 334 @RequiresBluetoothAdvertisePermission 335 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) 336 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String 337 ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"; 338 339 /** 340 * Used as an optional int extra field in {@link 341 * #ACTION_REQUEST_DISCOVERABLE} intents to request a specific duration 342 * for discoverability in seconds. The current default is 120 seconds, and 343 * requests over 300 seconds will be capped. These values could change. 344 */ 345 public static final String EXTRA_DISCOVERABLE_DURATION = 346 "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION"; 347 348 /** 349 * Activity Action: Show a system activity that allows the user to turn on 350 * Bluetooth. 351 * <p>This system activity will return once Bluetooth has completed turning 352 * on, or the user has decided not to turn Bluetooth on. 353 * <p>Notification of the result of this activity is posted using the 354 * {@link android.app.Activity#onActivityResult} callback. The 355 * <code>resultCode</code> 356 * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been 357 * turned on or {@link android.app.Activity#RESULT_CANCELED} if the user 358 * has rejected the request or an error has occurred. 359 * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED} 360 * for global notification whenever Bluetooth is turned on or off. 361 */ 362 @RequiresLegacyBluetoothPermission 363 @RequiresBluetoothConnectPermission 364 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 365 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String 366 ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; 367 368 /** 369 * Activity Action: Show a system activity that allows the user to turn off 370 * Bluetooth. This is used only if permission review is enabled which is for 371 * apps targeting API less than 23 require a permission review before any of 372 * the app's components can run. 373 * <p>This system activity will return once Bluetooth has completed turning 374 * off, or the user has decided not to turn Bluetooth off. 375 * <p>Notification of the result of this activity is posted using the 376 * {@link android.app.Activity#onActivityResult} callback. The 377 * <code>resultCode</code> 378 * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been 379 * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user 380 * has rejected the request or an error has occurred. 381 * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED} 382 * for global notification whenever Bluetooth is turned on or off. 383 * 384 * @hide 385 */ 386 @SystemApi 387 @RequiresLegacyBluetoothPermission 388 @RequiresBluetoothConnectPermission 389 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 390 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 391 @SuppressLint("ActionValue") 392 public static final String 393 ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE"; 394 395 /** 396 * Activity Action: Show a system activity that allows user to enable BLE scans even when 397 * Bluetooth is turned off.<p> 398 * 399 * Notification of result of this activity is posted using 400 * {@link android.app.Activity#onActivityResult}. The <code>resultCode</code> will be 401 * {@link android.app.Activity#RESULT_OK} if BLE scan always available setting is turned on or 402 * {@link android.app.Activity#RESULT_CANCELED} if the user has rejected the request or an 403 * error occurred. 404 * 405 * @hide 406 */ 407 @SystemApi 408 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 409 public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = 410 "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE"; 411 412 /** 413 * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter 414 * has changed. 415 * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link 416 * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes 417 * respectively. 418 */ 419 @RequiresLegacyBluetoothPermission 420 @RequiresBluetoothScanPermission 421 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) 422 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String 423 ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; 424 425 /** 426 * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} 427 * intents to request the current scan mode. Possible values are: 428 * {@link #SCAN_MODE_NONE}, 429 * {@link #SCAN_MODE_CONNECTABLE}, 430 * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}, 431 */ 432 public static final String EXTRA_SCAN_MODE = "android.bluetooth.adapter.extra.SCAN_MODE"; 433 /** 434 * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} 435 * intents to request the previous scan mode. Possible values are: 436 * {@link #SCAN_MODE_NONE}, 437 * {@link #SCAN_MODE_CONNECTABLE}, 438 * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}, 439 */ 440 public static final String EXTRA_PREVIOUS_SCAN_MODE = 441 "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE"; 442 443 /** @hide */ 444 @IntDef(prefix = { "SCAN_" }, value = { 445 SCAN_MODE_NONE, 446 SCAN_MODE_CONNECTABLE, 447 SCAN_MODE_CONNECTABLE_DISCOVERABLE 448 }) 449 @Retention(RetentionPolicy.SOURCE) 450 public @interface ScanMode {} 451 452 /** @hide */ 453 @IntDef(value = { 454 BluetoothStatusCodes.SUCCESS, 455 BluetoothStatusCodes.ERROR_UNKNOWN, 456 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 457 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION 458 }) 459 @Retention(RetentionPolicy.SOURCE) 460 public @interface ScanModeStatusCode {} 461 462 /** 463 * Indicates that both inquiry scan and page scan are disabled on the local 464 * Bluetooth adapter. Therefore this device is neither discoverable 465 * nor connectable from remote Bluetooth devices. 466 */ 467 public static final int SCAN_MODE_NONE = 20; 468 /** 469 * Indicates that inquiry scan is disabled, but page scan is enabled on the 470 * local Bluetooth adapter. Therefore this device is not discoverable from 471 * remote Bluetooth devices, but is connectable from remote devices that 472 * have previously discovered this device. 473 */ 474 public static final int SCAN_MODE_CONNECTABLE = 21; 475 /** 476 * Indicates that both inquiry scan and page scan are enabled on the local 477 * Bluetooth adapter. Therefore this device is both discoverable and 478 * connectable from remote Bluetooth devices. 479 */ 480 public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23; 481 482 483 /** 484 * Used as parameter for {@link #setBluetoothHciSnoopLoggingMode}, indicates that 485 * the Bluetooth HCI snoop logging should be disabled. 486 * 487 * @hide 488 */ 489 @SystemApi 490 public static final int BT_SNOOP_LOG_MODE_DISABLED = 0; 491 492 /** 493 * Used as parameter for {@link #setBluetoothHciSnoopLoggingMode}, indicates that 494 * the Bluetooth HCI snoop logging should be enabled without collecting potential 495 * Personally Identifiable Information and packet data. 496 * 497 * See {@link #BT_SNOOP_LOG_MODE_FULL} to enable logging of all information available. 498 * 499 * @hide 500 */ 501 @SystemApi 502 public static final int BT_SNOOP_LOG_MODE_FILTERED = 1; 503 504 /** 505 * Used as parameter for {@link #setSnoopLogMode}, indicates that the Bluetooth HCI snoop 506 * logging should be enabled. 507 * 508 * See {@link #BT_SNOOP_LOG_MODE_FILTERED} to enable logging with filtered information. 509 * 510 * @hide 511 */ 512 @SystemApi 513 public static final int BT_SNOOP_LOG_MODE_FULL = 2; 514 515 /** @hide */ 516 @IntDef(value = { 517 BT_SNOOP_LOG_MODE_DISABLED, 518 BT_SNOOP_LOG_MODE_FILTERED, 519 BT_SNOOP_LOG_MODE_FULL 520 }) 521 @Retention(RetentionPolicy.SOURCE) 522 public @interface BluetoothSnoopLogMode {} 523 524 /** @hide */ 525 @IntDef(value = { 526 BluetoothStatusCodes.SUCCESS, 527 BluetoothStatusCodes.ERROR_UNKNOWN, 528 }) 529 @Retention(RetentionPolicy.SOURCE) 530 public @interface SetSnoopLogModeStatusCode {} 531 532 /** 533 * Device only has a display. 534 * 535 * @hide 536 */ 537 public static final int IO_CAPABILITY_OUT = 0; 538 539 /** 540 * Device has a display and the ability to input Yes/No. 541 * 542 * @hide 543 */ 544 public static final int IO_CAPABILITY_IO = 1; 545 546 /** 547 * Device only has a keyboard for entry but no display. 548 * 549 * @hide 550 */ 551 public static final int IO_CAPABILITY_IN = 2; 552 553 /** 554 * Device has no Input or Output capability. 555 * 556 * @hide 557 */ 558 public static final int IO_CAPABILITY_NONE = 3; 559 560 /** 561 * Device has a display and a full keyboard. 562 * 563 * @hide 564 */ 565 public static final int IO_CAPABILITY_KBDISP = 4; 566 567 /** 568 * Maximum range value for Input/Output capabilities. 569 * 570 * <p>This should be updated when adding a new Input/Output capability. Other code 571 * like validation depends on this being accurate. 572 * 573 * @hide 574 */ 575 public static final int IO_CAPABILITY_MAX = 5; 576 577 /** 578 * The Input/Output capability of the device is unknown. 579 * 580 * @hide 581 */ 582 public static final int IO_CAPABILITY_UNKNOWN = 255; 583 584 /** @hide */ 585 @IntDef({IO_CAPABILITY_OUT, IO_CAPABILITY_IO, IO_CAPABILITY_IN, IO_CAPABILITY_NONE, 586 IO_CAPABILITY_KBDISP}) 587 @Retention(RetentionPolicy.SOURCE) 588 public @interface IoCapability {} 589 590 /** @hide */ 591 @IntDef(prefix = "ACTIVE_DEVICE_", value = {ACTIVE_DEVICE_AUDIO, 592 ACTIVE_DEVICE_PHONE_CALL, ACTIVE_DEVICE_ALL}) 593 @Retention(RetentionPolicy.SOURCE) 594 public @interface ActiveDeviceUse {} 595 596 /** 597 * Use the specified device for audio (a2dp and hearing aid profile) 598 * 599 * @hide 600 */ 601 @SystemApi 602 public static final int ACTIVE_DEVICE_AUDIO = 0; 603 604 /** 605 * Use the specified device for phone calls (headset profile and hearing 606 * aid profile) 607 * 608 * @hide 609 */ 610 @SystemApi 611 public static final int ACTIVE_DEVICE_PHONE_CALL = 1; 612 613 /** 614 * Use the specified device for a2dp, hearing aid profile, and headset profile 615 * 616 * @hide 617 */ 618 @SystemApi 619 public static final int ACTIVE_DEVICE_ALL = 2; 620 621 /** @hide */ 622 @IntDef({BluetoothProfile.HEADSET, BluetoothProfile.A2DP, 623 BluetoothProfile.HEARING_AID}) 624 @Retention(RetentionPolicy.SOURCE) 625 public @interface ActiveDeviceProfile {} 626 627 /** 628 * Broadcast Action: The local Bluetooth adapter has started the remote 629 * device discovery process. 630 * <p>This usually involves an inquiry scan of about 12 seconds, followed 631 * by a page scan of each new device to retrieve its Bluetooth name. 632 * <p>Register for {@link BluetoothDevice#ACTION_FOUND} to be notified as 633 * remote Bluetooth devices are found. 634 * <p>Device discovery is a heavyweight procedure. New connections to 635 * remote Bluetooth devices should not be attempted while discovery is in 636 * progress, and existing connections will experience limited bandwidth 637 * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing 638 * discovery. 639 */ 640 @RequiresLegacyBluetoothPermission 641 @RequiresBluetoothScanPermission 642 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) 643 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String 644 ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED"; 645 /** 646 * Broadcast Action: The local Bluetooth adapter has finished the device 647 * discovery process. 648 */ 649 @RequiresLegacyBluetoothPermission 650 @RequiresBluetoothScanPermission 651 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) 652 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String 653 ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; 654 655 /** 656 * Broadcast Action: The local Bluetooth adapter has changed its friendly 657 * Bluetooth name. 658 * <p>This name is visible to remote Bluetooth devices. 659 * <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing 660 * the name. 661 */ 662 @RequiresLegacyBluetoothPermission 663 @RequiresBluetoothConnectPermission 664 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 665 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String 666 ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED"; 667 /** 668 * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED} 669 * intents to request the local Bluetooth name. 670 */ 671 public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME"; 672 673 /** 674 * Intent used to broadcast the change in connection state of the local 675 * Bluetooth adapter to a profile of the remote device. When the adapter is 676 * not connected to any profiles of any remote devices and it attempts a 677 * connection to a profile this intent will be sent. Once connected, this intent 678 * will not be sent for any more connection attempts to any profiles of any 679 * remote device. When the adapter disconnects from the last profile its 680 * connected to of any remote device, this intent will be sent. 681 * 682 * <p> This intent is useful for applications that are only concerned about 683 * whether the local adapter is connected to any profile of any device and 684 * are not really concerned about which profile. For example, an application 685 * which displays an icon to display whether Bluetooth is connected or not 686 * can use this intent. 687 * 688 * <p>This intent will have 3 extras: 689 * {@link #EXTRA_CONNECTION_STATE} - The current connection state. 690 * {@link #EXTRA_PREVIOUS_CONNECTION_STATE}- The previous connection state. 691 * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. 692 * 693 * {@link #EXTRA_CONNECTION_STATE} or {@link #EXTRA_PREVIOUS_CONNECTION_STATE} 694 * can be any of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, 695 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. 696 */ 697 @RequiresLegacyBluetoothPermission 698 @RequiresBluetoothConnectPermission 699 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 700 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String 701 ACTION_CONNECTION_STATE_CHANGED = 702 "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"; 703 704 /** 705 * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED} 706 * 707 * This extra represents the current connection state. 708 */ 709 public static final String EXTRA_CONNECTION_STATE = 710 "android.bluetooth.adapter.extra.CONNECTION_STATE"; 711 712 /** 713 * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED} 714 * 715 * This extra represents the previous connection state. 716 */ 717 public static final String EXTRA_PREVIOUS_CONNECTION_STATE = 718 "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE"; 719 720 /** 721 * Broadcast Action: The Bluetooth adapter state has changed in LE only mode. 722 * 723 * @hide 724 */ 725 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 726 @SystemApi public static final String ACTION_BLE_STATE_CHANGED = 727 "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; 728 729 /** 730 * Intent used to broadcast the change in the Bluetooth address 731 * of the local Bluetooth adapter. 732 * <p>Always contains the extra field {@link 733 * #EXTRA_BLUETOOTH_ADDRESS} containing the Bluetooth address. 734 * 735 * Note: only system level processes are allowed to send this 736 * defined broadcast. 737 * 738 * @hide 739 */ 740 @RequiresBluetoothConnectPermission 741 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 742 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 743 public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED = 744 "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED"; 745 746 /** 747 * Used as a String extra field in {@link 748 * #ACTION_BLUETOOTH_ADDRESS_CHANGED} intent to store the local 749 * Bluetooth address. 750 * 751 * @hide 752 */ 753 public static final String EXTRA_BLUETOOTH_ADDRESS = 754 "android.bluetooth.adapter.extra.BLUETOOTH_ADDRESS"; 755 756 /** 757 * Broadcast Action: The notifys Bluetooth ACL connected event. This will be 758 * by BLE Always on enabled application to know the ACL_CONNECTED event 759 * when Bluetooth state in STATE_BLE_ON. This denotes GATT connection 760 * as Bluetooth LE is the only feature available in STATE_BLE_ON 761 * 762 * This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which 763 * works in Bluetooth state STATE_ON 764 * 765 * @hide 766 */ 767 @RequiresBluetoothConnectPermission 768 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 769 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 770 public static final String ACTION_BLE_ACL_CONNECTED = 771 "android.bluetooth.adapter.action.BLE_ACL_CONNECTED"; 772 773 /** 774 * Broadcast Action: The notifys Bluetooth ACL connected event. This will be 775 * by BLE Always on enabled application to know the ACL_DISCONNECTED event 776 * when Bluetooth state in STATE_BLE_ON. This denotes GATT disconnection as Bluetooth 777 * LE is the only feature available in STATE_BLE_ON 778 * 779 * This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which 780 * works in Bluetooth state STATE_ON 781 * 782 * @hide 783 */ 784 @RequiresBluetoothConnectPermission 785 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 786 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 787 public static final String ACTION_BLE_ACL_DISCONNECTED = 788 "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; 789 790 /** The profile is in disconnected state */ 791 public static final int STATE_DISCONNECTED = 792 0; //BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED; 793 /** The profile is in connecting state */ 794 public static final int STATE_CONNECTING = 1; //BluetoothProtoEnums.CONNECTION_STATE_CONNECTING; 795 /** The profile is in connected state */ 796 public static final int STATE_CONNECTED = 2; //BluetoothProtoEnums.CONNECTION_STATE_CONNECTED; 797 /** The profile is in disconnecting state */ 798 public static final int STATE_DISCONNECTING = 799 3; //BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING; 800 801 /** @hide */ 802 @Retention(RetentionPolicy.SOURCE) 803 @IntDef(prefix = { "STATE_" }, value = { 804 STATE_DISCONNECTED, 805 STATE_CONNECTING, 806 STATE_CONNECTED, 807 STATE_DISCONNECTING, 808 }) 809 public @interface ConnectionState {} 810 811 /** 812 * Audio mode representing output only. 813 * @hide 814 */ 815 @SystemApi 816 public static final String AUDIO_MODE_OUTPUT_ONLY = "audio_mode_output_only"; 817 818 /** 819 * Audio mode representing both output and microphone input. 820 * @hide 821 */ 822 @SystemApi 823 public static final String AUDIO_MODE_DUPLEX = "audio_mode_duplex"; 824 825 /** @hide */ 826 public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; 827 private final IBinder mToken; 828 829 830 /** 831 * When creating a ServerSocket using listenUsingRfcommOn() or 832 * listenUsingL2capOn() use SOCKET_CHANNEL_AUTO_STATIC to create 833 * a ServerSocket that auto assigns a channel number to the first 834 * bluetooth socket. 835 * The channel number assigned to this first Bluetooth Socket will 836 * be stored in the ServerSocket, and reused for subsequent Bluetooth 837 * sockets. 838 * 839 * @hide 840 */ 841 public static final int SOCKET_CHANNEL_AUTO_STATIC_NO_SDP = -2; 842 843 844 private static final int ADDRESS_LENGTH = 17; 845 846 /** 847 * Lazily initialized singleton. Guaranteed final after first object 848 * constructed. 849 */ 850 private static BluetoothAdapter sAdapter; 851 852 private BluetoothLeScanner mBluetoothLeScanner; 853 private BluetoothLeAdvertiser mBluetoothLeAdvertiser; 854 private PeriodicAdvertisingManager mPeriodicAdvertisingManager; 855 private DistanceMeasurementManager mDistanceMeasurementManager; 856 857 private final IBluetoothManager mManagerService; 858 private final AttributionSource mAttributionSource; 859 860 // Yeah, keeping both mService and sService isn't pretty, but it's too late 861 // in the current release for a major refactoring, so we leave them both 862 // intact until this can be cleaned up in a future release 863 864 @UnsupportedAppUsage 865 @GuardedBy("mServiceLock") 866 private IBluetooth mService; 867 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); 868 869 @GuardedBy("sServiceLock") 870 private static boolean sServiceRegistered; 871 @GuardedBy("sServiceLock") 872 private static IBluetooth sService; 873 private static final Object sServiceLock = new Object(); 874 875 private final Object mLock = new Object(); 876 private final Map<LeScanCallback, ScanCallback> mLeScanClients; 877 private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>> 878 mMetadataListeners = new HashMap<>(); 879 private final Map<BluetoothConnectionCallback, Executor> 880 mBluetoothConnectionCallbackExecutorMap = new HashMap<>(); 881 private final Map<PreferredAudioProfilesChangedCallback, Executor> 882 mAudioProfilesChangedCallbackExecutorMap = new HashMap<>(); 883 private final Map<BluetoothQualityReportReadyCallback, Executor> 884 mBluetoothQualityReportReadyCallbackExecutorMap = new HashMap<>(); 885 886 /** 887 * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener 888 * implementation. 889 */ 890 @SuppressLint("AndroidFrameworkBluetoothPermission") 891 private final IBluetoothMetadataListener mBluetoothMetadataListener = 892 new IBluetoothMetadataListener.Stub() { 893 @Override 894 public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) { 895 Attributable.setAttributionSource(device, mAttributionSource); 896 synchronized (mMetadataListeners) { 897 if (mMetadataListeners.containsKey(device)) { 898 List<Pair<OnMetadataChangedListener, Executor>> list = 899 mMetadataListeners.get(device); 900 for (Pair<OnMetadataChangedListener, Executor> pair : list) { 901 OnMetadataChangedListener listener = pair.first; 902 Executor executor = pair.second; 903 executor.execute(() -> { 904 listener.onMetadataChanged(device, key, value); 905 }); 906 } 907 } 908 } 909 return; 910 } 911 }; 912 913 /** @hide */ 914 @IntDef(value = { 915 BluetoothStatusCodes.ERROR_UNKNOWN, 916 BluetoothStatusCodes.FEATURE_NOT_SUPPORTED, 917 BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, 918 }) 919 @Retention(RetentionPolicy.SOURCE) 920 public @interface BluetoothActivityEnergyInfoCallbackError {} 921 922 /** 923 * Interface for Bluetooth activity energy info callback. Should be implemented by applications 924 * and set when calling {@link #requestControllerActivityEnergyInfo}. 925 * 926 * @hide 927 */ 928 @SystemApi 929 public interface OnBluetoothActivityEnergyInfoCallback { 930 /** 931 * Called when Bluetooth activity energy info is available. 932 * Note: this callback is triggered at most once for each call to 933 * {@link #requestControllerActivityEnergyInfo}. 934 * 935 * @param info the latest {@link BluetoothActivityEnergyInfo} 936 */ onBluetoothActivityEnergyInfoAvailable( @onNull BluetoothActivityEnergyInfo info)937 void onBluetoothActivityEnergyInfoAvailable( 938 @NonNull BluetoothActivityEnergyInfo info); 939 940 /** 941 * Called when the latest {@link BluetoothActivityEnergyInfo} can't be retrieved. 942 * The reason of the failure is indicated by the {@link BluetoothStatusCodes} 943 * passed as an argument to this method. 944 * Note: this callback is triggered at most once for each call to 945 * {@link #requestControllerActivityEnergyInfo}. 946 * 947 * @param error code indicating the reason for the failure 948 */ onBluetoothActivityEnergyInfoError( @luetoothActivityEnergyInfoCallbackError int error)949 void onBluetoothActivityEnergyInfoError( 950 @BluetoothActivityEnergyInfoCallbackError int error); 951 } 952 953 private static class OnBluetoothActivityEnergyInfoProxy 954 extends IBluetoothActivityEnergyInfoListener.Stub { 955 private final Object mLock = new Object(); 956 @Nullable @GuardedBy("mLock") private Executor mExecutor; 957 @Nullable @GuardedBy("mLock") private OnBluetoothActivityEnergyInfoCallback mCallback; 958 OnBluetoothActivityEnergyInfoProxy(Executor executor, OnBluetoothActivityEnergyInfoCallback callback)959 OnBluetoothActivityEnergyInfoProxy(Executor executor, 960 OnBluetoothActivityEnergyInfoCallback callback) { 961 mExecutor = executor; 962 mCallback = callback; 963 } 964 965 @Override onBluetoothActivityEnergyInfoAvailable(BluetoothActivityEnergyInfo info)966 public void onBluetoothActivityEnergyInfoAvailable(BluetoothActivityEnergyInfo info) { 967 Executor executor; 968 OnBluetoothActivityEnergyInfoCallback callback; 969 synchronized (mLock) { 970 if (mExecutor == null || mCallback == null) { 971 return; 972 } 973 executor = mExecutor; 974 callback = mCallback; 975 mExecutor = null; 976 mCallback = null; 977 } 978 final long identity = Binder.clearCallingIdentity(); 979 try { 980 if (info == null) { 981 executor.execute(() -> callback.onBluetoothActivityEnergyInfoError( 982 BluetoothStatusCodes.FEATURE_NOT_SUPPORTED)); 983 } else { 984 executor.execute(() -> callback.onBluetoothActivityEnergyInfoAvailable(info)); 985 } 986 } finally { 987 Binder.restoreCallingIdentity(identity); 988 } 989 } 990 991 /** 992 * Framework only method that is called when the service can't be reached. 993 */ onError(int errorCode)994 public void onError(int errorCode) { 995 Executor executor; 996 OnBluetoothActivityEnergyInfoCallback callback; 997 synchronized (mLock) { 998 if (mExecutor == null || mCallback == null) { 999 return; 1000 } 1001 executor = mExecutor; 1002 callback = mCallback; 1003 mExecutor = null; 1004 mCallback = null; 1005 } 1006 final long identity = Binder.clearCallingIdentity(); 1007 try { 1008 executor.execute(() -> callback.onBluetoothActivityEnergyInfoError( 1009 errorCode)); 1010 } finally { 1011 Binder.restoreCallingIdentity(identity); 1012 } 1013 } 1014 } 1015 1016 /** 1017 * Get a handle to the default local Bluetooth adapter. 1018 * <p> 1019 * Currently Android only supports one Bluetooth adapter, but the API could 1020 * be extended to support more. This will always return the default adapter. 1021 * </p> 1022 * 1023 * @return the default local adapter, or null if Bluetooth is not supported 1024 * on this hardware platform 1025 * @deprecated this method will continue to work, but developers are 1026 * strongly encouraged to migrate to using 1027 * {@link BluetoothManager#getAdapter()}, since that approach 1028 * enables support for {@link Context#createAttributionContext}. 1029 */ 1030 @Deprecated 1031 @RequiresNoPermission getDefaultAdapter()1032 public static synchronized BluetoothAdapter getDefaultAdapter() { 1033 if (sAdapter == null) { 1034 sAdapter = createAdapter(AttributionSource.myAttributionSource()); 1035 } 1036 return sAdapter; 1037 } 1038 1039 /** {@hide} */ createAdapter(AttributionSource attributionSource)1040 public static BluetoothAdapter createAdapter(AttributionSource attributionSource) { 1041 BluetoothServiceManager manager = 1042 BluetoothFrameworkInitializer.getBluetoothServiceManager(); 1043 if (manager == null) { 1044 Log.e(TAG, "BluetoothServiceManager is null"); 1045 return null; 1046 } 1047 IBluetoothManager service = IBluetoothManager.Stub.asInterface( 1048 manager.getBluetoothManagerServiceRegisterer().get()); 1049 if (service != null) { 1050 return new BluetoothAdapter(service, attributionSource); 1051 } else { 1052 Log.e(TAG, "Bluetooth service is null"); 1053 return null; 1054 } 1055 } 1056 1057 /** 1058 * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. 1059 */ BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource)1060 BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) { 1061 mManagerService = requireNonNull(managerService); 1062 mAttributionSource = requireNonNull(attributionSource); 1063 mServiceLock.writeLock().lock(); 1064 try { 1065 mService = getBluetoothService(mManagerCallback); 1066 } finally { 1067 mServiceLock.writeLock().unlock(); 1068 } 1069 mLeScanClients = new HashMap<LeScanCallback, ScanCallback>(); 1070 mToken = new Binder(DESCRIPTOR); 1071 } 1072 1073 /** 1074 * Get a {@link BluetoothDevice} object for the given Bluetooth hardware 1075 * address. 1076 * <p>Valid Bluetooth hardware addresses must be upper case, in big endian byte order, and in a 1077 * format such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is 1078 * available to validate a Bluetooth address. 1079 * <p>A {@link BluetoothDevice} will always be returned for a valid 1080 * hardware address, even if this adapter has never seen that device. 1081 * 1082 * @param address valid Bluetooth MAC address 1083 * @throws IllegalArgumentException if address is invalid 1084 */ 1085 @RequiresNoPermission getRemoteDevice(String address)1086 public BluetoothDevice getRemoteDevice(String address) { 1087 final BluetoothDevice res = new BluetoothDevice(address); 1088 res.setAttributionSource(mAttributionSource); 1089 return res; 1090 } 1091 1092 /** 1093 * Get a {@link BluetoothDevice} object for the given Bluetooth hardware 1094 * address and addressType. 1095 * <p>Valid Bluetooth hardware addresses must be upper case, in big endian byte order, and in a 1096 * format such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is 1097 * available to validate a Bluetooth address. 1098 * <p>A {@link BluetoothDevice} will always be returned for a valid 1099 * hardware address and type, even if this adapter has never seen that device. 1100 * 1101 * @param address valid Bluetooth MAC address 1102 * @param addressType Bluetooth address type 1103 * @throws IllegalArgumentException if address is invalid 1104 */ 1105 @RequiresNoPermission 1106 @NonNull getRemoteLeDevice(@onNull String address, @AddressType int addressType)1107 public BluetoothDevice getRemoteLeDevice(@NonNull String address, 1108 @AddressType int addressType) { 1109 final BluetoothDevice res = new BluetoothDevice(address, addressType); 1110 res.setAttributionSource(mAttributionSource); 1111 return res; 1112 } 1113 1114 /** 1115 * Get a {@link BluetoothDevice} object for the given Bluetooth hardware 1116 * address. 1117 * <p>Valid Bluetooth hardware addresses must be 6 bytes. This method 1118 * expects the address in network byte order (MSB first). 1119 * <p>A {@link BluetoothDevice} will always be returned for a valid 1120 * hardware address, even if this adapter has never seen that device. 1121 * 1122 * @param address Bluetooth MAC address (6 bytes) 1123 * @throws IllegalArgumentException if address is invalid 1124 */ 1125 @RequiresNoPermission getRemoteDevice(byte[] address)1126 public BluetoothDevice getRemoteDevice(byte[] address) { 1127 if (address == null || address.length != 6) { 1128 throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); 1129 } 1130 final BluetoothDevice res = new BluetoothDevice( 1131 String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1], 1132 address[2], address[3], address[4], address[5])); 1133 res.setAttributionSource(mAttributionSource); 1134 return res; 1135 } 1136 1137 /** 1138 * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations. 1139 * Will return null if Bluetooth is turned off or if Bluetooth LE Advertising is not 1140 * supported on this device. 1141 * <p> 1142 * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported 1143 * on this device before calling this method. 1144 */ 1145 @RequiresNoPermission getBluetoothLeAdvertiser()1146 public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { 1147 if (!getLeAccess()) { 1148 return null; 1149 } 1150 synchronized (mLock) { 1151 if (mBluetoothLeAdvertiser == null) { 1152 mBluetoothLeAdvertiser = new BluetoothLeAdvertiser(this); 1153 } 1154 return mBluetoothLeAdvertiser; 1155 } 1156 } 1157 1158 /** 1159 * Returns a {@link PeriodicAdvertisingManager} object for Bluetooth LE Periodic Advertising 1160 * operations. Will return null if Bluetooth is turned off or if Bluetooth LE Periodic 1161 * Advertising is not supported on this device. 1162 * <p> 1163 * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is 1164 * supported on this device before calling this method. 1165 * 1166 * @hide 1167 */ 1168 @RequiresNoPermission getPeriodicAdvertisingManager()1169 public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { 1170 if (!getLeAccess()) { 1171 return null; 1172 } 1173 1174 if (!isLePeriodicAdvertisingSupported()) { 1175 return null; 1176 } 1177 1178 synchronized (mLock) { 1179 if (mPeriodicAdvertisingManager == null) { 1180 mPeriodicAdvertisingManager = new PeriodicAdvertisingManager(this); 1181 } 1182 return mPeriodicAdvertisingManager; 1183 } 1184 } 1185 1186 /** 1187 * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. 1188 */ 1189 @RequiresNoPermission getBluetoothLeScanner()1190 public BluetoothLeScanner getBluetoothLeScanner() { 1191 if (!getLeAccess()) { 1192 return null; 1193 } 1194 synchronized (mLock) { 1195 if (mBluetoothLeScanner == null) { 1196 mBluetoothLeScanner = new BluetoothLeScanner(this); 1197 } 1198 return mBluetoothLeScanner; 1199 } 1200 } 1201 1202 /** 1203 * Get a {@link DistanceMeasurementManager} object for distance measurement operations. 1204 * <p> 1205 * Use {@link #isDistanceMeasurementSupported()} to check whether distance 1206 * measurement is supported on this device before calling this method. 1207 * 1208 * @return a new instance of {@link DistanceMeasurementManager}, or {@code null} if Bluetooth is 1209 * turned off 1210 * @throws UnsupportedOperationException if distance measurement is not supported on this device 1211 * 1212 * @hide 1213 */ 1214 @SystemApi 1215 @RequiresPermission(allOf = { 1216 android.Manifest.permission.BLUETOOTH_CONNECT, 1217 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 1218 }) getDistanceMeasurementManager()1219 public @Nullable DistanceMeasurementManager getDistanceMeasurementManager() { 1220 if (!getLeAccess()) { 1221 return null; 1222 } 1223 1224 if (isDistanceMeasurementSupported() != BluetoothStatusCodes.FEATURE_SUPPORTED) { 1225 throw new UnsupportedOperationException("Distance measurement is unsupported"); 1226 } 1227 1228 synchronized (mLock) { 1229 if (mDistanceMeasurementManager == null) { 1230 mDistanceMeasurementManager = new DistanceMeasurementManager(this); 1231 } 1232 return mDistanceMeasurementManager; 1233 } 1234 } 1235 1236 /** 1237 * Return true if Bluetooth is currently enabled and ready for use. 1238 * <p>Equivalent to: 1239 * <code>getBluetoothState() == STATE_ON</code> 1240 * 1241 * @return true if the local adapter is turned on 1242 */ 1243 @RequiresLegacyBluetoothPermission 1244 @RequiresNoPermission isEnabled()1245 public boolean isEnabled() { 1246 return getState() == BluetoothAdapter.STATE_ON; 1247 } 1248 1249 /** 1250 * Return true if Bluetooth LE(Always BLE On feature) is currently 1251 * enabled and ready for use 1252 * <p>This returns true if current state is either STATE_ON or STATE_BLE_ON 1253 * 1254 * @return true if the local Bluetooth LE adapter is turned on 1255 * @hide 1256 */ 1257 @SystemApi 1258 @RequiresNoPermission isLeEnabled()1259 public boolean isLeEnabled() { 1260 final int state = getLeState(); 1261 if (DBG) { 1262 Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state)); 1263 } 1264 return (state == BluetoothAdapter.STATE_ON 1265 || state == BluetoothAdapter.STATE_BLE_ON 1266 || state == BluetoothAdapter.STATE_TURNING_ON 1267 || state == BluetoothAdapter.STATE_TURNING_OFF); 1268 } 1269 1270 /** 1271 * Turns off Bluetooth LE which was earlier turned on by calling enableBLE(). 1272 * 1273 * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition 1274 * to STATE_OFF and completely shut-down Bluetooth 1275 * 1276 * <p> If the Adapter state is STATE_ON, This would unregister the existance of 1277 * special Bluetooth LE application and hence the further turning off of Bluetooth 1278 * from UI would ensure the complete turn-off of Bluetooth rather than staying back 1279 * BLE only state 1280 * 1281 * <p>This is an asynchronous call: it will return immediately, and 1282 * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} 1283 * to be notified of subsequent adapter state changes If this call returns 1284 * true, then the adapter state will immediately transition from {@link 1285 * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time 1286 * later transition to either {@link #STATE_BLE_ON} or {@link 1287 * #STATE_OFF} based on the existance of the further Always BLE ON enabled applications 1288 * If this call returns false then there was an 1289 * immediate problem that will prevent the QAdapter from being turned off - 1290 * such as the QAadapter already being turned off. 1291 * 1292 * @return true to indicate success, or false on immediate error 1293 * @hide 1294 */ 1295 @SystemApi 1296 @RequiresBluetoothConnectPermission 1297 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) disableBLE()1298 public boolean disableBLE() { 1299 if (!isBleScanAlwaysAvailable()) { 1300 return false; 1301 } 1302 try { 1303 return mManagerService.disableBle(mAttributionSource, mToken); 1304 } catch (RemoteException e) { 1305 Log.e(TAG, "", e); 1306 } 1307 return false; 1308 } 1309 1310 /** 1311 * Applications who want to only use Bluetooth Low Energy (BLE) can call enableBLE. 1312 * 1313 * enableBLE registers the existence of an app using only LE functions. 1314 * 1315 * enableBLE may enable Bluetooth to an LE only mode so that an app can use 1316 * LE related features (BluetoothGatt or BluetoothGattServer classes) 1317 * 1318 * If the user disables Bluetooth while an app is registered to use LE only features, 1319 * Bluetooth will remain on in LE only mode for the app. 1320 * 1321 * When Bluetooth is in LE only mode, it is not shown as ON to the UI. 1322 * 1323 * <p>This is an asynchronous call: it returns immediately, and 1324 * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} 1325 * to be notified of adapter state changes. 1326 * 1327 * If this call returns * true, then the adapter state is either in a mode where 1328 * LE is available, or will transition from {@link #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, 1329 * and some time later transition to either {@link #STATE_OFF} or {@link #STATE_BLE_ON}. 1330 * 1331 * If this call returns false then there was an immediate problem that prevents the 1332 * adapter from being turned on - such as Airplane mode. 1333 * 1334 * {@link #ACTION_BLE_STATE_CHANGED} returns the Bluetooth Adapter's various 1335 * states, It includes all the classic Bluetooth Adapter states along with 1336 * internal BLE only states 1337 * 1338 * @return true to indicate Bluetooth LE will be available, or false on immediate error 1339 * @hide 1340 */ 1341 @SystemApi 1342 @RequiresBluetoothConnectPermission 1343 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) enableBLE()1344 public boolean enableBLE() { 1345 if (!isBleScanAlwaysAvailable()) { 1346 return false; 1347 } 1348 try { 1349 return mManagerService.enableBle(mAttributionSource, mToken); 1350 } catch (RemoteException e) { 1351 Log.e(TAG, "", e); 1352 } 1353 1354 return false; 1355 } 1356 1357 /** 1358 * There are several instances of IpcDataCache used in this class. 1359 * BluetoothCache wraps up the common code. All caches are created with a maximum of 1360 * eight entries, and the key is in the bluetooth module. The name is set to the api. 1361 */ 1362 private static class BluetoothCache<Q, R> extends IpcDataCache<Q, R> { BluetoothCache(String api, IpcDataCache.QueryHandler query)1363 BluetoothCache(String api, IpcDataCache.QueryHandler query) { 1364 super(8, IpcDataCache.MODULE_BLUETOOTH, api, api, query); 1365 }}; 1366 1367 /** 1368 * Invalidate a bluetooth cache. This method is just a short-hand wrapper that 1369 * enforces the bluetooth module. 1370 */ invalidateCache(@onNull String api)1371 private static void invalidateCache(@NonNull String api) { 1372 IpcDataCache.invalidateCache(IpcDataCache.MODULE_BLUETOOTH, api); 1373 } 1374 1375 private static final IpcDataCache.QueryHandler<IBluetooth, Integer> sBluetoothGetStateQuery = 1376 new IpcDataCache.QueryHandler<>() { 1377 @RequiresLegacyBluetoothPermission 1378 @RequiresNoPermission 1379 @Override 1380 public @InternalAdapterState Integer apply(IBluetooth serviceQuery) { 1381 try { 1382 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 1383 serviceQuery.getState(recv); 1384 return recv.awaitResultNoInterrupt(getSyncTimeout()) 1385 .getValue(BluetoothAdapter.STATE_OFF); 1386 } catch (RemoteException | TimeoutException e) { 1387 throw new RuntimeException(e); 1388 } 1389 } 1390 }; 1391 1392 private static final String GET_STATE_API = "BluetoothAdapter_getState"; 1393 1394 private static final IpcDataCache<IBluetooth, Integer> sBluetoothGetStateCache = 1395 new BluetoothCache<>(GET_STATE_API, sBluetoothGetStateQuery); 1396 1397 /** @hide */ 1398 @RequiresNoPermission disableBluetoothGetStateCache()1399 public void disableBluetoothGetStateCache() { 1400 sBluetoothGetStateCache.disableForCurrentProcess(); 1401 } 1402 1403 /** @hide */ invalidateBluetoothGetStateCache()1404 public static void invalidateBluetoothGetStateCache() { 1405 invalidateCache(GET_STATE_API); 1406 } 1407 1408 /** 1409 * Fetch the current bluetooth state. If the service is down, return 1410 * OFF. 1411 */ getStateInternal()1412 private @InternalAdapterState int getStateInternal() { 1413 mServiceLock.readLock().lock(); 1414 try { 1415 if (mService != null) { 1416 return sBluetoothGetStateCache.query(mService); 1417 } 1418 } catch (RuntimeException e) { 1419 if (!(e.getCause() instanceof TimeoutException) 1420 && !(e.getCause() instanceof RemoteException)) { 1421 throw e; 1422 } 1423 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1424 } finally { 1425 mServiceLock.readLock().unlock(); 1426 } 1427 return STATE_OFF; 1428 } 1429 1430 /** 1431 * Get the current state of the local Bluetooth adapter. 1432 * <p>Possible return values are 1433 * {@link #STATE_OFF}, 1434 * {@link #STATE_TURNING_ON}, 1435 * {@link #STATE_ON}, 1436 * {@link #STATE_TURNING_OFF}. 1437 * 1438 * @return current state of Bluetooth adapter 1439 */ 1440 @RequiresLegacyBluetoothPermission 1441 @RequiresNoPermission getState()1442 public @AdapterState int getState() { 1443 int state = getStateInternal(); 1444 1445 // Consider all internal states as OFF 1446 if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON 1447 || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { 1448 if (VDBG) { 1449 Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF"); 1450 } 1451 state = BluetoothAdapter.STATE_OFF; 1452 } 1453 if (VDBG) { 1454 Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState( 1455 state)); 1456 } 1457 return state; 1458 } 1459 1460 /** 1461 * Get the current state of the local Bluetooth adapter 1462 * <p>This returns current internal state of Adapter including LE ON/OFF 1463 * 1464 * <p>Possible return values are 1465 * {@link #STATE_OFF}, 1466 * {@link #STATE_BLE_TURNING_ON}, 1467 * {@link #STATE_BLE_ON}, 1468 * {@link #STATE_TURNING_ON}, 1469 * {@link #STATE_ON}, 1470 * {@link #STATE_TURNING_OFF}, 1471 * {@link #STATE_BLE_TURNING_OFF}. 1472 * 1473 * @return current state of Bluetooth adapter 1474 * @hide 1475 */ 1476 @RequiresLegacyBluetoothPermission 1477 @RequiresNoPermission 1478 @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine " 1479 + "whether you can use BLE & BT classic.") getLeState()1480 public @InternalAdapterState int getLeState() { 1481 int state = getStateInternal(); 1482 1483 if (VDBG) { 1484 Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state)); 1485 } 1486 return state; 1487 } 1488 getLeAccess()1489 boolean getLeAccess() { 1490 if (getLeState() == STATE_ON) { 1491 return true; 1492 } else if (getLeState() == STATE_BLE_ON) { 1493 return true; // TODO: FILTER SYSTEM APPS HERE <-- 1494 } 1495 1496 return false; 1497 } 1498 1499 /** 1500 * Turn on the local Bluetooth adapter—do not use without explicit 1501 * user action to turn on Bluetooth. 1502 * <p>This powers on the underlying Bluetooth hardware, and starts all 1503 * Bluetooth system services. 1504 * <p class="caution"><strong>Bluetooth should never be enabled without 1505 * direct user consent</strong>. If you want to turn on Bluetooth in order 1506 * to create a wireless connection, you should use the {@link 1507 * #ACTION_REQUEST_ENABLE} Intent, which will raise a dialog that requests 1508 * user permission to turn on Bluetooth. The {@link #enable()} method is 1509 * provided only for applications that include a user interface for changing 1510 * system settings, such as a "power manager" app.</p> 1511 * <p>This is an asynchronous call: it will return immediately, and 1512 * clients should listen for {@link #ACTION_STATE_CHANGED} 1513 * to be notified of subsequent adapter state changes. If this call returns 1514 * true, then the adapter state will immediately transition from {@link 1515 * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time 1516 * later transition to either {@link #STATE_OFF} or {@link 1517 * #STATE_ON}. If this call returns false then there was an 1518 * immediate problem that will prevent the adapter from being turned on - 1519 * such as Airplane mode, or the adapter is already turned on. 1520 * 1521 * @return true to indicate adapter startup has begun, or false on immediate error 1522 * 1523 * @deprecated Starting with {@link android.os.Build.VERSION_CODES#TIRAMISU}, applications 1524 * are not allowed to enable/disable Bluetooth. 1525 * <b>Compatibility Note:</b> For applications targeting 1526 * {@link android.os.Build.VERSION_CODES#TIRAMISU} or above, this API will always fail and return 1527 * {@code false}. If apps are targeting an older SDK ({@link android.os.Build.VERSION_CODES#S} 1528 * or below), they can continue to use this API. 1529 * <p> 1530 * Deprecation Exemptions: 1531 * <ul> 1532 * <li>Device Owner (DO), Profile Owner (PO) and system apps. 1533 * </ul> 1534 */ 1535 @Deprecated 1536 @RequiresLegacyBluetoothAdminPermission 1537 @RequiresBluetoothConnectPermission 1538 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) enable()1539 public boolean enable() { 1540 if (isEnabled()) { 1541 if (DBG) { 1542 Log.d(TAG, "enable(): BT already enabled!"); 1543 } 1544 return true; 1545 } 1546 try { 1547 return mManagerService.enable(mAttributionSource); 1548 } catch (RemoteException e) { 1549 Log.e(TAG, "", e); 1550 } 1551 return false; 1552 } 1553 1554 /** 1555 * Turn off the local Bluetooth adapter—do not use without explicit 1556 * user action to turn off Bluetooth. 1557 * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth 1558 * system services, and powers down the underlying Bluetooth hardware. 1559 * <p class="caution"><strong>Bluetooth should never be disabled without 1560 * direct user consent</strong>. The {@link #disable()} method is 1561 * provided only for applications that include a user interface for changing 1562 * system settings, such as a "power manager" app.</p> 1563 * <p>This is an asynchronous call: it will return immediately, and 1564 * clients should listen for {@link #ACTION_STATE_CHANGED} 1565 * to be notified of subsequent adapter state changes. If this call returns 1566 * true, then the adapter state will immediately transition from {@link 1567 * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time 1568 * later transition to either {@link #STATE_OFF} or {@link 1569 * #STATE_ON}. If this call returns false then there was an 1570 * immediate problem that will prevent the adapter from being turned off - 1571 * such as the adapter already being turned off. 1572 * 1573 * @return true to indicate adapter shutdown has begun, or false on immediate error 1574 * 1575 * @deprecated Starting with {@link android.os.Build.VERSION_CODES#TIRAMISU}, applications 1576 * are not allowed to enable/disable Bluetooth. 1577 * <b>Compatibility Note:</b> For applications targeting 1578 * {@link android.os.Build.VERSION_CODES#TIRAMISU} or above, this API will always fail and return 1579 * {@code false}. If apps are targeting an older SDK ({@link android.os.Build.VERSION_CODES#S} 1580 * or below), they can continue to use this API. 1581 * <p> 1582 * Deprecation Exemptions: 1583 * <ul> 1584 * <li>Device Owner (DO), Profile Owner (PO) and system apps. 1585 * </ul> 1586 */ 1587 @Deprecated 1588 @RequiresLegacyBluetoothAdminPermission 1589 @RequiresBluetoothConnectPermission 1590 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) disable()1591 public boolean disable() { 1592 return disable(true); 1593 } 1594 1595 /** 1596 * Turn off the local Bluetooth adapter and don't persist the setting. 1597 * 1598 * @param persist Indicate whether the off state should be persisted following the next reboot 1599 * @return true to indicate adapter shutdown has begun, or false on immediate error 1600 * @hide 1601 */ 1602 @SystemApi 1603 @RequiresLegacyBluetoothAdminPermission 1604 @RequiresBluetoothConnectPermission 1605 @RequiresPermission(allOf = { 1606 android.Manifest.permission.BLUETOOTH_CONNECT, 1607 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 1608 }) disable(boolean persist)1609 public boolean disable(boolean persist) { 1610 try { 1611 return mManagerService.disable(mAttributionSource, persist); 1612 } catch (RemoteException e) { 1613 Log.e(TAG, "", e); 1614 } 1615 return false; 1616 } 1617 1618 /** 1619 * Returns the hardware address of the local Bluetooth adapter. 1620 * <p>For example, "00:11:22:AA:BB:CC". 1621 * 1622 * @return Bluetooth hardware address as string 1623 * 1624 * Requires {@code android.Manifest.permission#LOCAL_MAC_ADDRESS} and 1625 * {@link android.Manifest.permission#BLUETOOTH_CONNECT}. 1626 */ 1627 @RequiresLegacyBluetoothPermission 1628 @RequiresBluetoothConnectPermission getAddress()1629 public String getAddress() { 1630 try { 1631 return mManagerService.getAddress(mAttributionSource); 1632 } catch (RemoteException e) { 1633 Log.e(TAG, "", e); 1634 } 1635 return null; 1636 } 1637 1638 /** 1639 * Get the friendly Bluetooth name of the local Bluetooth adapter. 1640 * <p>This name is visible to remote Bluetooth devices. 1641 * 1642 * @return the Bluetooth name, or null on error 1643 */ 1644 @RequiresLegacyBluetoothPermission 1645 @RequiresBluetoothConnectPermission 1646 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getName()1647 public String getName() { 1648 try { 1649 return mManagerService.getName(mAttributionSource); 1650 } catch (RemoteException e) { 1651 Log.e(TAG, "", e); 1652 } 1653 return null; 1654 } 1655 1656 /** {@hide} */ 1657 @RequiresBluetoothAdvertisePermission 1658 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) getNameLengthForAdvertise()1659 public int getNameLengthForAdvertise() { 1660 mServiceLock.readLock().lock(); 1661 try { 1662 if (mService != null) { 1663 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 1664 mService.getNameLengthForAdvertise(mAttributionSource, recv); 1665 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(-1); 1666 } 1667 } catch (RemoteException | TimeoutException e) { 1668 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1669 } finally { 1670 mServiceLock.readLock().unlock(); 1671 } 1672 return -1; 1673 } 1674 1675 /** 1676 * Factory reset bluetooth settings. 1677 * 1678 * @return true to indicate that the config file was successfully cleared 1679 * @hide 1680 */ 1681 @SystemApi 1682 @RequiresBluetoothConnectPermission 1683 @RequiresPermission(allOf = { 1684 android.Manifest.permission.BLUETOOTH_CONNECT, 1685 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 1686 }) clearBluetooth()1687 public boolean clearBluetooth() { 1688 mServiceLock.readLock().lock(); 1689 try { 1690 if (mService != null) { 1691 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 1692 mService.factoryReset(mAttributionSource, recv); 1693 if (recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false) 1694 && mManagerService != null 1695 && mManagerService.onFactoryReset(mAttributionSource)) { 1696 return true; 1697 } 1698 } 1699 Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later"); 1700 BluetoothProperties.factory_reset(true); 1701 } catch (RemoteException | TimeoutException e) { 1702 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1703 } finally { 1704 mServiceLock.readLock().unlock(); 1705 } 1706 return false; 1707 } 1708 1709 /** 1710 * See {@link #clearBluetooth()} 1711 * 1712 * @return true to indicate that the config file was successfully cleared 1713 * @hide 1714 */ 1715 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1716 @RequiresBluetoothConnectPermission 1717 @RequiresPermission(allOf = { 1718 android.Manifest.permission.BLUETOOTH_CONNECT, 1719 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 1720 }) factoryReset()1721 public boolean factoryReset() { 1722 return clearBluetooth(); 1723 } 1724 1725 /** 1726 * Get the UUIDs supported by the local Bluetooth adapter. 1727 * 1728 * @return the UUIDs supported by the local Bluetooth Adapter. 1729 * @hide 1730 */ 1731 @UnsupportedAppUsage 1732 @RequiresLegacyBluetoothPermission 1733 @RequiresBluetoothConnectPermission 1734 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getUuids()1735 public @NonNull ParcelUuid[] getUuids() { 1736 List<ParcelUuid> parcels = getUuidsList(); 1737 return parcels.toArray(new ParcelUuid[parcels.size()]); 1738 } 1739 1740 /** 1741 * Get the UUIDs supported by the local Bluetooth adapter. 1742 * 1743 * @return a list of the UUIDs supported by the local Bluetooth Adapter. 1744 * @hide 1745 */ 1746 @SystemApi 1747 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getUuidsList()1748 public @NonNull List<ParcelUuid> getUuidsList() { 1749 List<ParcelUuid> defaultValue = new ArrayList<>(); 1750 if (getState() != STATE_ON) { 1751 return defaultValue; 1752 } 1753 mServiceLock.readLock().lock(); 1754 try { 1755 if (mService != null) { 1756 final SynchronousResultReceiver<List<ParcelUuid>> recv = 1757 SynchronousResultReceiver.get(); 1758 mService.getUuids(mAttributionSource, recv); 1759 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 1760 } 1761 } catch (RemoteException | TimeoutException e) { 1762 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1763 } finally { 1764 mServiceLock.readLock().unlock(); 1765 } 1766 return defaultValue; 1767 } 1768 1769 /** 1770 * Set the friendly Bluetooth name of the local Bluetooth adapter. 1771 * <p>This name is visible to remote Bluetooth devices. 1772 * <p>Valid Bluetooth names are a maximum of 248 bytes using UTF-8 1773 * encoding, although many remote devices can only display the first 1774 * 40 characters, and some may be limited to just 20. 1775 * <p>If Bluetooth state is not {@link #STATE_ON}, this API 1776 * will return false. After turning on Bluetooth, 1777 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} 1778 * to get the updated value. 1779 * 1780 * @param name a valid Bluetooth name 1781 * @return true if the name was set, false otherwise 1782 */ 1783 @RequiresLegacyBluetoothAdminPermission 1784 @RequiresBluetoothConnectPermission 1785 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) setName(String name)1786 public boolean setName(String name) { 1787 if (getState() != STATE_ON) { 1788 return false; 1789 } 1790 mServiceLock.readLock().lock(); 1791 try { 1792 if (mService != null) { 1793 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 1794 mService.setName(name, mAttributionSource, recv); 1795 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 1796 } 1797 } catch (RemoteException | TimeoutException e) { 1798 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1799 } finally { 1800 mServiceLock.readLock().unlock(); 1801 } 1802 return false; 1803 } 1804 1805 /** 1806 * Returns the Input/Output capability of the device for classic Bluetooth. 1807 * 1808 * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT}, 1809 * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE}, 1810 * {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}. 1811 * 1812 * @hide 1813 */ 1814 @RequiresLegacyBluetoothAdminPermission 1815 @RequiresBluetoothConnectPermission 1816 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 1817 @IoCapability getIoCapability()1818 public int getIoCapability() { 1819 if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; 1820 mServiceLock.readLock().lock(); 1821 try { 1822 if (mService != null) { 1823 final SynchronousResultReceiver<Integer> recv = 1824 SynchronousResultReceiver.get(); 1825 mService.getIoCapability(mAttributionSource, recv); 1826 return recv.awaitResultNoInterrupt(getSyncTimeout()) 1827 .getValue(BluetoothAdapter.IO_CAPABILITY_UNKNOWN); 1828 } 1829 } catch (RemoteException | TimeoutException e) { 1830 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1831 } finally { 1832 mServiceLock.readLock().unlock(); 1833 } 1834 return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; 1835 } 1836 1837 /** 1838 * Sets the Input/Output capability of the device for classic Bluetooth. 1839 * 1840 * <p>Changing the Input/Output capability of a device only takes effect on restarting the 1841 * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()} 1842 * and {@link BluetoothAdapter#enable()} to see the changes. 1843 * 1844 * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT}, 1845 * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, 1846 * {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}. 1847 * 1848 * @hide 1849 */ 1850 @RequiresBluetoothConnectPermission 1851 @RequiresPermission(allOf = { 1852 android.Manifest.permission.BLUETOOTH_CONNECT, 1853 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 1854 }) setIoCapability(@oCapability int capability)1855 public boolean setIoCapability(@IoCapability int capability) { 1856 if (getState() != STATE_ON) return false; 1857 mServiceLock.readLock().lock(); 1858 try { 1859 if (mService != null) { 1860 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 1861 mService.setIoCapability(capability, mAttributionSource, recv); 1862 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 1863 } 1864 } catch (RemoteException | TimeoutException e) { 1865 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1866 } finally { 1867 mServiceLock.readLock().unlock(); 1868 } 1869 return false; 1870 } 1871 1872 /** 1873 * Get the current Bluetooth scan mode of the local Bluetooth adapter. 1874 * <p>The Bluetooth scan mode determines if the local adapter is 1875 * connectable and/or discoverable from remote Bluetooth devices. 1876 * <p>Possible values are: 1877 * {@link #SCAN_MODE_NONE}, 1878 * {@link #SCAN_MODE_CONNECTABLE}, 1879 * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. 1880 * <p>If Bluetooth state is not {@link #STATE_ON}, this API 1881 * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth, 1882 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} 1883 * to get the updated value. 1884 * 1885 * @return scan mode 1886 */ 1887 @RequiresLegacyBluetoothPermission 1888 @RequiresBluetoothScanPermission 1889 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) 1890 @ScanMode getScanMode()1891 public int getScanMode() { 1892 if (getState() != STATE_ON) { 1893 return SCAN_MODE_NONE; 1894 } 1895 mServiceLock.readLock().lock(); 1896 try { 1897 if (mService != null) { 1898 SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 1899 mService.getScanMode(mAttributionSource, recv); 1900 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(SCAN_MODE_NONE); 1901 } 1902 } catch (TimeoutException e) { 1903 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1904 } catch (RemoteException e) { 1905 throw e.rethrowFromSystemServer(); 1906 } finally { 1907 mServiceLock.readLock().unlock(); 1908 } 1909 return SCAN_MODE_NONE; 1910 } 1911 1912 /** 1913 * Set the local Bluetooth adapter connectablility and discoverability. 1914 * <p>If the scan mode is set to {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}, 1915 * it will change to {@link #SCAN_MODE_CONNECTABLE} after the discoverable timeout. 1916 * The discoverable timeout can be set with {@link #setDiscoverableTimeout} and 1917 * checked with {@link #getDiscoverableTimeout}. By default, the timeout is usually 1918 * 120 seconds on phones which is enough for a remote device to initiate and complete 1919 * its discovery process. 1920 * <p>Applications cannot set the scan mode. They should use 1921 * {@link #ACTION_REQUEST_DISCOVERABLE} instead. 1922 * 1923 * @param mode represents the desired state of the local device scan mode 1924 * 1925 * @return status code indicating whether the scan mode was successfully set 1926 * @throws IllegalArgumentException if the mode is not a valid scan mode 1927 * @hide 1928 */ 1929 @SystemApi 1930 @RequiresBluetoothScanPermission 1931 @RequiresPermission(allOf = { 1932 android.Manifest.permission.BLUETOOTH_SCAN, 1933 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 1934 }) 1935 @ScanModeStatusCode setScanMode(@canMode int mode)1936 public int setScanMode(@ScanMode int mode) { 1937 if (getState() != STATE_ON) { 1938 return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 1939 } 1940 if (mode != SCAN_MODE_NONE && mode != SCAN_MODE_CONNECTABLE 1941 && mode != SCAN_MODE_CONNECTABLE_DISCOVERABLE) { 1942 throw new IllegalArgumentException("Invalid scan mode param value"); 1943 } 1944 mServiceLock.readLock().lock(); 1945 try { 1946 if (mService != null) { 1947 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 1948 mService.setScanMode(mode, mAttributionSource, recv); 1949 return recv.awaitResultNoInterrupt(getSyncTimeout()) 1950 .getValue(BluetoothStatusCodes.ERROR_UNKNOWN); 1951 } 1952 } catch (TimeoutException e) { 1953 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1954 } catch (RemoteException e) { 1955 throw e.rethrowFromSystemServer(); 1956 } finally { 1957 mServiceLock.readLock().unlock(); 1958 } 1959 return BluetoothStatusCodes.ERROR_UNKNOWN; 1960 } 1961 1962 /** 1963 * Get the timeout duration of the {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. 1964 * 1965 * @return the duration of the discoverable timeout or null if an error has occurred 1966 */ 1967 @RequiresBluetoothScanPermission 1968 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) getDiscoverableTimeout()1969 public @Nullable Duration getDiscoverableTimeout() { 1970 if (getState() != STATE_ON) { 1971 return null; 1972 } 1973 mServiceLock.readLock().lock(); 1974 try { 1975 if (mService != null) { 1976 final SynchronousResultReceiver<Long> recv = SynchronousResultReceiver.get(); 1977 mService.getDiscoverableTimeout(mAttributionSource, recv); 1978 long timeout = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue((long) -1); 1979 return (timeout == -1) ? null : Duration.ofSeconds(timeout); 1980 } 1981 } catch (TimeoutException e) { 1982 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1983 } catch (RemoteException e) { 1984 throw e.rethrowFromSystemServer(); 1985 } finally { 1986 mServiceLock.readLock().unlock(); 1987 } 1988 return null; 1989 } 1990 1991 /** 1992 * Set the total time the Bluetooth local adapter will stay discoverable when 1993 * {@link #setScanMode} is called with {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE} mode. 1994 * After this timeout, the scan mode will fallback to {@link #SCAN_MODE_CONNECTABLE}. 1995 * <p>If <code>timeout</code> is set to 0, no timeout will occur and the scan mode will 1996 * be persisted until a subsequent call to {@link #setScanMode}. 1997 * 1998 * @param timeout represents the total duration the local Bluetooth adapter will remain 1999 * discoverable, or no timeout if set to 0 2000 * @return whether the timeout was successfully set 2001 * @throws IllegalArgumentException if <code>timeout</code> duration in seconds is more 2002 * than {@link Integer#MAX_VALUE} 2003 * @hide 2004 */ 2005 @SystemApi 2006 @RequiresBluetoothScanPermission 2007 @RequiresPermission(allOf = { 2008 android.Manifest.permission.BLUETOOTH_SCAN, 2009 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2010 }) 2011 @ScanModeStatusCode setDiscoverableTimeout(@onNull Duration timeout)2012 public int setDiscoverableTimeout(@NonNull Duration timeout) { 2013 if (getState() != STATE_ON) { 2014 return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 2015 } 2016 if (timeout.toSeconds() > Integer.MAX_VALUE) { 2017 throw new IllegalArgumentException("Timeout in seconds must be less or equal to " 2018 + Integer.MAX_VALUE); 2019 } 2020 mServiceLock.readLock().lock(); 2021 try { 2022 if (mService != null) { 2023 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2024 mService.setDiscoverableTimeout(timeout.toSeconds(), mAttributionSource, recv); 2025 return recv.awaitResultNoInterrupt(getSyncTimeout()) 2026 .getValue(BluetoothStatusCodes.ERROR_UNKNOWN); 2027 } 2028 } catch (TimeoutException e) { 2029 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2030 } catch (RemoteException e) { 2031 throw e.rethrowFromSystemServer(); 2032 } finally { 2033 mServiceLock.readLock().unlock(); 2034 } 2035 return BluetoothStatusCodes.ERROR_UNKNOWN; 2036 } 2037 2038 /** 2039 * Get the end time of the latest remote device discovery process. 2040 * 2041 * @return the latest time that the bluetooth adapter was/will be in discovery mode, in 2042 * milliseconds since the epoch. This time can be in the future if {@link #startDiscovery()} has 2043 * been called recently. 2044 * @hide 2045 */ 2046 @SystemApi 2047 @RequiresBluetoothConnectPermission 2048 @RequiresPermission(allOf = { 2049 android.Manifest.permission.BLUETOOTH_CONNECT, 2050 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2051 }) getDiscoveryEndMillis()2052 public long getDiscoveryEndMillis() { 2053 mServiceLock.readLock().lock(); 2054 try { 2055 if (mService != null) { 2056 final SynchronousResultReceiver<Long> recv = SynchronousResultReceiver.get(); 2057 mService.getDiscoveryEndMillis(mAttributionSource, recv); 2058 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue((long) -1); 2059 } 2060 } catch (RemoteException | TimeoutException e) { 2061 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2062 } finally { 2063 mServiceLock.readLock().unlock(); 2064 } 2065 return -1; 2066 } 2067 2068 /** 2069 * Start the remote device discovery process. 2070 * <p>The discovery process usually involves an inquiry scan of about 12 2071 * seconds, followed by a page scan of each new device to retrieve its 2072 * Bluetooth name. 2073 * <p>This is an asynchronous call, it will return immediately. Register 2074 * for {@link #ACTION_DISCOVERY_STARTED} and {@link 2075 * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the 2076 * discovery starts and completes. Register for {@link 2077 * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices 2078 * are found. 2079 * <p>Device discovery is a heavyweight procedure. New connections to 2080 * remote Bluetooth devices should not be attempted while discovery is in 2081 * progress, and existing connections will experience limited bandwidth 2082 * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing 2083 * discovery. Discovery is not managed by the Activity, 2084 * but is run as a system service, so an application should always call 2085 * {@link BluetoothAdapter#cancelDiscovery()} even if it 2086 * did not directly request a discovery, just to be sure. 2087 * <p>Device discovery will only find remote devices that are currently 2088 * <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are 2089 * not discoverable by default, and need to be entered into a special mode. 2090 * <p>If Bluetooth state is not {@link #STATE_ON}, this API 2091 * will return false. After turning on Bluetooth, wait for {@link #ACTION_STATE_CHANGED} 2092 * with {@link #STATE_ON} to get the updated value. 2093 * <p>If a device is currently bonding, this request will be queued and executed once that 2094 * device has finished bonding. If a request is already queued, this request will be ignored. 2095 * 2096 * @return true on success, false on error 2097 */ 2098 @RequiresLegacyBluetoothAdminPermission 2099 @RequiresBluetoothScanPermission 2100 @RequiresBluetoothLocationPermission 2101 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) startDiscovery()2102 public boolean startDiscovery() { 2103 if (getState() != STATE_ON) { 2104 return false; 2105 } 2106 mServiceLock.readLock().lock(); 2107 try { 2108 if (mService != null) { 2109 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2110 mService.startDiscovery(mAttributionSource, recv); 2111 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2112 } 2113 } catch (RemoteException | TimeoutException e) { 2114 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2115 } finally { 2116 mServiceLock.readLock().unlock(); 2117 } 2118 return false; 2119 } 2120 2121 /** 2122 * Cancel the current device discovery process. 2123 * <p>Because discovery is a heavyweight procedure for the Bluetooth 2124 * adapter, this method should always be called before attempting to connect 2125 * to a remote device with {@link 2126 * android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by 2127 * the Activity, but is run as a system service, so an application should 2128 * always call cancel discovery even if it did not directly request a 2129 * discovery, just to be sure. 2130 * <p>If Bluetooth state is not {@link #STATE_ON}, this API 2131 * will return false. After turning on Bluetooth, 2132 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} 2133 * to get the updated value. 2134 * 2135 * @return true on success, false on error 2136 */ 2137 @RequiresLegacyBluetoothAdminPermission 2138 @RequiresBluetoothScanPermission 2139 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) cancelDiscovery()2140 public boolean cancelDiscovery() { 2141 if (getState() != STATE_ON) { 2142 return false; 2143 } 2144 mServiceLock.readLock().lock(); 2145 try { 2146 if (mService != null) { 2147 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2148 mService.cancelDiscovery(mAttributionSource, recv); 2149 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2150 } 2151 } catch (RemoteException | TimeoutException e) { 2152 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2153 } finally { 2154 mServiceLock.readLock().unlock(); 2155 } 2156 return false; 2157 } 2158 2159 /** 2160 * Return true if the local Bluetooth adapter is currently in the device 2161 * discovery process. 2162 * <p>Device discovery is a heavyweight procedure. New connections to 2163 * remote Bluetooth devices should not be attempted while discovery is in 2164 * progress, and existing connections will experience limited bandwidth 2165 * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing 2166 * discovery. 2167 * <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED} 2168 * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery 2169 * starts or completes. 2170 * <p>If Bluetooth state is not {@link #STATE_ON}, this API 2171 * will return false. After turning on Bluetooth, 2172 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} 2173 * to get the updated value. 2174 * 2175 * @return true if discovering 2176 */ 2177 @RequiresLegacyBluetoothPermission 2178 @RequiresBluetoothScanPermission 2179 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) isDiscovering()2180 public boolean isDiscovering() { 2181 if (getState() != STATE_ON) { 2182 return false; 2183 } 2184 mServiceLock.readLock().lock(); 2185 try { 2186 if (mService != null) { 2187 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2188 mService.isDiscovering(mAttributionSource, recv); 2189 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2190 } 2191 } catch (RemoteException | TimeoutException e) { 2192 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2193 } finally { 2194 mServiceLock.readLock().unlock(); 2195 } 2196 return false; 2197 } 2198 2199 /** 2200 * Removes the active device for the grouping of @ActiveDeviceUse specified 2201 * 2202 * @param profiles represents the purpose for which we are setting this as the active device. 2203 * Possible values are: 2204 * {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO}, 2205 * {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL}, 2206 * {@link BluetoothAdapter#ACTIVE_DEVICE_ALL} 2207 * @return false on immediate error, true otherwise 2208 * @throws IllegalArgumentException if device is null or profiles is not one of 2209 * {@link ActiveDeviceUse} 2210 * @hide 2211 */ 2212 @SystemApi 2213 @RequiresBluetoothConnectPermission 2214 @RequiresPermission(allOf = { 2215 android.Manifest.permission.BLUETOOTH_CONNECT, 2216 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2217 android.Manifest.permission.MODIFY_PHONE_STATE, 2218 }) removeActiveDevice(@ctiveDeviceUse int profiles)2219 public boolean removeActiveDevice(@ActiveDeviceUse int profiles) { 2220 if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL 2221 && profiles != ACTIVE_DEVICE_ALL) { 2222 Log.e(TAG, "Invalid profiles param value in removeActiveDevice"); 2223 throw new IllegalArgumentException("Profiles must be one of " 2224 + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, " 2225 + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or " 2226 + "BluetoothAdapter.ACTIVE_DEVICE_ALL"); 2227 } 2228 mServiceLock.readLock().lock(); 2229 try { 2230 if (mService != null) { 2231 if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles); 2232 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2233 mService.removeActiveDevice(profiles, mAttributionSource, recv); 2234 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2235 } 2236 } catch (RemoteException | TimeoutException e) { 2237 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2238 } finally { 2239 mServiceLock.readLock().unlock(); 2240 } 2241 2242 return false; 2243 } 2244 2245 /** 2246 * Sets device as the active devices for the use cases passed into the function. Note that in 2247 * order to make a device active for LE Audio, it must be the active device for audio and 2248 * phone calls. 2249 * 2250 * @param device is the remote bluetooth device 2251 * @param profiles represents the purpose for which we are setting this as the active device. 2252 * Possible values are: 2253 * {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO}, 2254 * {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL}, 2255 * {@link BluetoothAdapter#ACTIVE_DEVICE_ALL} 2256 * @return false on immediate error, true otherwise 2257 * @throws IllegalArgumentException if device is null or profiles is not one of 2258 * {@link ActiveDeviceUse} 2259 * @hide 2260 */ 2261 @SystemApi 2262 @RequiresBluetoothConnectPermission 2263 @RequiresPermission(allOf = { 2264 android.Manifest.permission.BLUETOOTH_CONNECT, 2265 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2266 android.Manifest.permission.MODIFY_PHONE_STATE, 2267 }) setActiveDevice(@onNull BluetoothDevice device, @ActiveDeviceUse int profiles)2268 public boolean setActiveDevice(@NonNull BluetoothDevice device, 2269 @ActiveDeviceUse int profiles) { 2270 if (device == null) { 2271 Log.e(TAG, "setActiveDevice: Null device passed as parameter"); 2272 throw new IllegalArgumentException("device cannot be null"); 2273 } 2274 if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL 2275 && profiles != ACTIVE_DEVICE_ALL) { 2276 Log.e(TAG, "Invalid profiles param value in setActiveDevice"); 2277 throw new IllegalArgumentException("Profiles must be one of " 2278 + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, " 2279 + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or " 2280 + "BluetoothAdapter.ACTIVE_DEVICE_ALL"); 2281 } 2282 mServiceLock.readLock().lock(); 2283 try { 2284 if (mService != null) { 2285 if (DBG) { 2286 Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles); 2287 } 2288 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2289 mService.setActiveDevice(device, profiles, mAttributionSource, recv); 2290 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2291 } 2292 } catch (RemoteException | TimeoutException e) { 2293 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2294 } finally { 2295 mServiceLock.readLock().unlock(); 2296 } 2297 2298 return false; 2299 } 2300 2301 /** 2302 * Get the active devices for the BluetoothProfile specified 2303 * 2304 * @param profile is the profile from which we want the active devices. 2305 * Possible values are: 2306 * {@link BluetoothProfile#HEADSET}, 2307 * {@link BluetoothProfile#A2DP}, 2308 * {@link BluetoothProfile#HEARING_AID} 2309 * {@link BluetoothProfile#LE_AUDIO} 2310 * @return A list of active bluetooth devices 2311 * @throws IllegalArgumentException If profile is not one of {@link ActiveDeviceProfile} 2312 * @hide 2313 */ 2314 @SystemApi 2315 @RequiresPermission(allOf = { 2316 android.Manifest.permission.BLUETOOTH_CONNECT, 2317 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2318 }) getActiveDevices(@ctiveDeviceProfile int profile)2319 public @NonNull List<BluetoothDevice> getActiveDevices(@ActiveDeviceProfile int profile) { 2320 if (profile != BluetoothProfile.HEADSET 2321 && profile != BluetoothProfile.A2DP 2322 && profile != BluetoothProfile.HEARING_AID 2323 && profile != BluetoothProfile.LE_AUDIO) { 2324 Log.e(TAG, "Invalid profile param value in getActiveDevices"); 2325 throw new IllegalArgumentException("Profiles must be one of " 2326 + "BluetoothProfile.A2DP, " 2327 + "BluetoothProfile.HEARING_AID, or" 2328 + "BluetoothProfile.HEARING_AID" 2329 + "BluetoothProfile.LE_AUDIO"); 2330 } 2331 mServiceLock.readLock().lock(); 2332 try { 2333 if (mService != null) { 2334 if (DBG) { 2335 Log.d(TAG, "getActiveDevices(profile= " 2336 + BluetoothProfile.getProfileName(profile) + ")"); 2337 } 2338 final SynchronousResultReceiver<List<BluetoothDevice>> recv = 2339 SynchronousResultReceiver.get(); 2340 mService.getActiveDevices(profile, mAttributionSource, recv); 2341 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(new ArrayList<>()); 2342 } 2343 } catch (RemoteException | TimeoutException e) { 2344 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2345 } finally { 2346 mServiceLock.readLock().unlock(); 2347 } 2348 2349 return new ArrayList<>(); 2350 } 2351 2352 /** 2353 * Return true if the multi advertisement is supported by the chipset 2354 * 2355 * @return true if Multiple Advertisement feature is supported 2356 */ 2357 @RequiresLegacyBluetoothPermission 2358 @RequiresNoPermission isMultipleAdvertisementSupported()2359 public boolean isMultipleAdvertisementSupported() { 2360 if (getState() != STATE_ON) { 2361 return false; 2362 } 2363 mServiceLock.readLock().lock(); 2364 try { 2365 if (mService != null) { 2366 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2367 mService.isMultiAdvertisementSupported(recv); 2368 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2369 } 2370 } catch (RemoteException | TimeoutException e) { 2371 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2372 } finally { 2373 mServiceLock.readLock().unlock(); 2374 } 2375 return false; 2376 } 2377 2378 /** 2379 * Returns {@code true} if BLE scan is always available, {@code false} otherwise. <p> 2380 * 2381 * If this returns {@code true}, application can issue {@link BluetoothLeScanner#startScan} and 2382 * fetch scan results even when Bluetooth is turned off.<p> 2383 * 2384 * To change this setting, use {@link #ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE}. 2385 * 2386 * @hide 2387 */ 2388 @SystemApi 2389 @RequiresNoPermission isBleScanAlwaysAvailable()2390 public boolean isBleScanAlwaysAvailable() { 2391 try { 2392 return mManagerService.isBleScanAlwaysAvailable(); 2393 } catch (RemoteException e) { 2394 Log.e(TAG, "remote exception when calling isBleScanAlwaysAvailable", e); 2395 return false; 2396 } 2397 } 2398 2399 private static final IpcDataCache.QueryHandler<IBluetooth, Boolean> sBluetoothFilteringQuery = 2400 new IpcDataCache.QueryHandler<>() { 2401 @RequiresLegacyBluetoothPermission 2402 @RequiresNoPermission 2403 @Override 2404 public Boolean apply(IBluetooth serviceQuery) { 2405 try { 2406 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2407 serviceQuery.isOffloadedFilteringSupported(recv); 2408 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2409 } catch (RemoteException | TimeoutException e) { 2410 throw new RuntimeException(e); 2411 } 2412 }}; 2413 2414 private static final String FILTERING_API = "BluetoothAdapter_isOffloadedFilteringSupported"; 2415 2416 private static final IpcDataCache<IBluetooth, Boolean> sBluetoothFilteringCache = 2417 new BluetoothCache<>(FILTERING_API, sBluetoothFilteringQuery); 2418 2419 /** @hide */ 2420 @RequiresNoPermission disableIsOffloadedFilteringSupportedCache()2421 public void disableIsOffloadedFilteringSupportedCache() { 2422 sBluetoothFilteringCache.disableForCurrentProcess(); 2423 } 2424 2425 /** @hide */ invalidateIsOffloadedFilteringSupportedCache()2426 public static void invalidateIsOffloadedFilteringSupportedCache() { 2427 invalidateCache(FILTERING_API); 2428 } 2429 2430 /** 2431 * Return true if offloaded filters are supported 2432 * 2433 * @return true if chipset supports on-chip filtering 2434 */ 2435 @RequiresLegacyBluetoothPermission 2436 @RequiresNoPermission isOffloadedFilteringSupported()2437 public boolean isOffloadedFilteringSupported() { 2438 if (!getLeAccess()) { 2439 return false; 2440 } 2441 mServiceLock.readLock().lock(); 2442 try { 2443 if (mService != null) return sBluetoothFilteringCache.query(mService); 2444 } catch (RuntimeException e) { 2445 if (!(e.getCause() instanceof TimeoutException) 2446 && !(e.getCause() instanceof RemoteException)) { 2447 throw e; 2448 } 2449 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2450 } finally { 2451 mServiceLock.readLock().unlock(); 2452 } 2453 return false; 2454 } 2455 2456 /** 2457 * Return true if offloaded scan batching is supported 2458 * 2459 * @return true if chipset supports on-chip scan batching 2460 */ 2461 @RequiresLegacyBluetoothPermission 2462 @RequiresNoPermission isOffloadedScanBatchingSupported()2463 public boolean isOffloadedScanBatchingSupported() { 2464 if (!getLeAccess()) { 2465 return false; 2466 } 2467 mServiceLock.readLock().lock(); 2468 try { 2469 if (mService != null) { 2470 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2471 mService.isOffloadedScanBatchingSupported(recv); 2472 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2473 } 2474 } catch (RemoteException | TimeoutException e) { 2475 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2476 } finally { 2477 mServiceLock.readLock().unlock(); 2478 } 2479 return false; 2480 } 2481 2482 /** 2483 * Return true if LE 2M PHY feature is supported. 2484 * 2485 * @return true if chipset supports LE 2M PHY feature 2486 */ 2487 @RequiresLegacyBluetoothPermission 2488 @RequiresNoPermission isLe2MPhySupported()2489 public boolean isLe2MPhySupported() { 2490 if (!getLeAccess()) { 2491 return false; 2492 } 2493 mServiceLock.readLock().lock(); 2494 try { 2495 if (mService != null) { 2496 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2497 mService.isLe2MPhySupported(recv); 2498 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2499 } 2500 } catch (RemoteException | TimeoutException e) { 2501 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2502 } finally { 2503 mServiceLock.readLock().unlock(); 2504 } 2505 return false; 2506 } 2507 2508 /** 2509 * Return true if LE Coded PHY feature is supported. 2510 * 2511 * @return true if chipset supports LE Coded PHY feature 2512 */ 2513 @RequiresLegacyBluetoothPermission 2514 @RequiresNoPermission isLeCodedPhySupported()2515 public boolean isLeCodedPhySupported() { 2516 if (!getLeAccess()) { 2517 return false; 2518 } 2519 mServiceLock.readLock().lock(); 2520 try { 2521 if (mService != null) { 2522 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2523 mService.isLeCodedPhySupported(recv); 2524 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2525 } 2526 } catch (RemoteException | TimeoutException e) { 2527 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2528 } finally { 2529 mServiceLock.readLock().unlock(); 2530 } 2531 return false; 2532 } 2533 2534 /** 2535 * Return true if LE Extended Advertising feature is supported. 2536 * 2537 * @return true if chipset supports LE Extended Advertising feature 2538 */ 2539 @RequiresLegacyBluetoothPermission 2540 @RequiresNoPermission isLeExtendedAdvertisingSupported()2541 public boolean isLeExtendedAdvertisingSupported() { 2542 if (!getLeAccess()) { 2543 return false; 2544 } 2545 mServiceLock.readLock().lock(); 2546 try { 2547 if (mService != null) { 2548 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2549 mService.isLeExtendedAdvertisingSupported(recv); 2550 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2551 } 2552 } catch (RemoteException | TimeoutException e) { 2553 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2554 } finally { 2555 mServiceLock.readLock().unlock(); 2556 } 2557 return false; 2558 } 2559 2560 /** 2561 * Return true if LE Periodic Advertising feature is supported. 2562 * 2563 * @return true if chipset supports LE Periodic Advertising feature 2564 */ 2565 @RequiresLegacyBluetoothPermission 2566 @RequiresNoPermission isLePeriodicAdvertisingSupported()2567 public boolean isLePeriodicAdvertisingSupported() { 2568 if (!getLeAccess()) { 2569 return false; 2570 } 2571 mServiceLock.readLock().lock(); 2572 try { 2573 if (mService != null) { 2574 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 2575 mService.isLePeriodicAdvertisingSupported(recv); 2576 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 2577 } 2578 } catch (RemoteException | TimeoutException e) { 2579 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2580 } finally { 2581 mServiceLock.readLock().unlock(); 2582 } 2583 return false; 2584 } 2585 2586 /** @hide */ 2587 @Retention(RetentionPolicy.SOURCE) 2588 @IntDef(value = { 2589 BluetoothStatusCodes.FEATURE_SUPPORTED, 2590 BluetoothStatusCodes.ERROR_UNKNOWN, 2591 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 2592 BluetoothStatusCodes.FEATURE_NOT_SUPPORTED, 2593 }) 2594 public @interface LeFeatureReturnValues {} 2595 2596 /** 2597 * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio feature is 2598 * supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not 2599 * supported, or an error code. 2600 * 2601 * @return whether the LE audio is supported 2602 * @throws IllegalStateException if the bluetooth service is null 2603 */ 2604 @RequiresNoPermission isLeAudioSupported()2605 public @LeFeatureReturnValues int isLeAudioSupported() { 2606 if (!getLeAccess()) { 2607 return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 2608 } 2609 mServiceLock.readLock().lock(); 2610 try { 2611 if (mService != null) { 2612 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2613 mService.isLeAudioSupported(recv); 2614 return recv.awaitResultNoInterrupt(getSyncTimeout()) 2615 .getValue(BluetoothStatusCodes.ERROR_UNKNOWN); 2616 } else { 2617 throw new IllegalStateException( 2618 "LE state is on, but there is no bluetooth service."); 2619 } 2620 } catch (TimeoutException e) { 2621 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2622 } catch (RemoteException e) { 2623 e.rethrowFromSystemServer(); 2624 } finally { 2625 mServiceLock.readLock().unlock(); 2626 } 2627 return BluetoothStatusCodes.ERROR_UNKNOWN; 2628 } 2629 2630 /** 2631 * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio broadcast source 2632 * feature is supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature 2633 * is not supported, or an error code. 2634 * 2635 * @return whether the LE audio broadcast source is supported 2636 * @throws IllegalStateException if the bluetooth service is null 2637 */ 2638 @RequiresNoPermission isLeAudioBroadcastSourceSupported()2639 public @LeFeatureReturnValues int isLeAudioBroadcastSourceSupported() { 2640 if (!getLeAccess()) { 2641 return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 2642 } 2643 mServiceLock.readLock().lock(); 2644 try { 2645 if (mService != null) { 2646 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2647 mService.isLeAudioBroadcastSourceSupported(recv); 2648 return recv.awaitResultNoInterrupt(getSyncTimeout()) 2649 .getValue(BluetoothStatusCodes.ERROR_UNKNOWN); 2650 } else { 2651 throw new IllegalStateException( 2652 "LE state is on, but there is no bluetooth service."); 2653 } 2654 } catch (TimeoutException e) { 2655 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2656 } catch (RemoteException e) { 2657 e.rethrowFromSystemServer(); 2658 } finally { 2659 mServiceLock.readLock().unlock(); 2660 } 2661 2662 return BluetoothStatusCodes.ERROR_UNKNOWN; 2663 } 2664 2665 /** 2666 * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio broadcast assistant 2667 * feature is supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is 2668 * not supported, or an error code. 2669 * 2670 * @return whether the LE audio broadcast assistent is supported 2671 * @throws IllegalStateException if the bluetooth service is null 2672 */ 2673 @RequiresNoPermission isLeAudioBroadcastAssistantSupported()2674 public @LeFeatureReturnValues int isLeAudioBroadcastAssistantSupported() { 2675 if (!getLeAccess()) { 2676 return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 2677 } 2678 mServiceLock.readLock().lock(); 2679 try { 2680 if (mService != null) { 2681 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2682 mService.isLeAudioBroadcastAssistantSupported(recv); 2683 return recv.awaitResultNoInterrupt(getSyncTimeout()) 2684 .getValue(BluetoothStatusCodes.ERROR_UNKNOWN); 2685 } else { 2686 throw new IllegalStateException( 2687 "LE state is on, but there is no bluetooth service."); 2688 } 2689 } catch (TimeoutException e) { 2690 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2691 } catch (RemoteException e) { 2692 e.rethrowFromSystemServer(); 2693 } finally { 2694 mServiceLock.readLock().unlock(); 2695 } 2696 return BluetoothStatusCodes.ERROR_UNKNOWN; 2697 } 2698 2699 /** 2700 * Returns whether the distance measurement feature is supported. 2701 * 2702 * @return whether the Bluetooth distance measurement is supported 2703 * @throws IllegalStateException if the bluetooth service is null 2704 * 2705 * @hide 2706 */ 2707 @SystemApi 2708 @RequiresPermission(allOf = { 2709 android.Manifest.permission.BLUETOOTH_CONNECT, 2710 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2711 }) isDistanceMeasurementSupported()2712 public @LeFeatureReturnValues int isDistanceMeasurementSupported() { 2713 if (!getLeAccess()) { 2714 return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 2715 } 2716 mServiceLock.readLock().lock(); 2717 try { 2718 if (mService != null) { 2719 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2720 mService.isDistanceMeasurementSupported(mAttributionSource, recv); 2721 return recv.awaitResultNoInterrupt(getSyncTimeout()) 2722 .getValue(BluetoothStatusCodes.ERROR_UNKNOWN); 2723 } else { 2724 throw new IllegalStateException( 2725 "LE state is on, but there is no bluetooth service."); 2726 } 2727 } catch (TimeoutException e) { 2728 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2729 } catch (RemoteException e) { 2730 e.rethrowFromSystemServer(); 2731 } finally { 2732 mServiceLock.readLock().unlock(); 2733 } 2734 return BluetoothStatusCodes.ERROR_UNKNOWN; 2735 } 2736 2737 /** 2738 * Return the maximum LE advertising data length in bytes, 2739 * if LE Extended Advertising feature is supported, 0 otherwise. 2740 * 2741 * @return the maximum LE advertising data length. 2742 */ 2743 @RequiresLegacyBluetoothPermission 2744 @RequiresNoPermission getLeMaximumAdvertisingDataLength()2745 public int getLeMaximumAdvertisingDataLength() { 2746 if (!getLeAccess()) { 2747 return 0; 2748 } 2749 mServiceLock.readLock().lock(); 2750 try { 2751 if (mService != null) { 2752 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2753 mService.getLeMaximumAdvertisingDataLength(recv); 2754 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(0); 2755 } 2756 } catch (RemoteException | TimeoutException e) { 2757 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2758 } finally { 2759 mServiceLock.readLock().unlock(); 2760 } 2761 return 0; 2762 } 2763 2764 /** 2765 * Return true if Hearing Aid Profile is supported. 2766 * 2767 * @return true if phone supports Hearing Aid Profile 2768 */ 2769 @RequiresNoPermission isHearingAidProfileSupported()2770 private boolean isHearingAidProfileSupported() { 2771 try { 2772 return mManagerService.isHearingAidProfileSupported(); 2773 } catch (RemoteException e) { 2774 Log.e(TAG, "remote exception when calling isHearingAidProfileSupported", e); 2775 return false; 2776 } 2777 } 2778 2779 /** 2780 * Get the maximum number of connected devices per audio profile for this device. 2781 * 2782 * @return the number of allowed simultaneous connected devices for each audio profile 2783 * for this device, or -1 if the Bluetooth service can't be reached 2784 */ 2785 @RequiresLegacyBluetoothPermission 2786 @RequiresBluetoothConnectPermission 2787 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getMaxConnectedAudioDevices()2788 public int getMaxConnectedAudioDevices() { 2789 mServiceLock.readLock().lock(); 2790 try { 2791 if (mService != null) { 2792 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2793 mService.getMaxConnectedAudioDevices(mAttributionSource, recv); 2794 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(1); 2795 } 2796 } catch (RemoteException | TimeoutException e) { 2797 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2798 } finally { 2799 mServiceLock.readLock().unlock(); 2800 } 2801 return -1; 2802 } 2803 2804 /** 2805 * Return true if hardware has entries available for matching beacons 2806 * 2807 * @return true if there are hw entries available for matching beacons 2808 * @hide 2809 */ 2810 @RequiresBluetoothConnectPermission 2811 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) isHardwareTrackingFiltersAvailable()2812 public boolean isHardwareTrackingFiltersAvailable() { 2813 if (!getLeAccess()) { 2814 return false; 2815 } 2816 try { 2817 IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); 2818 if (iGatt == null) { 2819 // BLE is not supported 2820 return false; 2821 } 2822 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 2823 iGatt.numHwTrackFiltersAvailable(mAttributionSource, recv); 2824 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(0) != 0; 2825 } catch (TimeoutException | RemoteException e) { 2826 Log.e(TAG, "", e); 2827 } 2828 return false; 2829 } 2830 2831 /** 2832 * Request the record of {@link BluetoothActivityEnergyInfo} object that 2833 * has the activity and energy info. This can be used to ascertain what 2834 * the controller has been up to, since the last sample. 2835 * 2836 * The callback will be called only once, when the record is available. 2837 * 2838 * @param executor the executor that the callback will be invoked on 2839 * @param callback the callback that will be called with either the 2840 * {@link BluetoothActivityEnergyInfo} object, or the 2841 * error code if an error has occurred 2842 * @hide 2843 */ 2844 @SystemApi 2845 @RequiresBluetoothConnectPermission 2846 @RequiresPermission(allOf = { 2847 android.Manifest.permission.BLUETOOTH_CONNECT, 2848 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2849 }) requestControllerActivityEnergyInfo( @onNull @allbackExecutor Executor executor, @NonNull OnBluetoothActivityEnergyInfoCallback callback)2850 public void requestControllerActivityEnergyInfo( 2851 @NonNull @CallbackExecutor Executor executor, 2852 @NonNull OnBluetoothActivityEnergyInfoCallback callback) { 2853 requireNonNull(executor, "executor cannot be null"); 2854 requireNonNull(callback, "callback cannot be null"); 2855 OnBluetoothActivityEnergyInfoProxy proxy = 2856 new OnBluetoothActivityEnergyInfoProxy(executor, callback); 2857 mServiceLock.readLock().lock(); 2858 try { 2859 if (mService != null) { 2860 mService.requestActivityInfo( 2861 proxy, 2862 mAttributionSource); 2863 } else { 2864 proxy.onError(BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND); 2865 } 2866 } catch (RemoteException e) { 2867 Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e); 2868 proxy.onError(BluetoothStatusCodes.ERROR_UNKNOWN); 2869 } finally { 2870 mServiceLock.readLock().unlock(); 2871 } 2872 } 2873 2874 /** 2875 * Fetches a list of the most recently connected bluetooth devices ordered by how recently they 2876 * were connected with most recently first and least recently last 2877 * 2878 * @return {@link List} of bonded {@link BluetoothDevice} ordered by how recently they were 2879 * connected 2880 * 2881 * @hide 2882 */ 2883 @SystemApi 2884 @RequiresLegacyBluetoothAdminPermission 2885 @RequiresBluetoothConnectPermission 2886 @RequiresPermission(allOf = { 2887 android.Manifest.permission.BLUETOOTH_CONNECT, 2888 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2889 }) getMostRecentlyConnectedDevices()2890 public @NonNull List<BluetoothDevice> getMostRecentlyConnectedDevices() { 2891 if (getState() != STATE_ON) { 2892 return new ArrayList<>(); 2893 } 2894 mServiceLock.readLock().lock(); 2895 try { 2896 if (mService != null) { 2897 final SynchronousResultReceiver<List<BluetoothDevice>> recv = 2898 SynchronousResultReceiver.get(); 2899 mService.getMostRecentlyConnectedDevices(mAttributionSource, recv); 2900 return Attributable.setAttributionSource( 2901 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(new ArrayList<>()), 2902 mAttributionSource); 2903 } 2904 } catch (RemoteException | TimeoutException e) { 2905 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2906 } finally { 2907 mServiceLock.readLock().unlock(); 2908 } 2909 return new ArrayList<>(); 2910 } 2911 2912 /** 2913 * Return the set of {@link BluetoothDevice} objects that are bonded 2914 * (paired) to the local adapter. 2915 * <p>If Bluetooth state is not {@link #STATE_ON}, this API 2916 * will return an empty set. After turning on Bluetooth, 2917 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} 2918 * to get the updated value. 2919 * 2920 * @return unmodifiable set of {@link BluetoothDevice}, or null on error 2921 */ 2922 @RequiresLegacyBluetoothPermission 2923 @RequiresBluetoothConnectPermission 2924 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getBondedDevices()2925 public Set<BluetoothDevice> getBondedDevices() { 2926 if (getState() != STATE_ON) { 2927 return toDeviceSet(Arrays.asList()); 2928 } 2929 mServiceLock.readLock().lock(); 2930 try { 2931 if (mService != null) { 2932 final SynchronousResultReceiver<List<BluetoothDevice>> recv = 2933 SynchronousResultReceiver.get(); 2934 mService.getBondedDevices(mAttributionSource, recv); 2935 return toDeviceSet(Attributable.setAttributionSource( 2936 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(new ArrayList<>()), 2937 mAttributionSource)); 2938 } 2939 return toDeviceSet(Arrays.asList()); 2940 } catch (RemoteException | TimeoutException e) { 2941 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2942 } finally { 2943 mServiceLock.readLock().unlock(); 2944 } 2945 return null; 2946 } 2947 2948 /** 2949 * Gets the currently supported profiles by the adapter. 2950 * 2951 * <p> This can be used to check whether a profile is supported before attempting 2952 * to connect to its respective proxy. 2953 * 2954 * @return a list of integers indicating the ids of supported profiles as defined in {@link 2955 * BluetoothProfile}. 2956 * @hide 2957 */ 2958 @SystemApi 2959 @RequiresBluetoothConnectPermission 2960 @RequiresPermission(allOf = { 2961 android.Manifest.permission.BLUETOOTH_CONNECT, 2962 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 2963 }) getSupportedProfiles()2964 public @NonNull List<Integer> getSupportedProfiles() { 2965 final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>(); 2966 2967 mServiceLock.readLock().lock(); 2968 try { 2969 if (mService != null) { 2970 final SynchronousResultReceiver<Long> recv = SynchronousResultReceiver.get(); 2971 mService.getSupportedProfiles(mAttributionSource, recv); 2972 final long supportedProfilesBitMask = 2973 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue((long) 0); 2974 2975 for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) { 2976 if ((supportedProfilesBitMask & (1 << i)) != 0) { 2977 supportedProfiles.add(i); 2978 } 2979 } 2980 } else { 2981 // Bluetooth is disabled. Just fill in known supported Profiles 2982 if (isHearingAidProfileSupported()) { 2983 supportedProfiles.add(BluetoothProfile.HEARING_AID); 2984 } 2985 } 2986 } catch (RemoteException | TimeoutException e) { 2987 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 2988 } finally { 2989 mServiceLock.readLock().unlock(); 2990 } 2991 return supportedProfiles; 2992 } 2993 2994 private static final IpcDataCache.QueryHandler<IBluetooth, Integer> 2995 sBluetoothGetAdapterConnectionStateQuery = new IpcDataCache.QueryHandler<>() { 2996 @RequiresLegacyBluetoothPermission 2997 @RequiresNoPermission 2998 @Override 2999 public Integer apply(IBluetooth serviceQuery) { 3000 try { 3001 final SynchronousResultReceiver<Integer> recv = 3002 SynchronousResultReceiver.get(); 3003 serviceQuery.getAdapterConnectionState(recv); 3004 return recv.awaitResultNoInterrupt(getSyncTimeout()) 3005 .getValue(STATE_DISCONNECTED); 3006 } catch (RemoteException | TimeoutException e) { 3007 throw new RuntimeException(e); 3008 } 3009 } 3010 }; 3011 3012 private static final String GET_CONNECTION_API = "BluetoothAdapter_getConnectionState"; 3013 3014 private static final IpcDataCache<IBluetooth, Integer> 3015 sBluetoothGetAdapterConnectionStateCache = new BluetoothCache<>(GET_CONNECTION_API, 3016 sBluetoothGetAdapterConnectionStateQuery); 3017 3018 /** @hide */ 3019 @RequiresNoPermission disableGetAdapterConnectionStateCache()3020 public void disableGetAdapterConnectionStateCache() { 3021 sBluetoothGetAdapterConnectionStateCache.disableForCurrentProcess(); 3022 } 3023 3024 /** @hide */ invalidateGetAdapterConnectionStateCache()3025 public static void invalidateGetAdapterConnectionStateCache() { 3026 invalidateCache(GET_CONNECTION_API); 3027 } 3028 3029 /** 3030 * Get the current connection state of the local Bluetooth adapter. 3031 * This can be used to check whether the local Bluetooth adapter is connected 3032 * to any profile of any other remote Bluetooth Device. 3033 * 3034 * <p> Use this function along with {@link #ACTION_CONNECTION_STATE_CHANGED} 3035 * intent to get the connection state of the adapter. 3036 * 3037 * @return the connection state 3038 * @hide 3039 */ 3040 @SystemApi 3041 @RequiresNoPermission getConnectionState()3042 public @ConnectionState int getConnectionState() { 3043 if (getState() != STATE_ON) { 3044 return BluetoothAdapter.STATE_DISCONNECTED; 3045 } 3046 mServiceLock.readLock().lock(); 3047 try { 3048 if (mService != null) return sBluetoothGetAdapterConnectionStateCache.query(mService); 3049 } catch (RuntimeException e) { 3050 if (!(e.getCause() instanceof TimeoutException) 3051 && !(e.getCause() instanceof RemoteException)) { 3052 throw e; 3053 } 3054 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 3055 } finally { 3056 mServiceLock.readLock().unlock(); 3057 } 3058 return STATE_DISCONNECTED; 3059 } 3060 3061 private static final IpcDataCache 3062 .QueryHandler<Pair<IBluetooth, Pair<AttributionSource, Integer>>, Integer> 3063 sBluetoothProfileQuery = new IpcDataCache.QueryHandler<>() { 3064 @RequiresNoPermission 3065 @Override 3066 public Integer apply(Pair<IBluetooth, Pair<AttributionSource, Integer>> pairQuery) { 3067 IBluetooth service = pairQuery.first; 3068 AttributionSource source = pairQuery.second.first; 3069 Integer profile = pairQuery.second.second; 3070 final int defaultValue = STATE_DISCONNECTED; 3071 try { 3072 final SynchronousResultReceiver<Integer> recv = 3073 SynchronousResultReceiver.get(); 3074 service.getProfileConnectionState(profile, source, recv); 3075 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 3076 } catch (RemoteException | TimeoutException e) { 3077 throw new RuntimeException(e); 3078 } 3079 } 3080 }; 3081 3082 private static final String PROFILE_API = "BluetoothAdapter_getProfileConnectionState"; 3083 3084 private static final IpcDataCache<Pair<IBluetooth, Pair<AttributionSource, Integer>>, Integer> 3085 sGetProfileConnectionStateCache = new BluetoothCache<>(PROFILE_API, 3086 sBluetoothProfileQuery); 3087 3088 /** 3089 * @hide 3090 */ 3091 @RequiresNoPermission disableGetProfileConnectionStateCache()3092 public void disableGetProfileConnectionStateCache() { 3093 sGetProfileConnectionStateCache.disableForCurrentProcess(); 3094 } 3095 3096 /** 3097 * @hide 3098 */ invalidateGetProfileConnectionStateCache()3099 public static void invalidateGetProfileConnectionStateCache() { 3100 invalidateCache(PROFILE_API); 3101 } 3102 3103 /** 3104 * Get the current connection state of a profile. 3105 * This function can be used to check whether the local Bluetooth adapter 3106 * is connected to any remote device for a specific profile. 3107 * Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. 3108 * 3109 * <p> Return the profile connection state 3110 */ 3111 @RequiresLegacyBluetoothPermission 3112 @RequiresBluetoothConnectPermission 3113 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getProfileConnectionState(int profile)3114 public @ConnectionState int getProfileConnectionState(int profile) { 3115 if (getState() != STATE_ON) { 3116 return STATE_DISCONNECTED; 3117 } 3118 mServiceLock.readLock().lock(); 3119 try { 3120 if (mService != null) { 3121 return sGetProfileConnectionStateCache.query( 3122 new Pair<>(mService, new Pair<>(mAttributionSource, profile))); 3123 } 3124 } catch (RuntimeException e) { 3125 if (!(e.getCause() instanceof TimeoutException) 3126 && !(e.getCause() instanceof RemoteException)) { 3127 throw e; 3128 } 3129 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 3130 } finally { 3131 mServiceLock.readLock().unlock(); 3132 } 3133 return STATE_DISCONNECTED; 3134 } 3135 3136 /** 3137 * Create a listening, secure RFCOMM Bluetooth socket. 3138 * <p>A remote device connecting to this socket will be authenticated and 3139 * communication on this socket will be encrypted. 3140 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming 3141 * connections from a listening {@link BluetoothServerSocket}. 3142 * <p>Valid RFCOMM channels are in range 1 to 30. 3143 * 3144 * @param channel RFCOMM channel to listen on 3145 * @return a listening RFCOMM BluetoothServerSocket 3146 * @throws IOException on error, for example Bluetooth not available, or insufficient 3147 * permissions, or channel in use. 3148 * @hide 3149 */ 3150 @RequiresLegacyBluetoothAdminPermission 3151 @RequiresBluetoothConnectPermission 3152 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) listenUsingRfcommOn(int channel)3153 public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { 3154 return listenUsingRfcommOn(channel, false, false); 3155 } 3156 3157 /** 3158 * Create a listening, secure RFCOMM Bluetooth socket. 3159 * <p>A remote device connecting to this socket will be authenticated and 3160 * communication on this socket will be encrypted. 3161 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming 3162 * connections from a listening {@link BluetoothServerSocket}. 3163 * <p>Valid RFCOMM channels are in range 1 to 30. 3164 * <p>To auto assign a channel without creating a SDP record use 3165 * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. 3166 * 3167 * @param channel RFCOMM channel to listen on 3168 * @param mitm enforce person-in-the-middle protection for authentication. 3169 * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 3170 * connections. 3171 * @return a listening RFCOMM BluetoothServerSocket 3172 * @throws IOException on error, for example Bluetooth not available, or insufficient 3173 * permissions, or channel in use. 3174 * @hide 3175 */ 3176 @UnsupportedAppUsage 3177 @RequiresLegacyBluetoothAdminPermission 3178 @RequiresBluetoothConnectPermission 3179 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) listenUsingRfcommOn(int channel, boolean mitm, boolean min16DigitPin)3180 public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm, 3181 boolean min16DigitPin) throws IOException { 3182 BluetoothServerSocket socket = 3183 new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm, 3184 min16DigitPin); 3185 int errno = socket.mSocket.bindListen(); 3186 if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { 3187 socket.setChannel(socket.mSocket.getPort()); 3188 } 3189 if (errno != 0) { 3190 //TODO(BT): Throw the same exception error code 3191 // that the previous code was using. 3192 //socket.mSocket.throwErrnoNative(errno); 3193 throw new IOException("Error: " + errno); 3194 } 3195 return socket; 3196 } 3197 3198 /** 3199 * Create a listening, secure RFCOMM Bluetooth socket with Service Record. 3200 * <p>A remote device connecting to this socket will be authenticated and 3201 * communication on this socket will be encrypted. 3202 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming 3203 * connections from a listening {@link BluetoothServerSocket}. 3204 * <p>The system will assign an unused RFCOMM channel to listen on. 3205 * <p>The system will also register a Service Discovery 3206 * Protocol (SDP) record with the local SDP server containing the specified 3207 * UUID, service name, and auto-assigned channel. Remote Bluetooth devices 3208 * can use the same UUID to query our SDP server and discover which channel 3209 * to connect to. This SDP record will be removed when this socket is 3210 * closed, or if this application closes unexpectedly. 3211 * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to 3212 * connect to this socket from another device using the same {@link UUID}. 3213 * 3214 * @param name service name for SDP record 3215 * @param uuid uuid for SDP record 3216 * @return a listening RFCOMM BluetoothServerSocket 3217 * @throws IOException on error, for example Bluetooth not available, or insufficient 3218 * permissions, or channel in use. 3219 */ 3220 @RequiresLegacyBluetoothPermission 3221 @RequiresBluetoothConnectPermission 3222 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) listenUsingRfcommWithServiceRecord(String name, UUID uuid)3223 public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) 3224 throws IOException { 3225 return createNewRfcommSocketAndRecord(name, uuid, true, true); 3226 } 3227 3228 /** 3229 * Requests the framework to start an RFCOMM socket server which listens based on the provided 3230 * {@code name} and {@code uuid}. 3231 * <p> 3232 * Incoming connections will cause the system to start the component described in the {@link 3233 * PendingIntent}, {@code pendingIntent}. After the component is started, it should obtain a 3234 * {@link BluetoothAdapter} and retrieve the {@link BluetoothSocket} via {@link 3235 * #retrieveConnectedRfcommSocket(UUID)}. 3236 * <p> 3237 * An application may register multiple RFCOMM listeners. It is recommended to set the extra 3238 * field {@link #EXTRA_RFCOMM_LISTENER_ID} to help determine which service record the incoming 3239 * {@link BluetoothSocket} is using. 3240 * <p> 3241 * The provided {@link PendingIntent} must be created with the {@link 3242 * PendingIntent#FLAG_IMMUTABLE} flag. 3243 * 3244 * @param name service name for SDP record 3245 * @param uuid uuid for SDP record 3246 * @param pendingIntent component which is called when a new RFCOMM connection is available 3247 * @return a status code from {@link BluetoothStatusCodes} 3248 * @throws IllegalArgumentException if {@code pendingIntent} is not created with the {@link 3249 * PendingIntent#FLAG_IMMUTABLE} flag. 3250 * @hide 3251 */ 3252 @SystemApi 3253 @RequiresBluetoothConnectPermission 3254 @RequiresPermission(allOf = { 3255 android.Manifest.permission.BLUETOOTH_CONNECT, 3256 android.Manifest.permission.BLUETOOTH_PRIVILEGED 3257 }) 3258 @RfcommListenerResult startRfcommServer(@onNull String name, @NonNull UUID uuid, @NonNull PendingIntent pendingIntent)3259 public int startRfcommServer(@NonNull String name, @NonNull UUID uuid, 3260 @NonNull PendingIntent pendingIntent) { 3261 if (!pendingIntent.isImmutable()) { 3262 throw new IllegalArgumentException("The provided PendingIntent is not immutable"); 3263 } 3264 mServiceLock.readLock().lock(); 3265 try { 3266 if (mService != null) { 3267 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 3268 mService.startRfcommListener( 3269 name, new ParcelUuid(uuid), pendingIntent, mAttributionSource, recv); 3270 return recv.awaitResultNoInterrupt(getSyncTimeout()) 3271 .getValue(BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND); 3272 } 3273 } catch (RemoteException | TimeoutException e) { 3274 Log.e(TAG, "Failed to transact RFCOMM listener start request", e); 3275 return BluetoothStatusCodes.ERROR_TIMEOUT; 3276 } finally { 3277 mServiceLock.readLock().unlock(); 3278 } 3279 return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; 3280 } 3281 3282 /** 3283 * Closes the RFCOMM socket server listening on the given SDP record name and UUID. This can be 3284 * called by applications after calling {@link #startRfcommServer(String, UUID, 3285 * PendingIntent)} to stop listening for incoming RFCOMM connections. 3286 * 3287 * @param uuid uuid for SDP record 3288 * @return a status code from {@link BluetoothStatusCodes} 3289 * @hide 3290 */ 3291 @SystemApi 3292 @RequiresBluetoothConnectPermission 3293 @RequiresPermission(allOf = { 3294 android.Manifest.permission.BLUETOOTH_CONNECT, 3295 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 3296 }) 3297 @RfcommListenerResult stopRfcommServer(@onNull UUID uuid)3298 public int stopRfcommServer(@NonNull UUID uuid) { 3299 mServiceLock.readLock().lock(); 3300 try { 3301 if (mService != null) { 3302 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 3303 mService.stopRfcommListener(new ParcelUuid(uuid), mAttributionSource, recv); 3304 return recv.awaitResultNoInterrupt(getSyncTimeout()) 3305 .getValue(BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND); 3306 } 3307 } catch (RemoteException | TimeoutException e) { 3308 Log.e(TAG, "Failed to transact RFCOMM listener stop request", e); 3309 return BluetoothStatusCodes.ERROR_TIMEOUT; 3310 } finally { 3311 mServiceLock.readLock().unlock(); 3312 } 3313 return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; 3314 } 3315 3316 /** 3317 * Retrieves a connected {@link BluetoothSocket} for the given service record from a RFCOMM 3318 * listener which was registered with {@link #startRfcommServer(String, UUID, PendingIntent)}. 3319 * <p> 3320 * This method should be called by the component started by the {@link PendingIntent} which was 3321 * registered during the call to {@link #startRfcommServer(String, UUID, PendingIntent)} in 3322 * order to retrieve the socket. 3323 * 3324 * @param uuid the same UUID used to register the listener previously 3325 * @return a connected {@link BluetoothSocket} or {@code null} if no socket is available 3326 * @throws IllegalStateException if the socket could not be retrieved because the application is 3327 * trying to obtain a socket for a listener it did not register (incorrect {@code 3328 * uuid}). 3329 * @hide 3330 */ 3331 @SystemApi 3332 @RequiresBluetoothConnectPermission 3333 @RequiresPermission(allOf = { 3334 android.Manifest.permission.BLUETOOTH_CONNECT, 3335 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 3336 }) retrieveConnectedRfcommSocket(@onNull UUID uuid)3337 public @NonNull BluetoothSocket retrieveConnectedRfcommSocket(@NonNull UUID uuid) { 3338 IncomingRfcommSocketInfo socketInfo = null; 3339 3340 mServiceLock.readLock().lock(); 3341 try { 3342 if (mService != null) { 3343 final SynchronousResultReceiver<IncomingRfcommSocketInfo> recv = 3344 SynchronousResultReceiver.get(); 3345 mService.retrievePendingSocketForServiceRecord(new ParcelUuid(uuid), 3346 mAttributionSource, recv); 3347 socketInfo = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 3348 } 3349 } catch (RemoteException | TimeoutException e) { 3350 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 3351 return null; 3352 } finally { 3353 mServiceLock.readLock().unlock(); 3354 } 3355 if (socketInfo == null) { 3356 return null; 3357 } 3358 3359 switch (socketInfo.status) { 3360 case BluetoothStatusCodes.SUCCESS: 3361 try { 3362 return BluetoothSocket.createSocketFromOpenFd( 3363 socketInfo.pfd, 3364 socketInfo.bluetoothDevice, 3365 new ParcelUuid(uuid)); 3366 } catch (IOException e) { 3367 return null; 3368 } 3369 case BluetoothStatusCodes.RFCOMM_LISTENER_OPERATION_FAILED_DIFFERENT_APP: 3370 throw new IllegalStateException( 3371 String.format( 3372 "RFCOMM listener for UUID %s was not registered by this app", 3373 uuid)); 3374 case BluetoothStatusCodes.RFCOMM_LISTENER_NO_SOCKET_AVAILABLE: 3375 return null; 3376 default: 3377 Log.e(TAG, 3378 String.format( 3379 "Unexpected result: (%d), from the adapter service while retrieving" 3380 + " an rfcomm socket", 3381 socketInfo.status)); 3382 return null; 3383 } 3384 } 3385 3386 /** 3387 * Create a listening, insecure RFCOMM Bluetooth socket with Service Record. 3388 * <p>The link key is not required to be authenticated, i.e the communication may be 3389 * vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices, 3390 * the link will be encrypted, as encryption is mandatory. 3391 * For legacy devices (pre Bluetooth 2.1 devices) the link will not 3392 * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an 3393 * encrypted and authenticated communication channel is desired. 3394 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming 3395 * connections from a listening {@link BluetoothServerSocket}. 3396 * <p>The system will assign an unused RFCOMM channel to listen on. 3397 * <p>The system will also register a Service Discovery 3398 * Protocol (SDP) record with the local SDP server containing the specified 3399 * UUID, service name, and auto-assigned channel. Remote Bluetooth devices 3400 * can use the same UUID to query our SDP server and discover which channel 3401 * to connect to. This SDP record will be removed when this socket is 3402 * closed, or if this application closes unexpectedly. 3403 * <p>Use {@link BluetoothDevice#createInsecureRfcommSocketToServiceRecord} to 3404 * connect to this socket from another device using the same {@link UUID}. 3405 * 3406 * @param name service name for SDP record 3407 * @param uuid uuid for SDP record 3408 * @return a listening RFCOMM BluetoothServerSocket 3409 * @throws IOException on error, for example Bluetooth not available, or insufficient 3410 * permissions, or channel in use. 3411 */ 3412 @RequiresLegacyBluetoothPermission 3413 @RequiresBluetoothConnectPermission 3414 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)3415 public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) 3416 throws IOException { 3417 return createNewRfcommSocketAndRecord(name, uuid, false, false); 3418 } 3419 3420 /** 3421 * Create a listening, encrypted, 3422 * RFCOMM Bluetooth socket with Service Record. 3423 * <p>The link will be encrypted, but the link key is not required to be authenticated 3424 * i.e the communication is vulnerable to Person In the Middle attacks. Use 3425 * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key. 3426 * <p> Use this socket if authentication of link key is not possible. 3427 * For example, for Bluetooth 2.1 devices, if any of the devices does not have 3428 * an input and output capability or just has the ability to display a numeric key, 3429 * a secure socket connection is not possible and this socket can be used. 3430 * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required. 3431 * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandatory. 3432 * For more details, refer to the Security Model section 5.2 (vol 3) of 3433 * Bluetooth Core Specification version 2.1 + EDR. 3434 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming 3435 * connections from a listening {@link BluetoothServerSocket}. 3436 * <p>The system will assign an unused RFCOMM channel to listen on. 3437 * <p>The system will also register a Service Discovery 3438 * Protocol (SDP) record with the local SDP server containing the specified 3439 * UUID, service name, and auto-assigned channel. Remote Bluetooth devices 3440 * can use the same UUID to query our SDP server and discover which channel 3441 * to connect to. This SDP record will be removed when this socket is 3442 * closed, or if this application closes unexpectedly. 3443 * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to 3444 * connect to this socket from another device using the same {@link UUID}. 3445 * 3446 * @param name service name for SDP record 3447 * @param uuid uuid for SDP record 3448 * @return a listening RFCOMM BluetoothServerSocket 3449 * @throws IOException on error, for example Bluetooth not available, or insufficient 3450 * permissions, or channel in use. 3451 * @hide 3452 */ 3453 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 3454 @RequiresLegacyBluetoothPermission 3455 @RequiresBluetoothConnectPermission 3456 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid)3457 public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) 3458 throws IOException { 3459 return createNewRfcommSocketAndRecord(name, uuid, false, true); 3460 } 3461 3462 @RequiresBluetoothConnectPermission 3463 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) createNewRfcommSocketAndRecord(String name, UUID uuid, boolean auth, boolean encrypt)3464 private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, 3465 boolean auth, boolean encrypt) throws IOException { 3466 BluetoothServerSocket socket; 3467 socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth, encrypt, 3468 new ParcelUuid(uuid)); 3469 socket.setServiceName(name); 3470 int errno = socket.mSocket.bindListen(); 3471 if (errno != 0) { 3472 //TODO(BT): Throw the same exception error code 3473 // that the previous code was using. 3474 //socket.mSocket.throwErrnoNative(errno); 3475 throw new IOException("Error: " + errno); 3476 } 3477 return socket; 3478 } 3479 3480 /** 3481 * Construct an unencrypted, unauthenticated, RFCOMM server socket. 3482 * Call #accept to retrieve connections to this socket. 3483 * 3484 * @return An RFCOMM BluetoothServerSocket 3485 * @throws IOException On error, for example Bluetooth not available, or insufficient 3486 * permissions. 3487 * @hide 3488 */ 3489 @RequiresBluetoothConnectPermission 3490 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) listenUsingInsecureRfcommOn(int port)3491 public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { 3492 BluetoothServerSocket socket = 3493 new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port); 3494 int errno = socket.mSocket.bindListen(); 3495 if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { 3496 socket.setChannel(socket.mSocket.getPort()); 3497 } 3498 if (errno != 0) { 3499 //TODO(BT): Throw the same exception error code 3500 // that the previous code was using. 3501 //socket.mSocket.throwErrnoNative(errno); 3502 throw new IOException("Error: " + errno); 3503 } 3504 return socket; 3505 } 3506 3507 /** 3508 * Construct an encrypted, authenticated, L2CAP server socket. 3509 * Call #accept to retrieve connections to this socket. 3510 * <p>To auto assign a port without creating a SDP record use 3511 * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. 3512 * 3513 * @param port the PSM to listen on 3514 * @param mitm enforce person-in-the-middle protection for authentication. 3515 * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 3516 * connections. 3517 * @return An L2CAP BluetoothServerSocket 3518 * @throws IOException On error, for example Bluetooth not available, or insufficient 3519 * permissions. 3520 * @hide 3521 */ 3522 @RequiresBluetoothConnectPermission 3523 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin)3524 public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin) 3525 throws IOException { 3526 BluetoothServerSocket socket = 3527 new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, true, true, port, mitm, 3528 min16DigitPin); 3529 int errno = socket.mSocket.bindListen(); 3530 if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { 3531 int assignedChannel = socket.mSocket.getPort(); 3532 if (DBG) Log.d(TAG, "listenUsingL2capOn: set assigned channel to " + assignedChannel); 3533 socket.setChannel(assignedChannel); 3534 } 3535 if (errno != 0) { 3536 //TODO(BT): Throw the same exception error code 3537 // that the previous code was using. 3538 //socket.mSocket.throwErrnoNative(errno); 3539 throw new IOException("Error: " + errno); 3540 } 3541 return socket; 3542 } 3543 3544 /** 3545 * Construct an encrypted, authenticated, L2CAP server socket. 3546 * Call #accept to retrieve connections to this socket. 3547 * <p>To auto assign a port without creating a SDP record use 3548 * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. 3549 * 3550 * @param port the PSM to listen on 3551 * @return An L2CAP BluetoothServerSocket 3552 * @throws IOException On error, for example Bluetooth not available, or insufficient 3553 * permissions. 3554 * @hide 3555 */ 3556 @RequiresBluetoothConnectPermission 3557 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) listenUsingL2capOn(int port)3558 public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { 3559 return listenUsingL2capOn(port, false, false); 3560 } 3561 3562 /** 3563 * Construct an insecure L2CAP server socket. 3564 * Call #accept to retrieve connections to this socket. 3565 * <p>To auto assign a port without creating a SDP record use 3566 * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. 3567 * 3568 * @param port the PSM to listen on 3569 * @return An L2CAP BluetoothServerSocket 3570 * @throws IOException On error, for example Bluetooth not available, or insufficient 3571 * permissions. 3572 * @hide 3573 */ 3574 @RequiresBluetoothConnectPermission 3575 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) listenUsingInsecureL2capOn(int port)3576 public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { 3577 Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port); 3578 BluetoothServerSocket socket = 3579 new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false, 3580 false); 3581 int errno = socket.mSocket.bindListen(); 3582 if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { 3583 int assignedChannel = socket.mSocket.getPort(); 3584 if (DBG) { 3585 Log.d(TAG, "listenUsingInsecureL2capOn: set assigned channel to " 3586 + assignedChannel); 3587 } 3588 socket.setChannel(assignedChannel); 3589 } 3590 if (errno != 0) { 3591 //TODO(BT): Throw the same exception error code 3592 // that the previous code was using. 3593 //socket.mSocket.throwErrnoNative(errno); 3594 throw new IOException("Error: " + errno); 3595 } 3596 return socket; 3597 3598 } 3599 3600 /** 3601 * Read the local Out of Band Pairing Data 3602 * 3603 * @return Pair<byte[], byte[]> of Hash and Randomizer 3604 * @hide 3605 */ 3606 @RequiresLegacyBluetoothPermission 3607 @RequiresBluetoothConnectPermission 3608 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 3609 @SuppressLint("AndroidFrameworkRequiresPermission") readOutOfBandData()3610 public Pair<byte[], byte[]> readOutOfBandData() { 3611 return null; 3612 } 3613 3614 /** 3615 * Get the profile proxy object associated with the profile. 3616 * 3617 * <p>Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}, 3618 * {@link BluetoothProfile#GATT}, {@link BluetoothProfile#HEARING_AID}, or {@link 3619 * BluetoothProfile#GATT_SERVER}. Clients must implement {@link 3620 * BluetoothProfile.ServiceListener} to get notified of the connection status and to get the 3621 * proxy object. 3622 * 3623 * @param context Context of the application 3624 * @param listener The service Listener for connection callbacks. 3625 * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET}, 3626 * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, {@link 3627 * BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#GATT_SERVER}. 3628 * @return true on success, false on error 3629 */ 3630 @SuppressLint({ 3631 "AndroidFrameworkRequiresPermission", 3632 "AndroidFrameworkBluetoothPermission" 3633 }) getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, int profile)3634 public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, 3635 int profile) { 3636 if (context == null || listener == null) { 3637 return false; 3638 } 3639 3640 if (profile == BluetoothProfile.HEADSET) { 3641 BluetoothHeadset headset = new BluetoothHeadset(context, listener, this); 3642 return true; 3643 } else if (profile == BluetoothProfile.A2DP) { 3644 BluetoothA2dp a2dp = new BluetoothA2dp(context, listener, this); 3645 return true; 3646 } else if (profile == BluetoothProfile.A2DP_SINK) { 3647 BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener, this); 3648 return true; 3649 } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) { 3650 BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener, this); 3651 return true; 3652 } else if (profile == BluetoothProfile.HID_HOST) { 3653 BluetoothHidHost iDev = new BluetoothHidHost(context, listener, this); 3654 return true; 3655 } else if (profile == BluetoothProfile.PAN) { 3656 BluetoothPan pan = new BluetoothPan(context, listener, this); 3657 return true; 3658 } else if (profile == BluetoothProfile.PBAP) { 3659 BluetoothPbap pbap = new BluetoothPbap(context, listener, this); 3660 return true; 3661 } else if (profile == BluetoothProfile.HEALTH) { 3662 Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated"); 3663 return false; 3664 } else if (profile == BluetoothProfile.MAP) { 3665 BluetoothMap map = new BluetoothMap(context, listener, this); 3666 return true; 3667 } else if (profile == BluetoothProfile.HEADSET_CLIENT) { 3668 BluetoothHeadsetClient headsetClient = 3669 new BluetoothHeadsetClient(context, listener, this); 3670 return true; 3671 } else if (profile == BluetoothProfile.SAP) { 3672 BluetoothSap sap = new BluetoothSap(context, listener, this); 3673 return true; 3674 } else if (profile == BluetoothProfile.PBAP_CLIENT) { 3675 BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener, this); 3676 return true; 3677 } else if (profile == BluetoothProfile.MAP_CLIENT) { 3678 BluetoothMapClient mapClient = new BluetoothMapClient(context, listener, this); 3679 return true; 3680 } else if (profile == BluetoothProfile.HID_DEVICE) { 3681 BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this); 3682 return true; 3683 } else if (profile == BluetoothProfile.HAP_CLIENT) { 3684 BluetoothHapClient HapClient = new BluetoothHapClient(context, listener); 3685 return true; 3686 } else if (profile == BluetoothProfile.HEARING_AID) { 3687 if (isHearingAidProfileSupported()) { 3688 BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this); 3689 return true; 3690 } 3691 return false; 3692 } else if (profile == BluetoothProfile.LE_AUDIO) { 3693 BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this); 3694 return true; 3695 } else if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) { 3696 BluetoothLeBroadcast leAudio = new BluetoothLeBroadcast(context, listener); 3697 return true; 3698 } else if (profile == BluetoothProfile.VOLUME_CONTROL) { 3699 BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this); 3700 return true; 3701 } else if (profile == BluetoothProfile.CSIP_SET_COORDINATOR) { 3702 BluetoothCsipSetCoordinator csipSetCoordinator = 3703 new BluetoothCsipSetCoordinator(context, listener, this); 3704 return true; 3705 } else if (profile == BluetoothProfile.LE_CALL_CONTROL) { 3706 BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener); 3707 return true; 3708 } else if (profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) { 3709 BluetoothLeBroadcastAssistant leAudioBroadcastAssistant = 3710 new BluetoothLeBroadcastAssistant(context, listener); 3711 return true; 3712 } else { 3713 return false; 3714 } 3715 } 3716 3717 /** 3718 * Close the connection of the profile proxy to the Service. 3719 * 3720 * <p>Clients should call this when they are no longer using the proxy obtained from {@link 3721 * #getProfileProxy}. Profile can be one of {@link BluetoothProfile#HEADSET} or {@link 3722 * BluetoothProfile#A2DP} 3723 * 3724 * @param unusedProfile 3725 * @param proxy Profile proxy object 3726 */ 3727 @SuppressLint({"AndroidFrameworkRequiresPermission", "AndroidFrameworkBluetoothPermission"}) closeProfileProxy(int unusedProfile, BluetoothProfile proxy)3728 public void closeProfileProxy(int unusedProfile, BluetoothProfile proxy) { 3729 if (proxy == null) { 3730 return; 3731 } 3732 proxy.close(); 3733 } 3734 3735 private static final IBluetoothManagerCallback sManagerCallback = 3736 new IBluetoothManagerCallback.Stub() { 3737 public void onBluetoothServiceUp(IBluetooth bluetoothService) { 3738 if (DBG) { 3739 Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); 3740 } 3741 3742 synchronized (sServiceLock) { 3743 sService = bluetoothService; 3744 for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { 3745 try { 3746 if (cb != null) { 3747 cb.onBluetoothServiceUp(bluetoothService); 3748 } else { 3749 Log.d(TAG, "onBluetoothServiceUp: cb is null!"); 3750 } 3751 } catch (Exception e) { 3752 Log.e(TAG, "", e); 3753 } 3754 } 3755 } 3756 } 3757 3758 public void onBluetoothServiceDown() { 3759 if (DBG) { 3760 Log.d(TAG, "onBluetoothServiceDown"); 3761 } 3762 3763 synchronized (sServiceLock) { 3764 sService = null; 3765 for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { 3766 try { 3767 if (cb != null) { 3768 cb.onBluetoothServiceDown(); 3769 } else { 3770 Log.d(TAG, "onBluetoothServiceDown: cb is null!"); 3771 } 3772 } catch (Exception e) { 3773 Log.e(TAG, "", e); 3774 } 3775 } 3776 } 3777 } 3778 3779 public void onBrEdrDown() { 3780 if (VDBG) { 3781 Log.i(TAG, "onBrEdrDown"); 3782 } 3783 3784 synchronized (sServiceLock) { 3785 for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { 3786 try { 3787 if (cb != null) { 3788 cb.onBrEdrDown(); 3789 } else { 3790 Log.d(TAG, "onBrEdrDown: cb is null!"); 3791 } 3792 } catch (Exception e) { 3793 Log.e(TAG, "", e); 3794 } 3795 } 3796 } 3797 } 3798 }; 3799 3800 private final IBluetoothManagerCallback mManagerCallback = 3801 new IBluetoothManagerCallback.Stub() { 3802 public void onBluetoothServiceUp(@NonNull IBluetooth bluetoothService) { 3803 requireNonNull(bluetoothService, "bluetoothService cannot be null"); 3804 mServiceLock.writeLock().lock(); 3805 try { 3806 mService = bluetoothService; 3807 } finally { 3808 // lock downgrade is possible in ReentrantReadWriteLock 3809 mServiceLock.readLock().lock(); 3810 mServiceLock.writeLock().unlock(); 3811 } 3812 try { 3813 synchronized (mMetadataListeners) { 3814 mMetadataListeners.forEach((device, pair) -> { 3815 try { 3816 final SynchronousResultReceiver recv = 3817 SynchronousResultReceiver.get(); 3818 mService.registerMetadataListener(mBluetoothMetadataListener, 3819 device, mAttributionSource, recv); 3820 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 3821 } catch (RemoteException | TimeoutException e) { 3822 Log.e(TAG, "Failed to register metadata listener", e); 3823 Log.e(TAG, e.toString() + "\n" 3824 + Log.getStackTraceString(new Throwable())); 3825 } 3826 }); 3827 } 3828 synchronized (mAudioProfilesChangedCallbackExecutorMap) { 3829 if (!mAudioProfilesChangedCallbackExecutorMap.isEmpty()) { 3830 try { 3831 final SynchronousResultReceiver recv = 3832 SynchronousResultReceiver.get(); 3833 mService.registerPreferredAudioProfilesChangedCallback( 3834 mPreferredAudioProfilesChangedCallback, 3835 mAttributionSource, recv); 3836 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue( 3837 BluetoothStatusCodes.ERROR_UNKNOWN); 3838 } catch (RemoteException | TimeoutException e) { 3839 Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth" 3840 + "connection callback", e); 3841 } 3842 } 3843 } 3844 synchronized (mBluetoothQualityReportReadyCallbackExecutorMap) { 3845 if (!mBluetoothQualityReportReadyCallbackExecutorMap.isEmpty()) { 3846 try { 3847 final SynchronousResultReceiver recv = 3848 SynchronousResultReceiver.get(); 3849 mService.registerBluetoothQualityReportReadyCallback( 3850 mBluetoothQualityReportReadyCallback, 3851 mAttributionSource, recv); 3852 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue( 3853 BluetoothStatusCodes.ERROR_UNKNOWN); 3854 } catch (RemoteException | TimeoutException e) { 3855 Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth" 3856 + "quality report callback", e); 3857 } 3858 } 3859 } 3860 synchronized (mBluetoothConnectionCallbackExecutorMap) { 3861 if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { 3862 try { 3863 final SynchronousResultReceiver recv = 3864 SynchronousResultReceiver.get(); 3865 mService.registerBluetoothConnectionCallback( 3866 mConnectionCallback, 3867 mAttributionSource, recv); 3868 recv.awaitResultNoInterrupt(getSyncTimeout()) 3869 .getValue(null); 3870 } catch (RemoteException | TimeoutException e) { 3871 Log.e(TAG, "onBluetoothServiceUp: Failed to register " 3872 + "bluetooth connection callback", e); 3873 } 3874 } 3875 } 3876 } finally { 3877 mServiceLock.readLock().unlock(); 3878 } 3879 } 3880 3881 public void onBluetoothServiceDown() { 3882 mServiceLock.writeLock().lock(); 3883 try { 3884 mService = null; 3885 if (mLeScanClients != null) { 3886 mLeScanClients.clear(); 3887 } 3888 if (mBluetoothLeAdvertiser != null) { 3889 mBluetoothLeAdvertiser.cleanup(); 3890 } 3891 if (mBluetoothLeScanner != null) { 3892 mBluetoothLeScanner.cleanup(); 3893 } 3894 } finally { 3895 mServiceLock.writeLock().unlock(); 3896 } 3897 } 3898 3899 public void onBrEdrDown() { 3900 } 3901 }; 3902 3903 /** 3904 * Enable the Bluetooth Adapter, but don't auto-connect devices 3905 * and don't persist state. Only for use by system applications. 3906 * 3907 * @hide 3908 */ 3909 @SystemApi 3910 @RequiresLegacyBluetoothAdminPermission 3911 @RequiresBluetoothConnectPermission 3912 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) enableNoAutoConnect()3913 public boolean enableNoAutoConnect() { 3914 if (isEnabled()) { 3915 if (DBG) { 3916 Log.d(TAG, "enableNoAutoConnect(): BT already enabled!"); 3917 } 3918 return true; 3919 } 3920 try { 3921 return mManagerService.enableNoAutoConnect(mAttributionSource); 3922 } catch (RemoteException e) { 3923 Log.e(TAG, "", e); 3924 } 3925 return false; 3926 } 3927 3928 /** @hide */ 3929 @Retention(RetentionPolicy.SOURCE) 3930 @IntDef(value = { 3931 BluetoothStatusCodes.ERROR_UNKNOWN, 3932 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 3933 BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST, 3934 }) 3935 public @interface OobError {} 3936 3937 /** 3938 * Provides callback methods for receiving {@link OobData} from the host stack, as well as an 3939 * error interface in order to allow the caller to determine next steps based on the {@code 3940 * ErrorCode}. 3941 * 3942 * @hide 3943 */ 3944 @SystemApi 3945 public interface OobDataCallback { 3946 /** 3947 * Handles the {@link OobData} received from the host stack. 3948 * 3949 * @param transport - whether the {@link OobData} is generated for LE or Classic. 3950 * @param oobData - data generated in the host stack(LE) or controller (Classic) 3951 */ onOobData(@ransport int transport, @NonNull OobData oobData)3952 void onOobData(@Transport int transport, @NonNull OobData oobData); 3953 3954 /** 3955 * Provides feedback when things don't go as expected. 3956 * 3957 * @param errorCode - the code describing the type of error that occurred. 3958 */ onError(@obError int errorCode)3959 void onError(@OobError int errorCode); 3960 } 3961 3962 /** 3963 * Wraps an AIDL interface around an {@link OobDataCallback} interface. 3964 * 3965 * @see {@link IBluetoothOobDataCallback} for interface definition. 3966 * 3967 * @hide 3968 */ 3969 public class WrappedOobDataCallback extends IBluetoothOobDataCallback.Stub { 3970 private final OobDataCallback mCallback; 3971 private final Executor mExecutor; 3972 3973 /** 3974 * @param callback - object to receive {@link OobData} must be a non null argument 3975 * 3976 * @throws NullPointerException if the callback is null. 3977 */ WrappedOobDataCallback(@onNull OobDataCallback callback, @NonNull @CallbackExecutor Executor executor)3978 WrappedOobDataCallback(@NonNull OobDataCallback callback, 3979 @NonNull @CallbackExecutor Executor executor) { 3980 requireNonNull(callback); 3981 requireNonNull(executor); 3982 mCallback = callback; 3983 mExecutor = executor; 3984 } 3985 /** 3986 * Wrapper function to relay to the {@link OobDataCallback#onOobData} 3987 * 3988 * @param transport - whether the {@link OobData} is generated for LE or Classic. 3989 * @param oobData - data generated in the host stack(LE) or controller (Classic) 3990 * 3991 * @hide 3992 */ onOobData(@ransport int transport, @NonNull OobData oobData)3993 public void onOobData(@Transport int transport, @NonNull OobData oobData) { 3994 mExecutor.execute(new Runnable() { 3995 public void run() { 3996 mCallback.onOobData(transport, oobData); 3997 } 3998 }); 3999 } 4000 /** 4001 * Wrapper function to relay to the {@link OobDataCallback#onError} 4002 * 4003 * @param errorCode - the code descibing the type of error that occurred. 4004 * 4005 * @hide 4006 */ onError(@obError int errorCode)4007 public void onError(@OobError int errorCode) { 4008 mExecutor.execute(new Runnable() { 4009 public void run() { 4010 mCallback.onError(errorCode); 4011 } 4012 }); 4013 } 4014 } 4015 4016 /** 4017 * Fetches a secret data value that can be used for a secure and simple pairing experience. 4018 * 4019 * <p>This is the Local Out of Band data the comes from the 4020 * 4021 * <p>This secret is the local Out of Band data. This data is used to securely and quickly 4022 * pair two devices with minimal user interaction. 4023 * 4024 * <p>For example, this secret can be transferred to a remote device out of band (meaning any 4025 * other way besides using bluetooth). Once the remote device finds this device using the 4026 * information given in the data, such as the PUBLIC ADDRESS, the remote device could then 4027 * connect to this device using this secret when the pairing sequenece asks for the secret. 4028 * This device will respond by automatically accepting the pairing due to the secret being so 4029 * trustworthy. 4030 * 4031 * @param transport - provide type of transport (e.g. LE or Classic). 4032 * @param callback - target object to receive the {@link OobData} value. 4033 * 4034 * @throws NullPointerException if callback is null. 4035 * @throws IllegalArgumentException if the transport is not valid. 4036 * 4037 * @hide 4038 */ 4039 @SystemApi 4040 @RequiresBluetoothConnectPermission 4041 @RequiresPermission(allOf = { 4042 android.Manifest.permission.BLUETOOTH_CONNECT, 4043 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 4044 }) generateLocalOobData(@ransport int transport, @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback)4045 public void generateLocalOobData(@Transport int transport, 4046 @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) { 4047 if (transport != BluetoothDevice.TRANSPORT_BREDR && transport 4048 != BluetoothDevice.TRANSPORT_LE) { 4049 throw new IllegalArgumentException("Invalid transport '" + transport + "'!"); 4050 } 4051 requireNonNull(callback); 4052 if (!isEnabled()) { 4053 Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!"); 4054 callback.onError(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED); 4055 } else { 4056 mServiceLock.readLock().lock(); 4057 try { 4058 if (mService != null) { 4059 final SynchronousResultReceiver recv = SynchronousResultReceiver.get(); 4060 mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback, 4061 executor), mAttributionSource, recv); 4062 recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); 4063 } 4064 } catch (RemoteException | TimeoutException e) { 4065 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 4066 } finally { 4067 mServiceLock.readLock().unlock(); 4068 } 4069 } 4070 } 4071 4072 /** 4073 * Enable control of the Bluetooth Adapter for a single application. 4074 * 4075 * <p>Some applications need to use Bluetooth for short periods of time to 4076 * transfer data but don't want all the associated implications like 4077 * automatic connection to headsets etc. 4078 * 4079 * <p> Multiple applications can call this. This is reference counted and 4080 * Bluetooth disabled only when no one else is using it. There will be no UI 4081 * shown to the user while bluetooth is being enabled. Any user action will 4082 * override this call. For example, if user wants Bluetooth on and the last 4083 * user of this API wanted to disable Bluetooth, Bluetooth will not be 4084 * turned off. 4085 * 4086 * <p> This API is only meant to be used by internal applications. Third 4087 * party applications but use {@link #enable} and {@link #disable} APIs. 4088 * 4089 * <p> If this API returns true, it means the callback will be called. 4090 * The callback will be called with the current state of Bluetooth. 4091 * If the state is not what was requested, an internal error would be the 4092 * reason. If Bluetooth is already on and if this function is called to turn 4093 * it on, the api will return true and a callback will be called. 4094 * 4095 * @param on True for on, false for off. 4096 * @param callback The callback to notify changes to the state. 4097 * @hide 4098 */ 4099 @RequiresLegacyBluetoothPermission 4100 @RequiresBluetoothConnectPermission 4101 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 4102 @SuppressLint("AndroidFrameworkRequiresPermission") changeApplicationBluetoothState(boolean on, BluetoothStateChangeCallback callback)4103 public boolean changeApplicationBluetoothState(boolean on, 4104 BluetoothStateChangeCallback callback) { 4105 return false; 4106 } 4107 4108 /** 4109 * @hide 4110 */ 4111 public interface BluetoothStateChangeCallback { 4112 /** 4113 * @hide 4114 */ onBluetoothStateChange(boolean on)4115 void onBluetoothStateChange(boolean on); 4116 } 4117 4118 /** 4119 * @hide 4120 */ 4121 public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub { 4122 private BluetoothStateChangeCallback mCallback; 4123 StateChangeCallbackWrapper(BluetoothStateChangeCallback callback)4124 StateChangeCallbackWrapper(BluetoothStateChangeCallback callback) { 4125 mCallback = callback; 4126 } 4127 4128 @Override onBluetoothStateChange(boolean on)4129 public void onBluetoothStateChange(boolean on) { 4130 mCallback.onBluetoothStateChange(on); 4131 } 4132 } 4133 toDeviceSet(List<BluetoothDevice> devices)4134 private Set<BluetoothDevice> toDeviceSet(List<BluetoothDevice> devices) { 4135 Set<BluetoothDevice> deviceSet = new HashSet<BluetoothDevice>(devices); 4136 return Collections.unmodifiableSet(deviceSet); 4137 } 4138 4139 @SuppressLint("GenericException") finalize()4140 protected void finalize() throws Throwable { 4141 try { 4142 removeServiceStateCallback(mManagerCallback); 4143 } finally { 4144 super.finalize(); 4145 } 4146 } 4147 4148 /** 4149 * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0" 4150 * <p>Alphabetic characters must be uppercase to be valid. 4151 * 4152 * @param address Bluetooth address as string 4153 * @return true if the address is valid, false otherwise 4154 */ checkBluetoothAddress(String address)4155 public static boolean checkBluetoothAddress(String address) { 4156 if (address == null || address.length() != ADDRESS_LENGTH) { 4157 return false; 4158 } 4159 for (int i = 0; i < ADDRESS_LENGTH; i++) { 4160 char c = address.charAt(i); 4161 switch (i % 3) { 4162 case 0: 4163 case 1: 4164 if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { 4165 // hex character, OK 4166 break; 4167 } 4168 return false; 4169 case 2: 4170 if (c == ':') { 4171 break; // OK 4172 } 4173 return false; 4174 } 4175 } 4176 return true; 4177 } 4178 4179 /** 4180 * Determines whether a String Bluetooth address, such as "F0:43:A8:23:10:00" 4181 * is a RANDOM STATIC address. 4182 * 4183 * RANDOM STATIC: (addr & 0xC0) == 0xC0 4184 * RANDOM RESOLVABLE: (addr & 0xC0) == 0x40 4185 * RANDOM non-RESOLVABLE: (addr & 0xC0) == 0x00 4186 * 4187 * @param address Bluetooth address as string 4188 * @return true if the 2 Most Significant Bits of the address equals 0xC0. 4189 * 4190 * @hide 4191 */ isAddressRandomStatic(@onNull String address)4192 public static boolean isAddressRandomStatic(@NonNull String address) { 4193 requireNonNull(address); 4194 return checkBluetoothAddress(address) 4195 && (Integer.parseInt(address.split(":")[0], 16) & 0xC0) == 0xC0; 4196 } 4197 4198 /** {@hide} */ 4199 @UnsupportedAppUsage 4200 @RequiresNoPermission getBluetoothManager()4201 public IBluetoothManager getBluetoothManager() { 4202 return mManagerService; 4203 } 4204 4205 /** {@hide} */ 4206 @RequiresNoPermission getAttributionSource()4207 public AttributionSource getAttributionSource() { 4208 return mAttributionSource; 4209 } 4210 4211 @GuardedBy("sServiceLock") 4212 private static final WeakHashMap<IBluetoothManagerCallback, Void> sProxyServiceStateCallbacks = 4213 new WeakHashMap<>(); 4214 getBluetoothService()4215 /*package*/ IBluetooth getBluetoothService() { 4216 synchronized (sServiceLock) { 4217 return sService; 4218 } 4219 } 4220 4221 /** 4222 * Registers a IBluetoothManagerCallback and returns the cached 4223 * Bluetooth service proxy object. 4224 * 4225 * TODO: rename this API to registerBlueoothManagerCallback or something? 4226 * the current name does not match what it does very well. 4227 * 4228 * / 4229 @UnsupportedAppUsage getBluetoothService(IBluetoothManagerCallback cb)4230 /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { 4231 requireNonNull(cb); 4232 synchronized (sServiceLock) { 4233 sProxyServiceStateCallbacks.put(cb, null); 4234 registerOrUnregisterAdapterLocked(); 4235 return sService; 4236 } 4237 } 4238 removeServiceStateCallback(IBluetoothManagerCallback cb)4239 /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) { 4240 requireNonNull(cb); 4241 synchronized (sServiceLock) { 4242 sProxyServiceStateCallbacks.remove(cb); 4243 registerOrUnregisterAdapterLocked(); 4244 } 4245 } 4246 4247 /** 4248 * Handle registering (or unregistering) a single process-wide 4249 * {@link IBluetoothManagerCallback} based on the presence of local 4250 * {@link #sProxyServiceStateCallbacks} clients. 4251 */ 4252 @GuardedBy("sServiceLock") registerOrUnregisterAdapterLocked()4253 private void registerOrUnregisterAdapterLocked() { 4254 final boolean isRegistered = sServiceRegistered; 4255 final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty(); 4256 4257 if (isRegistered != wantRegistered) { 4258 if (wantRegistered) { 4259 try { 4260 sService = mManagerService.registerAdapter(sManagerCallback); 4261 } catch (RemoteException e) { 4262 throw e.rethrowFromSystemServer(); 4263 } 4264 } else { 4265 try { 4266 mManagerService.unregisterAdapter(sManagerCallback); 4267 sService = null; 4268 } catch (RemoteException e) { 4269 throw e.rethrowFromSystemServer(); 4270 } 4271 } 4272 sServiceRegistered = wantRegistered; 4273 } 4274 } 4275 4276 /** 4277 * Callback interface used to deliver LE scan results. 4278 * 4279 * @see #startLeScan(LeScanCallback) 4280 * @see #startLeScan(UUID[], LeScanCallback) 4281 */ 4282 public interface LeScanCallback { 4283 /** 4284 * Callback reporting an LE device found during a device scan initiated 4285 * by the {@link BluetoothAdapter#startLeScan} function. 4286 * 4287 * @param device Identifies the remote device 4288 * @param rssi The RSSI value for the remote device as reported by the Bluetooth hardware. 0 4289 * if no RSSI value is available. 4290 * @param scanRecord The content of the advertisement record offered by the remote device. 4291 */ onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)4292 void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord); 4293 } 4294 4295 /** 4296 * Register a callback to receive events whenever the bluetooth stack goes down and back up, 4297 * e.g. in the event the bluetooth is turned off/on via settings. 4298 * 4299 * If the bluetooth stack is currently up, there will not be an initial callback call. 4300 * You can use the return value as an indication of this being the case. 4301 * 4302 * Callbacks will be delivered on a binder thread. 4303 * 4304 * @return whether bluetooth is already up currently 4305 * 4306 * @hide 4307 */ 4308 @RequiresNoPermission registerServiceLifecycleCallback(@onNull ServiceLifecycleCallback callback)4309 public boolean registerServiceLifecycleCallback(@NonNull ServiceLifecycleCallback callback) { 4310 return getBluetoothService(callback.mRemote) != null; 4311 } 4312 4313 /** 4314 * Unregister a callback registered via {@link #registerServiceLifecycleCallback} 4315 * 4316 * @hide 4317 */ 4318 @RequiresNoPermission unregisterServiceLifecycleCallback(@onNull ServiceLifecycleCallback callback)4319 public void unregisterServiceLifecycleCallback(@NonNull ServiceLifecycleCallback callback) { 4320 removeServiceStateCallback(callback.mRemote); 4321 } 4322 4323 /** 4324 * A callback for {@link #registerServiceLifecycleCallback} 4325 * 4326 * @hide 4327 */ 4328 public abstract static class ServiceLifecycleCallback { 4329 4330 /** Called when the bluetooth stack is up */ onBluetoothServiceUp()4331 public abstract void onBluetoothServiceUp(); 4332 4333 /** Called when the bluetooth stack is down */ onBluetoothServiceDown()4334 public abstract void onBluetoothServiceDown(); 4335 4336 IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() { 4337 @Override 4338 public void onBluetoothServiceUp(IBluetooth bluetoothService) { 4339 ServiceLifecycleCallback.this.onBluetoothServiceUp(); 4340 } 4341 4342 @Override 4343 public void onBluetoothServiceDown() { 4344 ServiceLifecycleCallback.this.onBluetoothServiceDown(); 4345 } 4346 4347 @Override 4348 public void onBrEdrDown() {} 4349 }; 4350 } 4351 4352 /** 4353 * Starts a scan for Bluetooth LE devices. 4354 * 4355 * <p>Results of the scan are reported using the 4356 * {@link LeScanCallback#onLeScan} callback. 4357 * 4358 * @param callback the callback LE scan results are delivered 4359 * @return true, if the scan was started successfully 4360 * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)} 4361 * instead. 4362 */ 4363 @Deprecated 4364 @RequiresLegacyBluetoothAdminPermission 4365 @RequiresBluetoothScanPermission 4366 @RequiresBluetoothLocationPermission 4367 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) startLeScan(LeScanCallback callback)4368 public boolean startLeScan(LeScanCallback callback) { 4369 return startLeScan(null, callback); 4370 } 4371 4372 /** 4373 * Starts a scan for Bluetooth LE devices, looking for devices that 4374 * advertise given services. 4375 * 4376 * <p>Devices which advertise all specified services are reported using the 4377 * {@link LeScanCallback#onLeScan} callback. 4378 * 4379 * @param serviceUuids Array of services to look for 4380 * @param callback the callback LE scan results are delivered 4381 * @return true, if the scan was started successfully 4382 * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)} 4383 * instead. 4384 */ 4385 @Deprecated 4386 @RequiresLegacyBluetoothAdminPermission 4387 @RequiresBluetoothScanPermission 4388 @RequiresBluetoothLocationPermission 4389 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) startLeScan(final UUID[] serviceUuids, final LeScanCallback callback)4390 public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) { 4391 if (DBG) { 4392 Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids)); 4393 } 4394 if (callback == null) { 4395 if (DBG) { 4396 Log.e(TAG, "startLeScan: null callback"); 4397 } 4398 return false; 4399 } 4400 BluetoothLeScanner scanner = getBluetoothLeScanner(); 4401 if (scanner == null) { 4402 if (DBG) { 4403 Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner"); 4404 } 4405 return false; 4406 } 4407 4408 synchronized (mLeScanClients) { 4409 if (mLeScanClients.containsKey(callback)) { 4410 if (DBG) { 4411 Log.e(TAG, "LE Scan has already started"); 4412 } 4413 return false; 4414 } 4415 4416 try { 4417 IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); 4418 if (iGatt == null) { 4419 // BLE is not supported 4420 return false; 4421 } 4422 4423 @SuppressLint("AndroidFrameworkBluetoothPermission") 4424 ScanCallback scanCallback = new ScanCallback() { 4425 @Override 4426 public void onScanResult(int callbackType, ScanResult result) { 4427 if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) { 4428 // Should not happen. 4429 Log.e(TAG, "LE Scan has already started"); 4430 return; 4431 } 4432 ScanRecord scanRecord = result.getScanRecord(); 4433 if (scanRecord == null) { 4434 return; 4435 } 4436 if (serviceUuids != null) { 4437 List<ParcelUuid> uuids = new ArrayList<ParcelUuid>(); 4438 for (UUID uuid : serviceUuids) { 4439 uuids.add(new ParcelUuid(uuid)); 4440 } 4441 List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids(); 4442 if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) { 4443 if (DBG) { 4444 Log.d(TAG, "uuids does not match"); 4445 } 4446 return; 4447 } 4448 } 4449 callback.onLeScan(result.getDevice(), result.getRssi(), 4450 scanRecord.getBytes()); 4451 } 4452 }; 4453 ScanSettings settings = new ScanSettings.Builder().setCallbackType( 4454 ScanSettings.CALLBACK_TYPE_ALL_MATCHES) 4455 .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) 4456 .build(); 4457 4458 List<ScanFilter> filters = new ArrayList<ScanFilter>(); 4459 if (serviceUuids != null && serviceUuids.length > 0) { 4460 // Note scan filter does not support matching an UUID array so we put one 4461 // UUID to hardware and match the whole array in callback. 4462 ScanFilter filter = 4463 new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuids[0])) 4464 .build(); 4465 filters.add(filter); 4466 } 4467 scanner.startScan(filters, settings, scanCallback); 4468 4469 mLeScanClients.put(callback, scanCallback); 4470 return true; 4471 4472 } catch (RemoteException e) { 4473 Log.e(TAG, "", e); 4474 } 4475 } 4476 return false; 4477 } 4478 4479 /** 4480 * Stops an ongoing Bluetooth LE device scan. 4481 * 4482 * @param callback used to identify which scan to stop must be the same handle used to start the 4483 * scan 4484 * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead. 4485 */ 4486 @Deprecated 4487 @RequiresLegacyBluetoothAdminPermission 4488 @RequiresBluetoothScanPermission 4489 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) stopLeScan(LeScanCallback callback)4490 public void stopLeScan(LeScanCallback callback) { 4491 if (DBG) { 4492 Log.d(TAG, "stopLeScan()"); 4493 } 4494 BluetoothLeScanner scanner = getBluetoothLeScanner(); 4495 if (scanner == null) { 4496 return; 4497 } 4498 synchronized (mLeScanClients) { 4499 ScanCallback scanCallback = mLeScanClients.remove(callback); 4500 if (scanCallback == null) { 4501 if (DBG) { 4502 Log.d(TAG, "scan not started yet"); 4503 } 4504 return; 4505 } 4506 scanner.stopScan(scanCallback); 4507 } 4508 } 4509 4510 /** 4511 * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and 4512 * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen 4513 * for incoming connections. The supported Bluetooth transport is LE only. 4514 * <p>A remote device connecting to this socket will be authenticated and communication on this 4515 * socket will be encrypted. 4516 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening 4517 * {@link BluetoothServerSocket}. 4518 * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {@link 4519 * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is 4520 * closed, Bluetooth is turned off, or the application exits unexpectedly. 4521 * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is 4522 * defined and performed by the application. 4523 * <p>Use {@link BluetoothDevice#createL2capChannel(int)} to connect to this server 4524 * socket from another Android device that is given the PSM value. 4525 * 4526 * @return an L2CAP CoC BluetoothServerSocket 4527 * @throws IOException on error, for example Bluetooth not available, or insufficient 4528 * permissions, or unable to start this CoC 4529 */ 4530 @RequiresLegacyBluetoothPermission 4531 @RequiresBluetoothConnectPermission 4532 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) listenUsingL2capChannel()4533 public @NonNull BluetoothServerSocket listenUsingL2capChannel() 4534 throws IOException { 4535 BluetoothServerSocket socket = 4536 new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true, 4537 SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false); 4538 int errno = socket.mSocket.bindListen(); 4539 if (errno != 0) { 4540 throw new IOException("Error: " + errno); 4541 } 4542 4543 int assignedPsm = socket.mSocket.getPort(); 4544 if (assignedPsm == 0) { 4545 throw new IOException("Error: Unable to assign PSM value"); 4546 } 4547 if (DBG) { 4548 Log.d(TAG, "listenUsingL2capChannel: set assigned PSM to " 4549 + assignedPsm); 4550 } 4551 socket.setChannel(assignedPsm); 4552 4553 return socket; 4554 } 4555 4556 /** 4557 * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and 4558 * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The 4559 * supported Bluetooth transport is LE only. 4560 * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable 4561 * to person-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and 4562 * authenticated communication channel is desired. 4563 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening 4564 * {@link BluetoothServerSocket}. 4565 * <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value 4566 * can be read from the {@link BluetoothServerSocket#getPsm()} and this value will be released 4567 * when this server socket is closed, Bluetooth is turned off, or the application exits 4568 * unexpectedly. 4569 * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is 4570 * defined and performed by the application. 4571 * <p>Use {@link BluetoothDevice#createInsecureL2capChannel(int)} to connect to this server 4572 * socket from another Android device that is given the PSM value. 4573 * 4574 * @return an L2CAP CoC BluetoothServerSocket 4575 * @throws IOException on error, for example Bluetooth not available, or insufficient 4576 * permissions, or unable to start this CoC 4577 */ 4578 @RequiresLegacyBluetoothPermission 4579 @RequiresBluetoothConnectPermission 4580 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) listenUsingInsecureL2capChannel()4581 public @NonNull BluetoothServerSocket listenUsingInsecureL2capChannel() 4582 throws IOException { 4583 BluetoothServerSocket socket = 4584 new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false, 4585 SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false); 4586 int errno = socket.mSocket.bindListen(); 4587 if (errno != 0) { 4588 throw new IOException("Error: " + errno); 4589 } 4590 4591 int assignedPsm = socket.mSocket.getPort(); 4592 if (assignedPsm == 0) { 4593 throw new IOException("Error: Unable to assign PSM value"); 4594 } 4595 if (DBG) { 4596 Log.d(TAG, "listenUsingInsecureL2capChannel: set assigned PSM to " 4597 + assignedPsm); 4598 } 4599 socket.setChannel(assignedPsm); 4600 4601 return socket; 4602 } 4603 4604 /** 4605 * Register a {@link #OnMetadataChangedListener} to receive update about metadata 4606 * changes for this {@link BluetoothDevice}. 4607 * Registration must be done when Bluetooth is ON and will last until 4608 * {@link #removeOnMetadataChangedListener(BluetoothDevice)} is called, even when Bluetooth 4609 * restarted in the middle. 4610 * All input parameters should not be null or {@link NullPointerException} will be triggered. 4611 * The same {@link BluetoothDevice} and {@link #OnMetadataChangedListener} pair can only be 4612 * registered once, double registration would cause {@link IllegalArgumentException}. 4613 * 4614 * @param device {@link BluetoothDevice} that will be registered 4615 * @param executor the executor for listener callback 4616 * @param listener {@link #OnMetadataChangedListener} that will receive asynchronous callbacks 4617 * @return true on success, false on error 4618 * @throws NullPointerException If one of {@code listener}, {@code device} or {@code executor} 4619 * is null. 4620 * @throws IllegalArgumentException The same {@link #OnMetadataChangedListener} and 4621 * {@link BluetoothDevice} are registered twice. 4622 * @hide 4623 */ 4624 @SystemApi 4625 @RequiresBluetoothConnectPermission 4626 @RequiresPermission(allOf = { 4627 android.Manifest.permission.BLUETOOTH_CONNECT, 4628 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 4629 }) addOnMetadataChangedListener(@onNull BluetoothDevice device, @NonNull Executor executor, @NonNull OnMetadataChangedListener listener)4630 public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device, 4631 @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) { 4632 if (DBG) Log.d(TAG, "addOnMetadataChangedListener()"); 4633 4634 if (listener == null) { 4635 throw new NullPointerException("listener is null"); 4636 } 4637 if (device == null) { 4638 throw new NullPointerException("device is null"); 4639 } 4640 if (executor == null) { 4641 throw new NullPointerException("executor is null"); 4642 } 4643 4644 mServiceLock.readLock().lock(); 4645 try { 4646 if (mService == null) { 4647 Log.e(TAG, "Bluetooth is not enabled. Cannot register metadata listener"); 4648 return false; 4649 } 4650 4651 4652 synchronized (mMetadataListeners) { 4653 List<Pair<OnMetadataChangedListener, Executor>> listenerList = 4654 mMetadataListeners.get(device); 4655 if (listenerList == null) { 4656 // Create new listener/executor list for registration 4657 listenerList = new ArrayList<>(); 4658 mMetadataListeners.put(device, listenerList); 4659 } else { 4660 // Check whether this device is already registered by the listener 4661 if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) { 4662 throw new IllegalArgumentException("listener was already regestered" 4663 + " for the device"); 4664 } 4665 } 4666 4667 Pair<OnMetadataChangedListener, Executor> listenerPair = 4668 new Pair(listener, executor); 4669 listenerList.add(listenerPair); 4670 4671 boolean ret = false; 4672 try { 4673 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 4674 mService.registerMetadataListener(mBluetoothMetadataListener, device, 4675 mAttributionSource, recv); 4676 ret = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 4677 } catch (RemoteException | TimeoutException e) { 4678 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 4679 } finally { 4680 if (!ret) { 4681 // Remove listener registered earlier when fail. 4682 listenerList.remove(listenerPair); 4683 if (listenerList.isEmpty()) { 4684 // Remove the device if its listener list is empty 4685 mMetadataListeners.remove(device); 4686 } 4687 } 4688 } 4689 return ret; 4690 } 4691 } finally { 4692 mServiceLock.readLock().unlock(); 4693 } 4694 } 4695 4696 /** 4697 * Unregister a {@link #OnMetadataChangedListener} from a registered {@link BluetoothDevice}. 4698 * Unregistration can be done when Bluetooth is either ON or OFF. 4699 * {@link #addOnMetadataChangedListener(OnMetadataChangedListener, BluetoothDevice, Executor)} 4700 * must be called before unregisteration. 4701 * 4702 * @param device {@link BluetoothDevice} that will be unregistered. It 4703 * should not be null or {@link NullPointerException} will be triggered. 4704 * @param listener {@link OnMetadataChangedListener} that will be unregistered. It 4705 * should not be null or {@link NullPointerException} will be triggered. 4706 * @return true on success, false on error 4707 * @throws NullPointerException If {@code listener} or {@code device} is null. 4708 * @throws IllegalArgumentException If {@code device} has not been registered before. 4709 * @hide 4710 */ 4711 @SystemApi 4712 @RequiresBluetoothConnectPermission 4713 @RequiresPermission(allOf = { 4714 android.Manifest.permission.BLUETOOTH_CONNECT, 4715 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 4716 }) removeOnMetadataChangedListener(@onNull BluetoothDevice device, @NonNull OnMetadataChangedListener listener)4717 public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device, 4718 @NonNull OnMetadataChangedListener listener) { 4719 if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()"); 4720 if (device == null) { 4721 throw new NullPointerException("device is null"); 4722 } 4723 if (listener == null) { 4724 throw new NullPointerException("listener is null"); 4725 } 4726 4727 synchronized (mMetadataListeners) { 4728 if (!mMetadataListeners.containsKey(device)) { 4729 throw new IllegalArgumentException("device was not registered"); 4730 } 4731 // Remove issued listener from the registered device 4732 mMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener))); 4733 4734 if (mMetadataListeners.get(device).isEmpty()) { 4735 // Unregister to Bluetooth service if all listeners are removed from 4736 // the registered device 4737 mMetadataListeners.remove(device); 4738 mServiceLock.readLock().lock(); 4739 try { 4740 if (mService != null) { 4741 final SynchronousResultReceiver<Boolean> recv = 4742 SynchronousResultReceiver.get(); 4743 mService.unregisterMetadataListener(device, mAttributionSource, recv); 4744 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 4745 } 4746 } catch (RemoteException | TimeoutException e) { 4747 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 4748 return false; 4749 } finally { 4750 mServiceLock.readLock().unlock(); 4751 } 4752 4753 } 4754 } 4755 return true; 4756 } 4757 4758 /** 4759 * This interface is used to implement {@link BluetoothAdapter} metadata listener. 4760 * @hide 4761 */ 4762 @SystemApi 4763 public interface OnMetadataChangedListener { 4764 /** 4765 * Callback triggered if the metadata of {@link BluetoothDevice} registered in 4766 * {@link #addOnMetadataChangedListener}. 4767 * 4768 * @param device changed {@link BluetoothDevice}. 4769 * @param key changed metadata key, one of BluetoothDevice.METADATA_*. 4770 * @param value the new value of metadata as byte array. 4771 */ onMetadataChanged(@onNull BluetoothDevice device, int key, @Nullable byte[] value)4772 void onMetadataChanged(@NonNull BluetoothDevice device, int key, 4773 @Nullable byte[] value); 4774 } 4775 4776 @SuppressLint("AndroidFrameworkBluetoothPermission") 4777 private final IBluetoothConnectionCallback mConnectionCallback = 4778 new IBluetoothConnectionCallback.Stub() { 4779 @Override 4780 public void onDeviceConnected(BluetoothDevice device) { 4781 Attributable.setAttributionSource(device, mAttributionSource); 4782 for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry: 4783 mBluetoothConnectionCallbackExecutorMap.entrySet()) { 4784 BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); 4785 Executor executor = callbackExecutorEntry.getValue(); 4786 executor.execute(() -> callback.onDeviceConnected(device)); 4787 } 4788 } 4789 4790 @Override 4791 public void onDeviceDisconnected(BluetoothDevice device, int hciReason) { 4792 Attributable.setAttributionSource(device, mAttributionSource); 4793 for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry: 4794 mBluetoothConnectionCallbackExecutorMap.entrySet()) { 4795 BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); 4796 Executor executor = callbackExecutorEntry.getValue(); 4797 executor.execute(() -> callback.onDeviceDisconnected(device, hciReason)); 4798 } 4799 } 4800 }; 4801 4802 /** 4803 * Registers the BluetoothConnectionCallback to receive callback events when a bluetooth device 4804 * (classic or low energy) is connected or disconnected. 4805 * 4806 * @param executor is the callback executor 4807 * @param callback is the connection callback you wish to register 4808 * @return true if the callback was registered successfully, false otherwise 4809 * @throws IllegalArgumentException if the callback is already registered 4810 * @hide 4811 */ 4812 @SystemApi 4813 @RequiresBluetoothConnectPermission 4814 @RequiresPermission(allOf = { 4815 android.Manifest.permission.BLUETOOTH_CONNECT, 4816 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 4817 }) registerBluetoothConnectionCallback(@onNull @allbackExecutor Executor executor, @NonNull BluetoothConnectionCallback callback)4818 public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor, 4819 @NonNull BluetoothConnectionCallback callback) { 4820 if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()"); 4821 if (callback == null || executor == null) { 4822 return false; 4823 } 4824 4825 synchronized (mBluetoothConnectionCallbackExecutorMap) { 4826 // If the callback map is empty, we register the service-to-app callback 4827 if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) { 4828 mServiceLock.readLock().lock(); 4829 try { 4830 if (mService != null) { 4831 final SynchronousResultReceiver<Boolean> recv = 4832 SynchronousResultReceiver.get(); 4833 mService.registerBluetoothConnectionCallback(mConnectionCallback, 4834 mAttributionSource, recv); 4835 if (!recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false)) { 4836 return false; 4837 } 4838 } 4839 } catch (RemoteException | TimeoutException e) { 4840 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 4841 mBluetoothConnectionCallbackExecutorMap.remove(callback); 4842 } finally { 4843 mServiceLock.readLock().unlock(); 4844 } 4845 } 4846 4847 // Adds the passed in callback to our map of callbacks to executors 4848 if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) { 4849 throw new IllegalArgumentException("This callback has already been registered"); 4850 } 4851 mBluetoothConnectionCallbackExecutorMap.put(callback, executor); 4852 } 4853 4854 return true; 4855 } 4856 4857 /** 4858 * Unregisters the BluetoothConnectionCallback that was previously registered by the application 4859 * 4860 * @param callback is the connection callback you wish to unregister 4861 * @return true if the callback was unregistered successfully, false otherwise 4862 * @hide 4863 */ 4864 @SystemApi 4865 @RequiresBluetoothConnectPermission 4866 @RequiresPermission(allOf = { 4867 android.Manifest.permission.BLUETOOTH_CONNECT, 4868 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 4869 }) unregisterBluetoothConnectionCallback( @onNull BluetoothConnectionCallback callback)4870 public boolean unregisterBluetoothConnectionCallback( 4871 @NonNull BluetoothConnectionCallback callback) { 4872 if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()"); 4873 if (callback == null) { 4874 return false; 4875 } 4876 4877 synchronized (mBluetoothConnectionCallbackExecutorMap) { 4878 if (mBluetoothConnectionCallbackExecutorMap.remove(callback) != null) { 4879 return false; 4880 } 4881 } 4882 4883 if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { 4884 return true; 4885 } 4886 4887 // If the callback map is empty, we unregister the service-to-app callback 4888 mServiceLock.readLock().lock(); 4889 try { 4890 if (mService != null) { 4891 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 4892 mService.unregisterBluetoothConnectionCallback(mConnectionCallback, 4893 mAttributionSource, recv); 4894 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(false); 4895 } 4896 } catch (RemoteException | TimeoutException e) { 4897 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 4898 } finally { 4899 mServiceLock.readLock().unlock(); 4900 } 4901 4902 return false; 4903 } 4904 4905 /** 4906 * This abstract class is used to implement callbacks for when a bluetooth classic or Bluetooth 4907 * Low Energy (BLE) device is either connected or disconnected. 4908 * 4909 * @hide 4910 */ 4911 @SystemApi 4912 public abstract static class BluetoothConnectionCallback { 4913 /** 4914 * Callback triggered when a bluetooth device (classic or BLE) is connected 4915 * @param device is the connected bluetooth device 4916 */ onDeviceConnected(@onNull BluetoothDevice device)4917 public void onDeviceConnected(@NonNull BluetoothDevice device) {} 4918 4919 /** 4920 * Callback triggered when a bluetooth device (classic or BLE) is disconnected 4921 * @param device is the disconnected bluetooth device 4922 * @param reason is the disconnect reason 4923 */ onDeviceDisconnected(@onNull BluetoothDevice device, @DisconnectReason int reason)4924 public void onDeviceDisconnected(@NonNull BluetoothDevice device, 4925 @DisconnectReason int reason) {} 4926 4927 /** 4928 * @hide 4929 */ 4930 @Retention(RetentionPolicy.SOURCE) 4931 @IntDef(prefix = { "REASON_" }, value = { 4932 BluetoothStatusCodes.ERROR_UNKNOWN, 4933 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST, 4934 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST, 4935 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL, 4936 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE, 4937 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT, 4938 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY, 4939 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY, 4940 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED, 4941 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS, 4942 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS}) 4943 public @interface DisconnectReason {} 4944 4945 /** 4946 * Returns human-readable strings corresponding to {@link DisconnectReason}. 4947 */ 4948 @NonNull disconnectReasonToString(@isconnectReason int reason)4949 public static String disconnectReasonToString(@DisconnectReason int reason) { 4950 switch (reason) { 4951 case BluetoothStatusCodes.ERROR_UNKNOWN: 4952 return "Reason unknown"; 4953 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST: 4954 return "Local request"; 4955 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST: 4956 return "Remote request"; 4957 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL: 4958 return "Local error"; 4959 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE: 4960 return "Remote error"; 4961 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT: 4962 return "Timeout"; 4963 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY: 4964 return "Security"; 4965 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY: 4966 return "System policy"; 4967 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED: 4968 return "Resource constrained"; 4969 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS: 4970 return "Connection already exists"; 4971 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS: 4972 return "Bad parameters"; 4973 default: 4974 return "Unrecognized disconnect reason: " + reason; 4975 } 4976 } 4977 } 4978 4979 /** @hide */ 4980 @Retention(RetentionPolicy.SOURCE) 4981 @IntDef(value = { 4982 BluetoothStatusCodes.SUCCESS, 4983 BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_REQUEST, 4984 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 4985 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, 4986 BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED, 4987 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, 4988 BluetoothStatusCodes.ERROR_NOT_DUAL_MODE_AUDIO_DEVICE, 4989 BluetoothStatusCodes.ERROR_UNKNOWN, 4990 BluetoothStatusCodes.FEATURE_NOT_SUPPORTED, 4991 }) 4992 public @interface SetPreferredAudioProfilesReturnValues {} 4993 4994 /** 4995 * Sets the preferred profiles for each audio mode for system routed audio. The audio framework 4996 * and Telecomm will read this preference when routing system managed audio. Not supplying an 4997 * audio mode in the Bundle will reset that audio mode to the default profile preference for 4998 * that mode (e.g. an empty Bundle resets all audio modes to their default profiles). 4999 * <p> 5000 * Note: apps that invoke profile-specific audio APIs are not subject to the preference noted 5001 * here. These preferences will also be ignored if the remote device is not simultaneously 5002 * connected to a classic audio profile (A2DP and/or HFP) and LE Audio at the same time. If the 5003 * remote device does not support both BR/EDR audio and LE Audio, this API returns 5004 * {@link BluetoothStatusCodes#ERROR_NOT_DUAL_MODE_AUDIO_DEVICE}. If the system property 5005 * persist.bluetooth.enable_dual_mode_audio is set to {@code false}, this API returns 5006 * {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED}. 5007 * <p> 5008 * The Bundle is expected to contain the following mappings: 5009 * 1. For key {@link #AUDIO_MODE_OUTPUT_ONLY}, it expects an integer value of either 5010 * {@link BluetoothProfile#A2DP} or {@link BluetoothProfile#LE_AUDIO}. 5011 * 2. For key {@link #AUDIO_MODE_DUPLEX}, it expects an integer value of either 5012 * {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#LE_AUDIO}. 5013 * <p> 5014 * Apps should register for a callback with 5015 * {@link #registerPreferredAudioProfilesChangedCallback(Executor, 5016 * PreferredAudioProfilesChangedCallback)} to know if the preferences were successfully applied 5017 * to the audio framework. If there is an active preference change for this device that has not 5018 * taken effect with the audio framework, no additional calls to this API will be allowed until 5019 * that completes. 5020 * 5021 * @param modeToProfileBundle a mapping to indicate the preferred profile for each audio mode 5022 * @return whether the preferred audio profiles were requested to be set 5023 * @throws NullPointerException if modeToProfileBundle or device is null 5024 * @throws IllegalArgumentException if this BluetoothDevice object has an invalid address or the 5025 * Bundle doesn't conform to its requirements 5026 * 5027 * @hide 5028 */ 5029 @SystemApi 5030 @RequiresPermission(allOf = { 5031 android.Manifest.permission.BLUETOOTH_CONNECT, 5032 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 5033 }) 5034 @SetPreferredAudioProfilesReturnValues setPreferredAudioProfiles(@onNull BluetoothDevice device, @NonNull Bundle modeToProfileBundle)5035 public int setPreferredAudioProfiles(@NonNull BluetoothDevice device, 5036 @NonNull Bundle modeToProfileBundle) { 5037 if (DBG) { 5038 Log.d(TAG, "setPreferredAudioProfiles( " + modeToProfileBundle + ", " + device + ")"); 5039 } 5040 requireNonNull(modeToProfileBundle, "modeToProfileBundle must not be null"); 5041 requireNonNull(device, "device must not be null"); 5042 if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) { 5043 throw new IllegalArgumentException("device cannot have an invalid address"); 5044 } 5045 if (!modeToProfileBundle.containsKey(AUDIO_MODE_OUTPUT_ONLY) 5046 && !modeToProfileBundle.containsKey(AUDIO_MODE_DUPLEX)) { 5047 throw new IllegalArgumentException("Bundle does not contain a key " 5048 + "AUDIO_MODE_OUTPUT_ONLY or AUDIO_MODE_DUPLEX"); 5049 } 5050 if (modeToProfileBundle.containsKey(AUDIO_MODE_OUTPUT_ONLY) 5051 && modeToProfileBundle.getInt(AUDIO_MODE_OUTPUT_ONLY) != BluetoothProfile.A2DP 5052 && modeToProfileBundle.getInt( 5053 AUDIO_MODE_OUTPUT_ONLY) != BluetoothProfile.LE_AUDIO) { 5054 throw new IllegalArgumentException("Key AUDIO_MODE_OUTPUT_ONLY has an invalid value: " 5055 + modeToProfileBundle.getInt(AUDIO_MODE_OUTPUT_ONLY)); 5056 } 5057 if (modeToProfileBundle.containsKey(AUDIO_MODE_DUPLEX) 5058 && modeToProfileBundle.getInt(AUDIO_MODE_DUPLEX) != BluetoothProfile.HEADSET 5059 && modeToProfileBundle.getInt(AUDIO_MODE_DUPLEX) != BluetoothProfile.LE_AUDIO) { 5060 throw new IllegalArgumentException("Key AUDIO_MODE_DUPLEX has an invalid value: " 5061 + modeToProfileBundle.getInt(AUDIO_MODE_DUPLEX)); 5062 } 5063 5064 final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN; 5065 mServiceLock.readLock().lock(); 5066 try { 5067 if (mService != null) { 5068 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 5069 mService.setPreferredAudioProfiles(device, modeToProfileBundle, 5070 mAttributionSource, recv); 5071 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 5072 } else { 5073 return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 5074 } 5075 } catch (RemoteException e) { 5076 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 5077 throw e.rethrowFromSystemServer(); 5078 } catch (TimeoutException e) { 5079 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 5080 } finally { 5081 mServiceLock.readLock().unlock(); 5082 } 5083 return defaultValue; 5084 } 5085 5086 /** 5087 * Gets the preferred profile for each audio mode for system routed audio. This API 5088 * returns a Bundle with mappings between each audio mode and its preferred audio profile. If no 5089 * values are set via {@link #setPreferredAudioProfiles(BluetoothDevice, Bundle)}, this API 5090 * returns the default system preferences set via the sysprops 5091 * {@link BluetoothProperties#getDefaultOutputOnlyAudioProfile()} and 5092 * {@link BluetoothProperties#getDefaultDuplexAudioProfile()}. 5093 * <p> 5094 * An audio capable device must support at least one audio mode with a preferred audio profile. 5095 * If a device does not support an audio mode, the audio mode will be omitted from the keys of 5096 * the Bundle. If the device is not recognized as a dual mode audio capable device (e.g. because 5097 * it is not bonded, does not support any audio profiles, or does not support both BR/EDR audio 5098 * and LE Audio), this API returns an empty Bundle. If the system property 5099 * persist.bluetooth.enable_dual_mode_audio is set to {@code false}, this API returns an empty 5100 * Bundle. 5101 * <p> 5102 * The Bundle can contain the following mappings: 5103 * <ul> 5104 * <li>For key {@link #AUDIO_MODE_OUTPUT_ONLY}, if an audio profile preference was set, this 5105 * will have an int value of either {@link BluetoothProfile#A2DP} or 5106 * {@link BluetoothProfile#LE_AUDIO}. 5107 * <li>For key {@link #AUDIO_MODE_DUPLEX}, if an audio profile preference was set, this will 5108 * have an int value of either {@link BluetoothProfile#HEADSET} or 5109 * {@link BluetoothProfile#LE_AUDIO}. 5110 * </ul> 5111 * 5112 * @return a Bundle mapping each set audio mode and preferred audio profile pair 5113 * @throws NullPointerException if modeToProfileBundle or device is null 5114 * @throws IllegalArgumentException if this BluetoothDevice object has an invalid address or the 5115 * Bundle doesn't conform to its requirements 5116 * 5117 * @hide 5118 */ 5119 @SystemApi 5120 @RequiresPermission(allOf = { 5121 android.Manifest.permission.BLUETOOTH_CONNECT, 5122 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 5123 }) 5124 @NonNull getPreferredAudioProfiles(@onNull BluetoothDevice device)5125 public Bundle getPreferredAudioProfiles(@NonNull BluetoothDevice device) { 5126 if (DBG) Log.d(TAG, "getPreferredAudioProfiles(" + device + ")"); 5127 requireNonNull(device, "device cannot be null"); 5128 if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { 5129 throw new IllegalArgumentException("device cannot have an invalid address"); 5130 } 5131 5132 final Bundle defaultValue = Bundle.EMPTY; 5133 mServiceLock.readLock().lock(); 5134 try { 5135 if (mService != null) { 5136 final SynchronousResultReceiver<Bundle> recv = SynchronousResultReceiver.get(); 5137 mService.getPreferredAudioProfiles(device, mAttributionSource, recv); 5138 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 5139 } 5140 } catch (RemoteException e) { 5141 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 5142 throw e.rethrowFromSystemServer(); 5143 } catch (TimeoutException e) { 5144 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 5145 } finally { 5146 mServiceLock.readLock().unlock(); 5147 } 5148 5149 return defaultValue; 5150 } 5151 5152 /** @hide */ 5153 @Retention(RetentionPolicy.SOURCE) 5154 @IntDef(value = { 5155 BluetoothStatusCodes.SUCCESS, 5156 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 5157 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, 5158 BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED, 5159 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, 5160 BluetoothStatusCodes.ERROR_UNKNOWN 5161 }) 5162 public @interface NotifyActiveDeviceChangeAppliedReturnValues {} 5163 5164 /** 5165 * Called by audio framework to inform the Bluetooth stack that an active device change has 5166 * taken effect. If this active device change is triggered by an app calling 5167 * {@link #setPreferredAudioProfiles(BluetoothDevice, Bundle)}, the Bluetooth stack will invoke 5168 * {@link PreferredAudioProfilesChangedCallback#onPreferredAudioProfilesChanged( 5169 * BluetoothDevice, Bundle, int)} if all requested changes for the device have been applied. 5170 * <p> 5171 * This method will return 5172 * {@link BluetoothStatusCodes#ERROR_BLUETOOTH_NOT_ALLOWED} if called outside system server. 5173 * 5174 * @param device is the BluetoothDevice that had its preferred audio profile changed 5175 * @return whether the Bluetooth stack acknowledged the change successfully 5176 * @throws NullPointerException if device is null 5177 * @throws IllegalArgumentException if the device's address is invalid 5178 * 5179 * @hide 5180 */ 5181 @SystemApi 5182 @RequiresPermission(allOf = { 5183 android.Manifest.permission.BLUETOOTH_CONNECT, 5184 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 5185 }) 5186 @NotifyActiveDeviceChangeAppliedReturnValues notifyActiveDeviceChangeApplied(@onNull BluetoothDevice device)5187 public int notifyActiveDeviceChangeApplied(@NonNull BluetoothDevice device) { 5188 if (DBG) Log.d(TAG, "notifyActiveDeviceChangeApplied(" + device + ")"); 5189 requireNonNull(device, "device cannot be null"); 5190 if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { 5191 throw new IllegalArgumentException("device cannot have an invalid address"); 5192 } 5193 5194 final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN; 5195 mServiceLock.readLock().lock(); 5196 try { 5197 if (mService != null) { 5198 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 5199 mService.notifyActiveDeviceChangeApplied(device, 5200 mAttributionSource, recv); 5201 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 5202 } 5203 } catch (RemoteException e) { 5204 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 5205 throw e.rethrowFromSystemServer(); 5206 } catch (TimeoutException e) { 5207 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 5208 } finally { 5209 mServiceLock.readLock().unlock(); 5210 } 5211 5212 return defaultValue; 5213 } 5214 5215 @SuppressLint("AndroidFrameworkBluetoothPermission") 5216 private final IBluetoothPreferredAudioProfilesCallback mPreferredAudioProfilesChangedCallback = 5217 new IBluetoothPreferredAudioProfilesCallback.Stub() { 5218 @Override 5219 public void onPreferredAudioProfilesChanged(BluetoothDevice device, 5220 Bundle preferredAudioProfiles, int status) { 5221 for (Map.Entry<PreferredAudioProfilesChangedCallback, Executor> 5222 callbackExecutorEntry: 5223 mAudioProfilesChangedCallbackExecutorMap.entrySet()) { 5224 PreferredAudioProfilesChangedCallback callback = 5225 callbackExecutorEntry.getKey(); 5226 Executor executor = callbackExecutorEntry.getValue(); 5227 executor.execute(() -> callback.onPreferredAudioProfilesChanged(device, 5228 preferredAudioProfiles, status)); 5229 } 5230 } 5231 }; 5232 5233 /** @hide */ 5234 @Retention(RetentionPolicy.SOURCE) 5235 @IntDef(value = { 5236 BluetoothStatusCodes.SUCCESS, 5237 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 5238 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, 5239 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, 5240 BluetoothStatusCodes.ERROR_UNKNOWN, 5241 BluetoothStatusCodes.FEATURE_NOT_SUPPORTED 5242 }) 5243 public @interface RegisterPreferredAudioProfilesCallbackReturnValues {} 5244 5245 /** 5246 * Registers a callback to be notified when the preferred audio profile changes have taken 5247 * effect. To unregister this callback, call 5248 * {@link #unregisterPreferredAudioProfilesChangedCallback( 5249 * PreferredAudioProfilesChangedCallback)}. If the system property 5250 * persist.bluetooth.enable_dual_mode_audio is set to {@code false}, this API returns 5251 * {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED}. 5252 * 5253 * @param executor an {@link Executor} to execute the callbacks 5254 * @param callback user implementation of the {@link PreferredAudioProfilesChangedCallback} 5255 * @return whether the callback was registered successfully 5256 * @throws NullPointerException if executor or callback is null 5257 * @throws IllegalArgumentException if the callback is already registered 5258 * 5259 * @hide 5260 */ 5261 @SystemApi 5262 @RequiresPermission(allOf = { 5263 android.Manifest.permission.BLUETOOTH_CONNECT, 5264 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 5265 }) 5266 @RegisterPreferredAudioProfilesCallbackReturnValues registerPreferredAudioProfilesChangedCallback( @onNull @allbackExecutor Executor executor, @NonNull PreferredAudioProfilesChangedCallback callback)5267 public int registerPreferredAudioProfilesChangedCallback( 5268 @NonNull @CallbackExecutor Executor executor, 5269 @NonNull PreferredAudioProfilesChangedCallback callback) { 5270 if (DBG) Log.d(TAG, "registerPreferredAudioProfilesChangedCallback()"); 5271 requireNonNull(executor, "executor cannot be null"); 5272 requireNonNull(callback, "callback cannot be null"); 5273 5274 final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN; 5275 int serviceCallStatus = defaultValue; 5276 synchronized (mAudioProfilesChangedCallbackExecutorMap) { 5277 // If the callback map is empty, we register the service-to-app callback 5278 if (mAudioProfilesChangedCallbackExecutorMap.isEmpty()) { 5279 mServiceLock.readLock().lock(); 5280 try { 5281 if (mService != null) { 5282 final SynchronousResultReceiver<Integer> recv = 5283 SynchronousResultReceiver.get(); 5284 mService.registerPreferredAudioProfilesChangedCallback( 5285 mPreferredAudioProfilesChangedCallback, mAttributionSource, recv); 5286 serviceCallStatus = recv.awaitResultNoInterrupt(getSyncTimeout()) 5287 .getValue(defaultValue); 5288 } 5289 } catch (RemoteException e) { 5290 throw e.rethrowFromSystemServer(); 5291 } catch (TimeoutException e) { 5292 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 5293 } finally { 5294 mServiceLock.readLock().unlock(); 5295 } 5296 if (serviceCallStatus != BluetoothStatusCodes.SUCCESS) { 5297 return serviceCallStatus; 5298 } 5299 } 5300 5301 // Adds the passed in callback to our local mapping 5302 if (mAudioProfilesChangedCallbackExecutorMap.containsKey(callback)) { 5303 throw new IllegalArgumentException("This callback has already been registered"); 5304 } else { 5305 mAudioProfilesChangedCallbackExecutorMap.put(callback, executor); 5306 } 5307 } 5308 5309 return BluetoothStatusCodes.SUCCESS; 5310 } 5311 5312 /** @hide */ 5313 @Retention(RetentionPolicy.SOURCE) 5314 @IntDef(value = { 5315 BluetoothStatusCodes.SUCCESS, 5316 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 5317 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, 5318 BluetoothStatusCodes.ERROR_CALLBACK_NOT_REGISTERED, 5319 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, 5320 BluetoothStatusCodes.ERROR_UNKNOWN, 5321 BluetoothStatusCodes.FEATURE_NOT_SUPPORTED 5322 }) 5323 public @interface UnRegisterPreferredAudioProfilesCallbackReturnValues {} 5324 5325 /** 5326 * Unregisters a callback that was previously registered with 5327 * {@link #registerPreferredAudioProfilesChangedCallback(Executor, 5328 * PreferredAudioProfilesChangedCallback)}. 5329 * 5330 * @param callback user implementation of the {@link PreferredAudioProfilesChangedCallback} 5331 * @return whether the callback was successfully unregistered 5332 * @throws NullPointerException if the callback is null 5333 * @throws IllegalArgumentException if the callback has not been registered 5334 * 5335 * @hide 5336 */ 5337 @SystemApi 5338 @RequiresPermission(allOf = { 5339 android.Manifest.permission.BLUETOOTH_CONNECT, 5340 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 5341 }) 5342 @UnRegisterPreferredAudioProfilesCallbackReturnValues unregisterPreferredAudioProfilesChangedCallback( @onNull PreferredAudioProfilesChangedCallback callback)5343 public int unregisterPreferredAudioProfilesChangedCallback( 5344 @NonNull PreferredAudioProfilesChangedCallback callback) { 5345 if (DBG) Log.d(TAG, "unregisterPreferredAudioProfilesChangedCallback()"); 5346 requireNonNull(callback, "callback cannot be null"); 5347 5348 synchronized (mAudioProfilesChangedCallbackExecutorMap) { 5349 if (mAudioProfilesChangedCallbackExecutorMap.remove(callback) == null) { 5350 throw new IllegalArgumentException("This callback has not been registered"); 5351 } 5352 } 5353 5354 if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { 5355 return BluetoothStatusCodes.SUCCESS; 5356 } 5357 5358 final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN; 5359 // If the callback map is empty, we unregister the service-to-app callback 5360 mServiceLock.readLock().lock(); 5361 try { 5362 if (mService != null) { 5363 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 5364 mService.unregisterPreferredAudioProfilesChangedCallback( 5365 mPreferredAudioProfilesChangedCallback, mAttributionSource, recv); 5366 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 5367 } 5368 } catch (TimeoutException e) { 5369 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 5370 } catch (RemoteException e) { 5371 throw e.rethrowFromSystemServer(); 5372 } finally { 5373 mServiceLock.readLock().unlock(); 5374 } 5375 5376 return defaultValue; 5377 } 5378 5379 /** 5380 * A callback for preferred audio profile changes that arise from calls to 5381 * {@link #setPreferredAudioProfiles(BluetoothDevice, Bundle)}. 5382 * 5383 * @hide 5384 */ 5385 @SystemApi 5386 public interface PreferredAudioProfilesChangedCallback { 5387 /** 5388 * Called when the preferred audio profile change from a call to 5389 * {@link #setPreferredAudioProfiles(BluetoothDevice, Bundle)} has taken effect in the audio 5390 * framework or timed out. This callback includes a Bundle that indicates the current 5391 * preferred audio profile for each audio mode, if one was set. If an audio mode does not 5392 * have a profile preference, its key will be omitted from the Bundle. If both audio modes 5393 * do not have a preferred profile set, the Bundle will be empty. 5394 * 5395 * <p> 5396 * The Bundle can contain the following mappings: 5397 * <ul> 5398 * <li>For key {@link #AUDIO_MODE_OUTPUT_ONLY}, if an audio profile preference was set, this 5399 * will have an int value of either {@link BluetoothProfile#A2DP} or 5400 * {@link BluetoothProfile#LE_AUDIO}. 5401 * <li>For key {@link #AUDIO_MODE_DUPLEX}, if an audio profile preference was set, this will 5402 * have an int value of either {@link BluetoothProfile#HEADSET} or 5403 * {@link BluetoothProfile#LE_AUDIO}. 5404 * </ul> 5405 * 5406 * @param device is the device which had its preferred audio profiles changed 5407 * @param preferredAudioProfiles a Bundle mapping audio mode to its preferred audio profile 5408 * @param status whether the operation succeeded or timed out 5409 * 5410 * @hide 5411 */ 5412 @SystemApi onPreferredAudioProfilesChanged(@onNull BluetoothDevice device, @NonNull Bundle preferredAudioProfiles, int status)5413 void onPreferredAudioProfilesChanged(@NonNull BluetoothDevice device, @NonNull 5414 Bundle preferredAudioProfiles, int status); 5415 } 5416 5417 @SuppressLint("AndroidFrameworkBluetoothPermission") 5418 private final IBluetoothQualityReportReadyCallback mBluetoothQualityReportReadyCallback = 5419 new IBluetoothQualityReportReadyCallback.Stub() { 5420 @Override 5421 public void onBluetoothQualityReportReady(BluetoothDevice device, 5422 BluetoothQualityReport bluetoothQualityReport, int status) { 5423 for (Map.Entry<BluetoothQualityReportReadyCallback, Executor> 5424 callbackExecutorEntry: 5425 mBluetoothQualityReportReadyCallbackExecutorMap.entrySet()) { 5426 BluetoothQualityReportReadyCallback callback = 5427 callbackExecutorEntry.getKey(); 5428 Executor executor = callbackExecutorEntry.getValue(); 5429 executor.execute(() -> callback.onBluetoothQualityReportReady(device, 5430 bluetoothQualityReport, status)); 5431 } 5432 } 5433 }; 5434 5435 /** @hide */ 5436 @Retention(RetentionPolicy.SOURCE) 5437 @IntDef(value = { 5438 BluetoothStatusCodes.SUCCESS, 5439 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 5440 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, 5441 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, 5442 BluetoothStatusCodes.ERROR_UNKNOWN 5443 }) 5444 public @interface RegisterBluetoothQualityReportReadyCallbackReturnValues {} 5445 5446 /** 5447 * Registers a callback to be notified when Bluetooth Quality Report is ready. To unregister 5448 * this callback, call 5449 * {@link #unregisterBluetoothQualityReportReadyCallback( 5450 * BluetoothQualityReportReadyCallback)}. 5451 * 5452 * @param executor an {@link Executor} to execute the callbacks 5453 * @param callback user implementation of the {@link BluetoothQualityReportReadyCallback} 5454 * @return whether the callback was registered successfully 5455 * @throws NullPointerException if executor or callback is null 5456 * @throws IllegalArgumentException if the callback is already registered 5457 * 5458 * @hide 5459 */ 5460 @SystemApi 5461 @RequiresPermission(allOf = { 5462 android.Manifest.permission.BLUETOOTH_CONNECT, 5463 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 5464 }) 5465 @RegisterBluetoothQualityReportReadyCallbackReturnValues registerBluetoothQualityReportReadyCallback( @onNull @allbackExecutor Executor executor, @NonNull BluetoothQualityReportReadyCallback callback)5466 public int registerBluetoothQualityReportReadyCallback( 5467 @NonNull @CallbackExecutor Executor executor, 5468 @NonNull BluetoothQualityReportReadyCallback callback) { 5469 if (DBG) Log.d(TAG, "registerBluetoothQualityReportReadyCallback()"); 5470 requireNonNull(executor, "executor cannot be null"); 5471 requireNonNull(callback, "callback cannot be null"); 5472 5473 final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN; 5474 int serviceCallStatus = defaultValue; 5475 synchronized (mBluetoothQualityReportReadyCallbackExecutorMap) { 5476 // If the callback map is empty, we register the service-to-app callback 5477 if (mBluetoothQualityReportReadyCallbackExecutorMap.isEmpty()) { 5478 mServiceLock.readLock().lock(); 5479 try { 5480 if (mService != null) { 5481 final SynchronousResultReceiver<Integer> recv = 5482 SynchronousResultReceiver.get(); 5483 mService.registerBluetoothQualityReportReadyCallback( 5484 mBluetoothQualityReportReadyCallback, mAttributionSource, recv); 5485 serviceCallStatus = recv.awaitResultNoInterrupt(getSyncTimeout()) 5486 .getValue(defaultValue); 5487 } 5488 } catch (RemoteException e) { 5489 throw e.rethrowFromSystemServer(); 5490 } catch (TimeoutException e) { 5491 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 5492 } finally { 5493 mServiceLock.readLock().unlock(); 5494 } 5495 if (serviceCallStatus != BluetoothStatusCodes.SUCCESS) { 5496 return serviceCallStatus; 5497 } 5498 } 5499 5500 // Adds the passed in callback to our local mapping 5501 if (mBluetoothQualityReportReadyCallbackExecutorMap.containsKey(callback)) { 5502 throw new IllegalArgumentException("This callback has already been registered"); 5503 } else { 5504 mBluetoothQualityReportReadyCallbackExecutorMap.put(callback, executor); 5505 } 5506 } 5507 5508 return BluetoothStatusCodes.SUCCESS; 5509 } 5510 5511 /** @hide */ 5512 @Retention(RetentionPolicy.SOURCE) 5513 @IntDef(value = { 5514 BluetoothStatusCodes.SUCCESS, 5515 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 5516 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, 5517 BluetoothStatusCodes.ERROR_CALLBACK_NOT_REGISTERED, 5518 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, 5519 BluetoothStatusCodes.ERROR_UNKNOWN 5520 }) 5521 public @interface UnRegisterBluetoothQualityReportReadyCallbackReturnValues {} 5522 5523 /** 5524 * Unregisters a callback that was previously registered with 5525 * {@link #registerBluetoothQualityReportReadyCallback(Executor, 5526 * BluetoothQualityReportReadyCallback)}. 5527 * 5528 * @param callback user implementation of the {@link BluetoothQualityReportReadyCallback} 5529 * @return whether the callback was successfully unregistered 5530 * @throws NullPointerException if the callback is null 5531 * @throws IllegalArgumentException if the callback has not been registered 5532 * 5533 * @hide 5534 */ 5535 @SystemApi 5536 @RequiresPermission(allOf = { 5537 android.Manifest.permission.BLUETOOTH_CONNECT, 5538 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 5539 }) 5540 @UnRegisterBluetoothQualityReportReadyCallbackReturnValues unregisterBluetoothQualityReportReadyCallback( @onNull BluetoothQualityReportReadyCallback callback)5541 public int unregisterBluetoothQualityReportReadyCallback( 5542 @NonNull BluetoothQualityReportReadyCallback callback) { 5543 if (DBG) Log.d(TAG, "unregisterBluetoothQualityReportReadyCallback()"); 5544 requireNonNull(callback, "callback cannot be null"); 5545 5546 synchronized (mBluetoothQualityReportReadyCallbackExecutorMap) { 5547 if (mBluetoothQualityReportReadyCallbackExecutorMap.remove(callback) == null) { 5548 throw new IllegalArgumentException("This callback has not been registered"); 5549 } 5550 } 5551 5552 if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { 5553 return BluetoothStatusCodes.SUCCESS; 5554 } 5555 5556 final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN; 5557 // If the callback map is empty, we unregister the service-to-app callback 5558 mServiceLock.readLock().lock(); 5559 try { 5560 if (mService != null) { 5561 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 5562 mService.unregisterBluetoothQualityReportReadyCallback( 5563 mBluetoothQualityReportReadyCallback, mAttributionSource, recv); 5564 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 5565 } 5566 } catch (TimeoutException e) { 5567 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 5568 } catch (RemoteException e) { 5569 throw e.rethrowFromSystemServer(); 5570 } finally { 5571 mServiceLock.readLock().unlock(); 5572 } 5573 5574 return defaultValue; 5575 } 5576 5577 /** 5578 * A callback for Bluetooth Quality Report that arise from the controller. 5579 * 5580 * @hide 5581 */ 5582 @SystemApi 5583 public interface BluetoothQualityReportReadyCallback { 5584 /** 5585 * Called when the Bluetooth Quality Report coming from the controller is ready. This 5586 * callback includes a Parcel that contains information about Bluetooth Quality. 5587 * Currently the report supports five event types: Quality monitor event, 5588 * Approaching LSTO event, A2DP choppy event, SCO choppy event and Connect fail event. 5589 * To know which kind of event is wrapped in this {@link BluetoothQualityReport} object, 5590 * you need to call {@link #getQualityReportId}. 5591 * 5592 * 5593 * @param device is the BluetoothDevice which connection quality is being reported 5594 * @param bluetoothQualityReport a Parcel that contains info about Bluetooth Quality 5595 * @param status whether the operation succeeded or timed out 5596 * 5597 * @hide 5598 */ 5599 @SystemApi onBluetoothQualityReportReady(@onNull BluetoothDevice device, @NonNull BluetoothQualityReport bluetoothQualityReport, int status)5600 void onBluetoothQualityReportReady(@NonNull BluetoothDevice device, @NonNull 5601 BluetoothQualityReport bluetoothQualityReport, int status); 5602 } 5603 5604 /** 5605 * Converts old constant of priority to the new for connection policy 5606 * 5607 * @param priority is the priority to convert to connection policy 5608 * @return the equivalent connection policy constant to the priority 5609 * 5610 * @hide 5611 */ priorityToConnectionPolicy(int priority)5612 public static @ConnectionPolicy int priorityToConnectionPolicy(int priority) { 5613 switch(priority) { 5614 case BluetoothProfile.PRIORITY_AUTO_CONNECT: 5615 return BluetoothProfile.CONNECTION_POLICY_ALLOWED; 5616 case BluetoothProfile.PRIORITY_ON: 5617 return BluetoothProfile.CONNECTION_POLICY_ALLOWED; 5618 case BluetoothProfile.PRIORITY_OFF: 5619 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; 5620 case BluetoothProfile.PRIORITY_UNDEFINED: 5621 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 5622 default: 5623 Log.e(TAG, "setPriority: Invalid priority: " + priority); 5624 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 5625 } 5626 } 5627 5628 /** 5629 * Converts new constant of connection policy to the old for priority 5630 * 5631 * @param connectionPolicy is the connection policy to convert to priority 5632 * @return the equivalent priority constant to the connectionPolicy 5633 * 5634 * @hide 5635 */ connectionPolicyToPriority(@onnectionPolicy int connectionPolicy)5636 public static int connectionPolicyToPriority(@ConnectionPolicy int connectionPolicy) { 5637 switch(connectionPolicy) { 5638 case BluetoothProfile.CONNECTION_POLICY_ALLOWED: 5639 return BluetoothProfile.PRIORITY_ON; 5640 case BluetoothProfile.CONNECTION_POLICY_FORBIDDEN: 5641 return BluetoothProfile.PRIORITY_OFF; 5642 case BluetoothProfile.CONNECTION_POLICY_UNKNOWN: 5643 return BluetoothProfile.PRIORITY_UNDEFINED; 5644 } 5645 return BluetoothProfile.PRIORITY_UNDEFINED; 5646 } 5647 5648 /** 5649 * Sets the desired mode of the HCI snoop logging applied at Bluetooth startup. 5650 * 5651 * Please note that Bluetooth needs to be restarted in order for the change 5652 * to take effect. 5653 * 5654 * @param mode 5655 * @return status code indicating whether the logging mode was successfully set 5656 * @throws IllegalArgumentException if the mode is not a valid logging mode 5657 * @hide 5658 */ 5659 @SystemApi 5660 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) 5661 @SetSnoopLogModeStatusCode setBluetoothHciSnoopLoggingMode(@luetoothSnoopLogMode int mode)5662 public int setBluetoothHciSnoopLoggingMode(@BluetoothSnoopLogMode int mode) { 5663 if (mode != BT_SNOOP_LOG_MODE_DISABLED && mode != BT_SNOOP_LOG_MODE_FILTERED 5664 && mode != BT_SNOOP_LOG_MODE_FULL) { 5665 throw new IllegalArgumentException("Invalid Bluetooth HCI snoop log mode param value"); 5666 } 5667 try { 5668 return mManagerService.setBtHciSnoopLogMode(mode); 5669 } catch (RemoteException e) { 5670 Log.e(TAG, "", e); 5671 } 5672 return BluetoothStatusCodes.ERROR_UNKNOWN; 5673 } 5674 5675 /** 5676 * Gets the current desired mode of HCI snoop logging applied at Bluetooth startup. 5677 * 5678 * @return the current HCI snoop logging mode applied at Bluetooth startup 5679 * @hide 5680 */ 5681 @SystemApi 5682 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) 5683 @BluetoothSnoopLogMode getBluetoothHciSnoopLoggingMode()5684 public int getBluetoothHciSnoopLoggingMode() { 5685 try { 5686 return mManagerService.getBtHciSnoopLogMode(); 5687 } catch (RemoteException e) { 5688 Log.e(TAG, "", e); 5689 } 5690 return BT_SNOOP_LOG_MODE_DISABLED; 5691 } 5692 5693 /** @hide */ 5694 @Retention(RetentionPolicy.SOURCE) 5695 @IntDef(value = { 5696 BluetoothStatusCodes.FEATURE_SUPPORTED, 5697 BluetoothStatusCodes.ERROR_UNKNOWN, 5698 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 5699 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION, 5700 BluetoothStatusCodes.FEATURE_NOT_SUPPORTED 5701 }) 5702 public @interface GetOffloadedTransportDiscoveryDataScanSupportedReturnValues {} 5703 5704 /** 5705 * Check if offloaded transport discovery data scan is supported or not. 5706 * 5707 * @return {@code BluetoothStatusCodes.FEATURE_SUPPORTED} if chipset supports on-chip tds 5708 * filter scan 5709 * @hide 5710 */ 5711 @SystemApi 5712 @RequiresBluetoothScanPermission 5713 @RequiresPermission(allOf = { 5714 android.Manifest.permission.BLUETOOTH_SCAN, 5715 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 5716 }) 5717 @GetOffloadedTransportDiscoveryDataScanSupportedReturnValues getOffloadedTransportDiscoveryDataScanSupported()5718 public int getOffloadedTransportDiscoveryDataScanSupported() { 5719 if (!getLeAccess()) { 5720 return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; 5721 } 5722 mServiceLock.readLock().lock(); 5723 try { 5724 if (mService != null) { 5725 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 5726 mService.getOffloadedTransportDiscoveryDataScanSupported(mAttributionSource, recv); 5727 return recv.awaitResultNoInterrupt(getSyncTimeout()) 5728 .getValue(BluetoothStatusCodes.ERROR_UNKNOWN); 5729 } 5730 } catch (RemoteException | TimeoutException e) { 5731 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 5732 } finally { 5733 mServiceLock.readLock().unlock(); 5734 } 5735 return BluetoothStatusCodes.ERROR_UNKNOWN; 5736 } 5737 } 5738