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