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