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