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