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