blob: 5094498dbee559db68e4578140d5e2335f15e40a [file] [log] [blame]
Aurimas Liutikas93554f22022-04-19 16:51:35 -07001/*
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
18package android.bluetooth;
19
20import static java.util.Objects.requireNonNull;
21
22import android.annotation.CallbackExecutor;
23import android.annotation.IntDef;
24import android.annotation.NonNull;
25import android.annotation.Nullable;
26import android.annotation.RequiresNoPermission;
27import android.annotation.RequiresPermission;
28import android.annotation.SdkConstant;
29import android.annotation.SdkConstant.SdkConstantType;
30import android.annotation.SuppressLint;
31import android.annotation.SystemApi;
32import android.app.ActivityThread;
33import android.app.PropertyInvalidatedCache;
34import android.bluetooth.BluetoothDevice.Transport;
35import android.bluetooth.BluetoothProfile.ConnectionPolicy;
36import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
37import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
38import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
39import android.bluetooth.annotations.RequiresBluetoothScanPermission;
40import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
41import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
42import android.bluetooth.le.BluetoothLeAdvertiser;
43import android.bluetooth.le.BluetoothLeScanner;
44import android.bluetooth.le.PeriodicAdvertisingManager;
45import android.bluetooth.le.ScanCallback;
46import android.bluetooth.le.ScanFilter;
47import android.bluetooth.le.ScanRecord;
48import android.bluetooth.le.ScanResult;
49import android.bluetooth.le.ScanSettings;
50import android.compat.annotation.UnsupportedAppUsage;
51import android.content.Attributable;
52import android.content.AttributionSource;
53import android.content.Context;
54import android.os.BatteryStats;
55import android.os.Binder;
56import android.os.Build;
57import android.os.IBinder;
58import android.os.ParcelUuid;
59import android.os.RemoteException;
60import android.os.ResultReceiver;
61import android.os.ServiceManager;
62import android.os.SynchronousResultReceiver;
63import android.os.SystemProperties;
64import android.util.Log;
65import android.util.Pair;
66
67import com.android.internal.annotations.GuardedBy;
68
69import java.io.IOException;
70import java.lang.annotation.Retention;
71import java.lang.annotation.RetentionPolicy;
72import java.util.ArrayList;
73import java.util.Arrays;
74import java.util.Collections;
75import java.util.HashMap;
76import java.util.HashSet;
77import java.util.List;
78import java.util.Locale;
79import java.util.Map;
80import java.util.Objects;
81import java.util.Set;
82import java.util.UUID;
83import java.util.WeakHashMap;
84import java.util.concurrent.Executor;
85import java.util.concurrent.TimeoutException;
86import java.util.concurrent.locks.ReentrantReadWriteLock;
87
88/**
89 * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
90 * lets you perform fundamental Bluetooth tasks, such as initiate
91 * device discovery, query a list of bonded (paired) devices,
92 * instantiate a {@link BluetoothDevice} using a known MAC address, and create
93 * a {@link BluetoothServerSocket} to listen for connection requests from other
94 * devices, and start a scan for Bluetooth LE devices.
95 *
96 * <p>To get a {@link BluetoothAdapter} representing the local Bluetooth
97 * adapter, call the {@link BluetoothManager#getAdapter} function on {@link BluetoothManager}.
98 * On JELLY_BEAN_MR1 and below you will need to use the static {@link #getDefaultAdapter}
99 * method instead.
100 * </p><p>
101 * Fundamentally, this is your starting point for all
102 * Bluetooth actions. Once you have the local adapter, you can get a set of
103 * {@link BluetoothDevice} objects representing all paired devices with
104 * {@link #getBondedDevices()}; start device discovery with
105 * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
106 * listen for incoming RFComm connection requests with {@link
107 * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
108 * Channels (CoC) connection requests with {@link #listenUsingL2capChannel()}; or start a scan for
109 * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
110 * </p>
111 * <p>This class is thread safe.</p>
112 * <div class="special reference">
113 * <h3>Developer Guides</h3>
114 * <p>
115 * For more information about using Bluetooth, read the <a href=
116 * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer
117 * guide.
118 * </p>
119 * </div>
120 *
121 * {@see BluetoothDevice}
122 * {@see BluetoothServerSocket}
123 */
124public final class BluetoothAdapter {
125 private static final String TAG = "BluetoothAdapter";
126 private static final String DESCRIPTOR = "android.bluetooth.BluetoothAdapter";
127 private static final boolean DBG = true;
128 private static final boolean VDBG = false;
129
130 /**
131 * Default MAC address reported to a client that does not have the
132 * android.permission.LOCAL_MAC_ADDRESS permission.
133 *
134 * @hide
135 */
136 public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
137
138 /**
139 * Sentinel error value for this class. Guaranteed to not equal any other
140 * integer constant in this class. Provided as a convenience for functions
141 * that require a sentinel error value, for example:
142 * <p><code>Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
143 * BluetoothAdapter.ERROR)</code>
144 */
145 public static final int ERROR = Integer.MIN_VALUE;
146
147 /**
148 * Broadcast Action: The state of the local Bluetooth adapter has been
149 * changed.
150 * <p>For example, Bluetooth has been turned on or off.
151 * <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link
152 * #EXTRA_PREVIOUS_STATE} containing the new and old states
153 * respectively.
154 */
155 @RequiresLegacyBluetoothPermission
156 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
157 ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED";
158
159 /**
160 * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
161 * intents to request the current power state. Possible values are:
162 * {@link #STATE_OFF},
163 * {@link #STATE_TURNING_ON},
164 * {@link #STATE_ON},
165 * {@link #STATE_TURNING_OFF},
166 */
167 public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE";
168 /**
169 * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
170 * intents to request the previous power state. Possible values are:
171 * {@link #STATE_OFF},
172 * {@link #STATE_TURNING_ON},
173 * {@link #STATE_ON},
174 * {@link #STATE_TURNING_OFF}
175 */
176 public static final String EXTRA_PREVIOUS_STATE =
177 "android.bluetooth.adapter.extra.PREVIOUS_STATE";
178
179 /** @hide */
180 @IntDef(prefix = { "STATE_" }, value = {
181 STATE_OFF,
182 STATE_TURNING_ON,
183 STATE_ON,
184 STATE_TURNING_OFF,
185 STATE_BLE_TURNING_ON,
186 STATE_BLE_ON,
187 STATE_BLE_TURNING_OFF
188 })
189 @Retention(RetentionPolicy.SOURCE)
190 public @interface AdapterState {}
191
192 /**
193 * Indicates the local Bluetooth adapter is off.
194 */
195 public static final int STATE_OFF = 10;
196 /**
197 * Indicates the local Bluetooth adapter is turning on. However local
198 * clients should wait for {@link #STATE_ON} before attempting to
199 * use the adapter.
200 */
201 public static final int STATE_TURNING_ON = 11;
202 /**
203 * Indicates the local Bluetooth adapter is on, and ready for use.
204 */
205 public static final int STATE_ON = 12;
206 /**
207 * Indicates the local Bluetooth adapter is turning off. Local clients
208 * should immediately attempt graceful disconnection of any remote links.
209 */
210 public static final int STATE_TURNING_OFF = 13;
211
212 /**
213 * Indicates the local Bluetooth adapter is turning Bluetooth LE mode on.
214 *
215 * @hide
216 */
217 public static final int STATE_BLE_TURNING_ON = 14;
218
219 /**
220 * Indicates the local Bluetooth adapter is in LE only mode.
221 *
222 * @hide
223 */
224 public static final int STATE_BLE_ON = 15;
225
226 /**
227 * Indicates the local Bluetooth adapter is turning off LE only mode.
228 *
229 * @hide
230 */
231 public static final int STATE_BLE_TURNING_OFF = 16;
232
233 /**
234 * UUID of the GATT Read Characteristics for LE_PSM value.
235 *
236 * @hide
237 */
238 public static final UUID LE_PSM_CHARACTERISTIC_UUID =
239 UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
240
241 /**
242 * Human-readable string helper for AdapterState
243 *
244 * @hide
245 */
246 public static String nameForState(@AdapterState int state) {
247 switch (state) {
248 case STATE_OFF:
249 return "OFF";
250 case STATE_TURNING_ON:
251 return "TURNING_ON";
252 case STATE_ON:
253 return "ON";
254 case STATE_TURNING_OFF:
255 return "TURNING_OFF";
256 case STATE_BLE_TURNING_ON:
257 return "BLE_TURNING_ON";
258 case STATE_BLE_ON:
259 return "BLE_ON";
260 case STATE_BLE_TURNING_OFF:
261 return "BLE_TURNING_OFF";
262 default:
263 return "?!?!? (" + state + ")";
264 }
265 }
266
267 /**
268 * Activity Action: Show a system activity that requests discoverable mode.
269 * This activity will also request the user to turn on Bluetooth if it
270 * is not currently enabled.
271 * <p>Discoverable mode is equivalent to {@link
272 * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see
273 * this Bluetooth adapter when they perform a discovery.
274 * <p>For privacy, Android is not discoverable by default.
275 * <p>The sender of this Intent can optionally use extra field {@link
276 * #EXTRA_DISCOVERABLE_DURATION} to request the duration of
277 * discoverability. Currently the default duration is 120 seconds, and
278 * maximum duration is capped at 300 seconds for each request.
279 * <p>Notification of the result of this activity is posted using the
280 * {@link android.app.Activity#onActivityResult} callback. The
281 * <code>resultCode</code>
282 * will be the duration (in seconds) of discoverability or
283 * {@link android.app.Activity#RESULT_CANCELED} if the user rejected
284 * discoverability or an error has occurred.
285 * <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED}
286 * for global notification whenever the scan mode changes. For example, an
287 * application can be notified when the device has ended discoverability.
288 */
289 @RequiresLegacyBluetoothPermission
290 @RequiresBluetoothAdvertisePermission
291 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
292 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
293 ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
294
295 /**
296 * Used as an optional int extra field in {@link
297 * #ACTION_REQUEST_DISCOVERABLE} intents to request a specific duration
298 * for discoverability in seconds. The current default is 120 seconds, and
299 * requests over 300 seconds will be capped. These values could change.
300 */
301 public static final String EXTRA_DISCOVERABLE_DURATION =
302 "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION";
303
304 /**
305 * Activity Action: Show a system activity that allows the user to turn on
306 * Bluetooth.
307 * <p>This system activity will return once Bluetooth has completed turning
308 * on, or the user has decided not to turn Bluetooth on.
309 * <p>Notification of the result of this activity is posted using the
310 * {@link android.app.Activity#onActivityResult} callback. The
311 * <code>resultCode</code>
312 * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been
313 * turned on or {@link android.app.Activity#RESULT_CANCELED} if the user
314 * has rejected the request or an error has occurred.
315 * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
316 * for global notification whenever Bluetooth is turned on or off.
317 */
318 @RequiresLegacyBluetoothPermission
319 @RequiresBluetoothConnectPermission
320 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
321 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
322 ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
323
324 /**
325 * Activity Action: Show a system activity that allows the user to turn off
326 * Bluetooth. This is used only if permission review is enabled which is for
327 * apps targeting API less than 23 require a permission review before any of
328 * the app's components can run.
329 * <p>This system activity will return once Bluetooth has completed turning
330 * off, or the user has decided not to turn Bluetooth off.
331 * <p>Notification of the result of this activity is posted using the
332 * {@link android.app.Activity#onActivityResult} callback. The
333 * <code>resultCode</code>
334 * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been
335 * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user
336 * has rejected the request or an error has occurred.
337 * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
338 * for global notification whenever Bluetooth is turned on or off.
339 *
340 * @hide
341 */
342 @RequiresLegacyBluetoothPermission
343 @RequiresBluetoothConnectPermission
344 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
345 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
346 ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE";
347
348 /**
349 * Activity Action: Show a system activity that allows user to enable BLE scans even when
350 * Bluetooth is turned off.<p>
351 *
352 * Notification of result of this activity is posted using
353 * {@link android.app.Activity#onActivityResult}. The <code>resultCode</code> will be
354 * {@link android.app.Activity#RESULT_OK} if BLE scan always available setting is turned on or
355 * {@link android.app.Activity#RESULT_CANCELED} if the user has rejected the request or an
356 * error occurred.
357 *
358 * @hide
359 */
360 @SystemApi
361 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
362 public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE =
363 "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
364
365 /**
366 * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter
367 * has changed.
368 * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
369 * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes
370 * respectively.
371 */
372 @RequiresLegacyBluetoothPermission
373 @RequiresBluetoothScanPermission
374 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
375 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
376 ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
377
378 /**
379 * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
380 * intents to request the current scan mode. Possible values are:
381 * {@link #SCAN_MODE_NONE},
382 * {@link #SCAN_MODE_CONNECTABLE},
383 * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
384 */
385 public static final String EXTRA_SCAN_MODE = "android.bluetooth.adapter.extra.SCAN_MODE";
386 /**
387 * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
388 * intents to request the previous scan mode. Possible values are:
389 * {@link #SCAN_MODE_NONE},
390 * {@link #SCAN_MODE_CONNECTABLE},
391 * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
392 */
393 public static final String EXTRA_PREVIOUS_SCAN_MODE =
394 "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE";
395
396 /** @hide */
397 @IntDef(prefix = { "SCAN_" }, value = {
398 SCAN_MODE_NONE,
399 SCAN_MODE_CONNECTABLE,
400 SCAN_MODE_CONNECTABLE_DISCOVERABLE
401 })
402 @Retention(RetentionPolicy.SOURCE)
403 public @interface ScanMode {}
404
405 /**
406 * Indicates that both inquiry scan and page scan are disabled on the local
407 * Bluetooth adapter. Therefore this device is neither discoverable
408 * nor connectable from remote Bluetooth devices.
409 */
410 public static final int SCAN_MODE_NONE = 20;
411 /**
412 * Indicates that inquiry scan is disabled, but page scan is enabled on the
413 * local Bluetooth adapter. Therefore this device is not discoverable from
414 * remote Bluetooth devices, but is connectable from remote devices that
415 * have previously discovered this device.
416 */
417 public static final int SCAN_MODE_CONNECTABLE = 21;
418 /**
419 * Indicates that both inquiry scan and page scan are enabled on the local
420 * Bluetooth adapter. Therefore this device is both discoverable and
421 * connectable from remote Bluetooth devices.
422 */
423 public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23;
424
425 /**
426 * Device only has a display.
427 *
428 * @hide
429 */
430 public static final int IO_CAPABILITY_OUT = 0;
431
432 /**
433 * Device has a display and the ability to input Yes/No.
434 *
435 * @hide
436 */
437 public static final int IO_CAPABILITY_IO = 1;
438
439 /**
440 * Device only has a keyboard for entry but no display.
441 *
442 * @hide
443 */
444 public static final int IO_CAPABILITY_IN = 2;
445
446 /**
447 * Device has no Input or Output capability.
448 *
449 * @hide
450 */
451 public static final int IO_CAPABILITY_NONE = 3;
452
453 /**
454 * Device has a display and a full keyboard.
455 *
456 * @hide
457 */
458 public static final int IO_CAPABILITY_KBDISP = 4;
459
460 /**
461 * Maximum range value for Input/Output capabilities.
462 *
463 * <p>This should be updated when adding a new Input/Output capability. Other code
464 * like validation depends on this being accurate.
465 *
466 * @hide
467 */
468 public static final int IO_CAPABILITY_MAX = 5;
469
470 /**
471 * The Input/Output capability of the device is unknown.
472 *
473 * @hide
474 */
475 public static final int IO_CAPABILITY_UNKNOWN = 255;
476
477 /** @hide */
478 @IntDef({IO_CAPABILITY_OUT, IO_CAPABILITY_IO, IO_CAPABILITY_IN, IO_CAPABILITY_NONE,
479 IO_CAPABILITY_KBDISP})
480 @Retention(RetentionPolicy.SOURCE)
481 public @interface IoCapability {}
482
483 /** @hide */
484 @IntDef(prefix = "ACTIVE_DEVICE_", value = {ACTIVE_DEVICE_AUDIO,
485 ACTIVE_DEVICE_PHONE_CALL, ACTIVE_DEVICE_ALL})
486 @Retention(RetentionPolicy.SOURCE)
487 public @interface ActiveDeviceUse {}
488
489 /**
490 * Use the specified device for audio (a2dp and hearing aid profile)
491 *
492 * @hide
493 */
494 @SystemApi
495 public static final int ACTIVE_DEVICE_AUDIO = 0;
496
497 /**
498 * Use the specified device for phone calls (headset profile and hearing
499 * aid profile)
500 *
501 * @hide
502 */
503 @SystemApi
504 public static final int ACTIVE_DEVICE_PHONE_CALL = 1;
505
506 /**
507 * Use the specified device for a2dp, hearing aid profile, and headset profile
508 *
509 * @hide
510 */
511 @SystemApi
512 public static final int ACTIVE_DEVICE_ALL = 2;
513
514 /**
515 * Broadcast Action: The local Bluetooth adapter has started the remote
516 * device discovery process.
517 * <p>This usually involves an inquiry scan of about 12 seconds, followed
518 * by a page scan of each new device to retrieve its Bluetooth name.
519 * <p>Register for {@link BluetoothDevice#ACTION_FOUND} to be notified as
520 * remote Bluetooth devices are found.
521 * <p>Device discovery is a heavyweight procedure. New connections to
522 * remote Bluetooth devices should not be attempted while discovery is in
523 * progress, and existing connections will experience limited bandwidth
524 * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
525 * discovery.
526 */
527 @RequiresLegacyBluetoothPermission
528 @RequiresBluetoothScanPermission
529 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
530 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
531 ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
532 /**
533 * Broadcast Action: The local Bluetooth adapter has finished the device
534 * discovery process.
535 */
536 @RequiresLegacyBluetoothPermission
537 @RequiresBluetoothScanPermission
538 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
539 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
540 ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
541
542 /**
543 * Broadcast Action: The local Bluetooth adapter has changed its friendly
544 * Bluetooth name.
545 * <p>This name is visible to remote Bluetooth devices.
546 * <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing
547 * the name.
548 */
549 @RequiresLegacyBluetoothPermission
550 @RequiresBluetoothConnectPermission
551 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
552 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
553 ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
554 /**
555 * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED}
556 * intents to request the local Bluetooth name.
557 */
558 public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME";
559
560 /**
561 * Intent used to broadcast the change in connection state of the local
562 * Bluetooth adapter to a profile of the remote device. When the adapter is
563 * not connected to any profiles of any remote devices and it attempts a
564 * connection to a profile this intent will be sent. Once connected, this intent
565 * will not be sent for any more connection attempts to any profiles of any
566 * remote device. When the adapter disconnects from the last profile its
567 * connected to of any remote device, this intent will be sent.
568 *
569 * <p> This intent is useful for applications that are only concerned about
570 * whether the local adapter is connected to any profile of any device and
571 * are not really concerned about which profile. For example, an application
572 * which displays an icon to display whether Bluetooth is connected or not
573 * can use this intent.
574 *
575 * <p>This intent will have 3 extras:
576 * {@link #EXTRA_CONNECTION_STATE} - The current connection state.
577 * {@link #EXTRA_PREVIOUS_CONNECTION_STATE}- The previous connection state.
578 * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
579 *
580 * {@link #EXTRA_CONNECTION_STATE} or {@link #EXTRA_PREVIOUS_CONNECTION_STATE}
581 * can be any of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
582 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
583 */
584 @RequiresLegacyBluetoothPermission
585 @RequiresBluetoothConnectPermission
586 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
587 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
588 ACTION_CONNECTION_STATE_CHANGED =
589 "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
590
591 /**
592 * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
593 *
594 * This extra represents the current connection state.
595 */
596 public static final String EXTRA_CONNECTION_STATE =
597 "android.bluetooth.adapter.extra.CONNECTION_STATE";
598
599 /**
600 * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
601 *
602 * This extra represents the previous connection state.
603 */
604 public static final String EXTRA_PREVIOUS_CONNECTION_STATE =
605 "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE";
606
607 /**
608 * Broadcast Action: The Bluetooth adapter state has changed in LE only mode.
609 *
610 * @hide
611 */
612 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
613 @SystemApi public static final String ACTION_BLE_STATE_CHANGED =
614 "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
615
616 /**
617 * Intent used to broadcast the change in the Bluetooth address
618 * of the local Bluetooth adapter.
619 * <p>Always contains the extra field {@link
620 * #EXTRA_BLUETOOTH_ADDRESS} containing the Bluetooth address.
621 *
622 * Note: only system level processes are allowed to send this
623 * defined broadcast.
624 *
625 * @hide
626 */
627 @RequiresBluetoothConnectPermission
628 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
629 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
630 public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED =
631 "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED";
632
633 /**
634 * Used as a String extra field in {@link
635 * #ACTION_BLUETOOTH_ADDRESS_CHANGED} intent to store the local
636 * Bluetooth address.
637 *
638 * @hide
639 */
640 public static final String EXTRA_BLUETOOTH_ADDRESS =
641 "android.bluetooth.adapter.extra.BLUETOOTH_ADDRESS";
642
643 /**
644 * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
645 * by BLE Always on enabled application to know the ACL_CONNECTED event
646 * when Bluetooth state in STATE_BLE_ON. This denotes GATT connection
647 * as Bluetooth LE is the only feature available in STATE_BLE_ON
648 *
649 * This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which
650 * works in Bluetooth state STATE_ON
651 *
652 * @hide
653 */
654 @RequiresBluetoothConnectPermission
655 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
656 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
657 public static final String ACTION_BLE_ACL_CONNECTED =
658 "android.bluetooth.adapter.action.BLE_ACL_CONNECTED";
659
660 /**
661 * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
662 * by BLE Always on enabled application to know the ACL_DISCONNECTED event
663 * when Bluetooth state in STATE_BLE_ON. This denotes GATT disconnection as Bluetooth
664 * LE is the only feature available in STATE_BLE_ON
665 *
666 * This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which
667 * works in Bluetooth state STATE_ON
668 *
669 * @hide
670 */
671 @RequiresBluetoothConnectPermission
672 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
673 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
674 public static final String ACTION_BLE_ACL_DISCONNECTED =
675 "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED";
676
677 /** The profile is in disconnected state */
678 public static final int STATE_DISCONNECTED = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED;
679 /** The profile is in connecting state */
680 public static final int STATE_CONNECTING = BluetoothProtoEnums.CONNECTION_STATE_CONNECTING;
681 /** The profile is in connected state */
682 public static final int STATE_CONNECTED = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED;
683 /** The profile is in disconnecting state */
684 public static final int STATE_DISCONNECTING =
685 BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING;
686
687 /** @hide */
688 public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
689 private final IBinder mToken;
690
691
692 /**
693 * When creating a ServerSocket using listenUsingRfcommOn() or
694 * listenUsingL2capOn() use SOCKET_CHANNEL_AUTO_STATIC to create
695 * a ServerSocket that auto assigns a channel number to the first
696 * bluetooth socket.
697 * The channel number assigned to this first Bluetooth Socket will
698 * be stored in the ServerSocket, and reused for subsequent Bluetooth
699 * sockets.
700 *
701 * @hide
702 */
703 public static final int SOCKET_CHANNEL_AUTO_STATIC_NO_SDP = -2;
704
705
706 private static final int ADDRESS_LENGTH = 17;
707
708 /**
709 * Lazily initialized singleton. Guaranteed final after first object
710 * constructed.
711 */
712 private static BluetoothAdapter sAdapter;
713
714 private BluetoothLeScanner mBluetoothLeScanner;
715 private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
716 private PeriodicAdvertisingManager mPeriodicAdvertisingManager;
717
718 private final IBluetoothManager mManagerService;
719 private final AttributionSource mAttributionSource;
720
721 // Yeah, keeping both mService and sService isn't pretty, but it's too late
722 // in the current release for a major refactoring, so we leave them both
723 // intact until this can be cleaned up in a future release
724
725 @UnsupportedAppUsage
726 @GuardedBy("mServiceLock")
727 private IBluetooth mService;
728 private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
729
730 @GuardedBy("sServiceLock")
731 private static boolean sServiceRegistered;
732 @GuardedBy("sServiceLock")
733 private static IBluetooth sService;
734 private static final Object sServiceLock = new Object();
735
736 private final Object mLock = new Object();
737 private final Map<LeScanCallback, ScanCallback> mLeScanClients;
738 private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>>
739 mMetadataListeners = new HashMap<>();
740 private final Map<BluetoothConnectionCallback, Executor>
741 mBluetoothConnectionCallbackExecutorMap = new HashMap<>();
742
743 /**
744 * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener
745 * implementation.
746 */
747 @SuppressLint("AndroidFrameworkBluetoothPermission")
748 private final IBluetoothMetadataListener mBluetoothMetadataListener =
749 new IBluetoothMetadataListener.Stub() {
750 @Override
751 public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) {
752 Attributable.setAttributionSource(device, mAttributionSource);
753 synchronized (mMetadataListeners) {
754 if (mMetadataListeners.containsKey(device)) {
755 List<Pair<OnMetadataChangedListener, Executor>> list =
756 mMetadataListeners.get(device);
757 for (Pair<OnMetadataChangedListener, Executor> pair : list) {
758 OnMetadataChangedListener listener = pair.first;
759 Executor executor = pair.second;
760 executor.execute(() -> {
761 listener.onMetadataChanged(device, key, value);
762 });
763 }
764 }
765 }
766 return;
767 }
768 };
769
770 /**
771 * Get a handle to the default local Bluetooth adapter.
772 * <p>
773 * Currently Android only supports one Bluetooth adapter, but the API could
774 * be extended to support more. This will always return the default adapter.
775 * </p>
776 *
777 * @return the default local adapter, or null if Bluetooth is not supported
778 * on this hardware platform
779 * @deprecated this method will continue to work, but developers are
780 * strongly encouraged to migrate to using
781 * {@link BluetoothManager#getAdapter()}, since that approach
782 * enables support for {@link Context#createAttributionContext}.
783 */
784 @Deprecated
785 @RequiresNoPermission
786 public static synchronized BluetoothAdapter getDefaultAdapter() {
787 if (sAdapter == null) {
788 sAdapter = createAdapter(BluetoothManager.resolveAttributionSource(null));
789 }
790 return sAdapter;
791 }
792
793 /** {@hide} */
794 public static BluetoothAdapter createAdapter(AttributionSource attributionSource) {
795 IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
796 if (binder != null) {
797 return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder),
798 attributionSource);
799 } else {
800 Log.e(TAG, "Bluetooth binder is null");
801 return null;
802 }
803 }
804
805 /**
806 * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
807 */
808 BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) {
809 mManagerService = Objects.requireNonNull(managerService);
810 mAttributionSource = Objects.requireNonNull(attributionSource);
811 synchronized (mServiceLock.writeLock()) {
812 mService = getBluetoothService(mManagerCallback);
813 }
814 mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
815 mToken = new Binder(DESCRIPTOR);
816 }
817
818 /**
819 * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
820 * address.
821 * <p>Valid Bluetooth hardware addresses must be upper case, in a format
822 * such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is
823 * available to validate a Bluetooth address.
824 * <p>A {@link BluetoothDevice} will always be returned for a valid
825 * hardware address, even if this adapter has never seen that device.
826 *
827 * @param address valid Bluetooth MAC address
828 * @throws IllegalArgumentException if address is invalid
829 */
830 @RequiresNoPermission
831 public BluetoothDevice getRemoteDevice(String address) {
832 final BluetoothDevice res = new BluetoothDevice(address);
833 res.setAttributionSource(mAttributionSource);
834 return res;
835 }
836
837 /**
838 * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
839 * address.
840 * <p>Valid Bluetooth hardware addresses must be 6 bytes. This method
841 * expects the address in network byte order (MSB first).
842 * <p>A {@link BluetoothDevice} will always be returned for a valid
843 * hardware address, even if this adapter has never seen that device.
844 *
845 * @param address Bluetooth MAC address (6 bytes)
846 * @throws IllegalArgumentException if address is invalid
847 */
848 @RequiresNoPermission
849 public BluetoothDevice getRemoteDevice(byte[] address) {
850 if (address == null || address.length != 6) {
851 throw new IllegalArgumentException("Bluetooth address must have 6 bytes");
852 }
853 final BluetoothDevice res = new BluetoothDevice(
854 String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1],
855 address[2], address[3], address[4], address[5]));
856 res.setAttributionSource(mAttributionSource);
857 return res;
858 }
859
860 /**
861 * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations.
862 * Will return null if Bluetooth is turned off or if Bluetooth LE Advertising is not
863 * supported on this device.
864 * <p>
865 * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported
866 * on this device before calling this method.
867 */
868 @RequiresNoPermission
869 public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
870 if (!getLeAccess()) {
871 return null;
872 }
873 synchronized (mLock) {
874 if (mBluetoothLeAdvertiser == null) {
875 mBluetoothLeAdvertiser = new BluetoothLeAdvertiser(this);
876 }
877 return mBluetoothLeAdvertiser;
878 }
879 }
880
881 /**
882 * Returns a {@link PeriodicAdvertisingManager} object for Bluetooth LE Periodic Advertising
883 * operations. Will return null if Bluetooth is turned off or if Bluetooth LE Periodic
884 * Advertising is not supported on this device.
885 * <p>
886 * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is
887 * supported on this device before calling this method.
888 *
889 * @hide
890 */
891 @RequiresNoPermission
892 public PeriodicAdvertisingManager getPeriodicAdvertisingManager() {
893 if (!getLeAccess()) {
894 return null;
895 }
896
897 if (!isLePeriodicAdvertisingSupported()) {
898 return null;
899 }
900
901 synchronized (mLock) {
902 if (mPeriodicAdvertisingManager == null) {
903 mPeriodicAdvertisingManager = new PeriodicAdvertisingManager(this);
904 }
905 return mPeriodicAdvertisingManager;
906 }
907 }
908
909 /**
910 * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
911 */
912 @RequiresNoPermission
913 public BluetoothLeScanner getBluetoothLeScanner() {
914 if (!getLeAccess()) {
915 return null;
916 }
917 synchronized (mLock) {
918 if (mBluetoothLeScanner == null) {
919 mBluetoothLeScanner = new BluetoothLeScanner(this);
920 }
921 return mBluetoothLeScanner;
922 }
923 }
924
925 /**
926 * Return true if Bluetooth is currently enabled and ready for use.
927 * <p>Equivalent to:
928 * <code>getBluetoothState() == STATE_ON</code>
929 *
930 * @return true if the local adapter is turned on
931 */
932 @RequiresLegacyBluetoothPermission
933 @RequiresNoPermission
934 public boolean isEnabled() {
935 return getState() == BluetoothAdapter.STATE_ON;
936 }
937
938 /**
939 * Return true if Bluetooth LE(Always BLE On feature) is currently
940 * enabled and ready for use
941 * <p>This returns true if current state is either STATE_ON or STATE_BLE_ON
942 *
943 * @return true if the local Bluetooth LE adapter is turned on
944 * @hide
945 */
946 @SystemApi
947 @RequiresNoPermission
948 public boolean isLeEnabled() {
949 final int state = getLeState();
950 if (DBG) {
951 Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state));
952 }
953 return (state == BluetoothAdapter.STATE_ON
954 || state == BluetoothAdapter.STATE_BLE_ON
955 || state == BluetoothAdapter.STATE_TURNING_ON
956 || state == BluetoothAdapter.STATE_TURNING_OFF);
957 }
958
959 /**
960 * Turns off Bluetooth LE which was earlier turned on by calling enableBLE().
961 *
962 * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition
963 * to STATE_OFF and completely shut-down Bluetooth
964 *
965 * <p> If the Adapter state is STATE_ON, This would unregister the existance of
966 * special Bluetooth LE application and hence the further turning off of Bluetooth
967 * from UI would ensure the complete turn-off of Bluetooth rather than staying back
968 * BLE only state
969 *
970 * <p>This is an asynchronous call: it will return immediately, and
971 * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
972 * to be notified of subsequent adapter state changes If this call returns
973 * true, then the adapter state will immediately transition from {@link
974 * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
975 * later transition to either {@link #STATE_BLE_ON} or {@link
976 * #STATE_OFF} based on the existance of the further Always BLE ON enabled applications
977 * If this call returns false then there was an
978 * immediate problem that will prevent the QAdapter from being turned off -
979 * such as the QAadapter already being turned off.
980 *
981 * @return true to indicate success, or false on immediate error
982 * @hide
983 */
984 @SystemApi
985 @RequiresBluetoothConnectPermission
986 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
987 public boolean disableBLE() {
988 if (!isBleScanAlwaysAvailable()) {
989 return false;
990 }
991 String packageName = ActivityThread.currentPackageName();
992 try {
993 return mManagerService.disableBle(mAttributionSource, mToken);
994 } catch (RemoteException e) {
995 Log.e(TAG, "", e);
996 }
997 return false;
998 }
999
1000 /**
1001 * Applications who want to only use Bluetooth Low Energy (BLE) can call enableBLE.
1002 *
1003 * enableBLE registers the existence of an app using only LE functions.
1004 *
1005 * enableBLE may enable Bluetooth to an LE only mode so that an app can use
1006 * LE related features (BluetoothGatt or BluetoothGattServer classes)
1007 *
1008 * If the user disables Bluetooth while an app is registered to use LE only features,
1009 * Bluetooth will remain on in LE only mode for the app.
1010 *
1011 * When Bluetooth is in LE only mode, it is not shown as ON to the UI.
1012 *
1013 * <p>This is an asynchronous call: it returns immediately, and
1014 * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
1015 * to be notified of adapter state changes.
1016 *
1017 * If this call returns * true, then the adapter state is either in a mode where
1018 * LE is available, or will transition from {@link #STATE_OFF} to {@link #STATE_BLE_TURNING_ON},
1019 * and some time later transition to either {@link #STATE_OFF} or {@link #STATE_BLE_ON}.
1020 *
1021 * If this call returns false then there was an immediate problem that prevents the
1022 * adapter from being turned on - such as Airplane mode.
1023 *
1024 * {@link #ACTION_BLE_STATE_CHANGED} returns the Bluetooth Adapter's various
1025 * states, It includes all the classic Bluetooth Adapter states along with
1026 * internal BLE only states
1027 *
1028 * @return true to indicate Bluetooth LE will be available, or false on immediate error
1029 * @hide
1030 */
1031 @SystemApi
1032 @RequiresBluetoothConnectPermission
1033 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1034 public boolean enableBLE() {
1035 if (!isBleScanAlwaysAvailable()) {
1036 return false;
1037 }
1038 String packageName = ActivityThread.currentPackageName();
1039 try {
1040 return mManagerService.enableBle(mAttributionSource, mToken);
1041 } catch (RemoteException e) {
1042 Log.e(TAG, "", e);
1043 }
1044
1045 return false;
1046 }
1047
1048 private static final String BLUETOOTH_GET_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_state";
1049
1050 private final PropertyInvalidatedCache<Void, Integer> mBluetoothGetStateCache =
1051 new PropertyInvalidatedCache<Void, Integer>(
1052 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) {
1053 @Override
1054 @SuppressLint("AndroidFrameworkRequiresPermission")
1055 protected Integer recompute(Void query) {
1056 try {
1057 return mService.getState();
1058 } catch (RemoteException e) {
1059 throw e.rethrowFromSystemServer();
1060 }
1061 }
1062 };
1063
1064 /** @hide */
1065 @RequiresNoPermission
1066 public void disableBluetoothGetStateCache() {
1067 mBluetoothGetStateCache.disableLocal();
1068 }
1069
1070 /** @hide */
1071 public static void invalidateBluetoothGetStateCache() {
1072 PropertyInvalidatedCache.invalidateCache(BLUETOOTH_GET_STATE_CACHE_PROPERTY);
1073 }
1074
1075 /**
1076 * Fetch the current bluetooth state. If the service is down, return
1077 * OFF.
1078 */
1079 @AdapterState
1080 private int getStateInternal() {
1081 int state = BluetoothAdapter.STATE_OFF;
1082 try {
1083 mServiceLock.readLock().lock();
1084 if (mService != null) {
1085 state = mBluetoothGetStateCache.query(null);
1086 }
1087 } catch (RuntimeException e) {
1088 if (e.getCause() instanceof RemoteException) {
1089 Log.e(TAG, "", e.getCause());
1090 } else {
1091 throw e;
1092 }
1093 } finally {
1094 mServiceLock.readLock().unlock();
1095 }
1096 return state;
1097 }
1098
1099 /**
1100 * Get the current state of the local Bluetooth adapter.
1101 * <p>Possible return values are
1102 * {@link #STATE_OFF},
1103 * {@link #STATE_TURNING_ON},
1104 * {@link #STATE_ON},
1105 * {@link #STATE_TURNING_OFF}.
1106 *
1107 * @return current state of Bluetooth adapter
1108 */
1109 @RequiresLegacyBluetoothPermission
1110 @RequiresNoPermission
1111 @AdapterState
1112 public int getState() {
1113 int state = getStateInternal();
1114
1115 // Consider all internal states as OFF
1116 if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
1117 || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
1118 if (VDBG) {
1119 Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF");
1120 }
1121 state = BluetoothAdapter.STATE_OFF;
1122 }
1123 if (VDBG) {
1124 Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(
1125 state));
1126 }
1127 return state;
1128 }
1129
1130 /**
1131 * Get the current state of the local Bluetooth adapter
1132 * <p>This returns current internal state of Adapter including LE ON/OFF
1133 *
1134 * <p>Possible return values are
1135 * {@link #STATE_OFF},
1136 * {@link #STATE_BLE_TURNING_ON},
1137 * {@link #STATE_BLE_ON},
1138 * {@link #STATE_TURNING_ON},
1139 * {@link #STATE_ON},
1140 * {@link #STATE_TURNING_OFF},
1141 * {@link #STATE_BLE_TURNING_OFF}.
1142 *
1143 * @return current state of Bluetooth adapter
1144 * @hide
1145 */
1146 @RequiresLegacyBluetoothPermission
1147 @RequiresNoPermission
1148 @AdapterState
1149 @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine "
1150 + "whether you can use BLE & BT classic.")
1151 public int getLeState() {
1152 int state = getStateInternal();
1153
1154 if (VDBG) {
1155 Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state));
1156 }
1157 return state;
1158 }
1159
1160 boolean getLeAccess() {
1161 if (getLeState() == STATE_ON) {
1162 return true;
1163 } else if (getLeState() == STATE_BLE_ON) {
1164 return true; // TODO: FILTER SYSTEM APPS HERE <--
1165 }
1166
1167 return false;
1168 }
1169
1170 /**
1171 * Turn on the local Bluetooth adapter&mdash;do not use without explicit
1172 * user action to turn on Bluetooth.
1173 * <p>This powers on the underlying Bluetooth hardware, and starts all
1174 * Bluetooth system services.
1175 * <p class="caution"><strong>Bluetooth should never be enabled without
1176 * direct user consent</strong>. If you want to turn on Bluetooth in order
1177 * to create a wireless connection, you should use the {@link
1178 * #ACTION_REQUEST_ENABLE} Intent, which will raise a dialog that requests
1179 * user permission to turn on Bluetooth. The {@link #enable()} method is
1180 * provided only for applications that include a user interface for changing
1181 * system settings, such as a "power manager" app.</p>
1182 * <p>This is an asynchronous call: it will return immediately, and
1183 * clients should listen for {@link #ACTION_STATE_CHANGED}
1184 * to be notified of subsequent adapter state changes. If this call returns
1185 * true, then the adapter state will immediately transition from {@link
1186 * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time
1187 * later transition to either {@link #STATE_OFF} or {@link
1188 * #STATE_ON}. If this call returns false then there was an
1189 * immediate problem that will prevent the adapter from being turned on -
1190 * such as Airplane mode, or the adapter is already turned on.
1191 *
1192 * @return true to indicate adapter startup has begun, or false on immediate error
1193 */
1194 @RequiresLegacyBluetoothAdminPermission
1195 @RequiresBluetoothConnectPermission
1196 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1197 public boolean enable() {
1198 if (isEnabled()) {
1199 if (DBG) {
1200 Log.d(TAG, "enable(): BT already enabled!");
1201 }
1202 return true;
1203 }
1204 try {
1205 return mManagerService.enable(mAttributionSource);
1206 } catch (RemoteException e) {
1207 Log.e(TAG, "", e);
1208 }
1209 return false;
1210 }
1211
1212 /**
1213 * Turn off the local Bluetooth adapter&mdash;do not use without explicit
1214 * user action to turn off Bluetooth.
1215 * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
1216 * system services, and powers down the underlying Bluetooth hardware.
1217 * <p class="caution"><strong>Bluetooth should never be disabled without
1218 * direct user consent</strong>. The {@link #disable()} method is
1219 * provided only for applications that include a user interface for changing
1220 * system settings, such as a "power manager" app.</p>
1221 * <p>This is an asynchronous call: it will return immediately, and
1222 * clients should listen for {@link #ACTION_STATE_CHANGED}
1223 * to be notified of subsequent adapter state changes. If this call returns
1224 * true, then the adapter state will immediately transition from {@link
1225 * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
1226 * later transition to either {@link #STATE_OFF} or {@link
1227 * #STATE_ON}. If this call returns false then there was an
1228 * immediate problem that will prevent the adapter from being turned off -
1229 * such as the adapter already being turned off.
1230 *
1231 * @return true to indicate adapter shutdown has begun, or false on immediate error
1232 */
1233 @RequiresLegacyBluetoothAdminPermission
1234 @RequiresBluetoothConnectPermission
1235 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1236 public boolean disable() {
1237 try {
1238 return mManagerService.disable(mAttributionSource, true);
1239 } catch (RemoteException e) {
1240 Log.e(TAG, "", e);
1241 }
1242 return false;
1243 }
1244
1245 /**
1246 * Turn off the local Bluetooth adapter and don't persist the setting.
1247 *
1248 * @return true to indicate adapter shutdown has begun, or false on immediate error
1249 * @hide
1250 */
1251 @UnsupportedAppUsage(trackingBug = 171933273)
1252 @RequiresLegacyBluetoothAdminPermission
1253 @RequiresBluetoothConnectPermission
1254 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1255 public boolean disable(boolean persist) {
1256
1257 try {
1258 return mManagerService.disable(mAttributionSource, persist);
1259 } catch (RemoteException e) {
1260 Log.e(TAG, "", e);
1261 }
1262 return false;
1263 }
1264
1265 /**
1266 * Returns the hardware address of the local Bluetooth adapter.
1267 * <p>For example, "00:11:22:AA:BB:CC".
1268 *
1269 * @return Bluetooth hardware address as string
1270 */
1271 @RequiresLegacyBluetoothPermission
1272 @RequiresBluetoothConnectPermission
1273 @RequiresPermission(allOf = {
1274 android.Manifest.permission.BLUETOOTH_CONNECT,
1275 android.Manifest.permission.LOCAL_MAC_ADDRESS,
1276 })
1277 public String getAddress() {
1278 try {
1279 return mManagerService.getAddress(mAttributionSource);
1280 } catch (RemoteException e) {
1281 Log.e(TAG, "", e);
1282 }
1283 return null;
1284 }
1285
1286 /**
1287 * Get the friendly Bluetooth name of the local Bluetooth adapter.
1288 * <p>This name is visible to remote Bluetooth devices.
1289 *
1290 * @return the Bluetooth name, or null on error
1291 */
1292 @RequiresLegacyBluetoothPermission
1293 @RequiresBluetoothConnectPermission
1294 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1295 public String getName() {
1296 try {
1297 return mManagerService.getName(mAttributionSource);
1298 } catch (RemoteException e) {
1299 Log.e(TAG, "", e);
1300 }
1301 return null;
1302 }
1303
1304 /** {@hide} */
1305 @RequiresBluetoothAdvertisePermission
1306 @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
1307 public int getNameLengthForAdvertise() {
1308 try {
1309 return mService.getNameLengthForAdvertise(mAttributionSource);
1310 } catch (RemoteException e) {
1311 Log.e(TAG, "", e);
1312 }
1313 return -1;
1314 }
1315
1316 /**
1317 * Factory reset bluetooth settings.
1318 *
1319 * @return true to indicate that the config file was successfully cleared
1320 * @hide
1321 */
1322 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1323 @RequiresBluetoothConnectPermission
1324 @RequiresPermission(allOf = {
1325 android.Manifest.permission.BLUETOOTH_CONNECT,
1326 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
1327 })
1328 public boolean factoryReset() {
1329 try {
1330 mServiceLock.readLock().lock();
1331 if (mService != null && mService.factoryReset(mAttributionSource)
1332 && mManagerService != null
1333 && mManagerService.onFactoryReset(mAttributionSource)) {
1334 return true;
1335 }
1336 Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later");
1337 SystemProperties.set("persist.bluetooth.factoryreset", "true");
1338 } catch (RemoteException e) {
1339 Log.e(TAG, "", e);
1340 } finally {
1341 mServiceLock.readLock().unlock();
1342 }
1343 return false;
1344 }
1345
1346 /**
1347 * Get the UUIDs supported by the local Bluetooth adapter.
1348 *
1349 * @return the UUIDs supported by the local Bluetooth Adapter.
1350 * @hide
1351 */
1352 @UnsupportedAppUsage
1353 @RequiresLegacyBluetoothPermission
1354 @RequiresBluetoothConnectPermission
1355 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1356 public @Nullable ParcelUuid[] getUuids() {
1357 if (getState() != STATE_ON) {
1358 return null;
1359 }
1360 try {
1361 mServiceLock.readLock().lock();
1362 if (mService != null) {
1363 return mService.getUuids(mAttributionSource);
1364 }
1365 } catch (RemoteException e) {
1366 Log.e(TAG, "", e);
1367 } finally {
1368 mServiceLock.readLock().unlock();
1369 }
1370 return null;
1371 }
1372
1373 /**
1374 * Set the friendly Bluetooth name of the local Bluetooth adapter.
1375 * <p>This name is visible to remote Bluetooth devices.
1376 * <p>Valid Bluetooth names are a maximum of 248 bytes using UTF-8
1377 * encoding, although many remote devices can only display the first
1378 * 40 characters, and some may be limited to just 20.
1379 * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1380 * will return false. After turning on Bluetooth,
1381 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1382 * to get the updated value.
1383 *
1384 * @param name a valid Bluetooth name
1385 * @return true if the name was set, false otherwise
1386 */
1387 @RequiresLegacyBluetoothAdminPermission
1388 @RequiresBluetoothConnectPermission
1389 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1390 public boolean setName(String name) {
1391 if (getState() != STATE_ON) {
1392 return false;
1393 }
1394 try {
1395 mServiceLock.readLock().lock();
1396 if (mService != null) {
1397 return mService.setName(name, mAttributionSource);
1398 }
1399 } catch (RemoteException e) {
1400 Log.e(TAG, "", e);
1401 } finally {
1402 mServiceLock.readLock().unlock();
1403 }
1404 return false;
1405 }
1406
1407 /**
1408 * Returns the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth
1409 * adapter.
1410 *
1411 * @return {@link BluetoothClass} Bluetooth CoD of local Bluetooth device.
1412 *
1413 * @hide
1414 */
1415 @RequiresLegacyBluetoothAdminPermission
1416 @RequiresBluetoothConnectPermission
1417 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1418 public BluetoothClass getBluetoothClass() {
1419 if (getState() != STATE_ON) {
1420 return null;
1421 }
1422 try {
1423 mServiceLock.readLock().lock();
1424 if (mService != null) {
1425 return mService.getBluetoothClass(mAttributionSource);
1426 }
1427 } catch (RemoteException e) {
1428 Log.e(TAG, "", e);
1429 } finally {
1430 mServiceLock.readLock().unlock();
1431 }
1432 return null;
1433 }
1434
1435 /**
1436 * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth
1437 * adapter.
1438 *
1439 * <p>Note: This value persists across system reboot.
1440 *
1441 * @param bluetoothClass {@link BluetoothClass} to set the local Bluetooth adapter to.
1442 * @return true if successful, false if unsuccessful.
1443 *
1444 * @hide
1445 */
1446 @RequiresBluetoothConnectPermission
1447 @RequiresPermission(allOf = {
1448 android.Manifest.permission.BLUETOOTH_CONNECT,
1449 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
1450 })
1451 public boolean setBluetoothClass(BluetoothClass bluetoothClass) {
1452 if (getState() != STATE_ON) {
1453 return false;
1454 }
1455 try {
1456 mServiceLock.readLock().lock();
1457 if (mService != null) {
1458 return mService.setBluetoothClass(bluetoothClass, mAttributionSource);
1459 }
1460 } catch (RemoteException e) {
1461 Log.e(TAG, "", e);
1462 } finally {
1463 mServiceLock.readLock().unlock();
1464 }
1465 return false;
1466 }
1467
1468 /**
1469 * Returns the Input/Output capability of the device for classic Bluetooth.
1470 *
1471 * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
1472 * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE},
1473 * {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}.
1474 *
1475 * @hide
1476 */
1477 @RequiresLegacyBluetoothAdminPermission
1478 @RequiresBluetoothConnectPermission
1479 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1480 @IoCapability
1481 public int getIoCapability() {
1482 if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
1483 try {
1484 mServiceLock.readLock().lock();
1485 if (mService != null) return mService.getIoCapability(mAttributionSource);
1486 } catch (RemoteException e) {
1487 Log.e(TAG, e.getMessage(), e);
1488 } finally {
1489 mServiceLock.readLock().unlock();
1490 }
1491 return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
1492 }
1493
1494 /**
1495 * Sets the Input/Output capability of the device for classic Bluetooth.
1496 *
1497 * <p>Changing the Input/Output capability of a device only takes effect on restarting the
1498 * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()}
1499 * and {@link BluetoothAdapter#enable()} to see the changes.
1500 *
1501 * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
1502 * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN},
1503 * {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}.
1504 *
1505 * @hide
1506 */
1507 @RequiresBluetoothConnectPermission
1508 @RequiresPermission(allOf = {
1509 android.Manifest.permission.BLUETOOTH_CONNECT,
1510 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
1511 })
1512 public boolean setIoCapability(@IoCapability int capability) {
1513 if (getState() != STATE_ON) return false;
1514 try {
1515 mServiceLock.readLock().lock();
1516 if (mService != null) return mService.setIoCapability(capability, mAttributionSource);
1517 } catch (RemoteException e) {
1518 Log.e(TAG, e.getMessage(), e);
1519 } finally {
1520 mServiceLock.readLock().unlock();
1521 }
1522 return false;
1523 }
1524
1525 /**
1526 * Returns the Input/Output capability of the device for BLE operations.
1527 *
1528 * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
1529 * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE},
1530 * {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}.
1531 *
1532 * @hide
1533 */
1534 @RequiresLegacyBluetoothAdminPermission
1535 @RequiresBluetoothConnectPermission
1536 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1537 @IoCapability
1538 public int getLeIoCapability() {
1539 if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
1540 try {
1541 mServiceLock.readLock().lock();
1542 if (mService != null) return mService.getLeIoCapability(mAttributionSource);
1543 } catch (RemoteException e) {
1544 Log.e(TAG, e.getMessage(), e);
1545 } finally {
1546 mServiceLock.readLock().unlock();
1547 }
1548 return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
1549 }
1550
1551 /**
1552 * Sets the Input/Output capability of the device for BLE operations.
1553 *
1554 * <p>Changing the Input/Output capability of a device only takes effect on restarting the
1555 * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()}
1556 * and {@link BluetoothAdapter#enable()} to see the changes.
1557 *
1558 * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
1559 * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN},
1560 * {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}.
1561 *
1562 * @hide
1563 */
1564 @RequiresBluetoothConnectPermission
1565 @RequiresPermission(allOf = {
1566 android.Manifest.permission.BLUETOOTH_CONNECT,
1567 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
1568 })
1569 public boolean setLeIoCapability(@IoCapability int capability) {
1570 if (getState() != STATE_ON) return false;
1571 try {
1572 mServiceLock.readLock().lock();
1573 if (mService != null) return mService.setLeIoCapability(capability, mAttributionSource);
1574 } catch (RemoteException e) {
1575 Log.e(TAG, e.getMessage(), e);
1576 } finally {
1577 mServiceLock.readLock().unlock();
1578 }
1579 return false;
1580 }
1581
1582 /**
1583 * Get the current Bluetooth scan mode of the local Bluetooth adapter.
1584 * <p>The Bluetooth scan mode determines if the local adapter is
1585 * connectable and/or discoverable from remote Bluetooth devices.
1586 * <p>Possible values are:
1587 * {@link #SCAN_MODE_NONE},
1588 * {@link #SCAN_MODE_CONNECTABLE},
1589 * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
1590 * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1591 * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth,
1592 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1593 * to get the updated value.
1594 *
1595 * @return scan mode
1596 */
1597 @RequiresLegacyBluetoothPermission
1598 @RequiresBluetoothScanPermission
1599 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
1600 @ScanMode
1601 public int getScanMode() {
1602 if (getState() != STATE_ON) {
1603 return SCAN_MODE_NONE;
1604 }
1605 try {
1606 mServiceLock.readLock().lock();
1607 if (mService != null) {
1608 return mService.getScanMode(mAttributionSource);
1609 }
1610 } catch (RemoteException e) {
1611 Log.e(TAG, "", e);
1612 } finally {
1613 mServiceLock.readLock().unlock();
1614 }
1615 return SCAN_MODE_NONE;
1616 }
1617
1618 /**
1619 * Set the Bluetooth scan mode of the local Bluetooth adapter.
1620 * <p>The Bluetooth scan mode determines if the local adapter is
1621 * connectable and/or discoverable from remote Bluetooth devices.
1622 * <p>For privacy reasons, discoverable mode is automatically turned off
1623 * after <code>durationMillis</code> milliseconds. For example, 120000 milliseconds should be
1624 * enough for a remote device to initiate and complete its discovery process.
1625 * <p>Valid scan mode values are:
1626 * {@link #SCAN_MODE_NONE},
1627 * {@link #SCAN_MODE_CONNECTABLE},
1628 * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
1629 * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1630 * will return false. After turning on Bluetooth,
1631 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1632 * to get the updated value.
1633 * <p>Applications cannot set the scan mode. They should use
1634 * <code>startActivityForResult(
1635 * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE})
1636 * </code>instead.
1637 *
1638 * @param mode valid scan mode
1639 * @param durationMillis time in milliseconds to apply scan mode, only used for {@link
1640 * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}
1641 * @return true if the scan mode was set, false otherwise
1642 * @hide
1643 */
1644 @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which "
1645 + "shows UI that confirms the user wants to go into discoverable mode.")
1646 @RequiresLegacyBluetoothPermission
1647 @RequiresBluetoothScanPermission
1648 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
1649 public boolean setScanMode(@ScanMode int mode, long durationMillis) {
1650 if (getState() != STATE_ON) {
1651 return false;
1652 }
1653 try {
1654 mServiceLock.readLock().lock();
1655 if (mService != null) {
1656 int durationSeconds = Math.toIntExact(durationMillis / 1000);
1657 return mService.setScanMode(mode, durationSeconds, mAttributionSource);
1658 }
1659 } catch (RemoteException e) {
1660 Log.e(TAG, "", e);
1661 } catch (ArithmeticException ex) {
1662 Log.e(TAG, "setScanMode: Duration in seconds outside of the bounds of an int");
1663 throw new IllegalArgumentException("Duration not in bounds. In seconds, the "
1664 + "durationMillis must be in the range of an int");
1665 } finally {
1666 mServiceLock.readLock().unlock();
1667 }
1668 return false;
1669 }
1670
1671 /**
1672 * Set the Bluetooth scan mode of the local Bluetooth adapter.
1673 * <p>The Bluetooth scan mode determines if the local adapter is
1674 * connectable and/or discoverable from remote Bluetooth devices.
1675 * <p>For privacy reasons, discoverable mode is automatically turned off
1676 * after <code>duration</code> seconds. For example, 120 seconds should be
1677 * enough for a remote device to initiate and complete its discovery
1678 * process.
1679 * <p>Valid scan mode values are:
1680 * {@link #SCAN_MODE_NONE},
1681 * {@link #SCAN_MODE_CONNECTABLE},
1682 * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
1683 * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1684 * will return false. After turning on Bluetooth,
1685 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1686 * to get the updated value.
1687 * <p>Applications cannot set the scan mode. They should use
1688 * <code>startActivityForResult(
1689 * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE})
1690 * </code>instead.
1691 *
1692 * @param mode valid scan mode
1693 * @return true if the scan mode was set, false otherwise
1694 * @hide
1695 */
1696 @UnsupportedAppUsage
1697 @RequiresLegacyBluetoothPermission
1698 @RequiresBluetoothScanPermission
1699 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
1700 public boolean setScanMode(@ScanMode int mode) {
1701 if (getState() != STATE_ON) {
1702 return false;
1703 }
1704 try {
1705 mServiceLock.readLock().lock();
1706 if (mService != null) {
1707 return mService.setScanMode(mode, getDiscoverableTimeout(), mAttributionSource);
1708 }
1709 } catch (RemoteException e) {
1710 Log.e(TAG, "", e);
1711 } finally {
1712 mServiceLock.readLock().unlock();
1713 }
1714 return false;
1715 }
1716
1717 /** @hide */
1718 @UnsupportedAppUsage
1719 @RequiresBluetoothScanPermission
1720 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
1721 public int getDiscoverableTimeout() {
1722 if (getState() != STATE_ON) {
1723 return -1;
1724 }
1725 try {
1726 mServiceLock.readLock().lock();
1727 if (mService != null) {
1728 return mService.getDiscoverableTimeout(mAttributionSource);
1729 }
1730 } catch (RemoteException e) {
1731 Log.e(TAG, "", e);
1732 } finally {
1733 mServiceLock.readLock().unlock();
1734 }
1735 return -1;
1736 }
1737
1738 /** @hide */
1739 @UnsupportedAppUsage
1740 @RequiresBluetoothScanPermission
1741 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
1742 public void setDiscoverableTimeout(int timeout) {
1743 if (getState() != STATE_ON) {
1744 return;
1745 }
1746 try {
1747 mServiceLock.readLock().lock();
1748 if (mService != null) {
1749 mService.setDiscoverableTimeout(timeout, mAttributionSource);
1750 }
1751 } catch (RemoteException e) {
1752 Log.e(TAG, "", e);
1753 } finally {
1754 mServiceLock.readLock().unlock();
1755 }
1756 }
1757
1758 /**
1759 * Get the end time of the latest remote device discovery process.
1760 *
1761 * @return the latest time that the bluetooth adapter was/will be in discovery mode, in
1762 * milliseconds since the epoch. This time can be in the future if {@link #startDiscovery()} has
1763 * been called recently.
1764 * @hide
1765 */
1766 @SystemApi
1767 @RequiresBluetoothConnectPermission
1768 @RequiresPermission(allOf = {
1769 android.Manifest.permission.BLUETOOTH_CONNECT,
1770 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
1771 })
1772 public long getDiscoveryEndMillis() {
1773 try {
1774 mServiceLock.readLock().lock();
1775 if (mService != null) {
1776 return mService.getDiscoveryEndMillis(mAttributionSource);
1777 }
1778 } catch (RemoteException e) {
1779 Log.e(TAG, "", e);
1780 } finally {
1781 mServiceLock.readLock().unlock();
1782 }
1783 return -1;
1784 }
1785
1786 /**
1787 * Start the remote device discovery process.
1788 * <p>The discovery process usually involves an inquiry scan of about 12
1789 * seconds, followed by a page scan of each new device to retrieve its
1790 * Bluetooth name.
1791 * <p>This is an asynchronous call, it will return immediately. Register
1792 * for {@link #ACTION_DISCOVERY_STARTED} and {@link
1793 * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the
1794 * discovery starts and completes. Register for {@link
1795 * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices
1796 * are found.
1797 * <p>Device discovery is a heavyweight procedure. New connections to
1798 * remote Bluetooth devices should not be attempted while discovery is in
1799 * progress, and existing connections will experience limited bandwidth
1800 * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
1801 * discovery. Discovery is not managed by the Activity,
1802 * but is run as a system service, so an application should always call
1803 * {@link BluetoothAdapter#cancelDiscovery()} even if it
1804 * did not directly request a discovery, just to be sure.
1805 * <p>Device discovery will only find remote devices that are currently
1806 * <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are
1807 * not discoverable by default, and need to be entered into a special mode.
1808 * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1809 * will return false. After turning on Bluetooth, wait for {@link #ACTION_STATE_CHANGED}
1810 * with {@link #STATE_ON} to get the updated value.
1811 * <p>If a device is currently bonding, this request will be queued and executed once that
1812 * device has finished bonding. If a request is already queued, this request will be ignored.
1813 *
1814 * @return true on success, false on error
1815 */
1816 @RequiresLegacyBluetoothAdminPermission
1817 @RequiresBluetoothScanPermission
1818 @RequiresBluetoothLocationPermission
1819 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
1820 public boolean startDiscovery() {
1821 if (getState() != STATE_ON) {
1822 return false;
1823 }
1824 try {
1825 mServiceLock.readLock().lock();
1826 if (mService != null) {
1827 return mService.startDiscovery(mAttributionSource);
1828 }
1829 } catch (RemoteException e) {
1830 Log.e(TAG, "", e);
1831 } finally {
1832 mServiceLock.readLock().unlock();
1833 }
1834 return false;
1835 }
1836
1837 /**
1838 * Cancel the current device discovery process.
1839 * <p>Because discovery is a heavyweight procedure for the Bluetooth
1840 * adapter, this method should always be called before attempting to connect
1841 * to a remote device with {@link
1842 * android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by
1843 * the Activity, but is run as a system service, so an application should
1844 * always call cancel discovery even if it did not directly request a
1845 * discovery, just to be sure.
1846 * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1847 * will return false. After turning on Bluetooth,
1848 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1849 * to get the updated value.
1850 *
1851 * @return true on success, false on error
1852 */
1853 @RequiresLegacyBluetoothAdminPermission
1854 @RequiresBluetoothScanPermission
1855 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
1856 public boolean cancelDiscovery() {
1857 if (getState() != STATE_ON) {
1858 return false;
1859 }
1860 try {
1861 mServiceLock.readLock().lock();
1862 if (mService != null) {
1863 return mService.cancelDiscovery(mAttributionSource);
1864 }
1865 } catch (RemoteException e) {
1866 Log.e(TAG, "", e);
1867 } finally {
1868 mServiceLock.readLock().unlock();
1869 }
1870 return false;
1871 }
1872
1873 /**
1874 * Return true if the local Bluetooth adapter is currently in the device
1875 * discovery process.
1876 * <p>Device discovery is a heavyweight procedure. New connections to
1877 * remote Bluetooth devices should not be attempted while discovery is in
1878 * progress, and existing connections will experience limited bandwidth
1879 * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
1880 * discovery.
1881 * <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED}
1882 * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery
1883 * starts or completes.
1884 * <p>If Bluetooth state is not {@link #STATE_ON}, this API
1885 * will return false. After turning on Bluetooth,
1886 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
1887 * to get the updated value.
1888 *
1889 * @return true if discovering
1890 */
1891 @RequiresLegacyBluetoothPermission
1892 @RequiresBluetoothScanPermission
1893 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
1894 public boolean isDiscovering() {
1895 if (getState() != STATE_ON) {
1896 return false;
1897 }
1898 try {
1899 mServiceLock.readLock().lock();
1900 if (mService != null) {
1901 return mService.isDiscovering(mAttributionSource);
1902 }
1903 } catch (RemoteException e) {
1904 Log.e(TAG, "", e);
1905 } finally {
1906 mServiceLock.readLock().unlock();
1907 }
1908 return false;
1909 }
1910
1911 /**
1912 * Removes the active device for the grouping of @ActiveDeviceUse specified
1913 *
1914 * @param profiles represents the purpose for which we are setting this as the active device.
1915 * Possible values are:
1916 * {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO},
1917 * {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
1918 * {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
1919 * @return false on immediate error, true otherwise
1920 * @throws IllegalArgumentException if device is null or profiles is not one of
1921 * {@link ActiveDeviceUse}
1922 * @hide
1923 */
1924 @SystemApi
1925 @RequiresBluetoothConnectPermission
1926 @RequiresPermission(allOf = {
1927 android.Manifest.permission.BLUETOOTH_CONNECT,
1928 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
1929 android.Manifest.permission.MODIFY_PHONE_STATE,
1930 })
1931 public boolean removeActiveDevice(@ActiveDeviceUse int profiles) {
1932 if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
1933 && profiles != ACTIVE_DEVICE_ALL) {
1934 Log.e(TAG, "Invalid profiles param value in removeActiveDevice");
1935 throw new IllegalArgumentException("Profiles must be one of "
1936 + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, "
1937 + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or "
1938 + "BluetoothAdapter.ACTIVE_DEVICE_ALL");
1939 }
1940 try {
1941 mServiceLock.readLock().lock();
1942 if (mService != null) {
1943 if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles);
1944 return mService.removeActiveDevice(profiles, mAttributionSource);
1945 }
1946 } catch (RemoteException e) {
1947 Log.e(TAG, "", e);
1948 } finally {
1949 mServiceLock.readLock().unlock();
1950 }
1951
1952 return false;
1953 }
1954
1955 /**
1956 * Sets device as the active devices for the profiles passed into the function
1957 *
1958 * @param device is the remote bluetooth device
1959 * @param profiles represents the purpose for which we are setting this as the active device.
1960 * Possible values are:
1961 * {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO},
1962 * {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
1963 * {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
1964 * @return false on immediate error, true otherwise
1965 * @throws IllegalArgumentException if device is null or profiles is not one of
1966 * {@link ActiveDeviceUse}
1967 * @hide
1968 */
1969 @SystemApi
1970 @RequiresBluetoothConnectPermission
1971 @RequiresPermission(allOf = {
1972 android.Manifest.permission.BLUETOOTH_CONNECT,
1973 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
1974 android.Manifest.permission.MODIFY_PHONE_STATE,
1975 })
1976 public boolean setActiveDevice(@NonNull BluetoothDevice device,
1977 @ActiveDeviceUse int profiles) {
1978 if (device == null) {
1979 Log.e(TAG, "setActiveDevice: Null device passed as parameter");
1980 throw new IllegalArgumentException("device cannot be null");
1981 }
1982 if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
1983 && profiles != ACTIVE_DEVICE_ALL) {
1984 Log.e(TAG, "Invalid profiles param value in setActiveDevice");
1985 throw new IllegalArgumentException("Profiles must be one of "
1986 + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, "
1987 + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or "
1988 + "BluetoothAdapter.ACTIVE_DEVICE_ALL");
1989 }
1990 try {
1991 mServiceLock.readLock().lock();
1992 if (mService != null) {
1993 if (DBG) {
1994 Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles);
1995 }
1996 return mService.setActiveDevice(device, profiles, mAttributionSource);
1997 }
1998 } catch (RemoteException e) {
1999 Log.e(TAG, "", e);
2000 } finally {
2001 mServiceLock.readLock().unlock();
2002 }
2003
2004 return false;
2005 }
2006
2007 /**
2008 * Connects all enabled and supported bluetooth profiles between the local and remote device.
2009 * Connection is asynchronous and you should listen to each profile's broadcast intent
2010 * ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful. For example,
2011 * to verify a2dp is connected, you would listen for
2012 * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
2013 *
2014 * @param device is the remote device with which to connect these profiles
2015 * @return true if message sent to try to connect all profiles, false if an error occurred
2016 *
2017 * @hide
2018 */
2019 @RequiresBluetoothConnectPermission
2020 @RequiresPermission(allOf = {
2021 android.Manifest.permission.BLUETOOTH_CONNECT,
2022 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2023 android.Manifest.permission.MODIFY_PHONE_STATE,
2024 })
2025 public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) {
2026 try {
2027 mServiceLock.readLock().lock();
2028 if (mService != null) {
2029 return mService.connectAllEnabledProfiles(device, mAttributionSource);
2030 }
2031 } catch (RemoteException e) {
2032 Log.e(TAG, "", e);
2033 } finally {
2034 mServiceLock.readLock().unlock();
2035 }
2036
2037 return false;
2038 }
2039
2040 /**
2041 * Disconnects all enabled and supported bluetooth profiles between the local and remote device.
2042 * Disconnection is asynchronous and you should listen to each profile's broadcast intent
2043 * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example,
2044 * to verify a2dp is disconnected, you would listen for
2045 * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
2046 *
2047 * @param device is the remote device with which to disconnect these profiles
2048 * @return true if message sent to try to disconnect all profiles, false if an error occurred
2049 *
2050 * @hide
2051 */
2052 @RequiresBluetoothConnectPermission
2053 @RequiresPermission(allOf = {
2054 android.Manifest.permission.BLUETOOTH_CONNECT,
2055 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2056 })
2057 public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) {
2058 try {
2059 mServiceLock.readLock().lock();
2060 if (mService != null) {
2061 return mService.disconnectAllEnabledProfiles(device, mAttributionSource);
2062 }
2063 } catch (RemoteException e) {
2064 Log.e(TAG, "", e);
2065 } finally {
2066 mServiceLock.readLock().unlock();
2067 }
2068
2069 return false;
2070 }
2071
2072 /**
2073 * Return true if the multi advertisement is supported by the chipset
2074 *
2075 * @return true if Multiple Advertisement feature is supported
2076 */
2077 @RequiresLegacyBluetoothPermission
2078 @RequiresNoPermission
2079 public boolean isMultipleAdvertisementSupported() {
2080 if (getState() != STATE_ON) {
2081 return false;
2082 }
2083 try {
2084 mServiceLock.readLock().lock();
2085 if (mService != null) {
2086 return mService.isMultiAdvertisementSupported();
2087 }
2088 } catch (RemoteException e) {
2089 Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e);
2090 } finally {
2091 mServiceLock.readLock().unlock();
2092 }
2093 return false;
2094 }
2095
2096 /**
2097 * Returns {@code true} if BLE scan is always available, {@code false} otherwise. <p>
2098 *
2099 * If this returns {@code true}, application can issue {@link BluetoothLeScanner#startScan} and
2100 * fetch scan results even when Bluetooth is turned off.<p>
2101 *
2102 * To change this setting, use {@link #ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE}.
2103 *
2104 * @hide
2105 */
2106 @SystemApi
2107 @RequiresNoPermission
2108 public boolean isBleScanAlwaysAvailable() {
2109 try {
2110 return mManagerService.isBleScanAlwaysAvailable();
2111 } catch (RemoteException e) {
2112 Log.e(TAG, "remote expection when calling isBleScanAlwaysAvailable", e);
2113 return false;
2114 }
2115 }
2116
2117 private static final String BLUETOOTH_FILTERING_CACHE_PROPERTY =
2118 "cache_key.bluetooth.is_offloaded_filtering_supported";
2119 private final PropertyInvalidatedCache<Void, Boolean> mBluetoothFilteringCache =
2120 new PropertyInvalidatedCache<Void, Boolean>(
2121 8, BLUETOOTH_FILTERING_CACHE_PROPERTY) {
2122 @Override
2123 @SuppressLint("AndroidFrameworkRequiresPermission")
2124 protected Boolean recompute(Void query) {
2125 try {
2126 mServiceLock.readLock().lock();
2127 if (mService != null) {
2128 return mService.isOffloadedFilteringSupported();
2129 }
2130 } catch (RemoteException e) {
2131 Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
2132 } finally {
2133 mServiceLock.readLock().unlock();
2134 }
2135 return false;
2136
2137 }
2138 };
2139
2140 /** @hide */
2141 @RequiresNoPermission
2142 public void disableIsOffloadedFilteringSupportedCache() {
2143 mBluetoothFilteringCache.disableLocal();
2144 }
2145
2146 /** @hide */
2147 public static void invalidateIsOffloadedFilteringSupportedCache() {
2148 PropertyInvalidatedCache.invalidateCache(BLUETOOTH_FILTERING_CACHE_PROPERTY);
2149 }
2150
2151 /**
2152 * Return true if offloaded filters are supported
2153 *
2154 * @return true if chipset supports on-chip filtering
2155 */
2156 @RequiresLegacyBluetoothPermission
2157 @RequiresNoPermission
2158 public boolean isOffloadedFilteringSupported() {
2159 if (!getLeAccess()) {
2160 return false;
2161 }
2162 return mBluetoothFilteringCache.query(null);
2163 }
2164
2165 /**
2166 * Return true if offloaded scan batching is supported
2167 *
2168 * @return true if chipset supports on-chip scan batching
2169 */
2170 @RequiresLegacyBluetoothPermission
2171 @RequiresNoPermission
2172 public boolean isOffloadedScanBatchingSupported() {
2173 if (!getLeAccess()) {
2174 return false;
2175 }
2176 try {
2177 mServiceLock.readLock().lock();
2178 if (mService != null) {
2179 return mService.isOffloadedScanBatchingSupported();
2180 }
2181 } catch (RemoteException e) {
2182 Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e);
2183 } finally {
2184 mServiceLock.readLock().unlock();
2185 }
2186 return false;
2187 }
2188
2189 /**
2190 * Return true if LE 2M PHY feature is supported.
2191 *
2192 * @return true if chipset supports LE 2M PHY feature
2193 */
2194 @RequiresLegacyBluetoothPermission
2195 @RequiresNoPermission
2196 public boolean isLe2MPhySupported() {
2197 if (!getLeAccess()) {
2198 return false;
2199 }
2200 try {
2201 mServiceLock.readLock().lock();
2202 if (mService != null) {
2203 return mService.isLe2MPhySupported();
2204 }
2205 } catch (RemoteException e) {
2206 Log.e(TAG, "failed to get isExtendedAdvertisingSupported, error: ", e);
2207 } finally {
2208 mServiceLock.readLock().unlock();
2209 }
2210 return false;
2211 }
2212
2213 /**
2214 * Return true if LE Coded PHY feature is supported.
2215 *
2216 * @return true if chipset supports LE Coded PHY feature
2217 */
2218 @RequiresLegacyBluetoothPermission
2219 @RequiresNoPermission
2220 public boolean isLeCodedPhySupported() {
2221 if (!getLeAccess()) {
2222 return false;
2223 }
2224 try {
2225 mServiceLock.readLock().lock();
2226 if (mService != null) {
2227 return mService.isLeCodedPhySupported();
2228 }
2229 } catch (RemoteException e) {
2230 Log.e(TAG, "failed to get isLeCodedPhySupported, error: ", e);
2231 } finally {
2232 mServiceLock.readLock().unlock();
2233 }
2234 return false;
2235 }
2236
2237 /**
2238 * Return true if LE Extended Advertising feature is supported.
2239 *
2240 * @return true if chipset supports LE Extended Advertising feature
2241 */
2242 @RequiresLegacyBluetoothPermission
2243 @RequiresNoPermission
2244 public boolean isLeExtendedAdvertisingSupported() {
2245 if (!getLeAccess()) {
2246 return false;
2247 }
2248 try {
2249 mServiceLock.readLock().lock();
2250 if (mService != null) {
2251 return mService.isLeExtendedAdvertisingSupported();
2252 }
2253 } catch (RemoteException e) {
2254 Log.e(TAG, "failed to get isLeExtendedAdvertisingSupported, error: ", e);
2255 } finally {
2256 mServiceLock.readLock().unlock();
2257 }
2258 return false;
2259 }
2260
2261 /**
2262 * Return true if LE Periodic Advertising feature is supported.
2263 *
2264 * @return true if chipset supports LE Periodic Advertising feature
2265 */
2266 @RequiresLegacyBluetoothPermission
2267 @RequiresNoPermission
2268 public boolean isLePeriodicAdvertisingSupported() {
2269 if (!getLeAccess()) {
2270 return false;
2271 }
2272 try {
2273 mServiceLock.readLock().lock();
2274 if (mService != null) {
2275 return mService.isLePeriodicAdvertisingSupported();
2276 }
2277 } catch (RemoteException e) {
2278 Log.e(TAG, "failed to get isLePeriodicAdvertisingSupported, error: ", e);
2279 } finally {
2280 mServiceLock.readLock().unlock();
2281 }
2282 return false;
2283 }
2284
2285 /**
2286 * Return the maximum LE advertising data length in bytes,
2287 * if LE Extended Advertising feature is supported, 0 otherwise.
2288 *
2289 * @return the maximum LE advertising data length.
2290 */
2291 @RequiresLegacyBluetoothPermission
2292 @RequiresNoPermission
2293 public int getLeMaximumAdvertisingDataLength() {
2294 if (!getLeAccess()) {
2295 return 0;
2296 }
2297 try {
2298 mServiceLock.readLock().lock();
2299 if (mService != null) {
2300 return mService.getLeMaximumAdvertisingDataLength();
2301 }
2302 } catch (RemoteException e) {
2303 Log.e(TAG, "failed to get getLeMaximumAdvertisingDataLength, error: ", e);
2304 } finally {
2305 mServiceLock.readLock().unlock();
2306 }
2307 return 0;
2308 }
2309
2310 /**
2311 * Return true if Hearing Aid Profile is supported.
2312 *
2313 * @return true if phone supports Hearing Aid Profile
2314 */
2315 @RequiresNoPermission
2316 private boolean isHearingAidProfileSupported() {
2317 try {
2318 return mManagerService.isHearingAidProfileSupported();
2319 } catch (RemoteException e) {
2320 Log.e(TAG, "remote expection when calling isHearingAidProfileSupported", e);
2321 return false;
2322 }
2323 }
2324
2325 /**
2326 * Get the maximum number of connected audio devices.
2327 *
2328 * @return the maximum number of connected audio devices
2329 * @hide
2330 */
2331 @RequiresLegacyBluetoothPermission
2332 @RequiresBluetoothConnectPermission
2333 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2334 public int getMaxConnectedAudioDevices() {
2335 try {
2336 mServiceLock.readLock().lock();
2337 if (mService != null) {
2338 return mService.getMaxConnectedAudioDevices(mAttributionSource);
2339 }
2340 } catch (RemoteException e) {
2341 Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e);
2342 } finally {
2343 mServiceLock.readLock().unlock();
2344 }
2345 return 1;
2346 }
2347
2348 /**
2349 * Return true if hardware has entries available for matching beacons
2350 *
2351 * @return true if there are hw entries available for matching beacons
2352 * @hide
2353 */
2354 @RequiresBluetoothConnectPermission
2355 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2356 public boolean isHardwareTrackingFiltersAvailable() {
2357 if (!getLeAccess()) {
2358 return false;
2359 }
2360 try {
2361 IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
2362 if (iGatt == null) {
2363 // BLE is not supported
2364 return false;
2365 }
2366 return (iGatt.numHwTrackFiltersAvailable(mAttributionSource) != 0);
2367 } catch (RemoteException e) {
2368 Log.e(TAG, "", e);
2369 }
2370 return false;
2371 }
2372
2373 /**
2374 * Return the record of {@link BluetoothActivityEnergyInfo} object that
2375 * has the activity and energy info. This can be used to ascertain what
2376 * the controller has been up to, since the last sample.
2377 *
2378 * @param updateType Type of info, cached vs refreshed.
2379 * @return a record with {@link BluetoothActivityEnergyInfo} or null if report is unavailable or
2380 * unsupported
2381 * @hide
2382 * @deprecated use the asynchronous {@link #requestControllerActivityEnergyInfo(ResultReceiver)}
2383 * instead.
2384 */
2385 @Deprecated
2386 @RequiresBluetoothConnectPermission
2387 @RequiresPermission(allOf = {
2388 android.Manifest.permission.BLUETOOTH_CONNECT,
2389 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2390 })
2391 public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
2392 SynchronousResultReceiver receiver = new SynchronousResultReceiver();
2393 requestControllerActivityEnergyInfo(receiver);
2394 try {
2395 SynchronousResultReceiver.Result result = receiver.awaitResult(1000);
2396 if (result.bundle != null) {
2397 return result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY);
2398 }
2399 } catch (TimeoutException e) {
2400 Log.e(TAG, "getControllerActivityEnergyInfo timed out");
2401 }
2402 return null;
2403 }
2404
2405 /**
2406 * Request the record of {@link BluetoothActivityEnergyInfo} object that
2407 * has the activity and energy info. This can be used to ascertain what
2408 * the controller has been up to, since the last sample.
2409 *
2410 * A null value for the activity info object may be sent if the bluetooth service is
2411 * unreachable or the device does not support reporting such information.
2412 *
2413 * @param result The callback to which to send the activity info.
2414 * @hide
2415 */
2416 @RequiresBluetoothConnectPermission
2417 @RequiresPermission(allOf = {
2418 android.Manifest.permission.BLUETOOTH_CONNECT,
2419 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2420 })
2421 public void requestControllerActivityEnergyInfo(ResultReceiver result) {
2422 try {
2423 mServiceLock.readLock().lock();
2424 if (mService != null) {
2425 mService.requestActivityInfo(result, mAttributionSource);
2426 result = null;
2427 }
2428 } catch (RemoteException e) {
2429 Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e);
2430 } finally {
2431 mServiceLock.readLock().unlock();
2432 if (result != null) {
2433 // Only send an immediate result if we failed.
2434 result.send(0, null);
2435 }
2436 }
2437 }
2438
2439 /**
2440 * Fetches a list of the most recently connected bluetooth devices ordered by how recently they
2441 * were connected with most recently first and least recently last
2442 *
2443 * @return {@link List} of bonded {@link BluetoothDevice} ordered by how recently they were
2444 * connected
2445 *
2446 * @hide
2447 */
2448 @RequiresLegacyBluetoothAdminPermission
2449 @RequiresBluetoothConnectPermission
2450 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2451 public @NonNull List<BluetoothDevice> getMostRecentlyConnectedDevices() {
2452 if (getState() != STATE_ON) {
2453 return new ArrayList<>();
2454 }
2455 try {
2456 mServiceLock.readLock().lock();
2457 if (mService != null) {
2458 return Attributable.setAttributionSource(
2459 mService.getMostRecentlyConnectedDevices(mAttributionSource),
2460 mAttributionSource);
2461 }
2462 } catch (RemoteException e) {
2463 Log.e(TAG, "", e);
2464 } finally {
2465 mServiceLock.readLock().unlock();
2466 }
2467 return new ArrayList<>();
2468 }
2469
2470 /**
2471 * Return the set of {@link BluetoothDevice} objects that are bonded
2472 * (paired) to the local adapter.
2473 * <p>If Bluetooth state is not {@link #STATE_ON}, this API
2474 * will return an empty set. After turning on Bluetooth,
2475 * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
2476 * to get the updated value.
2477 *
2478 * @return unmodifiable set of {@link BluetoothDevice}, or null on error
2479 */
2480 @RequiresLegacyBluetoothPermission
2481 @RequiresBluetoothConnectPermission
2482 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2483 public Set<BluetoothDevice> getBondedDevices() {
2484 if (getState() != STATE_ON) {
2485 return toDeviceSet(Arrays.asList());
2486 }
2487 try {
2488 mServiceLock.readLock().lock();
2489 if (mService != null) {
2490 return toDeviceSet(Attributable.setAttributionSource(
2491 Arrays.asList(mService.getBondedDevices(mAttributionSource)),
2492 mAttributionSource));
2493 }
2494 return toDeviceSet(Arrays.asList());
2495 } catch (RemoteException e) {
2496 Log.e(TAG, "", e);
2497 } finally {
2498 mServiceLock.readLock().unlock();
2499 }
2500 return null;
2501 }
2502
2503 /**
2504 * Gets the currently supported profiles by the adapter.
2505 *
2506 * <p> This can be used to check whether a profile is supported before attempting
2507 * to connect to its respective proxy.
2508 *
2509 * @return a list of integers indicating the ids of supported profiles as defined in {@link
2510 * BluetoothProfile}.
2511 * @hide
2512 */
2513 @RequiresNoPermission
2514 public @NonNull List<Integer> getSupportedProfiles() {
2515 final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>();
2516
2517 try {
2518 synchronized (mManagerCallback) {
2519 if (mService != null) {
2520 final long supportedProfilesBitMask = mService.getSupportedProfiles();
2521
2522 for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) {
2523 if ((supportedProfilesBitMask & (1 << i)) != 0) {
2524 supportedProfiles.add(i);
2525 }
2526 }
2527 } else {
2528 // Bluetooth is disabled. Just fill in known supported Profiles
2529 if (isHearingAidProfileSupported()) {
2530 supportedProfiles.add(BluetoothProfile.HEARING_AID);
2531 }
2532 }
2533 }
2534 } catch (RemoteException e) {
2535 Log.e(TAG, "getSupportedProfiles:", e);
2536 }
2537 return supportedProfiles;
2538 }
2539
2540 private static final String BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY =
2541 "cache_key.bluetooth.get_adapter_connection_state";
2542 private final PropertyInvalidatedCache<Void, Integer>
2543 mBluetoothGetAdapterConnectionStateCache =
2544 new PropertyInvalidatedCache<Void, Integer> (
2545 8, BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY) {
2546 /**
2547 * This method must not be called when mService is null.
2548 */
2549 @Override
2550 @SuppressLint("AndroidFrameworkRequiresPermission")
2551 protected Integer recompute(Void query) {
2552 try {
2553 return mService.getAdapterConnectionState();
2554 } catch (RemoteException e) {
2555 throw e.rethrowAsRuntimeException();
2556 }
2557 }
2558 };
2559
2560 /** @hide */
2561 @RequiresNoPermission
2562 public void disableGetAdapterConnectionStateCache() {
2563 mBluetoothGetAdapterConnectionStateCache.disableLocal();
2564 }
2565
2566 /** @hide */
2567 public static void invalidateGetAdapterConnectionStateCache() {
2568 PropertyInvalidatedCache.invalidateCache(
2569 BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY);
2570 }
2571
2572 /**
2573 * Get the current connection state of the local Bluetooth adapter.
2574 * This can be used to check whether the local Bluetooth adapter is connected
2575 * to any profile of any other remote Bluetooth Device.
2576 *
2577 * <p> Use this function along with {@link #ACTION_CONNECTION_STATE_CHANGED}
2578 * intent to get the connection state of the adapter.
2579 *
2580 * @return One of {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTED}, {@link
2581 * #STATE_CONNECTING} or {@link #STATE_DISCONNECTED}
2582 * @hide
2583 */
2584 @UnsupportedAppUsage
2585 @RequiresLegacyBluetoothPermission
2586 @RequiresNoPermission
2587 public int getConnectionState() {
2588 if (getState() != STATE_ON) {
2589 return BluetoothAdapter.STATE_DISCONNECTED;
2590 }
2591 try {
2592 mServiceLock.readLock().lock();
2593 if (mService != null) {
2594 return mBluetoothGetAdapterConnectionStateCache.query(null);
2595 }
2596 } catch (RuntimeException e) {
2597 if (e.getCause() instanceof RemoteException) {
2598 Log.e(TAG, "getConnectionState:", e.getCause());
2599 } else {
2600 throw e;
2601 }
2602 } finally {
2603 mServiceLock.readLock().unlock();
2604 }
2605 return BluetoothAdapter.STATE_DISCONNECTED;
2606 }
2607
2608 private static final String BLUETOOTH_PROFILE_CACHE_PROPERTY =
2609 "cache_key.bluetooth.get_profile_connection_state";
2610 private final PropertyInvalidatedCache<Integer, Integer>
2611 mGetProfileConnectionStateCache =
2612 new PropertyInvalidatedCache<Integer, Integer>(
2613 8, BLUETOOTH_PROFILE_CACHE_PROPERTY) {
2614 @Override
2615 @SuppressLint("AndroidFrameworkRequiresPermission")
2616 protected Integer recompute(Integer query) {
2617 try {
2618 mServiceLock.readLock().lock();
2619 if (mService != null) {
2620 return mService.getProfileConnectionState(query);
2621 }
2622 } catch (RemoteException e) {
2623 Log.e(TAG, "getProfileConnectionState:", e);
2624 } finally {
2625 mServiceLock.readLock().unlock();
2626 }
2627 return BluetoothProfile.STATE_DISCONNECTED;
2628 }
2629 @Override
2630 public String queryToString(Integer query) {
2631 return String.format("getProfileConnectionState(profile=\"%d\")",
2632 query);
2633 }
2634 };
2635
2636 /** @hide */
2637 @RequiresNoPermission
2638 public void disableGetProfileConnectionStateCache() {
2639 mGetProfileConnectionStateCache.disableLocal();
2640 }
2641
2642 /** @hide */
2643 public static void invalidateGetProfileConnectionStateCache() {
2644 PropertyInvalidatedCache.invalidateCache(BLUETOOTH_PROFILE_CACHE_PROPERTY);
2645 }
2646
2647 /**
2648 * Get the current connection state of a profile.
2649 * This function can be used to check whether the local Bluetooth adapter
2650 * is connected to any remote device for a specific profile.
2651 * Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}.
2652 *
2653 * <p> Return value can be one of
2654 * {@link BluetoothProfile#STATE_DISCONNECTED},
2655 * {@link BluetoothProfile#STATE_CONNECTING},
2656 * {@link BluetoothProfile#STATE_CONNECTED},
2657 * {@link BluetoothProfile#STATE_DISCONNECTING}
2658 */
2659 @RequiresLegacyBluetoothPermission
2660 @RequiresBluetoothConnectPermission
2661 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2662 @SuppressLint("AndroidFrameworkRequiresPermission")
2663 public int getProfileConnectionState(int profile) {
2664 if (getState() != STATE_ON) {
2665 return BluetoothProfile.STATE_DISCONNECTED;
2666 }
2667 return mGetProfileConnectionStateCache.query(new Integer(profile));
2668 }
2669
2670 /**
2671 * Create a listening, secure RFCOMM Bluetooth socket.
2672 * <p>A remote device connecting to this socket will be authenticated and
2673 * communication on this socket will be encrypted.
2674 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
2675 * connections from a listening {@link BluetoothServerSocket}.
2676 * <p>Valid RFCOMM channels are in range 1 to 30.
2677 *
2678 * @param channel RFCOMM channel to listen on
2679 * @return a listening RFCOMM BluetoothServerSocket
2680 * @throws IOException on error, for example Bluetooth not available, or insufficient
2681 * permissions, or channel in use.
2682 * @hide
2683 */
2684 @RequiresLegacyBluetoothAdminPermission
2685 @RequiresBluetoothConnectPermission
2686 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2687 public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
2688 return listenUsingRfcommOn(channel, false, false);
2689 }
2690
2691 /**
2692 * Create a listening, secure RFCOMM Bluetooth socket.
2693 * <p>A remote device connecting to this socket will be authenticated and
2694 * communication on this socket will be encrypted.
2695 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
2696 * connections from a listening {@link BluetoothServerSocket}.
2697 * <p>Valid RFCOMM channels are in range 1 to 30.
2698 * <p>To auto assign a channel without creating a SDP record use
2699 * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
2700 *
2701 * @param channel RFCOMM channel to listen on
2702 * @param mitm enforce person-in-the-middle protection for authentication.
2703 * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2
2704 * connections.
2705 * @return a listening RFCOMM BluetoothServerSocket
2706 * @throws IOException on error, for example Bluetooth not available, or insufficient
2707 * permissions, or channel in use.
2708 * @hide
2709 */
2710 @UnsupportedAppUsage
2711 @RequiresLegacyBluetoothAdminPermission
2712 @RequiresBluetoothConnectPermission
2713 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2714 public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm,
2715 boolean min16DigitPin) throws IOException {
2716 BluetoothServerSocket socket =
2717 new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm,
2718 min16DigitPin);
2719 int errno = socket.mSocket.bindListen();
2720 if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
2721 socket.setChannel(socket.mSocket.getPort());
2722 }
2723 if (errno != 0) {
2724 //TODO(BT): Throw the same exception error code
2725 // that the previous code was using.
2726 //socket.mSocket.throwErrnoNative(errno);
2727 throw new IOException("Error: " + errno);
2728 }
2729 return socket;
2730 }
2731
2732 /**
2733 * Create a listening, secure RFCOMM Bluetooth socket with Service Record.
2734 * <p>A remote device connecting to this socket will be authenticated and
2735 * communication on this socket will be encrypted.
2736 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
2737 * connections from a listening {@link BluetoothServerSocket}.
2738 * <p>The system will assign an unused RFCOMM channel to listen on.
2739 * <p>The system will also register a Service Discovery
2740 * Protocol (SDP) record with the local SDP server containing the specified
2741 * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
2742 * can use the same UUID to query our SDP server and discover which channel
2743 * to connect to. This SDP record will be removed when this socket is
2744 * closed, or if this application closes unexpectedly.
2745 * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
2746 * connect to this socket from another device using the same {@link UUID}.
2747 *
2748 * @param name service name for SDP record
2749 * @param uuid uuid for SDP record
2750 * @return a listening RFCOMM BluetoothServerSocket
2751 * @throws IOException on error, for example Bluetooth not available, or insufficient
2752 * permissions, or channel in use.
2753 */
2754 @RequiresLegacyBluetoothPermission
2755 @RequiresBluetoothConnectPermission
2756 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2757 public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
2758 throws IOException {
2759 return createNewRfcommSocketAndRecord(name, uuid, true, true);
2760 }
2761
2762 /**
2763 * Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
2764 * <p>The link key is not required to be authenticated, i.e the communication may be
2765 * vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices,
2766 * the link will be encrypted, as encryption is mandatory.
2767 * For legacy devices (pre Bluetooth 2.1 devices) the link will not
2768 * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an
2769 * encrypted and authenticated communication channel is desired.
2770 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
2771 * connections from a listening {@link BluetoothServerSocket}.
2772 * <p>The system will assign an unused RFCOMM channel to listen on.
2773 * <p>The system will also register a Service Discovery
2774 * Protocol (SDP) record with the local SDP server containing the specified
2775 * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
2776 * can use the same UUID to query our SDP server and discover which channel
2777 * to connect to. This SDP record will be removed when this socket is
2778 * closed, or if this application closes unexpectedly.
2779 * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
2780 * connect to this socket from another device using the same {@link UUID}.
2781 *
2782 * @param name service name for SDP record
2783 * @param uuid uuid for SDP record
2784 * @return a listening RFCOMM BluetoothServerSocket
2785 * @throws IOException on error, for example Bluetooth not available, or insufficient
2786 * permissions, or channel in use.
2787 */
2788 @RequiresLegacyBluetoothPermission
2789 @RequiresBluetoothConnectPermission
2790 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2791 public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
2792 throws IOException {
2793 return createNewRfcommSocketAndRecord(name, uuid, false, false);
2794 }
2795
2796 /**
2797 * Create a listening, encrypted,
2798 * RFCOMM Bluetooth socket with Service Record.
2799 * <p>The link will be encrypted, but the link key is not required to be authenticated
2800 * i.e the communication is vulnerable to Person In the Middle attacks. Use
2801 * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key.
2802 * <p> Use this socket if authentication of link key is not possible.
2803 * For example, for Bluetooth 2.1 devices, if any of the devices does not have
2804 * an input and output capability or just has the ability to display a numeric key,
2805 * a secure socket connection is not possible and this socket can be used.
2806 * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required.
2807 * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandatory.
2808 * For more details, refer to the Security Model section 5.2 (vol 3) of
2809 * Bluetooth Core Specification version 2.1 + EDR.
2810 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
2811 * connections from a listening {@link BluetoothServerSocket}.
2812 * <p>The system will assign an unused RFCOMM channel to listen on.
2813 * <p>The system will also register a Service Discovery
2814 * Protocol (SDP) record with the local SDP server containing the specified
2815 * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
2816 * can use the same UUID to query our SDP server and discover which channel
2817 * to connect to. This SDP record will be removed when this socket is
2818 * closed, or if this application closes unexpectedly.
2819 * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
2820 * connect to this socket from another device using the same {@link UUID}.
2821 *
2822 * @param name service name for SDP record
2823 * @param uuid uuid for SDP record
2824 * @return a listening RFCOMM BluetoothServerSocket
2825 * @throws IOException on error, for example Bluetooth not available, or insufficient
2826 * permissions, or channel in use.
2827 * @hide
2828 */
2829 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2830 @RequiresLegacyBluetoothPermission
2831 @RequiresBluetoothConnectPermission
2832 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2833 public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid)
2834 throws IOException {
2835 return createNewRfcommSocketAndRecord(name, uuid, false, true);
2836 }
2837
2838 @RequiresBluetoothConnectPermission
2839 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2840 private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
2841 boolean auth, boolean encrypt) throws IOException {
2842 BluetoothServerSocket socket;
2843 socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth, encrypt,
2844 new ParcelUuid(uuid));
2845 socket.setServiceName(name);
2846 int errno = socket.mSocket.bindListen();
2847 if (errno != 0) {
2848 //TODO(BT): Throw the same exception error code
2849 // that the previous code was using.
2850 //socket.mSocket.throwErrnoNative(errno);
2851 throw new IOException("Error: " + errno);
2852 }
2853 return socket;
2854 }
2855
2856 /**
2857 * Construct an unencrypted, unauthenticated, RFCOMM server socket.
2858 * Call #accept to retrieve connections to this socket.
2859 *
2860 * @return An RFCOMM BluetoothServerSocket
2861 * @throws IOException On error, for example Bluetooth not available, or insufficient
2862 * permissions.
2863 * @hide
2864 */
2865 @RequiresBluetoothConnectPermission
2866 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2867 public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
2868 BluetoothServerSocket socket =
2869 new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port);
2870 int errno = socket.mSocket.bindListen();
2871 if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
2872 socket.setChannel(socket.mSocket.getPort());
2873 }
2874 if (errno != 0) {
2875 //TODO(BT): Throw the same exception error code
2876 // that the previous code was using.
2877 //socket.mSocket.throwErrnoNative(errno);
2878 throw new IOException("Error: " + errno);
2879 }
2880 return socket;
2881 }
2882
2883 /**
2884 * Construct an encrypted, authenticated, L2CAP server socket.
2885 * Call #accept to retrieve connections to this socket.
2886 * <p>To auto assign a port without creating a SDP record use
2887 * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
2888 *
2889 * @param port the PSM to listen on
2890 * @param mitm enforce person-in-the-middle protection for authentication.
2891 * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2
2892 * connections.
2893 * @return An L2CAP BluetoothServerSocket
2894 * @throws IOException On error, for example Bluetooth not available, or insufficient
2895 * permissions.
2896 * @hide
2897 */
2898 @RequiresBluetoothConnectPermission
2899 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2900 public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin)
2901 throws IOException {
2902 BluetoothServerSocket socket =
2903 new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, true, true, port, mitm,
2904 min16DigitPin);
2905 int errno = socket.mSocket.bindListen();
2906 if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
2907 int assignedChannel = socket.mSocket.getPort();
2908 if (DBG) Log.d(TAG, "listenUsingL2capOn: set assigned channel to " + assignedChannel);
2909 socket.setChannel(assignedChannel);
2910 }
2911 if (errno != 0) {
2912 //TODO(BT): Throw the same exception error code
2913 // that the previous code was using.
2914 //socket.mSocket.throwErrnoNative(errno);
2915 throw new IOException("Error: " + errno);
2916 }
2917 return socket;
2918 }
2919
2920 /**
2921 * Construct an encrypted, authenticated, L2CAP server socket.
2922 * Call #accept to retrieve connections to this socket.
2923 * <p>To auto assign a port without creating a SDP record use
2924 * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
2925 *
2926 * @param port the PSM to listen on
2927 * @return An L2CAP BluetoothServerSocket
2928 * @throws IOException On error, for example Bluetooth not available, or insufficient
2929 * permissions.
2930 * @hide
2931 */
2932 @RequiresBluetoothConnectPermission
2933 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2934 public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException {
2935 return listenUsingL2capOn(port, false, false);
2936 }
2937
2938 /**
2939 * Construct an insecure L2CAP server socket.
2940 * Call #accept to retrieve connections to this socket.
2941 * <p>To auto assign a port without creating a SDP record use
2942 * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
2943 *
2944 * @param port the PSM to listen on
2945 * @return An L2CAP BluetoothServerSocket
2946 * @throws IOException On error, for example Bluetooth not available, or insufficient
2947 * permissions.
2948 * @hide
2949 */
2950 @RequiresBluetoothConnectPermission
2951 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2952 public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
2953 Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port);
2954 BluetoothServerSocket socket =
2955 new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false,
2956 false);
2957 int errno = socket.mSocket.bindListen();
2958 if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
2959 int assignedChannel = socket.mSocket.getPort();
2960 if (DBG) {
2961 Log.d(TAG, "listenUsingInsecureL2capOn: set assigned channel to "
2962 + assignedChannel);
2963 }
2964 socket.setChannel(assignedChannel);
2965 }
2966 if (errno != 0) {
2967 //TODO(BT): Throw the same exception error code
2968 // that the previous code was using.
2969 //socket.mSocket.throwErrnoNative(errno);
2970 throw new IOException("Error: " + errno);
2971 }
2972 return socket;
2973
2974 }
2975
2976 /**
2977 * Read the local Out of Band Pairing Data
2978 *
2979 * @return Pair<byte[], byte[]> of Hash and Randomizer
2980 * @hide
2981 */
2982 @RequiresLegacyBluetoothPermission
2983 @RequiresBluetoothConnectPermission
2984 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2985 @SuppressLint("AndroidFrameworkRequiresPermission")
2986 public Pair<byte[], byte[]> readOutOfBandData() {
2987 return null;
2988 }
2989
2990 /**
2991 * Get the profile proxy object associated with the profile.
2992 *
2993 * <p>Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP},
2994 * {@link BluetoothProfile#GATT}, {@link BluetoothProfile#HEARING_AID}, or {@link
2995 * BluetoothProfile#GATT_SERVER}. Clients must implement {@link
2996 * BluetoothProfile.ServiceListener} to get notified of the connection status and to get the
2997 * proxy object.
2998 *
2999 * @param context Context of the application
3000 * @param listener The service Listener for connection callbacks.
3001 * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET},
3002 * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, {@link
3003 * BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#GATT_SERVER}.
3004 * @return true on success, false on error
3005 */
3006 @SuppressLint({
3007 "AndroidFrameworkRequiresPermission",
3008 "AndroidFrameworkBluetoothPermission"
3009 })
3010 public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
3011 int profile) {
3012 if (context == null || listener == null) {
3013 return false;
3014 }
3015
3016 if (profile == BluetoothProfile.HEADSET) {
3017 BluetoothHeadset headset = new BluetoothHeadset(context, listener, this);
3018 return true;
3019 } else if (profile == BluetoothProfile.A2DP) {
3020 BluetoothA2dp a2dp = new BluetoothA2dp(context, listener, this);
3021 return true;
3022 } else if (profile == BluetoothProfile.A2DP_SINK) {
3023 BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener, this);
3024 return true;
3025 } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
3026 BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener, this);
3027 return true;
3028 } else if (profile == BluetoothProfile.HID_HOST) {
3029 BluetoothHidHost iDev = new BluetoothHidHost(context, listener, this);
3030 return true;
3031 } else if (profile == BluetoothProfile.PAN) {
3032 BluetoothPan pan = new BluetoothPan(context, listener, this);
3033 return true;
3034 } else if (profile == BluetoothProfile.PBAP) {
3035 BluetoothPbap pbap = new BluetoothPbap(context, listener, this);
3036 return true;
3037 } else if (profile == BluetoothProfile.HEALTH) {
3038 Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated");
3039 return false;
3040 } else if (profile == BluetoothProfile.MAP) {
3041 BluetoothMap map = new BluetoothMap(context, listener, this);
3042 return true;
3043 } else if (profile == BluetoothProfile.HEADSET_CLIENT) {
3044 BluetoothHeadsetClient headsetClient =
3045 new BluetoothHeadsetClient(context, listener, this);
3046 return true;
3047 } else if (profile == BluetoothProfile.SAP) {
3048 BluetoothSap sap = new BluetoothSap(context, listener, this);
3049 return true;
3050 } else if (profile == BluetoothProfile.PBAP_CLIENT) {
3051 BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener, this);
3052 return true;
3053 } else if (profile == BluetoothProfile.MAP_CLIENT) {
3054 BluetoothMapClient mapClient = new BluetoothMapClient(context, listener, this);
3055 return true;
3056 } else if (profile == BluetoothProfile.HID_DEVICE) {
3057 BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this);
3058 return true;
3059 } else if (profile == BluetoothProfile.HEARING_AID) {
3060 if (isHearingAidProfileSupported()) {
3061 BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this);
3062 return true;
3063 }
3064 return false;
3065 } else if (profile == BluetoothProfile.LE_AUDIO) {
3066 BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this);
3067 return true;
3068 } else {
3069 return false;
3070 }
3071 }
3072
3073 /**
3074 * Close the connection of the profile proxy to the Service.
3075 *
3076 * <p> Clients should call this when they are no longer using
3077 * the proxy obtained from {@link #getProfileProxy}.
3078 * Profile can be one of {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP}
3079 *
3080 * @param profile
3081 * @param proxy Profile proxy object
3082 */
3083 @SuppressLint({
3084 "AndroidFrameworkRequiresPermission",
3085 "AndroidFrameworkBluetoothPermission"
3086 })
3087 public void closeProfileProxy(int profile, BluetoothProfile proxy) {
3088 if (proxy == null) {
3089 return;
3090 }
3091
3092 switch (profile) {
3093 case BluetoothProfile.HEADSET:
3094 BluetoothHeadset headset = (BluetoothHeadset) proxy;
3095 headset.close();
3096 break;
3097 case BluetoothProfile.A2DP:
3098 BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
3099 a2dp.close();
3100 break;
3101 case BluetoothProfile.A2DP_SINK:
3102 BluetoothA2dpSink a2dpSink = (BluetoothA2dpSink) proxy;
3103 a2dpSink.close();
3104 break;
3105 case BluetoothProfile.AVRCP_CONTROLLER:
3106 BluetoothAvrcpController avrcp = (BluetoothAvrcpController) proxy;
3107 avrcp.close();
3108 break;
3109 case BluetoothProfile.HID_HOST:
3110 BluetoothHidHost iDev = (BluetoothHidHost) proxy;
3111 iDev.close();
3112 break;
3113 case BluetoothProfile.PAN:
3114 BluetoothPan pan = (BluetoothPan) proxy;
3115 pan.close();
3116 break;
3117 case BluetoothProfile.PBAP:
3118 BluetoothPbap pbap = (BluetoothPbap) proxy;
3119 pbap.close();
3120 break;
3121 case BluetoothProfile.GATT:
3122 BluetoothGatt gatt = (BluetoothGatt) proxy;
3123 gatt.close();
3124 break;
3125 case BluetoothProfile.GATT_SERVER:
3126 BluetoothGattServer gattServer = (BluetoothGattServer) proxy;
3127 gattServer.close();
3128 break;
3129 case BluetoothProfile.MAP:
3130 BluetoothMap map = (BluetoothMap) proxy;
3131 map.close();
3132 break;
3133 case BluetoothProfile.HEADSET_CLIENT:
3134 BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient) proxy;
3135 headsetClient.close();
3136 break;
3137 case BluetoothProfile.SAP:
3138 BluetoothSap sap = (BluetoothSap) proxy;
3139 sap.close();
3140 break;
3141 case BluetoothProfile.PBAP_CLIENT:
3142 BluetoothPbapClient pbapClient = (BluetoothPbapClient) proxy;
3143 pbapClient.close();
3144 break;
3145 case BluetoothProfile.MAP_CLIENT:
3146 BluetoothMapClient mapClient = (BluetoothMapClient) proxy;
3147 mapClient.close();
3148 break;
3149 case BluetoothProfile.HID_DEVICE:
3150 BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy;
3151 hidDevice.close();
3152 break;
3153 case BluetoothProfile.HEARING_AID:
3154 BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
3155 hearingAid.close();
3156 break;
3157 case BluetoothProfile.LE_AUDIO:
3158 BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy;
3159 leAudio.close();
3160 }
3161 }
3162
3163 private static final IBluetoothManagerCallback sManagerCallback =
3164 new IBluetoothManagerCallback.Stub() {
3165 public void onBluetoothServiceUp(IBluetooth bluetoothService) {
3166 if (DBG) {
3167 Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
3168 }
3169
3170 synchronized (sServiceLock) {
3171 sService = bluetoothService;
3172 for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
3173 try {
3174 if (cb != null) {
3175 cb.onBluetoothServiceUp(bluetoothService);
3176 } else {
3177 Log.d(TAG, "onBluetoothServiceUp: cb is null!");
3178 }
3179 } catch (Exception e) {
3180 Log.e(TAG, "", e);
3181 }
3182 }
3183 }
3184 }
3185
3186 public void onBluetoothServiceDown() {
3187 if (DBG) {
3188 Log.d(TAG, "onBluetoothServiceDown");
3189 }
3190
3191 synchronized (sServiceLock) {
3192 sService = null;
3193 for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
3194 try {
3195 if (cb != null) {
3196 cb.onBluetoothServiceDown();
3197 } else {
3198 Log.d(TAG, "onBluetoothServiceDown: cb is null!");
3199 }
3200 } catch (Exception e) {
3201 Log.e(TAG, "", e);
3202 }
3203 }
3204 }
3205 }
3206
3207 public void onBrEdrDown() {
3208 if (VDBG) {
3209 Log.i(TAG, "onBrEdrDown");
3210 }
3211
3212 synchronized (sServiceLock) {
3213 for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
3214 try {
3215 if (cb != null) {
3216 cb.onBrEdrDown();
3217 } else {
3218 Log.d(TAG, "onBrEdrDown: cb is null!");
3219 }
3220 } catch (Exception e) {
3221 Log.e(TAG, "", e);
3222 }
3223 }
3224 }
3225 }
3226 };
3227
3228 private final IBluetoothManagerCallback mManagerCallback =
3229 new IBluetoothManagerCallback.Stub() {
3230 public void onBluetoothServiceUp(IBluetooth bluetoothService) {
3231 synchronized (mServiceLock.writeLock()) {
3232 mService = bluetoothService;
3233 }
3234 synchronized (mMetadataListeners) {
3235 mMetadataListeners.forEach((device, pair) -> {
3236 try {
3237 mService.registerMetadataListener(mBluetoothMetadataListener,
3238 device, mAttributionSource);
3239 } catch (RemoteException e) {
3240 Log.e(TAG, "Failed to register metadata listener", e);
3241 }
3242 });
3243 }
3244 synchronized (mBluetoothConnectionCallbackExecutorMap) {
3245 if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
3246 try {
3247 mService.registerBluetoothConnectionCallback(mConnectionCallback,
3248 mAttributionSource);
3249 } catch (RemoteException e) {
3250 Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth"
3251 + "connection callback", e);
3252 }
3253 }
3254 }
3255 }
3256
3257 public void onBluetoothServiceDown() {
3258 synchronized (mServiceLock.writeLock()) {
3259 mService = null;
3260 if (mLeScanClients != null) {
3261 mLeScanClients.clear();
3262 }
3263 if (mBluetoothLeAdvertiser != null) {
3264 mBluetoothLeAdvertiser.cleanup();
3265 }
3266 if (mBluetoothLeScanner != null) {
3267 mBluetoothLeScanner.cleanup();
3268 }
3269 }
3270 }
3271
3272 public void onBrEdrDown() {
3273 }
3274 };
3275
3276 /**
3277 * Enable the Bluetooth Adapter, but don't auto-connect devices
3278 * and don't persist state. Only for use by system applications.
3279 *
3280 * @hide
3281 */
3282 @SystemApi
3283 @RequiresLegacyBluetoothAdminPermission
3284 @RequiresBluetoothConnectPermission
3285 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
3286 public boolean enableNoAutoConnect() {
3287 if (isEnabled()) {
3288 if (DBG) {
3289 Log.d(TAG, "enableNoAutoConnect(): BT already enabled!");
3290 }
3291 return true;
3292 }
3293 try {
3294 return mManagerService.enableNoAutoConnect(mAttributionSource);
3295 } catch (RemoteException e) {
3296 Log.e(TAG, "", e);
3297 }
3298 return false;
3299 }
3300
3301 /** @hide */
3302 @Retention(RetentionPolicy.SOURCE)
3303 @IntDef(value = {
3304 BluetoothStatusCodes.ERROR_UNKNOWN,
3305 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
3306 BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST,
3307 })
3308 public @interface OobError {}
3309
3310 /**
3311 * Provides callback methods for receiving {@link OobData} from the host stack, as well as an
3312 * error interface in order to allow the caller to determine next steps based on the {@code
3313 * ErrorCode}.
3314 *
3315 * @hide
3316 */
3317 @SystemApi
3318 public interface OobDataCallback {
3319 /**
3320 * Handles the {@link OobData} received from the host stack.
3321 *
3322 * @param transport - whether the {@link OobData} is generated for LE or Classic.
3323 * @param oobData - data generated in the host stack(LE) or controller (Classic)
3324 */
3325 void onOobData(@Transport int transport, @NonNull OobData oobData);
3326
3327 /**
3328 * Provides feedback when things don't go as expected.
3329 *
3330 * @param errorCode - the code describing the type of error that occurred.
3331 */
3332 void onError(@OobError int errorCode);
3333 }
3334
3335 /**
3336 * Wraps an AIDL interface around an {@link OobDataCallback} interface.
3337 *
3338 * @see {@link IBluetoothOobDataCallback} for interface definition.
3339 *
3340 * @hide
3341 */
3342 public class WrappedOobDataCallback extends IBluetoothOobDataCallback.Stub {
3343 private final OobDataCallback mCallback;
3344 private final Executor mExecutor;
3345
3346 /**
3347 * @param callback - object to receive {@link OobData} must be a non null argument
3348 *
3349 * @throws NullPointerException if the callback is null.
3350 */
3351 WrappedOobDataCallback(@NonNull OobDataCallback callback,
3352 @NonNull @CallbackExecutor Executor executor) {
3353 requireNonNull(callback);
3354 requireNonNull(executor);
3355 mCallback = callback;
3356 mExecutor = executor;
3357 }
3358 /**
3359 * Wrapper function to relay to the {@link OobDataCallback#onOobData}
3360 *
3361 * @param transport - whether the {@link OobData} is generated for LE or Classic.
3362 * @param oobData - data generated in the host stack(LE) or controller (Classic)
3363 *
3364 * @hide
3365 */
3366 public void onOobData(@Transport int transport, @NonNull OobData oobData) {
3367 mExecutor.execute(new Runnable() {
3368 public void run() {
3369 mCallback.onOobData(transport, oobData);
3370 }
3371 });
3372 }
3373 /**
3374 * Wrapper function to relay to the {@link OobDataCallback#onError}
3375 *
3376 * @param errorCode - the code descibing the type of error that occurred.
3377 *
3378 * @hide
3379 */
3380 public void onError(@OobError int errorCode) {
3381 mExecutor.execute(new Runnable() {
3382 public void run() {
3383 mCallback.onError(errorCode);
3384 }
3385 });
3386 }
3387 }
3388
3389 /**
3390 * Fetches a secret data value that can be used for a secure and simple pairing experience.
3391 *
3392 * <p>This is the Local Out of Band data the comes from the
3393 *
3394 * <p>This secret is the local Out of Band data. This data is used to securely and quickly
3395 * pair two devices with minimal user interaction.
3396 *
3397 * <p>For example, this secret can be transferred to a remote device out of band (meaning any
3398 * other way besides using bluetooth). Once the remote device finds this device using the
3399 * information given in the data, such as the PUBLIC ADDRESS, the remote device could then
3400 * connect to this device using this secret when the pairing sequenece asks for the secret.
3401 * This device will respond by automatically accepting the pairing due to the secret being so
3402 * trustworthy.
3403 *
3404 * @param transport - provide type of transport (e.g. LE or Classic).
3405 * @param callback - target object to receive the {@link OobData} value.
3406 *
3407 * @throws NullPointerException if callback is null.
3408 * @throws IllegalArgumentException if the transport is not valid.
3409 *
3410 * @hide
3411 */
3412 @SystemApi
3413 @RequiresBluetoothConnectPermission
3414 @RequiresPermission(allOf = {
3415 android.Manifest.permission.BLUETOOTH_CONNECT,
3416 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
3417 })
3418 public void generateLocalOobData(@Transport int transport,
3419 @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) {
3420 if (transport != BluetoothDevice.TRANSPORT_BREDR && transport
3421 != BluetoothDevice.TRANSPORT_LE) {
3422 throw new IllegalArgumentException("Invalid transport '" + transport + "'!");
3423 }
3424 requireNonNull(callback);
3425 if (!isEnabled()) {
3426 Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!");
3427 callback.onError(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED);
3428 } else {
3429 try {
3430 mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback,
3431 executor), mAttributionSource);
3432 } catch (RemoteException e) {
3433 Log.e(TAG, "", e);
3434 }
3435 }
3436 }
3437
3438 /**
3439 * Enable control of the Bluetooth Adapter for a single application.
3440 *
3441 * <p>Some applications need to use Bluetooth for short periods of time to
3442 * transfer data but don't want all the associated implications like
3443 * automatic connection to headsets etc.
3444 *
3445 * <p> Multiple applications can call this. This is reference counted and
3446 * Bluetooth disabled only when no one else is using it. There will be no UI
3447 * shown to the user while bluetooth is being enabled. Any user action will
3448 * override this call. For example, if user wants Bluetooth on and the last
3449 * user of this API wanted to disable Bluetooth, Bluetooth will not be
3450 * turned off.
3451 *
3452 * <p> This API is only meant to be used by internal applications. Third
3453 * party applications but use {@link #enable} and {@link #disable} APIs.
3454 *
3455 * <p> If this API returns true, it means the callback will be called.
3456 * The callback will be called with the current state of Bluetooth.
3457 * If the state is not what was requested, an internal error would be the
3458 * reason. If Bluetooth is already on and if this function is called to turn
3459 * it on, the api will return true and a callback will be called.
3460 *
3461 * @param on True for on, false for off.
3462 * @param callback The callback to notify changes to the state.
3463 * @hide
3464 */
3465 @RequiresLegacyBluetoothPermission
3466 @RequiresBluetoothConnectPermission
3467 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
3468 @SuppressLint("AndroidFrameworkRequiresPermission")
3469 public boolean changeApplicationBluetoothState(boolean on,
3470 BluetoothStateChangeCallback callback) {
3471 return false;
3472 }
3473
3474 /**
3475 * @hide
3476 */
3477 public interface BluetoothStateChangeCallback {
3478 /**
3479 * @hide
3480 */
3481 void onBluetoothStateChange(boolean on);
3482 }
3483
3484 /**
3485 * @hide
3486 */
3487 public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub {
3488 private BluetoothStateChangeCallback mCallback;
3489
3490 StateChangeCallbackWrapper(BluetoothStateChangeCallback callback) {
3491 mCallback = callback;
3492 }
3493
3494 @Override
3495 public void onBluetoothStateChange(boolean on) {
3496 mCallback.onBluetoothStateChange(on);
3497 }
3498 }
3499
3500 private Set<BluetoothDevice> toDeviceSet(List<BluetoothDevice> devices) {
3501 Set<BluetoothDevice> deviceSet = new HashSet<BluetoothDevice>(devices);
3502 return Collections.unmodifiableSet(deviceSet);
3503 }
3504
3505 protected void finalize() throws Throwable {
3506 try {
3507 removeServiceStateCallback(mManagerCallback);
3508 } finally {
3509 super.finalize();
3510 }
3511 }
3512
3513 /**
3514 * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0"
3515 * <p>Alphabetic characters must be uppercase to be valid.
3516 *
3517 * @param address Bluetooth address as string
3518 * @return true if the address is valid, false otherwise
3519 */
3520 public static boolean checkBluetoothAddress(String address) {
3521 if (address == null || address.length() != ADDRESS_LENGTH) {
3522 return false;
3523 }
3524 for (int i = 0; i < ADDRESS_LENGTH; i++) {
3525 char c = address.charAt(i);
3526 switch (i % 3) {
3527 case 0:
3528 case 1:
3529 if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
3530 // hex character, OK
3531 break;
3532 }
3533 return false;
3534 case 2:
3535 if (c == ':') {
3536 break; // OK
3537 }
3538 return false;
3539 }
3540 }
3541 return true;
3542 }
3543
3544 /**
3545 * Determines whether a String Bluetooth address, such as "F0:43:A8:23:10:00"
3546 * is a RANDOM STATIC address.
3547 *
3548 * RANDOM STATIC: (addr & 0xC0) == 0xC0
3549 * RANDOM RESOLVABLE: (addr & 0xC0) == 0x40
3550 * RANDOM non-RESOLVABLE: (addr & 0xC0) == 0x00
3551 *
3552 * @param address Bluetooth address as string
3553 * @return true if the 2 Most Significant Bits of the address equals 0xC0.
3554 *
3555 * @hide
3556 */
3557 public static boolean isAddressRandomStatic(@NonNull String address) {
3558 requireNonNull(address);
3559 return checkBluetoothAddress(address)
3560 && (Integer.parseInt(address.split(":")[0], 16) & 0xC0) == 0xC0;
3561 }
3562
3563 /** {@hide} */
3564 @UnsupportedAppUsage
3565 @RequiresNoPermission
3566 public IBluetoothManager getBluetoothManager() {
3567 return mManagerService;
3568 }
3569
3570 /** {@hide} */
3571 @RequiresNoPermission
3572 public AttributionSource getAttributionSource() {
3573 return mAttributionSource;
3574 }
3575
3576 @GuardedBy("sServiceLock")
3577 private static final WeakHashMap<IBluetoothManagerCallback, Void> sProxyServiceStateCallbacks =
3578 new WeakHashMap<>();
3579
3580 /*package*/ IBluetooth getBluetoothService() {
3581 synchronized (sServiceLock) {
3582 if (sProxyServiceStateCallbacks.isEmpty()) {
3583 throw new IllegalStateException(
3584 "Anonymous service access requires at least one lifecycle in process");
3585 }
3586 return sService;
3587 }
3588 }
3589
3590 @UnsupportedAppUsage
3591 /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) {
3592 Objects.requireNonNull(cb);
3593 synchronized (sServiceLock) {
3594 sProxyServiceStateCallbacks.put(cb, null);
3595 registerOrUnregisterAdapterLocked();
3596 return sService;
3597 }
3598 }
3599
3600 /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) {
3601 Objects.requireNonNull(cb);
3602 synchronized (sServiceLock) {
3603 sProxyServiceStateCallbacks.remove(cb);
3604 registerOrUnregisterAdapterLocked();
3605 }
3606 }
3607
3608 /**
3609 * Handle registering (or unregistering) a single process-wide
3610 * {@link IBluetoothManagerCallback} based on the presence of local
3611 * {@link #sProxyServiceStateCallbacks} clients.
3612 */
3613 @GuardedBy("sServiceLock")
3614 private void registerOrUnregisterAdapterLocked() {
3615 final boolean isRegistered = sServiceRegistered;
3616 final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty();
3617
3618 if (isRegistered != wantRegistered) {
3619 if (wantRegistered) {
3620 try {
3621 sService = mManagerService.registerAdapter(sManagerCallback);
3622 } catch (RemoteException e) {
3623 throw e.rethrowFromSystemServer();
3624 }
3625 } else {
3626 try {
3627 mManagerService.unregisterAdapter(sManagerCallback);
3628 sService = null;
3629 } catch (RemoteException e) {
3630 throw e.rethrowFromSystemServer();
3631 }
3632 }
3633 sServiceRegistered = wantRegistered;
3634 }
3635 }
3636
3637 /**
3638 * Callback interface used to deliver LE scan results.
3639 *
3640 * @see #startLeScan(LeScanCallback)
3641 * @see #startLeScan(UUID[], LeScanCallback)
3642 */
3643 public interface LeScanCallback {
3644 /**
3645 * Callback reporting an LE device found during a device scan initiated
3646 * by the {@link BluetoothAdapter#startLeScan} function.
3647 *
3648 * @param device Identifies the remote device
3649 * @param rssi The RSSI value for the remote device as reported by the Bluetooth hardware. 0
3650 * if no RSSI value is available.
3651 * @param scanRecord The content of the advertisement record offered by the remote device.
3652 */
3653 void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
3654 }
3655
3656 /**
3657 * Register a callback to receive events whenever the bluetooth stack goes down and back up,
3658 * e.g. in the event the bluetooth is turned off/on via settings.
3659 *
3660 * If the bluetooth stack is currently up, there will not be an initial callback call.
3661 * You can use the return value as an indication of this being the case.
3662 *
3663 * Callbacks will be delivered on a binder thread.
3664 *
3665 * @return whether bluetooth is already up currently
3666 *
3667 * @hide
3668 */
3669 public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) {
3670 return getBluetoothService(callback.mRemote) != null;
3671 }
3672
3673 /**
3674 * Unregister a callback registered via {@link #registerServiceLifecycleCallback}
3675 *
3676 * @hide
3677 */
3678 public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) {
3679 removeServiceStateCallback(callback.mRemote);
3680 }
3681
3682 /**
3683 * A callback for {@link #registerServiceLifecycleCallback}
3684 *
3685 * @hide
3686 */
3687 public abstract static class ServiceLifecycleCallback {
3688
3689 /** Called when the bluetooth stack is up */
3690 public abstract void onBluetoothServiceUp();
3691
3692 /** Called when the bluetooth stack is down */
3693 public abstract void onBluetoothServiceDown();
3694
3695 IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() {
3696 @Override
3697 public void onBluetoothServiceUp(IBluetooth bluetoothService) {
3698 ServiceLifecycleCallback.this.onBluetoothServiceUp();
3699 }
3700
3701 @Override
3702 public void onBluetoothServiceDown() {
3703 ServiceLifecycleCallback.this.onBluetoothServiceDown();
3704 }
3705
3706 @Override
3707 public void onBrEdrDown() {}
3708 };
3709 }
3710
3711 /**
3712 * Starts a scan for Bluetooth LE devices.
3713 *
3714 * <p>Results of the scan are reported using the
3715 * {@link LeScanCallback#onLeScan} callback.
3716 *
3717 * @param callback the callback LE scan results are delivered
3718 * @return true, if the scan was started successfully
3719 * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)}
3720 * instead.
3721 */
3722 @Deprecated
3723 @RequiresLegacyBluetoothAdminPermission
3724 @RequiresBluetoothScanPermission
3725 @RequiresBluetoothLocationPermission
3726 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
3727 public boolean startLeScan(LeScanCallback callback) {
3728 return startLeScan(null, callback);
3729 }
3730
3731 /**
3732 * Starts a scan for Bluetooth LE devices, looking for devices that
3733 * advertise given services.
3734 *
3735 * <p>Devices which advertise all specified services are reported using the
3736 * {@link LeScanCallback#onLeScan} callback.
3737 *
3738 * @param serviceUuids Array of services to look for
3739 * @param callback the callback LE scan results are delivered
3740 * @return true, if the scan was started successfully
3741 * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)}
3742 * instead.
3743 */
3744 @Deprecated
3745 @RequiresLegacyBluetoothAdminPermission
3746 @RequiresBluetoothScanPermission
3747 @RequiresBluetoothLocationPermission
3748 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
3749 public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
3750 if (DBG) {
3751 Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
3752 }
3753 if (callback == null) {
3754 if (DBG) {
3755 Log.e(TAG, "startLeScan: null callback");
3756 }
3757 return false;
3758 }
3759 BluetoothLeScanner scanner = getBluetoothLeScanner();
3760 if (scanner == null) {
3761 if (DBG) {
3762 Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
3763 }
3764 return false;
3765 }
3766
3767 synchronized (mLeScanClients) {
3768 if (mLeScanClients.containsKey(callback)) {
3769 if (DBG) {
3770 Log.e(TAG, "LE Scan has already started");
3771 }
3772 return false;
3773 }
3774
3775 try {
3776 IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
3777 if (iGatt == null) {
3778 // BLE is not supported
3779 return false;
3780 }
3781
3782 @SuppressLint("AndroidFrameworkBluetoothPermission")
3783 ScanCallback scanCallback = new ScanCallback() {
3784 @Override
3785 public void onScanResult(int callbackType, ScanResult result) {
3786 if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
3787 // Should not happen.
3788 Log.e(TAG, "LE Scan has already started");
3789 return;
3790 }
3791 ScanRecord scanRecord = result.getScanRecord();
3792 if (scanRecord == null) {
3793 return;
3794 }
3795 if (serviceUuids != null) {
3796 List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
3797 for (UUID uuid : serviceUuids) {
3798 uuids.add(new ParcelUuid(uuid));
3799 }
3800 List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids();
3801 if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) {
3802 if (DBG) {
3803 Log.d(TAG, "uuids does not match");
3804 }
3805 return;
3806 }
3807 }
3808 callback.onLeScan(result.getDevice(), result.getRssi(),
3809 scanRecord.getBytes());
3810 }
3811 };
3812 ScanSettings settings = new ScanSettings.Builder().setCallbackType(
3813 ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
3814 .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
3815 .build();
3816
3817 List<ScanFilter> filters = new ArrayList<ScanFilter>();
3818 if (serviceUuids != null && serviceUuids.length > 0) {
3819 // Note scan filter does not support matching an UUID array so we put one
3820 // UUID to hardware and match the whole array in callback.
3821 ScanFilter filter =
3822 new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuids[0]))
3823 .build();
3824 filters.add(filter);
3825 }
3826 scanner.startScan(filters, settings, scanCallback);
3827
3828 mLeScanClients.put(callback, scanCallback);
3829 return true;
3830
3831 } catch (RemoteException e) {
3832 Log.e(TAG, "", e);
3833 }
3834 }
3835 return false;
3836 }
3837
3838 /**
3839 * Stops an ongoing Bluetooth LE device scan.
3840 *
3841 * @param callback used to identify which scan to stop must be the same handle used to start the
3842 * scan
3843 * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead.
3844 */
3845 @Deprecated
3846 @RequiresLegacyBluetoothAdminPermission
3847 @RequiresBluetoothScanPermission
3848 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
3849 public void stopLeScan(LeScanCallback callback) {
3850 if (DBG) {
3851 Log.d(TAG, "stopLeScan()");
3852 }
3853 BluetoothLeScanner scanner = getBluetoothLeScanner();
3854 if (scanner == null) {
3855 return;
3856 }
3857 synchronized (mLeScanClients) {
3858 ScanCallback scanCallback = mLeScanClients.remove(callback);
3859 if (scanCallback == null) {
3860 if (DBG) {
3861 Log.d(TAG, "scan not started yet");
3862 }
3863 return;
3864 }
3865 scanner.stopScan(scanCallback);
3866 }
3867 }
3868
3869 /**
3870 * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
3871 * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen
3872 * for incoming connections. The supported Bluetooth transport is LE only.
3873 * <p>A remote device connecting to this socket will be authenticated and communication on this
3874 * socket will be encrypted.
3875 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
3876 * {@link BluetoothServerSocket}.
3877 * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {@link
3878 * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is
3879 * closed, Bluetooth is turned off, or the application exits unexpectedly.
3880 * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
3881 * defined and performed by the application.
3882 * <p>Use {@link BluetoothDevice#createL2capChannel(int)} to connect to this server
3883 * socket from another Android device that is given the PSM value.
3884 *
3885 * @return an L2CAP CoC BluetoothServerSocket
3886 * @throws IOException on error, for example Bluetooth not available, or insufficient
3887 * permissions, or unable to start this CoC
3888 */
3889 @RequiresLegacyBluetoothPermission
3890 @RequiresBluetoothConnectPermission
3891 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
3892 public @NonNull BluetoothServerSocket listenUsingL2capChannel()
3893 throws IOException {
3894 BluetoothServerSocket socket =
3895 new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true,
3896 SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
3897 int errno = socket.mSocket.bindListen();
3898 if (errno != 0) {
3899 throw new IOException("Error: " + errno);
3900 }
3901
3902 int assignedPsm = socket.mSocket.getPort();
3903 if (assignedPsm == 0) {
3904 throw new IOException("Error: Unable to assign PSM value");
3905 }
3906 if (DBG) {
3907 Log.d(TAG, "listenUsingL2capChannel: set assigned PSM to "
3908 + assignedPsm);
3909 }
3910 socket.setChannel(assignedPsm);
3911
3912 return socket;
3913 }
3914
3915 /**
3916 * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
3917 * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The
3918 * supported Bluetooth transport is LE only.
3919 * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
3920 * to person-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and
3921 * authenticated communication channel is desired.
3922 * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
3923 * {@link BluetoothServerSocket}.
3924 * <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value
3925 * can be read from the {@link BluetoothServerSocket#getPsm()} and this value will be released
3926 * when this server socket is closed, Bluetooth is turned off, or the application exits
3927 * unexpectedly.
3928 * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
3929 * defined and performed by the application.
3930 * <p>Use {@link BluetoothDevice#createInsecureL2capChannel(int)} to connect to this server
3931 * socket from another Android device that is given the PSM value.
3932 *
3933 * @return an L2CAP CoC BluetoothServerSocket
3934 * @throws IOException on error, for example Bluetooth not available, or insufficient
3935 * permissions, or unable to start this CoC
3936 */
3937 @RequiresLegacyBluetoothPermission
3938 @RequiresBluetoothConnectPermission
3939 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
3940 public @NonNull BluetoothServerSocket listenUsingInsecureL2capChannel()
3941 throws IOException {
3942 BluetoothServerSocket socket =
3943 new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false,
3944 SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
3945 int errno = socket.mSocket.bindListen();
3946 if (errno != 0) {
3947 throw new IOException("Error: " + errno);
3948 }
3949
3950 int assignedPsm = socket.mSocket.getPort();
3951 if (assignedPsm == 0) {
3952 throw new IOException("Error: Unable to assign PSM value");
3953 }
3954 if (DBG) {
3955 Log.d(TAG, "listenUsingInsecureL2capChannel: set assigned PSM to "
3956 + assignedPsm);
3957 }
3958 socket.setChannel(assignedPsm);
3959
3960 return socket;
3961 }
3962
3963 /**
3964 * Register a {@link #OnMetadataChangedListener} to receive update about metadata
3965 * changes for this {@link BluetoothDevice}.
3966 * Registration must be done when Bluetooth is ON and will last until
3967 * {@link #removeOnMetadataChangedListener(BluetoothDevice)} is called, even when Bluetooth
3968 * restarted in the middle.
3969 * All input parameters should not be null or {@link NullPointerException} will be triggered.
3970 * The same {@link BluetoothDevice} and {@link #OnMetadataChangedListener} pair can only be
3971 * registered once, double registration would cause {@link IllegalArgumentException}.
3972 *
3973 * @param device {@link BluetoothDevice} that will be registered
3974 * @param executor the executor for listener callback
3975 * @param listener {@link #OnMetadataChangedListener} that will receive asynchronous callbacks
3976 * @return true on success, false on error
3977 * @throws NullPointerException If one of {@code listener}, {@code device} or {@code executor}
3978 * is null.
3979 * @throws IllegalArgumentException The same {@link #OnMetadataChangedListener} and
3980 * {@link BluetoothDevice} are registered twice.
3981 * @hide
3982 */
3983 @SystemApi
3984 @RequiresBluetoothConnectPermission
3985 @RequiresPermission(allOf = {
3986 android.Manifest.permission.BLUETOOTH_CONNECT,
3987 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
3988 })
3989 public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device,
3990 @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) {
3991 if (DBG) Log.d(TAG, "addOnMetadataChangedListener()");
3992
3993 final IBluetooth service = mService;
3994 if (service == null) {
3995 Log.e(TAG, "Bluetooth is not enabled. Cannot register metadata listener");
3996 return false;
3997 }
3998 if (listener == null) {
3999 throw new NullPointerException("listener is null");
4000 }
4001 if (device == null) {
4002 throw new NullPointerException("device is null");
4003 }
4004 if (executor == null) {
4005 throw new NullPointerException("executor is null");
4006 }
4007
4008 synchronized (mMetadataListeners) {
4009 List<Pair<OnMetadataChangedListener, Executor>> listenerList =
4010 mMetadataListeners.get(device);
4011 if (listenerList == null) {
4012 // Create new listener/executor list for registeration
4013 listenerList = new ArrayList<>();
4014 mMetadataListeners.put(device, listenerList);
4015 } else {
4016 // Check whether this device was already registed by the lisenter
4017 if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) {
4018 throw new IllegalArgumentException("listener was already regestered"
4019 + " for the device");
4020 }
4021 }
4022
4023 Pair<OnMetadataChangedListener, Executor> listenerPair = new Pair(listener, executor);
4024 listenerList.add(listenerPair);
4025
4026 boolean ret = false;
4027 try {
4028 ret = service.registerMetadataListener(mBluetoothMetadataListener, device,
4029 mAttributionSource);
4030 } catch (RemoteException e) {
4031 Log.e(TAG, "registerMetadataListener fail", e);
4032 } finally {
4033 if (!ret) {
4034 // Remove listener registered earlier when fail.
4035 listenerList.remove(listenerPair);
4036 if (listenerList.isEmpty()) {
4037 // Remove the device if its listener list is empty
4038 mMetadataListeners.remove(device);
4039 }
4040 }
4041 }
4042 return ret;
4043 }
4044 }
4045
4046 /**
4047 * Unregister a {@link #OnMetadataChangedListener} from a registered {@link BluetoothDevice}.
4048 * Unregistration can be done when Bluetooth is either ON or OFF.
4049 * {@link #addOnMetadataChangedListener(OnMetadataChangedListener, BluetoothDevice, Executor)}
4050 * must be called before unregisteration.
4051 *
4052 * @param device {@link BluetoothDevice} that will be unregistered. It
4053 * should not be null or {@link NullPointerException} will be triggered.
4054 * @param listener {@link OnMetadataChangedListener} that will be unregistered. It
4055 * should not be null or {@link NullPointerException} will be triggered.
4056 * @return true on success, false on error
4057 * @throws NullPointerException If {@code listener} or {@code device} is null.
4058 * @throws IllegalArgumentException If {@code device} has not been registered before.
4059 * @hide
4060 */
4061 @SystemApi
4062 @RequiresBluetoothConnectPermission
4063 @RequiresPermission(allOf = {
4064 android.Manifest.permission.BLUETOOTH_CONNECT,
4065 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
4066 })
4067 public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device,
4068 @NonNull OnMetadataChangedListener listener) {
4069 if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()");
4070 if (device == null) {
4071 throw new NullPointerException("device is null");
4072 }
4073 if (listener == null) {
4074 throw new NullPointerException("listener is null");
4075 }
4076
4077 synchronized (mMetadataListeners) {
4078 if (!mMetadataListeners.containsKey(device)) {
4079 throw new IllegalArgumentException("device was not registered");
4080 }
4081 // Remove issued listener from the registered device
4082 mMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener)));
4083
4084 if (mMetadataListeners.get(device).isEmpty()) {
4085 // Unregister to Bluetooth service if all listeners are removed from
4086 // the registered device
4087 mMetadataListeners.remove(device);
4088 final IBluetooth service = mService;
4089 if (service == null) {
4090 // Bluetooth is OFF, do nothing to Bluetooth service.
4091 return true;
4092 }
4093 try {
4094 return service.unregisterMetadataListener(device, mAttributionSource);
4095 } catch (RemoteException e) {
4096 Log.e(TAG, "unregisterMetadataListener fail", e);
4097 return false;
4098 }
4099 }
4100 }
4101 return true;
4102 }
4103
4104 /**
4105 * This interface is used to implement {@link BluetoothAdapter} metadata listener.
4106 * @hide
4107 */
4108 @SystemApi
4109 public interface OnMetadataChangedListener {
4110 /**
4111 * Callback triggered if the metadata of {@link BluetoothDevice} registered in
4112 * {@link #addOnMetadataChangedListener}.
4113 *
4114 * @param device changed {@link BluetoothDevice}.
4115 * @param key changed metadata key, one of BluetoothDevice.METADATA_*.
4116 * @param value the new value of metadata as byte array.
4117 */
4118 void onMetadataChanged(@NonNull BluetoothDevice device, int key,
4119 @Nullable byte[] value);
4120 }
4121
4122 @SuppressLint("AndroidFrameworkBluetoothPermission")
4123 private final IBluetoothConnectionCallback mConnectionCallback =
4124 new IBluetoothConnectionCallback.Stub() {
4125 @Override
4126 public void onDeviceConnected(BluetoothDevice device) {
4127 Attributable.setAttributionSource(device, mAttributionSource);
4128 for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry:
4129 mBluetoothConnectionCallbackExecutorMap.entrySet()) {
4130 BluetoothConnectionCallback callback = callbackExecutorEntry.getKey();
4131 Executor executor = callbackExecutorEntry.getValue();
4132 executor.execute(() -> callback.onDeviceConnected(device));
4133 }
4134 }
4135
4136 @Override
4137 public void onDeviceDisconnected(BluetoothDevice device, int hciReason) {
4138 Attributable.setAttributionSource(device, mAttributionSource);
4139 for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry:
4140 mBluetoothConnectionCallbackExecutorMap.entrySet()) {
4141 BluetoothConnectionCallback callback = callbackExecutorEntry.getKey();
4142 Executor executor = callbackExecutorEntry.getValue();
4143 executor.execute(() -> callback.onDeviceDisconnected(device, hciReason));
4144 }
4145 }
4146 };
4147
4148 /**
4149 * Registers the BluetoothConnectionCallback to receive callback events when a bluetooth device
4150 * (classic or low energy) is connected or disconnected.
4151 *
4152 * @param executor is the callback executor
4153 * @param callback is the connection callback you wish to register
4154 * @return true if the callback was registered successfully, false otherwise
4155 * @throws IllegalArgumentException if the callback is already registered
4156 * @hide
4157 */
4158 @RequiresBluetoothConnectPermission
4159 @RequiresPermission(allOf = {
4160 android.Manifest.permission.BLUETOOTH_CONNECT,
4161 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
4162 })
4163 public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor,
4164 @NonNull BluetoothConnectionCallback callback) {
4165 if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()");
4166 if (callback == null) {
4167 return false;
4168 }
4169
4170 synchronized (mBluetoothConnectionCallbackExecutorMap) {
4171 // If the callback map is empty, we register the service-to-app callback
4172 if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
4173 try {
4174 mServiceLock.readLock().lock();
4175 if (mService != null) {
4176 if (!mService.registerBluetoothConnectionCallback(mConnectionCallback,
4177 mAttributionSource)) {
4178 return false;
4179 }
4180 }
4181 } catch (RemoteException e) {
4182 Log.e(TAG, "", e);
4183 mBluetoothConnectionCallbackExecutorMap.remove(callback);
4184 } finally {
4185 mServiceLock.readLock().unlock();
4186 }
4187 }
4188
4189 // Adds the passed in callback to our map of callbacks to executors
4190 if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) {
4191 throw new IllegalArgumentException("This callback has already been registered");
4192 }
4193 mBluetoothConnectionCallbackExecutorMap.put(callback, executor);
4194 }
4195
4196 return true;
4197 }
4198
4199 /**
4200 * Unregisters the BluetoothConnectionCallback that was previously registered by the application
4201 *
4202 * @param callback is the connection callback you wish to unregister
4203 * @return true if the callback was unregistered successfully, false otherwise
4204 * @hide
4205 */
4206 @RequiresBluetoothConnectPermission
4207 @RequiresPermission(allOf = {
4208 android.Manifest.permission.BLUETOOTH_CONNECT,
4209 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
4210 })
4211 public boolean unregisterBluetoothConnectionCallback(
4212 @NonNull BluetoothConnectionCallback callback) {
4213 if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()");
4214 if (callback == null) {
4215 return false;
4216 }
4217
4218 synchronized (mBluetoothConnectionCallbackExecutorMap) {
4219 if (mBluetoothConnectionCallbackExecutorMap.remove(callback) != null) {
4220 return false;
4221 }
4222 }
4223
4224 if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
4225 return true;
4226 }
4227
4228 // If the callback map is empty, we unregister the service-to-app callback
4229 try {
4230 mServiceLock.readLock().lock();
4231 if (mService != null) {
4232 return mService.unregisterBluetoothConnectionCallback(mConnectionCallback,
4233 mAttributionSource);
4234 }
4235 } catch (RemoteException e) {
4236 Log.e(TAG, "", e);
4237 } finally {
4238 mServiceLock.readLock().unlock();
4239 }
4240
4241 return false;
4242 }
4243
4244 /**
4245 * This abstract class is used to implement callbacks for when a bluetooth classic or Bluetooth
4246 * Low Energy (BLE) device is either connected or disconnected.
4247 *
4248 * @hide
4249 */
4250 public abstract static class BluetoothConnectionCallback {
4251 /**
4252 * Callback triggered when a bluetooth device (classic or BLE) is connected
4253 * @param device is the connected bluetooth device
4254 */
4255 public void onDeviceConnected(BluetoothDevice device) {}
4256
4257 /**
4258 * Callback triggered when a bluetooth device (classic or BLE) is disconnected
4259 * @param device is the disconnected bluetooth device
4260 * @param reason is the disconnect reason
4261 */
4262 public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {}
4263
4264 /**
4265 * @hide
4266 */
4267 @Retention(RetentionPolicy.SOURCE)
4268 @IntDef(prefix = { "REASON_" }, value = {
4269 BluetoothStatusCodes.ERROR_UNKNOWN,
4270 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST,
4271 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST,
4272 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL,
4273 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE,
4274 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT,
4275 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY,
4276 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY,
4277 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED,
4278 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS,
4279 BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS})
4280 public @interface DisconnectReason {}
4281
4282 /**
4283 * Returns human-readable strings corresponding to {@link DisconnectReason}.
4284 */
4285 public static String disconnectReasonText(@DisconnectReason int reason) {
4286 switch (reason) {
4287 case BluetoothStatusCodes.ERROR_UNKNOWN:
4288 return "Reason unknown";
4289 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST:
4290 return "Local request";
4291 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST:
4292 return "Remote request";
4293 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL:
4294 return "Local error";
4295 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE:
4296 return "Remote error";
4297 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT:
4298 return "Timeout";
4299 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY:
4300 return "Security";
4301 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY:
4302 return "System policy";
4303 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED:
4304 return "Resource constrained";
4305 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS:
4306 return "Connection already exists";
4307 case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS:
4308 return "Bad parameters";
4309 default:
4310 return "Unrecognized disconnect reason: " + reason;
4311 }
4312 }
4313 }
4314
4315 /**
4316 * Converts old constant of priority to the new for connection policy
4317 *
4318 * @param priority is the priority to convert to connection policy
4319 * @return the equivalent connection policy constant to the priority
4320 *
4321 * @hide
4322 */
4323 public static @ConnectionPolicy int priorityToConnectionPolicy(int priority) {
4324 switch(priority) {
4325 case BluetoothProfile.PRIORITY_AUTO_CONNECT:
4326 return BluetoothProfile.CONNECTION_POLICY_ALLOWED;
4327 case BluetoothProfile.PRIORITY_ON:
4328 return BluetoothProfile.CONNECTION_POLICY_ALLOWED;
4329 case BluetoothProfile.PRIORITY_OFF:
4330 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
4331 case BluetoothProfile.PRIORITY_UNDEFINED:
4332 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
4333 default:
4334 Log.e(TAG, "setPriority: Invalid priority: " + priority);
4335 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
4336 }
4337 }
4338
4339 /**
4340 * Converts new constant of connection policy to the old for priority
4341 *
4342 * @param connectionPolicy is the connection policy to convert to priority
4343 * @return the equivalent priority constant to the connectionPolicy
4344 *
4345 * @hide
4346 */
4347 public static int connectionPolicyToPriority(@ConnectionPolicy int connectionPolicy) {
4348 switch(connectionPolicy) {
4349 case BluetoothProfile.CONNECTION_POLICY_ALLOWED:
4350 return BluetoothProfile.PRIORITY_ON;
4351 case BluetoothProfile.CONNECTION_POLICY_FORBIDDEN:
4352 return BluetoothProfile.PRIORITY_OFF;
4353 case BluetoothProfile.CONNECTION_POLICY_UNKNOWN:
4354 return BluetoothProfile.PRIORITY_UNDEFINED;
4355 }
4356 return BluetoothProfile.PRIORITY_UNDEFINED;
4357 }
4358}