• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.bluetooth;
18 
19 import static android.bluetooth.BluetoothUtils.getSyncTimeout;
20 
21 import android.annotation.IntDef;
22 import android.annotation.IntRange;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SdkConstant;
27 import android.annotation.SdkConstant.SdkConstantType;
28 import android.annotation.SuppressLint;
29 import android.annotation.SystemApi;
30 import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
31 import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
32 import android.bluetooth.annotations.RequiresBluetoothScanPermission;
33 import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
34 import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
35 import android.companion.AssociationRequest;
36 import android.compat.annotation.UnsupportedAppUsage;
37 import android.content.AttributionSource;
38 import android.content.Context;
39 import android.os.Build;
40 import android.os.Handler;
41 import android.os.IpcDataCache;
42 import android.os.Parcel;
43 import android.os.ParcelUuid;
44 import android.os.Parcelable;
45 import android.os.Process;
46 import android.os.RemoteException;
47 import android.util.Log;
48 import android.util.Pair;
49 
50 import com.android.modules.utils.SynchronousResultReceiver;
51 
52 import java.io.IOException;
53 import java.io.UnsupportedEncodingException;
54 import java.lang.annotation.Retention;
55 import java.lang.annotation.RetentionPolicy;
56 import java.util.List;
57 import java.util.UUID;
58 import java.util.concurrent.TimeoutException;
59 
60 /**
61  * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you
62  * create a connection with the respective device or query information about
63  * it, such as the name, address, class, and bonding state.
64  *
65  * <p>This class is really just a thin wrapper for a Bluetooth hardware
66  * address. Objects of this class are immutable. Operations on this class
67  * are performed on the remote Bluetooth hardware address, using the
68  * {@link BluetoothAdapter} that was used to create this {@link
69  * BluetoothDevice}.
70  *
71  * <p>To get a {@link BluetoothDevice}, use
72  * {@link BluetoothAdapter#getRemoteDevice(String)
73  * BluetoothAdapter.getRemoteDevice(String)} to create one representing a device
74  * of a known MAC address (which you can get through device discovery with
75  * {@link BluetoothAdapter}) or get one from the set of bonded devices
76  * returned by {@link BluetoothAdapter#getBondedDevices()
77  * BluetoothAdapter.getBondedDevices()}. You can then open a
78  * {@link BluetoothSocket} for communication with the remote device, using
79  * {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using
80  * {@link #createL2capChannel(int)} over Bluetooth LE.
81  *
82  * <div class="special reference">
83  * <h3>Developer Guides</h3>
84  * <p>
85  * For more information about using Bluetooth, read the <a href=
86  * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer
87  * guide.
88  * </p>
89  * </div>
90  *
91  * {@see BluetoothAdapter}
92  * {@see BluetoothSocket}
93  */
94 public final class BluetoothDevice implements Parcelable, Attributable {
95     private static final String TAG = "BluetoothDevice";
96     private static final boolean DBG = false;
97 
98     /**
99      * Connection state bitmask as returned by getConnectionState.
100      */
101     private static final int CONNECTION_STATE_DISCONNECTED = 0;
102     private static final int CONNECTION_STATE_CONNECTED = 1;
103     private static final int CONNECTION_STATE_ENCRYPTED_BREDR = 2;
104     private static final int CONNECTION_STATE_ENCRYPTED_LE = 4;
105 
106     /**
107      * Sentinel error value for this class. Guaranteed to not equal any other
108      * integer constant in this class. Provided as a convenience for functions
109      * that require a sentinel error value, for example:
110      * <p><code>Intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
111      * BluetoothDevice.ERROR)</code>
112      */
113     public static final int ERROR = Integer.MIN_VALUE;
114 
115     /**
116      * Broadcast Action: Remote device discovered.
117      * <p>Sent when a remote device is found during discovery.
118      * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
119      * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or
120      * {@link #EXTRA_RSSI} and/or {@link #EXTRA_IS_COORDINATED_SET_MEMBER} if they are available.
121      */
122     // TODO: Change API to not broadcast RSSI if not available (incoming connection)
123     @RequiresLegacyBluetoothPermission
124     @RequiresBluetoothScanPermission
125     @RequiresBluetoothLocationPermission
126     @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
127     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
128     public static final String ACTION_FOUND =
129             "android.bluetooth.device.action.FOUND";
130 
131     /**
132      * Broadcast Action: Bluetooth class of a remote device has changed.
133      * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
134      * #EXTRA_CLASS}.
135      * {@see BluetoothClass}
136      */
137     @RequiresLegacyBluetoothPermission
138     @RequiresBluetoothConnectPermission
139     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
140     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
141     public static final String ACTION_CLASS_CHANGED =
142             "android.bluetooth.device.action.CLASS_CHANGED";
143 
144     /**
145      * Broadcast Action: Indicates a low level (ACL) connection has been
146      * established with a remote device.
147      * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link #EXTRA_TRANSPORT}.
148      * <p>ACL connections are managed automatically by the Android Bluetooth
149      * stack.
150      */
151     @RequiresLegacyBluetoothPermission
152     @RequiresBluetoothConnectPermission
153     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
154     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
155     public static final String ACTION_ACL_CONNECTED =
156             "android.bluetooth.device.action.ACL_CONNECTED";
157 
158     /**
159      * Broadcast Action: Indicates that a low level (ACL) disconnection has
160      * been requested for a remote device, and it will soon be disconnected.
161      * <p>This is useful for graceful disconnection. Applications should use
162      * this intent as a hint to immediately terminate higher level connections
163      * (RFCOMM, L2CAP, or profile connections) to the remote device.
164      * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
165      */
166     @RequiresLegacyBluetoothPermission
167     @RequiresBluetoothConnectPermission
168     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
169     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
170     public static final String ACTION_ACL_DISCONNECT_REQUESTED =
171             "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
172 
173     /**
174      * Broadcast Action: Indicates a low level (ACL) disconnection from a
175      * remote device.
176      * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link #EXTRA_TRANSPORT}.
177      * <p>ACL connections are managed automatically by the Android Bluetooth
178      * stack.
179      */
180     @RequiresLegacyBluetoothPermission
181     @RequiresBluetoothConnectPermission
182     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
183     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
184     public static final String ACTION_ACL_DISCONNECTED =
185             "android.bluetooth.device.action.ACL_DISCONNECTED";
186 
187     /**
188      * Broadcast Action: Indicates the friendly name of a remote device has
189      * been retrieved for the first time, or changed since the last retrieval.
190      * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
191      * #EXTRA_NAME}.
192      */
193     @RequiresLegacyBluetoothPermission
194     @RequiresBluetoothConnectPermission
195     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
196     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
197     public static final String ACTION_NAME_CHANGED =
198             "android.bluetooth.device.action.NAME_CHANGED";
199 
200     /**
201      * Broadcast Action: Indicates the alias of a remote device has been
202      * changed.
203      * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
204      */
205     @SuppressLint("ActionValue")
206     @RequiresLegacyBluetoothPermission
207     @RequiresBluetoothConnectPermission
208     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
209     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
210     public static final String ACTION_ALIAS_CHANGED =
211             "android.bluetooth.device.action.ALIAS_CHANGED";
212 
213     /**
214      * Broadcast Action: Indicates a change in the bond state of a remote
215      * device. For example, if a device is bonded (paired).
216      * <p>Always contains the extra fields {@link #EXTRA_DEVICE}, {@link
217      * #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}.
218      */
219     // Note: When EXTRA_BOND_STATE is BOND_NONE then this will also
220     // contain a hidden extra field EXTRA_UNBOND_REASON with the result code.
221     @RequiresLegacyBluetoothPermission
222     @RequiresBluetoothConnectPermission
223     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
224     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
225     public static final String ACTION_BOND_STATE_CHANGED =
226             "android.bluetooth.device.action.BOND_STATE_CHANGED";
227 
228     /**
229      * Broadcast Action: Indicates the battery level of a remote device has
230      * been retrieved for the first time, or changed since the last retrieval
231      * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
232      * #EXTRA_BATTERY_LEVEL}.
233      *
234      * @hide
235      */
236     @SystemApi
237     @RequiresLegacyBluetoothPermission
238     @RequiresBluetoothConnectPermission
239     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
240     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
241     @SuppressLint("ActionValue")
242     public static final String ACTION_BATTERY_LEVEL_CHANGED =
243             "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED";
244 
245     /**
246      * Broadcast Action: Indicates the audio buffer size should be switched
247      * between a low latency buffer size and a higher and larger latency buffer size.
248      * Only registered receivers will receive this intent.
249      * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
250      * #EXTRA_LOW_LATENCY_BUFFER_SIZE}.
251      *
252      * @hide
253      */
254     @SuppressLint("ActionValue")
255     @RequiresBluetoothConnectPermission
256     @RequiresPermission(allOf = {
257             android.Manifest.permission.BLUETOOTH_CONNECT,
258             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
259     })
260     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
261     @SystemApi
262     public static final String ACTION_SWITCH_BUFFER_SIZE =
263             "android.bluetooth.device.action.SWITCH_BUFFER_SIZE";
264 
265     /**
266      * Used as an Integer extra field in {@link #ACTION_BATTERY_LEVEL_CHANGED}
267      * intent. It contains the most recently retrieved battery level information
268      * ranging from 0% to 100% for a remote device, {@link #BATTERY_LEVEL_UNKNOWN}
269      * when the valid is unknown or there is an error, {@link #BATTERY_LEVEL_BLUETOOTH_OFF} when the
270      * bluetooth is off
271      *
272      * @hide
273      */
274     @SuppressLint("ActionValue")
275     @SystemApi
276     public static final String EXTRA_BATTERY_LEVEL =
277             "android.bluetooth.device.extra.BATTERY_LEVEL";
278 
279     /**
280      * Used as the unknown value for {@link #EXTRA_BATTERY_LEVEL} and {@link #getBatteryLevel()}
281      *
282      * @hide
283      */
284     @SystemApi
285     public static final int BATTERY_LEVEL_UNKNOWN = -1;
286 
287     /**
288      * Used as an error value for {@link #getBatteryLevel()} to represent bluetooth is off
289      *
290      * @hide
291      */
292     @SystemApi
293     public static final int BATTERY_LEVEL_BLUETOOTH_OFF = -100;
294 
295     /**
296      * Used as a Parcelable {@link BluetoothDevice} extra field in every intent
297      * broadcast by this class. It contains the {@link BluetoothDevice} that
298      * the intent applies to.
299      */
300     public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE";
301 
302     /**
303      * Used as a String extra field in {@link #ACTION_NAME_CHANGED} and {@link
304      * #ACTION_FOUND} intents. It contains the friendly Bluetooth name.
305      */
306     public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME";
307 
308     /**
309      * Used as an optional short extra field in {@link #ACTION_FOUND} intents.
310      * Contains the RSSI value of the remote device as reported by the
311      * Bluetooth hardware.
312      */
313     public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI";
314 
315     /**
316     * Used as an bool extra field in {@link #ACTION_FOUND} intents.
317     * It contains the information if device is discovered as member of a coordinated set or not.
318     * Pairing with device that belongs to a set would trigger pairing with the rest of set members.
319     * See Bluetooth CSIP specification for more details.
320     */
321     public static final String EXTRA_IS_COORDINATED_SET_MEMBER =
322             "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER";
323 
324     /**
325      * Used as a Parcelable {@link BluetoothClass} extra field in {@link
326      * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents.
327      */
328     public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS";
329 
330     /**
331      * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
332      * Contains the bond state of the remote device.
333      * <p>Possible values are:
334      * {@link #BOND_NONE},
335      * {@link #BOND_BONDING},
336      * {@link #BOND_BONDED}.
337      */
338     public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE";
339     /**
340      * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
341      * Contains the previous bond state of the remote device.
342      * <p>Possible values are:
343      * {@link #BOND_NONE},
344      * {@link #BOND_BONDING},
345      * {@link #BOND_BONDED}.
346      */
347     public static final String EXTRA_PREVIOUS_BOND_STATE =
348             "android.bluetooth.device.extra.PREVIOUS_BOND_STATE";
349 
350     /**
351      * Used as a boolean extra field to indicate if audio buffer size is low latency or not
352      *
353      * @hide
354      */
355     @SuppressLint("ActionValue")
356     @SystemApi
357     public static final String EXTRA_LOW_LATENCY_BUFFER_SIZE =
358             "android.bluetooth.device.extra.LOW_LATENCY_BUFFER_SIZE";
359 
360     /**
361      * Indicates the remote device is not bonded (paired).
362      * <p>There is no shared link key with the remote device, so communication
363      * (if it is allowed at all) will be unauthenticated and unencrypted.
364      */
365     public static final int BOND_NONE = 10;
366     /**
367      * Indicates bonding (pairing) is in progress with the remote device.
368      */
369     public static final int BOND_BONDING = 11;
370     /**
371      * Indicates the remote device is bonded (paired).
372      * <p>A shared link keys exists locally for the remote device, so
373      * communication can be authenticated and encrypted.
374      * <p><i>Being bonded (paired) with a remote device does not necessarily
375      * mean the device is currently connected. It just means that the pending
376      * procedure was completed at some earlier time, and the link key is still
377      * stored locally, ready to use on the next connection.
378      * </i>
379      */
380     public static final int BOND_BONDED = 12;
381 
382     /**
383      * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} intents for unbond reason.
384      * Possible value are :
385      *  - {@link #UNBOND_REASON_AUTH_FAILED}
386      *  - {@link #UNBOND_REASON_AUTH_REJECTED}
387      *  - {@link #UNBOND_REASON_AUTH_CANCELED}
388      *  - {@link #UNBOND_REASON_REMOTE_DEVICE_DOWN}
389      *  - {@link #UNBOND_REASON_DISCOVERY_IN_PROGRESS}
390      *  - {@link #UNBOND_REASON_AUTH_TIMEOUT}
391      *  - {@link #UNBOND_REASON_REPEATED_ATTEMPTS}
392      *  - {@link #UNBOND_REASON_REMOTE_AUTH_CANCELED}
393      *  - {@link #UNBOND_REASON_REMOVED}
394      *
395      * Note: Can be added as a hidden extra field for {@link #ACTION_BOND_STATE_CHANGED} when the
396      * {@link #EXTRA_BOND_STATE} is {@link #BOND_NONE}
397      *
398      * @hide
399      */
400     @SystemApi
401     @SuppressLint("ActionValue")
402     public static final String EXTRA_UNBOND_REASON = "android.bluetooth.device.extra.REASON";
403 
404     /**
405      * Use {@link EXTRA_UNBOND_REASON} instead
406      * @hide
407      */
408     @UnsupportedAppUsage
409     public static final String EXTRA_REASON = EXTRA_UNBOND_REASON;
410 
411 
412     /**
413      * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
414      * intents to indicate pairing method used. Possible values are:
415      * {@link #PAIRING_VARIANT_PIN},
416      * {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION},
417      */
418     public static final String EXTRA_PAIRING_VARIANT =
419             "android.bluetooth.device.extra.PAIRING_VARIANT";
420 
421     /**
422      * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
423      * intents as the value of passkey.
424      * The Bluetooth Passkey is a 6-digit numerical value represented as integer value
425      * in the range 0x00000000 – 0x000F423F (000000 to 999999).
426      */
427     public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY";
428 
429     /**
430      * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
431      * intents as the location of initiator. Possible value are:
432      * {@link #EXTRA_PAIRING_INITIATOR_FOREGROUND},
433      * {@link #EXTRA_PAIRING_INITIATOR_BACKGROUND},
434      *
435      * @hide
436      */
437     @SystemApi
438     @SuppressLint("ActionValue")
439     public static final String EXTRA_PAIRING_INITIATOR =
440             "android.bluetooth.device.extra.PAIRING_INITIATOR";
441 
442     /**
443      * Bluetooth pairing initiator, Foreground App
444      * @hide
445      */
446     @SystemApi
447     public static final int EXTRA_PAIRING_INITIATOR_FOREGROUND = 1;
448 
449     /**
450      * Bluetooth pairing initiator, Background
451      * @hide
452      */
453     @SystemApi
454     public static final int EXTRA_PAIRING_INITIATOR_BACKGROUND = 2;
455 
456     /**
457      * Bluetooth device type, Unknown
458      */
459     public static final int DEVICE_TYPE_UNKNOWN = 0;
460 
461     /**
462      * Bluetooth device type, Classic - BR/EDR devices
463      */
464     public static final int DEVICE_TYPE_CLASSIC = 1;
465 
466     /**
467      * Bluetooth device type, Low Energy - LE-only
468      */
469     public static final int DEVICE_TYPE_LE = 2;
470 
471     /**
472      * Bluetooth device type, Dual Mode - BR/EDR/LE
473      */
474     public static final int DEVICE_TYPE_DUAL = 3;
475 
476 
477     /** @hide */
478     @RequiresBluetoothConnectPermission
479     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
480     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
481     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
482     public static final String ACTION_SDP_RECORD =
483             "android.bluetooth.device.action.SDP_RECORD";
484 
485     /** @hide */
486     @IntDef(prefix = "METADATA_", value = {
487             METADATA_MANUFACTURER_NAME,
488             METADATA_MODEL_NAME,
489             METADATA_SOFTWARE_VERSION,
490             METADATA_HARDWARE_VERSION,
491             METADATA_COMPANION_APP,
492             METADATA_MAIN_ICON,
493             METADATA_IS_UNTETHERED_HEADSET,
494             METADATA_UNTETHERED_LEFT_ICON,
495             METADATA_UNTETHERED_RIGHT_ICON,
496             METADATA_UNTETHERED_CASE_ICON,
497             METADATA_UNTETHERED_LEFT_BATTERY,
498             METADATA_UNTETHERED_RIGHT_BATTERY,
499             METADATA_UNTETHERED_CASE_BATTERY,
500             METADATA_UNTETHERED_LEFT_CHARGING,
501             METADATA_UNTETHERED_RIGHT_CHARGING,
502             METADATA_UNTETHERED_CASE_CHARGING,
503             METADATA_ENHANCED_SETTINGS_UI_URI,
504             METADATA_DEVICE_TYPE,
505             METADATA_MAIN_BATTERY,
506             METADATA_MAIN_CHARGING,
507             METADATA_MAIN_LOW_BATTERY_THRESHOLD,
508             METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD,
509             METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD,
510             METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD,
511             METADATA_SPATIAL_AUDIO,
512             METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
513             METADATA_LE_AUDIO,
514             METADATA_GMCS_CCCD,
515             METADATA_GTBS_CCCD})
516     @Retention(RetentionPolicy.SOURCE)
517     public @interface MetadataKey{}
518 
519     /**
520      * Maximum length of a metadata entry, this is to avoid exploding Bluetooth
521      * disk usage
522      * @hide
523      */
524     @SystemApi
525     public static final int METADATA_MAX_LENGTH = 2048;
526 
527     /**
528      * Manufacturer name of this Bluetooth device
529      * Data type should be {@String} as {@link Byte} array.
530      * @hide
531      */
532     @SystemApi
533     public static final int METADATA_MANUFACTURER_NAME = 0;
534 
535     /**
536      * Model name of this Bluetooth device
537      * Data type should be {@String} as {@link Byte} array.
538      * @hide
539      */
540     @SystemApi
541     public static final int METADATA_MODEL_NAME = 1;
542 
543     /**
544      * Software version of this Bluetooth device
545      * Data type should be {@String} as {@link Byte} array.
546      * @hide
547      */
548     @SystemApi
549     public static final int METADATA_SOFTWARE_VERSION = 2;
550 
551     /**
552      * Hardware version of this Bluetooth device
553      * Data type should be {@String} as {@link Byte} array.
554      * @hide
555      */
556     @SystemApi
557     public static final int METADATA_HARDWARE_VERSION = 3;
558 
559     /**
560      * Package name of the companion app, if any
561      * Data type should be {@String} as {@link Byte} array.
562      * @hide
563      */
564     @SystemApi
565     public static final int METADATA_COMPANION_APP = 4;
566 
567     /**
568      * URI to the main icon shown on the settings UI
569      * Data type should be {@link Byte} array.
570      * @hide
571      */
572     @SystemApi
573     public static final int METADATA_MAIN_ICON = 5;
574 
575     /**
576      * Whether this device is an untethered headset with left, right and case
577      * Data type should be {@String} as {@link Byte} array.
578      * @hide
579      */
580     @SystemApi
581     public static final int METADATA_IS_UNTETHERED_HEADSET = 6;
582 
583     /**
584      * URI to icon of the left headset
585      * Data type should be {@link Byte} array.
586      * @hide
587      */
588     @SystemApi
589     public static final int METADATA_UNTETHERED_LEFT_ICON = 7;
590 
591     /**
592      * URI to icon of the right headset
593      * Data type should be {@link Byte} array.
594      * @hide
595      */
596     @SystemApi
597     public static final int METADATA_UNTETHERED_RIGHT_ICON = 8;
598 
599     /**
600      * URI to icon of the headset charging case
601      * Data type should be {@link Byte} array.
602      * @hide
603      */
604     @SystemApi
605     public static final int METADATA_UNTETHERED_CASE_ICON = 9;
606 
607     /**
608      * Battery level of left headset
609      * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
610      * as invalid.
611      * @hide
612      */
613     @SystemApi
614     public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10;
615 
616     /**
617      * Battery level of rigth headset
618      * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
619      * as invalid.
620      * @hide
621      */
622     @SystemApi
623     public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11;
624 
625     /**
626      * Battery level of the headset charging case
627      * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
628      * as invalid.
629      * @hide
630      */
631     @SystemApi
632     public static final int METADATA_UNTETHERED_CASE_BATTERY = 12;
633 
634     /**
635      * Whether the left headset is charging
636      * Data type should be {@String} as {@link Byte} array.
637      * @hide
638      */
639     @SystemApi
640     public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13;
641 
642     /**
643      * Whether the right headset is charging
644      * Data type should be {@String} as {@link Byte} array.
645      * @hide
646      */
647     @SystemApi
648     public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14;
649 
650     /**
651      * Whether the headset charging case is charging
652      * Data type should be {@String} as {@link Byte} array.
653      * @hide
654      */
655     @SystemApi
656     public static final int METADATA_UNTETHERED_CASE_CHARGING = 15;
657 
658     /**
659      * URI to the enhanced settings UI slice
660      * Data type should be {@String} as {@link Byte} array, null means
661      * the UI does not exist.
662      * @hide
663      */
664     @SystemApi
665     public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16;
666 
667     /**
668      * @hide
669      */
670     public static final String COMPANION_TYPE_PRIMARY = "COMPANION_PRIMARY";
671 
672     /**
673      * @hide
674      */
675     public static final String COMPANION_TYPE_SECONDARY = "COMPANION_SECONDARY";
676 
677     /**
678      * @hide
679      */
680     public static final String COMPANION_TYPE_NONE = "COMPANION_NONE";
681 
682     /**
683      * Type of the Bluetooth device, must be within the list of
684      * BluetoothDevice.DEVICE_TYPE_*
685      * Data type should be {@String} as {@link Byte} array.
686      * @hide
687      */
688     @SystemApi
689     public static final int METADATA_DEVICE_TYPE = 17;
690 
691     /**
692      * Battery level of the Bluetooth device, use when the Bluetooth device
693      * does not support HFP battery indicator.
694      * Data type should be {@String} as {@link Byte} array.
695      * @hide
696      */
697     @SystemApi
698     public static final int METADATA_MAIN_BATTERY = 18;
699 
700     /**
701      * Whether the device is charging.
702      * Data type should be {@String} as {@link Byte} array.
703      * @hide
704      */
705     @SystemApi
706     public static final int METADATA_MAIN_CHARGING = 19;
707 
708     /**
709      * The battery threshold of the Bluetooth device to show low battery icon.
710      * Data type should be {@String} as {@link Byte} array.
711      * @hide
712      */
713     @SystemApi
714     public static final int METADATA_MAIN_LOW_BATTERY_THRESHOLD = 20;
715 
716     /**
717      * The battery threshold of the left headset to show low battery icon.
718      * Data type should be {@String} as {@link Byte} array.
719      * @hide
720      */
721     @SystemApi
722     public static final int METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD = 21;
723 
724     /**
725      * The battery threshold of the right headset to show low battery icon.
726      * Data type should be {@String} as {@link Byte} array.
727      * @hide
728      */
729     @SystemApi
730     public static final int METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD = 22;
731 
732     /**
733      * The battery threshold of the case to show low battery icon.
734      * Data type should be {@String} as {@link Byte} array.
735      * @hide
736      */
737     @SystemApi
738     public static final int METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD = 23;
739 
740 
741     /**
742      * The metadata of the audio spatial data.
743      * Data type should be {@link Byte} array.
744      * @hide
745      */
746     public static final int METADATA_SPATIAL_AUDIO = 24;
747 
748     /**
749      * The metadata of the Fast Pair for any custmized feature.
750      * Data type should be {@link Byte} array.
751      * @hide
752      */
753     public static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
754 
755     /**
756      * The metadata of the Fast Pair for LE Audio capable devices.
757      * Data type should be {@link Byte} array.
758      * @hide
759      */
760     public static final int METADATA_LE_AUDIO = 26;
761 
762     /**
763      * The UUIDs (16-bit) of registered to CCC characteristics from Media Control services.
764      * Data type should be {@link Byte} array.
765      * @hide
766      */
767     public static final int METADATA_GMCS_CCCD = 27;
768 
769     /**
770      * The UUIDs (16-bit) of registered to CCC characteristics from Telephony Bearer service.
771      * Data type should be {@link Byte} array.
772      * @hide
773      */
774     public static final int METADATA_GTBS_CCCD = 28;
775 
776     private static final int METADATA_MAX_KEY = METADATA_GTBS_CCCD;
777 
778     /**
779      * Device type which is used in METADATA_DEVICE_TYPE
780      * Indicates this Bluetooth device is a standard Bluetooth accessory or
781      * not listed in METADATA_DEVICE_TYPE_*.
782      * @hide
783      */
784     @SystemApi
785     public static final String DEVICE_TYPE_DEFAULT = "Default";
786 
787     /**
788      * Device type which is used in METADATA_DEVICE_TYPE
789      * Indicates this Bluetooth device is a watch.
790      * @hide
791      */
792     @SystemApi
793     public static final String DEVICE_TYPE_WATCH = "Watch";
794 
795     /**
796      * Device type which is used in METADATA_DEVICE_TYPE
797      * Indicates this Bluetooth device is an untethered headset.
798      * @hide
799      */
800     @SystemApi
801     public static final String DEVICE_TYPE_UNTETHERED_HEADSET = "Untethered Headset";
802 
803     /**
804      * Broadcast Action: This intent is used to broadcast the {@link UUID}
805      * wrapped as a {@link android.os.ParcelUuid} of the remote device after it
806      * has been fetched. This intent is sent only when the UUIDs of the remote
807      * device are requested to be fetched using Service Discovery Protocol
808      * <p> Always contains the extra field {@link #EXTRA_DEVICE}
809      * <p> Always contains the extra field {@link #EXTRA_UUID}
810      */
811     @RequiresLegacyBluetoothAdminPermission
812     @RequiresBluetoothConnectPermission
813     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
814     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
815     public static final String ACTION_UUID =
816             "android.bluetooth.device.action.UUID";
817 
818     /** @hide */
819     @RequiresBluetoothConnectPermission
820     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
821     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
822     public static final String ACTION_MAS_INSTANCE =
823             "android.bluetooth.device.action.MAS_INSTANCE";
824 
825     /**
826      * Broadcast Action: Indicates a failure to retrieve the name of a remote
827      * device.
828      * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
829      *
830      * @hide
831      */
832     //TODO: is this actually useful?
833     @RequiresLegacyBluetoothPermission
834     @RequiresBluetoothConnectPermission
835     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
836     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
837     public static final String ACTION_NAME_FAILED =
838             "android.bluetooth.device.action.NAME_FAILED";
839 
840     /**
841      * Broadcast Action: This intent is used to broadcast PAIRING REQUEST
842      */
843     @RequiresLegacyBluetoothAdminPermission
844     @RequiresBluetoothConnectPermission
845     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
846     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
847     public static final String ACTION_PAIRING_REQUEST =
848             "android.bluetooth.device.action.PAIRING_REQUEST";
849 
850     /**
851      * Broadcast Action: This intent is used to broadcast PAIRING CANCEL
852      *
853      * @hide
854      */
855     @SystemApi
856     @RequiresBluetoothConnectPermission
857     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
858     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
859     @SuppressLint("ActionValue")
860     public static final String ACTION_PAIRING_CANCEL =
861             "android.bluetooth.device.action.PAIRING_CANCEL";
862 
863     /**
864      * Broadcast Action: This intent is used to broadcast CONNECTION ACCESS REQUEST
865      *
866      * This action will trigger a prompt for the user to accept or deny giving the
867      * permission for this device. Permissions can be specified with
868      * {@link #EXTRA_ACCESS_REQUEST_TYPE}.
869      *
870      * The reply will be an {@link #ACTION_CONNECTION_ACCESS_REPLY} sent to the specified
871      * {@link #EXTRA_PACKAGE_NAME} and {@link #EXTRA_CLASS_NAME}.
872      *
873      * This action can be cancelled with {@link #ACTION_CONNECTION_ACCESS_CANCEL}.
874      *
875      * @hide
876      */
877     @SystemApi
878     @RequiresBluetoothConnectPermission
879     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
880     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
881     @SuppressLint("ActionValue")
882     public static final String ACTION_CONNECTION_ACCESS_REQUEST =
883             "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST";
884 
885     /**
886      * Broadcast Action: This intent is used to broadcast CONNECTION ACCESS REPLY
887      *
888      * This action is the reply from {@link #ACTION_CONNECTION_ACCESS_REQUEST}
889      * that is sent to the specified {@link #EXTRA_PACKAGE_NAME}
890      * and {@link #EXTRA_CLASS_NAME}.
891      *
892      * See the extra fields {@link #EXTRA_CONNECTION_ACCESS_RESULT} and
893      * {@link #EXTRA_ALWAYS_ALLOWED} for possible results.
894      *
895      * @hide
896      */
897     @SystemApi
898     @RequiresBluetoothConnectPermission
899     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
900     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
901     @SuppressLint("ActionValue")
902     public static final String ACTION_CONNECTION_ACCESS_REPLY =
903             "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY";
904 
905     /**
906      * Broadcast Action: This intent is used to broadcast CONNECTION ACCESS CANCEL
907      *
908      * @hide
909      */
910     @SystemApi
911     @RequiresBluetoothConnectPermission
912     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
913     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
914     @SuppressLint("ActionValue")
915     public static final String ACTION_CONNECTION_ACCESS_CANCEL =
916             "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL";
917 
918     /**
919      * Intent to broadcast silence mode changed.
920      * Alway contains the extra field {@link #EXTRA_DEVICE}
921      *
922      * @hide
923      */
924     @RequiresBluetoothConnectPermission
925     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
926     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
927     @SystemApi
928     public static final String ACTION_SILENCE_MODE_CHANGED =
929             "android.bluetooth.device.action.SILENCE_MODE_CHANGED";
930 
931     /**
932      * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST}.
933      *
934      * Possible values are {@link #REQUEST_TYPE_PROFILE_CONNECTION},
935      * {@link #REQUEST_TYPE_PHONEBOOK_ACCESS}, {@link #REQUEST_TYPE_MESSAGE_ACCESS}
936      * and {@link #REQUEST_TYPE_SIM_ACCESS}
937      *
938      * @hide
939      */
940     @SystemApi
941     @SuppressLint("ActionValue")
942     public static final String EXTRA_ACCESS_REQUEST_TYPE =
943             "android.bluetooth.device.extra.ACCESS_REQUEST_TYPE";
944 
945     /** @hide */
946     @SystemApi
947     public static final int REQUEST_TYPE_PROFILE_CONNECTION = 1;
948 
949     /** @hide */
950     @SystemApi
951     public static final int REQUEST_TYPE_PHONEBOOK_ACCESS = 2;
952 
953     /** @hide */
954     @SystemApi
955     public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3;
956 
957     /** @hide */
958     @SystemApi
959     public static final int REQUEST_TYPE_SIM_ACCESS = 4;
960 
961     /**
962      * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents,
963      * Contains package name to return reply intent to.
964      *
965      * @hide
966      */
967     public static final String EXTRA_PACKAGE_NAME = "android.bluetooth.device.extra.PACKAGE_NAME";
968 
969     /**
970      * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents,
971      * Contains class name to return reply intent to.
972      *
973      * @hide
974      */
975     public static final String EXTRA_CLASS_NAME = "android.bluetooth.device.extra.CLASS_NAME";
976 
977     /**
978      * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent.
979      *
980      * Possible values are {@link #CONNECTION_ACCESS_YES} and {@link #CONNECTION_ACCESS_NO}.
981      *
982      * @hide
983      */
984     @SystemApi
985     @SuppressLint("ActionValue")
986     public static final String EXTRA_CONNECTION_ACCESS_RESULT =
987             "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT";
988 
989     /** @hide */
990     @SystemApi
991     public static final int CONNECTION_ACCESS_YES = 1;
992 
993     /** @hide */
994     @SystemApi
995     public static final int CONNECTION_ACCESS_NO = 2;
996 
997     /**
998      * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intents,
999      * Contains boolean to indicate if the allowed response is once-for-all so that
1000      * next request will be granted without asking user again.
1001      *
1002      * @hide
1003      */
1004     @SystemApi
1005     @SuppressLint("ActionValue")
1006     public static final String EXTRA_ALWAYS_ALLOWED =
1007             "android.bluetooth.device.extra.ALWAYS_ALLOWED";
1008 
1009     /**
1010      * A bond attempt succeeded
1011      *
1012      * @hide
1013      */
1014     public static final int BOND_SUCCESS = 0;
1015 
1016     /**
1017      * A bond attempt failed because pins did not match, or remote device did
1018      * not respond to pin request in time
1019      *
1020      * @hide
1021      */
1022     @SystemApi
1023     public static final int UNBOND_REASON_AUTH_FAILED = 1;
1024 
1025     /**
1026      * A bond attempt failed because the other side explicitly rejected
1027      * bonding
1028      *
1029      * @hide
1030      */
1031     @SystemApi
1032     public static final int UNBOND_REASON_AUTH_REJECTED = 2;
1033 
1034     /**
1035      * A bond attempt failed because we canceled the bonding process
1036      *
1037      * @hide
1038      */
1039     @SystemApi
1040     public static final int UNBOND_REASON_AUTH_CANCELED = 3;
1041 
1042     /**
1043      * A bond attempt failed because we could not contact the remote device
1044      *
1045      * @hide
1046      */
1047     @SystemApi
1048     public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4;
1049 
1050     /**
1051      * A bond attempt failed because a discovery is in progress
1052      *
1053      * @hide
1054      */
1055     @SystemApi
1056     public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5;
1057 
1058     /**
1059      * A bond attempt failed because of authentication timeout
1060      *
1061      * @hide
1062      */
1063     @SystemApi
1064     public static final int UNBOND_REASON_AUTH_TIMEOUT = 6;
1065 
1066     /**
1067      * A bond attempt failed because of repeated attempts
1068      *
1069      * @hide
1070      */
1071     @SystemApi
1072     public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7;
1073 
1074     /**
1075      * A bond attempt failed because we received an Authentication Cancel
1076      * by remote end
1077      *
1078      * @hide
1079      */
1080     @SystemApi
1081     public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8;
1082 
1083     /**
1084      * An existing bond was explicitly revoked
1085      *
1086      * @hide
1087      */
1088     @SystemApi
1089     public static final int UNBOND_REASON_REMOVED = 9;
1090 
1091     /**
1092      * The user will be prompted to enter a pin or
1093      * an app will enter a pin for user.
1094      */
1095     public static final int PAIRING_VARIANT_PIN = 0;
1096 
1097     /**
1098      * The user will be prompted to enter a passkey
1099      *
1100      * @hide
1101      */
1102     @SystemApi
1103     public static final int PAIRING_VARIANT_PASSKEY = 1;
1104 
1105     /**
1106      * The user will be prompted to confirm the passkey displayed on the screen or
1107      * an app will confirm the passkey for the user.
1108      */
1109     public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2;
1110 
1111     /**
1112      * The user will be prompted to accept or deny the incoming pairing request
1113      *
1114      * @hide
1115      */
1116     @SystemApi
1117     public static final int PAIRING_VARIANT_CONSENT = 3;
1118 
1119     /**
1120      * The user will be prompted to enter the passkey displayed on remote device
1121      * This is used for Bluetooth 2.1 pairing.
1122      *
1123      * @hide
1124      */
1125     @SystemApi
1126     public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
1127 
1128     /**
1129      * The user will be prompted to enter the PIN displayed on remote device.
1130      * This is used for Bluetooth 2.0 pairing.
1131      *
1132      * @hide
1133      */
1134     @SystemApi
1135     public static final int PAIRING_VARIANT_DISPLAY_PIN = 5;
1136 
1137     /**
1138      * The user will be prompted to accept or deny the OOB pairing request.
1139      * This is used for Bluetooth 2.1 secure simple pairing.
1140      *
1141      * @hide
1142      */
1143     @SystemApi
1144     public static final int PAIRING_VARIANT_OOB_CONSENT = 6;
1145 
1146     /**
1147      * The user will be prompted to enter a 16 digit pin or
1148      * an app will enter a 16 digit pin for user.
1149      *
1150      * @hide
1151      */
1152     @SystemApi
1153     public static final int PAIRING_VARIANT_PIN_16_DIGITS = 7;
1154 
1155     /**
1156      * Used as an extra field in {@link #ACTION_UUID} intents,
1157      * Contains the {@link android.os.ParcelUuid}s of the remote device which
1158      * is a parcelable version of {@link UUID}.
1159      */
1160     public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
1161 
1162     /** @hide */
1163     public static final String EXTRA_SDP_RECORD =
1164             "android.bluetooth.device.extra.SDP_RECORD";
1165 
1166     /** @hide */
1167     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1168     public static final String EXTRA_SDP_SEARCH_STATUS =
1169             "android.bluetooth.device.extra.SDP_SEARCH_STATUS";
1170 
1171     /** @hide */
1172     @IntDef(prefix = "ACCESS_", value = {ACCESS_UNKNOWN,
1173             ACCESS_ALLOWED, ACCESS_REJECTED})
1174     @Retention(RetentionPolicy.SOURCE)
1175     public @interface AccessPermission{}
1176 
1177     /**
1178      * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
1179      * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
1180      *
1181      * @hide
1182      */
1183     @SystemApi
1184     public static final int ACCESS_UNKNOWN = 0;
1185 
1186     /**
1187      * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
1188      * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
1189      *
1190      * @hide
1191      */
1192     @SystemApi
1193     public static final int ACCESS_ALLOWED = 1;
1194 
1195     /**
1196      * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
1197      * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
1198      *
1199      * @hide
1200      */
1201     @SystemApi
1202     public static final int ACCESS_REJECTED = 2;
1203 
1204     /** @hide */
1205     @Retention(RetentionPolicy.SOURCE)
1206     @IntDef(
1207         prefix = { "TRANSPORT_" },
1208         value = {
1209             /** Allow host to automatically select a transport (dual-mode only) */
1210             TRANSPORT_AUTO,
1211             /** Use Classic or BR/EDR transport.*/
1212             TRANSPORT_BREDR,
1213             /** Use Low Energy transport.*/
1214             TRANSPORT_LE,
1215         }
1216     )
1217     public @interface Transport {}
1218 
1219     /**
1220      * No preference of physical transport for GATT connections to remote dual-mode devices
1221      */
1222     public static final int TRANSPORT_AUTO = 0;
1223 
1224     /**
1225      * Constant representing the BR/EDR transport.
1226      */
1227     public static final int TRANSPORT_BREDR = 1;
1228 
1229     /**
1230      * Constant representing the Bluetooth Low Energy (BLE) Transport.
1231      */
1232     public static final int TRANSPORT_LE = 2;
1233 
1234     /**
1235      * Bluetooth LE 1M PHY. Used to refer to LE 1M Physical Channel for advertising, scanning or
1236      * connection.
1237      */
1238     public static final int PHY_LE_1M = 1;
1239 
1240     /**
1241      * Bluetooth LE 2M PHY. Used to refer to LE 2M Physical Channel for advertising, scanning or
1242      * connection.
1243      */
1244     public static final int PHY_LE_2M = 2;
1245 
1246     /**
1247      * Bluetooth LE Coded PHY. Used to refer to LE Coded Physical Channel for advertising, scanning
1248      * or connection.
1249      */
1250     public static final int PHY_LE_CODED = 3;
1251 
1252     /**
1253      * Bluetooth LE 1M PHY mask. Used to specify LE 1M Physical Channel as one of many available
1254      * options in a bitmask.
1255      */
1256     public static final int PHY_LE_1M_MASK = 1;
1257 
1258     /**
1259      * Bluetooth LE 2M PHY mask. Used to specify LE 2M Physical Channel as one of many available
1260      * options in a bitmask.
1261      */
1262     public static final int PHY_LE_2M_MASK = 2;
1263 
1264     /**
1265      * Bluetooth LE Coded PHY mask. Used to specify LE Coded Physical Channel as one of many
1266      * available options in a bitmask.
1267      */
1268     public static final int PHY_LE_CODED_MASK = 4;
1269 
1270     /**
1271      * No preferred coding when transmitting on the LE Coded PHY.
1272      */
1273     public static final int PHY_OPTION_NO_PREFERRED = 0;
1274 
1275     /**
1276      * Prefer the S=2 coding to be used when transmitting on the LE Coded PHY.
1277      */
1278     public static final int PHY_OPTION_S2 = 1;
1279 
1280     /**
1281      * Prefer the S=8 coding to be used when transmitting on the LE Coded PHY.
1282      */
1283     public static final int PHY_OPTION_S8 = 2;
1284 
1285 
1286     /** @hide */
1287     public static final String EXTRA_MAS_INSTANCE =
1288             "android.bluetooth.device.extra.MAS_INSTANCE";
1289 
1290     /**
1291      * Used as an int extra field in {@link #ACTION_ACL_CONNECTED} and
1292      * {@link #ACTION_ACL_DISCONNECTED} intents to indicate which transport is connected.
1293      * Possible values are: {@link #TRANSPORT_BREDR} and {@link #TRANSPORT_LE}.
1294      */
1295     @SuppressLint("ActionValue")
1296     public static final String EXTRA_TRANSPORT = "android.bluetooth.device.extra.TRANSPORT";
1297 
1298     /** @hide */
1299     @Retention(RetentionPolicy.SOURCE)
1300     @IntDef(
1301         prefix = { "ADDRESS_TYPE_" },
1302         value = {
1303             /** Hardware MAC Address */
1304             ADDRESS_TYPE_PUBLIC,
1305             /** Address is either resolvable, non-resolvable or static.*/
1306             ADDRESS_TYPE_RANDOM,
1307             /** Address type is unknown or unavailable **/
1308             ADDRESS_TYPE_UNKNOWN,
1309         }
1310     )
1311     public @interface AddressType {}
1312 
1313     /** Hardware MAC Address of the device */
1314     public static final int ADDRESS_TYPE_PUBLIC = 0;
1315     /** Address is either resolvable, non-resolvable or static. */
1316     public static final int ADDRESS_TYPE_RANDOM = 1;
1317     /** Address type is unknown or unavailable **/
1318     public static final int ADDRESS_TYPE_UNKNOWN = 0xFFFF;
1319 
1320     private static final String NULL_MAC_ADDRESS = "00:00:00:00:00:00";
1321 
1322     private final String mAddress;
1323     @AddressType private final int mAddressType;
1324 
1325     private AttributionSource mAttributionSource;
1326 
getService()1327     static IBluetooth getService() {
1328         return BluetoothAdapter.getDefaultAdapter().getBluetoothService();
1329     }
1330 
1331     /**
1332      * Create a new BluetoothDevice.
1333      * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB",
1334      * and is validated in this constructor.
1335      *
1336      * @param address valid Bluetooth MAC address
1337      * @param addressType valid address type
1338      * @throws RuntimeException Bluetooth is not available on this platform
1339      * @throws IllegalArgumentException address or addressType is invalid
1340      * @hide
1341      */
BluetoothDevice(String address, int addressType)1342     /*package*/ BluetoothDevice(String address, int addressType) {
1343         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1344             throw new IllegalArgumentException(address + " is not a valid Bluetooth address");
1345         }
1346 
1347         if (addressType != ADDRESS_TYPE_PUBLIC && addressType != ADDRESS_TYPE_RANDOM) {
1348             throw new IllegalArgumentException(addressType + " is not a Bluetooth address type");
1349         }
1350 
1351         mAddress = address;
1352         mAddressType = addressType;
1353         mAttributionSource = AttributionSource.myAttributionSource();
1354     }
1355 
1356     /**
1357      * Create a new BluetoothDevice.
1358      * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB",
1359      * and is validated in this constructor.
1360      *
1361      * @param address valid Bluetooth MAC address
1362      * @throws RuntimeException Bluetooth is not available on this platform
1363      * @throws IllegalArgumentException address is invalid
1364      * @hide
1365      */
1366     @UnsupportedAppUsage
BluetoothDevice(String address)1367     /*package*/ BluetoothDevice(String address) {
1368         this(address, ADDRESS_TYPE_PUBLIC);
1369     }
1370 
1371     /** {@hide} */
setAttributionSource(@onNull AttributionSource attributionSource)1372     public void setAttributionSource(@NonNull AttributionSource attributionSource) {
1373         mAttributionSource = attributionSource;
1374     }
1375 
1376     /**
1377      * Method should never be used anywhere. Only exception is from {@link Intent}
1378      * Used to set the device current attribution source
1379      *
1380      * @param attributionSource The associated {@link AttributionSource} for this device in this
1381      * process
1382      * @hide
1383      */
1384     @SystemApi
1385     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
prepareToEnterProcess(@onNull AttributionSource attributionSource)1386     public void prepareToEnterProcess(@NonNull AttributionSource attributionSource) {
1387         setAttributionSource(attributionSource);
1388     }
1389 
1390     @Override
equals(@ullable Object o)1391     public boolean equals(@Nullable Object o) {
1392         if (o instanceof BluetoothDevice) {
1393             return mAddress.equals(((BluetoothDevice) o).getAddress());
1394         }
1395         return false;
1396     }
1397 
1398     @Override
hashCode()1399     public int hashCode() {
1400         return mAddress.hashCode();
1401     }
1402 
1403     /**
1404      * Returns a string representation of this BluetoothDevice.
1405      * <p>Currently this is the Bluetooth hardware address, for example
1406      * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress}
1407      * if you explicitly require the Bluetooth hardware address in case the
1408      * {@link #toString} representation changes in the future.
1409      *
1410      * @return string representation of this BluetoothDevice
1411      */
1412     @Override
toString()1413     public String toString() {
1414         return mAddress;
1415     }
1416 
1417     @Override
describeContents()1418     public int describeContents() {
1419         return 0;
1420     }
1421 
1422     public static final @android.annotation.NonNull Parcelable.Creator<BluetoothDevice> CREATOR =
1423             new Parcelable.Creator<BluetoothDevice>() {
1424                 public BluetoothDevice createFromParcel(Parcel in) {
1425                     return new BluetoothDevice(in.readString());
1426                 }
1427 
1428                 public BluetoothDevice[] newArray(int size) {
1429                     return new BluetoothDevice[size];
1430                 }
1431             };
1432 
1433     @Override
writeToParcel(Parcel out, int flags)1434     public void writeToParcel(Parcel out, int flags) {
1435         out.writeString(mAddress);
1436     }
1437 
1438     /**
1439      * Returns the hardware address of this BluetoothDevice.
1440      * <p> For example, "00:11:22:AA:BB:CC".
1441      *
1442      * @return Bluetooth hardware address as string
1443      */
getAddress()1444     public String getAddress() {
1445         if (DBG) Log.d(TAG, "mAddress: " + mAddress);
1446         return mAddress;
1447     }
1448 
1449     /**
1450      * Returns the address type of this BluetoothDevice.
1451      *
1452      * @return Bluetooth address type
1453      * @hide
1454      */
getAddressType()1455     public int getAddressType() {
1456         if (DBG) Log.d(TAG, "mAddressType: " + mAddressType);
1457         return mAddressType;
1458     }
1459 
1460     /**
1461      * Returns the anonymized hardware address of this BluetoothDevice. The first three octets
1462      * will be suppressed for anonymization.
1463      * <p> For example, "XX:XX:XX:AA:BB:CC".
1464      *
1465      * @return Anonymized bluetooth hardware address as string
1466      * @hide
1467      */
1468     @SystemApi
1469     @NonNull
1470     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
getAnonymizedAddress()1471     public String getAnonymizedAddress() {
1472         return "XX:XX:XX" + getAddress().substring(8);
1473     }
1474 
1475     /**
1476      * Returns the identity address of this BluetoothDevice.
1477      * <p> For example, "00:11:22:AA:BB:CC".
1478      *
1479      * @return Bluetooth identity address as a string
1480      * @hide
1481      */
1482     @SystemApi
1483     @RequiresBluetoothConnectPermission
1484     @RequiresPermission(allOf = {
1485             android.Manifest.permission.BLUETOOTH_CONNECT,
1486             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
1487     })
getIdentityAddress()1488     public @Nullable String getIdentityAddress() {
1489         if (DBG) log("getIdentityAddress()");
1490         final IBluetooth service = getService();
1491         final String defaultValue = null;
1492         if (service == null || !isBluetoothEnabled()) {
1493             Log.e(TAG, "BT not enabled. Cannot get identity address");
1494         } else {
1495             try {
1496                 final SynchronousResultReceiver<String> recv = SynchronousResultReceiver.get();
1497                 service.getIdentityAddress(mAddress, recv);
1498                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
1499             } catch (RemoteException | TimeoutException e) {
1500                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
1501             }
1502         }
1503         return defaultValue;
1504     }
1505 
1506     /**
1507      * Get the friendly Bluetooth name of the remote device.
1508      *
1509      * <p>The local adapter will automatically retrieve remote names when
1510      * performing a device scan, and will cache them. This method just returns
1511      * the name for this device from the cache.
1512      *
1513      * @return the Bluetooth name, or null if there was a problem.
1514      */
1515     @RequiresLegacyBluetoothPermission
1516     @RequiresBluetoothConnectPermission
1517     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getName()1518     public String getName() {
1519         if (DBG) log("getName()");
1520         final IBluetooth service = getService();
1521         final String defaultValue = null;
1522         if (service == null || !isBluetoothEnabled()) {
1523             Log.e(TAG, "BT not enabled. Cannot get Remote Device name");
1524             if (DBG) log(Log.getStackTraceString(new Throwable()));
1525         } else {
1526             try {
1527                 final SynchronousResultReceiver<String> recv = SynchronousResultReceiver.get();
1528                 service.getRemoteName(this, mAttributionSource, recv);
1529                 String name = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
1530                 if (name != null) {
1531                     // remove whitespace characters from the name
1532                     return name
1533                         .replace('\t', ' ')
1534                         .replace('\n', ' ')
1535                         .replace('\r', ' ');
1536                 }
1537             } catch (RemoteException | TimeoutException e) {
1538                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
1539             }
1540         }
1541         return defaultValue;
1542     }
1543 
1544     /**
1545      * Get the Bluetooth device type of the remote device.
1546      *
1547      * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} {@link
1548      * #DEVICE_TYPE_DUAL}. {@link #DEVICE_TYPE_UNKNOWN} if it's not available
1549      */
1550     @RequiresLegacyBluetoothPermission
1551     @RequiresBluetoothConnectPermission
1552     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getType()1553     public int getType() {
1554         if (DBG) log("getType()");
1555         final IBluetooth service = getService();
1556         final int defaultValue = DEVICE_TYPE_UNKNOWN;
1557         if (service == null || !isBluetoothEnabled()) {
1558             Log.e(TAG, "BT not enabled. Cannot get Remote Device type");
1559             if (DBG) log(Log.getStackTraceString(new Throwable()));
1560         } else {
1561             try {
1562                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
1563                 service.getRemoteType(this, mAttributionSource, recv);
1564                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
1565             } catch (RemoteException | TimeoutException e) {
1566                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
1567             }
1568         }
1569         return defaultValue;
1570     }
1571 
1572     /**
1573      * Get the locally modifiable name (alias) of the remote Bluetooth device.
1574      *
1575      * @return the Bluetooth alias, the friendly device name if no alias, or
1576      * null if there was a problem
1577      */
1578     @Nullable
1579     @RequiresLegacyBluetoothPermission
1580     @RequiresBluetoothConnectPermission
1581     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getAlias()1582     public String getAlias() {
1583         if (DBG) log("getAlias()");
1584         final IBluetooth service = getService();
1585         final String defaultValue = null;
1586         if (service == null || !isBluetoothEnabled()) {
1587             Log.e(TAG, "BT not enabled. Cannot get Remote Device Alias");
1588             if (DBG) log(Log.getStackTraceString(new Throwable()));
1589         } else {
1590             try {
1591                 final SynchronousResultReceiver<String> recv = SynchronousResultReceiver.get();
1592                 service.getRemoteAliasWithAttribution(this, mAttributionSource, recv);
1593                 String alias = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
1594                 if (alias == null) {
1595                     return getName();
1596                 }
1597                 return alias
1598                         .replace('\t', ' ')
1599                         .replace('\n', ' ')
1600                         .replace('\r', ' ');
1601             } catch (RemoteException | TimeoutException e) {
1602                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
1603             }
1604         }
1605         return defaultValue;
1606     }
1607 
1608     /** @hide */
1609     @Retention(RetentionPolicy.SOURCE)
1610     @IntDef(value = {
1611             BluetoothStatusCodes.SUCCESS,
1612             BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
1613             BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
1614             BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
1615             BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED
1616     })
1617     public @interface SetAliasReturnValues{}
1618 
1619     /**
1620      * Sets the locally modifiable name (alias) of the remote Bluetooth device. This method
1621      * overwrites the previously stored alias. The new alias is saved in local
1622      * storage so that the change is preserved over power cycles.
1623      *
1624      * <p>This method requires the calling app to be associated with Companion Device Manager (see
1625      * {@link android.companion.CompanionDeviceManager#associate(AssociationRequest,
1626      * android.companion.CompanionDeviceManager.Callback, Handler)}) and have the
1627      * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission. Alternatively, if the
1628      * caller has the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can
1629      * bypass the Companion Device Manager association requirement as well as other permission
1630      * requirements.
1631      *
1632      * @param alias is the new locally modifiable name for the remote Bluetooth device which must
1633      *              be the empty string. If null, we clear the alias.
1634      * @return whether the alias was successfully changed
1635      * @throws IllegalArgumentException if the alias is the empty string
1636      */
1637     @RequiresLegacyBluetoothPermission
1638     @RequiresBluetoothConnectPermission
1639     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
setAlias(@ullable String alias)1640     public @SetAliasReturnValues int setAlias(@Nullable String alias) {
1641         if (alias != null && alias.isEmpty()) {
1642             throw new IllegalArgumentException("alias cannot be the empty string");
1643         }
1644         if (DBG) log("setAlias(" + alias + ")");
1645         final IBluetooth service = getService();
1646         final int defaultValue = BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
1647         if (service == null || !isBluetoothEnabled()) {
1648             Log.e(TAG, "BT not enabled. Cannot set Remote Device name");
1649             if (DBG) log(Log.getStackTraceString(new Throwable()));
1650         } else {
1651             try {
1652                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
1653                 service.setRemoteAlias(this, alias, mAttributionSource, recv);
1654                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
1655             } catch (TimeoutException e) {
1656                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
1657             } catch (RemoteException e) {
1658                 Log.e(TAG, "", e);
1659                 throw e.rethrowFromSystemServer();
1660             }
1661         }
1662         return defaultValue;
1663     }
1664 
1665     /**
1666      * Get the most recent identified battery level of this Bluetooth device
1667      *
1668      * @return Battery level in percents from 0 to 100, {@link #BATTERY_LEVEL_BLUETOOTH_OFF} if
1669      * Bluetooth is disabled or {@link #BATTERY_LEVEL_UNKNOWN} if device is disconnected, or does
1670      * not have any battery reporting service, or return value is invalid
1671      * @hide
1672      */
1673     @SystemApi
1674     @RequiresLegacyBluetoothPermission
1675     @RequiresBluetoothConnectPermission
1676     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getBatteryLevel()1677     public @IntRange(from = -100, to = 100) int getBatteryLevel() {
1678         if (DBG) log("getBatteryLevel()");
1679         final IBluetooth service = getService();
1680         final int defaultValue = BATTERY_LEVEL_BLUETOOTH_OFF;
1681         if (service == null || !isBluetoothEnabled()) {
1682             Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level");
1683             if (DBG) log(Log.getStackTraceString(new Throwable()));
1684         } else {
1685             try {
1686                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
1687                 service.getBatteryLevel(this, mAttributionSource, recv);
1688                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
1689             } catch (RemoteException | TimeoutException e) {
1690                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
1691             }
1692         }
1693         return defaultValue;
1694     }
1695 
1696     /**
1697      * Start the bonding (pairing) process with the remote device.
1698      * <p>This is an asynchronous call, it will return immediately. Register
1699      * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
1700      * the bonding process completes, and its result.
1701      * <p>Android system services will handle the necessary user interactions
1702      * to confirm and complete the bonding process.
1703      *
1704      * @return false on immediate error, true if bonding will begin
1705      */
1706     @RequiresLegacyBluetoothAdminPermission
1707     @RequiresBluetoothConnectPermission
1708     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
createBond()1709     public boolean createBond() {
1710         return createBond(TRANSPORT_AUTO);
1711     }
1712 
1713     /**
1714      * Start the bonding (pairing) process with the remote device using the
1715      * specified transport.
1716      *
1717      * <p>This is an asynchronous call, it will return immediately. Register
1718      * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
1719      * the bonding process completes, and its result.
1720      * <p>Android system services will handle the necessary user interactions
1721      * to confirm and complete the bonding process.
1722      *
1723      * @param transport The transport to use for the pairing procedure.
1724      * @return false on immediate error, true if bonding will begin
1725      * @throws IllegalArgumentException if an invalid transport was specified
1726      * @hide
1727      */
1728     @SystemApi
1729     @RequiresLegacyBluetoothAdminPermission
1730     @RequiresBluetoothConnectPermission
1731     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
createBond(int transport)1732     public boolean createBond(int transport) {
1733         return createBondInternal(transport, null, null);
1734     }
1735 
1736     /**
1737      * Start the bonding (pairing) process with the remote device using the
1738      * Out Of Band mechanism.
1739      *
1740      * <p>This is an asynchronous call, it will return immediately. Register
1741      * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
1742      * the bonding process completes, and its result.
1743      *
1744      * <p>Android system services will handle the necessary user interactions
1745      * to confirm and complete the bonding process.
1746      *
1747      * <p>There are two possible versions of OOB Data.  This data can come in as
1748      * P192 or P256.  This is a reference to the cryptography used to generate the key.
1749      * The caller may pass one or both.  If both types of data are passed, then the
1750      * P256 data will be preferred, and thus used.
1751      *
1752      * @param transport - Transport to use
1753      * @param remoteP192Data - Out Of Band data (P192) or null
1754      * @param remoteP256Data - Out Of Band data (P256) or null
1755      * @return false on immediate error, true if bonding will begin
1756      * @hide
1757      */
1758     @SystemApi
1759     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
createBondOutOfBand(int transport, @Nullable OobData remoteP192Data, @Nullable OobData remoteP256Data)1760     public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data,
1761             @Nullable OobData remoteP256Data) {
1762         if (remoteP192Data == null && remoteP256Data == null) {
1763             throw new IllegalArgumentException(
1764                 "One or both arguments for the OOB data types are required to not be null."
1765                 + "  Please use createBond() instead if you do not have OOB data to pass.");
1766         }
1767         return createBondInternal(transport, remoteP192Data, remoteP256Data);
1768     }
1769 
1770     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
createBondInternal(int transport, @Nullable OobData remoteP192Data, @Nullable OobData remoteP256Data)1771     private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data,
1772             @Nullable OobData remoteP256Data) {
1773         if (DBG) log("createBondOutOfBand()");
1774         final IBluetooth service = getService();
1775         final boolean defaultValue = false;
1776         if (service == null || !isBluetoothEnabled()) {
1777             Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
1778             if (DBG) log(Log.getStackTraceString(new Throwable()));
1779         } else if (NULL_MAC_ADDRESS.equals(mAddress)) {
1780             Log.e(TAG, "Unable to create bond, invalid address " + mAddress);
1781         } else {
1782             try {
1783                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
1784                 service.createBond(this, transport, remoteP192Data, remoteP256Data,
1785                         mAttributionSource, recv);
1786                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
1787             } catch (RemoteException | TimeoutException e) {
1788                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
1789             }
1790         }
1791         return defaultValue;
1792     }
1793 
1794     /**
1795      * Gets whether bonding was initiated locally
1796      *
1797      * @return true if bonding is initiated locally, false otherwise
1798      *
1799      * @hide
1800      */
1801     @SystemApi
1802     @RequiresLegacyBluetoothPermission
1803     @RequiresBluetoothConnectPermission
1804     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
isBondingInitiatedLocally()1805     public boolean isBondingInitiatedLocally() {
1806         if (DBG) log("isBondingInitiatedLocally()");
1807         final IBluetooth service = getService();
1808         final boolean defaultValue = false;
1809         if (service == null || !isBluetoothEnabled()) {
1810             Log.w(TAG, "BT not enabled, isBondingInitiatedLocally failed");
1811             if (DBG) log(Log.getStackTraceString(new Throwable()));
1812         } else {
1813             try {
1814                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
1815                 service.isBondingInitiatedLocally(this, mAttributionSource, recv);
1816                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
1817             } catch (RemoteException | TimeoutException e) {
1818                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
1819             }
1820         }
1821         return defaultValue;
1822     }
1823 
1824     /**
1825      * Cancel an in-progress bonding request started with {@link #createBond}.
1826      *
1827      * @return true on success, false on error
1828      * @hide
1829      */
1830     @SystemApi
1831     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
cancelBondProcess()1832     public boolean cancelBondProcess() {
1833         if (DBG) log("cancelBondProcess()");
1834         final IBluetooth service = getService();
1835         final boolean defaultValue = false;
1836         if (service == null || !isBluetoothEnabled()) {
1837             Log.e(TAG, "BT not enabled. Cannot cancel Remote Device bond");
1838             if (DBG) log(Log.getStackTraceString(new Throwable()));
1839         } else {
1840             Log.i(TAG, "cancelBondProcess() for device " + getAddress()
1841                     + " called by pid: " + Process.myPid()
1842                     + " tid: " + Process.myTid());
1843             try {
1844                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
1845                 service.cancelBondProcess(this, mAttributionSource, recv);
1846                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
1847             } catch (RemoteException | TimeoutException e) {
1848                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
1849             }
1850         }
1851         return defaultValue;
1852     }
1853 
1854     /**
1855      * Remove bond (pairing) with the remote device.
1856      * <p>Delete the link key associated with the remote device, and
1857      * immediately terminate connections to that device that require
1858      * authentication and encryption.
1859      *
1860      * @return true on success, false on error
1861      * @hide
1862      */
1863     @SystemApi
1864     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
removeBond()1865     public boolean removeBond() {
1866         if (DBG) log("removeBond()");
1867         final IBluetooth service = getService();
1868         final boolean defaultValue = false;
1869         if (service == null || !isBluetoothEnabled()) {
1870             Log.e(TAG, "BT not enabled. Cannot remove Remote Device bond");
1871             if (DBG) log(Log.getStackTraceString(new Throwable()));
1872         } else {
1873             Log.i(TAG, "removeBond() for device " + getAddress()
1874                     + " called by pid: " + Process.myPid()
1875                     + " tid: " + Process.myTid());
1876             try {
1877                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
1878                 service.removeBond(this, mAttributionSource, recv);
1879                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
1880             } catch (RemoteException | TimeoutException e) {
1881                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
1882             }
1883         }
1884         return defaultValue;
1885     }
1886 
1887     /**
1888      * There are several instances of IpcDataCache used in this class.
1889      * BluetoothCache wraps up the common code.  All caches are created with a maximum of
1890      * eight entries, and the key is in the bluetooth module.  The name is set to the api.
1891      */
1892     private static class BluetoothCache<Q, R> extends IpcDataCache<Q, R> {
BluetoothCache(String api, IpcDataCache.QueryHandler query)1893         BluetoothCache(String api, IpcDataCache.QueryHandler query) {
1894             super(8, IpcDataCache.MODULE_BLUETOOTH, api, api, query);
1895         }};
1896 
1897     /**
1898      * Invalidate a bluetooth cache.  This method is just a short-hand wrapper that
1899      * enforces the bluetooth module.
1900      */
invalidateCache(@onNull String api)1901     private static void invalidateCache(@NonNull String api) {
1902         IpcDataCache.invalidateCache(IpcDataCache.MODULE_BLUETOOTH, api);
1903     }
1904 
1905     private final IpcDataCache.QueryHandler<Pair<IBluetooth, BluetoothDevice>, Integer>
1906             mBluetoothBondQuery = new IpcDataCache.QueryHandler<>() {
1907                 @RequiresLegacyBluetoothPermission
1908                 @RequiresBluetoothConnectPermission
1909                 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1910                 @Override
1911                 public Integer apply(Pair<IBluetooth, BluetoothDevice> pairQuery) {
1912                     if (DBG) {
1913                         log("getConnectionState(" + pairQuery.second.getAnonymizedAddress()
1914                                 + ") uncached");
1915                     }
1916                     try {
1917                         final SynchronousResultReceiver<Integer> recv =
1918                                 SynchronousResultReceiver.get();
1919                         pairQuery.first.getBondState(pairQuery.second, mAttributionSource, recv);
1920                         return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(BOND_NONE);
1921                     } catch (RemoteException | TimeoutException e) {
1922                         throw new RuntimeException(e);
1923                     }
1924                 }
1925             };
1926 
1927     private static final String GET_BOND_STATE_API = "BluetoothDevice_getBondState";
1928 
1929     private final BluetoothCache<Pair<IBluetooth, BluetoothDevice>, Integer> mBluetoothBondCache =
1930             new BluetoothCache<>(GET_BOND_STATE_API, mBluetoothBondQuery);
1931 
1932     /** @hide */
disableBluetoothGetBondStateCache()1933     public void disableBluetoothGetBondStateCache() {
1934         mBluetoothBondCache.disableForCurrentProcess();
1935     }
1936 
1937     /** @hide */
invalidateBluetoothGetBondStateCache()1938     public static void invalidateBluetoothGetBondStateCache() {
1939         invalidateCache(GET_BOND_STATE_API);
1940     }
1941 
1942     /**
1943      * Get the bond state of the remote device.
1944      * <p>Possible values for the bond state are:
1945      * {@link #BOND_NONE},
1946      * {@link #BOND_BONDING},
1947      * {@link #BOND_BONDED}.
1948      *
1949      * @return the bond state
1950      */
1951     @RequiresLegacyBluetoothPermission
1952     @RequiresBluetoothConnectPermission
1953     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1954     @SuppressLint("AndroidFrameworkRequiresPermission")
getBondState()1955     public int getBondState() {
1956         if (DBG) log("getBondState(" + getAnonymizedAddress() + ")");
1957         final IBluetooth service = getService();
1958         if (service == null) {
1959             Log.e(TAG, "BT not enabled. Cannot get bond state");
1960             if (DBG) log(Log.getStackTraceString(new Throwable()));
1961         } else {
1962             try {
1963                 return mBluetoothBondCache.query(new Pair<>(service, BluetoothDevice.this));
1964             } catch (RuntimeException e) {
1965                 if (!(e.getCause() instanceof TimeoutException)
1966                         && !(e.getCause() instanceof RemoteException)) {
1967                     throw e;
1968                 }
1969                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
1970             }
1971         }
1972         return BOND_NONE;
1973     }
1974 
1975     /**
1976      * Checks whether this bluetooth device is associated with CDM and meets the criteria to skip
1977      * the bluetooth pairing dialog because it has been already consented by the CDM prompt.
1978      *
1979      * @return true if we can bond without the dialog, false otherwise
1980      *
1981      * @hide
1982      */
1983     @SystemApi
1984     @RequiresPermission(allOf = {
1985             android.Manifest.permission.BLUETOOTH_CONNECT,
1986             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
1987     })
canBondWithoutDialog()1988     public boolean canBondWithoutDialog() {
1989         if (DBG) log("canBondWithoutDialog, device: " + this);
1990         final IBluetooth service = getService();
1991         final boolean defaultValue = false;
1992         if (service == null || !isBluetoothEnabled()) {
1993             Log.e(TAG, "BT not enabled. Cannot check if we can skip pairing dialog");
1994             if (DBG) log(Log.getStackTraceString(new Throwable()));
1995         } else {
1996             try {
1997                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
1998                 service.canBondWithoutDialog(this, mAttributionSource, recv);
1999                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2000             } catch (RemoteException | TimeoutException e) {
2001                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2002             }
2003         }
2004         return defaultValue;
2005     }
2006 
2007     /** @hide */
2008     @Retention(RetentionPolicy.SOURCE)
2009     @IntDef(value = {
2010             BluetoothStatusCodes.SUCCESS,
2011             BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
2012             BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
2013             BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
2014             BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED
2015     })
2016     public @interface ConnectionReturnValues{}
2017 
2018     /**
2019      * Connects all user enabled and supported bluetooth profiles between the local and remote
2020      * device. If no profiles are user enabled (e.g. first connection), we connect all supported
2021      * profiles. If the device is not already connected, this will page the device before initiating
2022      * profile connections. Connection is asynchronous and you should listen to each profile's
2023      * broadcast intent ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful.
2024      * For example, to verify a2dp is connected, you would listen for
2025      * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
2026      *
2027      * @return whether the messages were successfully sent to try to connect all profiles
2028      * @throws IllegalArgumentException if the device address is invalid
2029      *
2030      * @hide
2031      */
2032     @SystemApi
2033     @RequiresBluetoothConnectPermission
2034     @RequiresPermission(allOf = {
2035             android.Manifest.permission.BLUETOOTH_CONNECT,
2036             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2037             android.Manifest.permission.MODIFY_PHONE_STATE,
2038     })
connect()2039     public @ConnectionReturnValues int connect() {
2040         if (DBG) log("connect()");
2041         if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) {
2042             throw new IllegalArgumentException("device cannot have an invalid address");
2043         }
2044         final IBluetooth service = getService();
2045         final int defaultValue = BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
2046         if (service == null || !isBluetoothEnabled()) {
2047             Log.e(TAG, "BT not enabled. Cannot connect to remote device.");
2048             if (DBG) log(Log.getStackTraceString(new Throwable()));
2049         } else {
2050             try {
2051                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
2052                 service.connectAllEnabledProfiles(this, mAttributionSource, recv);
2053                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2054             } catch (TimeoutException e) {
2055                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2056             } catch (RemoteException e) {
2057                 Log.e(TAG, "", e);
2058                 throw e.rethrowFromSystemServer();
2059             }
2060         }
2061         return defaultValue;
2062     }
2063 
2064     /**
2065      * Disconnects all connected bluetooth profiles between the local and remote device.
2066      * Disconnection is asynchronous, so you should listen to each profile's broadcast intent
2067      * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example,
2068      * to verify a2dp is disconnected, you would listen for
2069      * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}. Once all profiles have disconnected,
2070      * the ACL link should come down and {@link #ACTION_ACL_DISCONNECTED} should be broadcast.
2071      * <p>
2072      * In the rare event that one or more profiles fail to disconnect, call this method again to
2073      * send another request to disconnect each connected profile.
2074      *
2075      * @return whether the messages were successfully sent to try to disconnect all profiles
2076      * @throws IllegalArgumentException if the device address is invalid
2077      *
2078      * @hide
2079      */
2080     @SystemApi
2081     @RequiresBluetoothConnectPermission
2082     @RequiresPermission(allOf = {
2083             android.Manifest.permission.BLUETOOTH_CONNECT,
2084             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2085     })
disconnect()2086     public @ConnectionReturnValues int disconnect() {
2087         if (DBG) log("disconnect()");
2088         if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) {
2089             throw new IllegalArgumentException("device cannot have an invalid address");
2090         }
2091         final IBluetooth service = getService();
2092         final int defaultValue = BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
2093         if (service == null || !isBluetoothEnabled()) {
2094             Log.e(TAG, "BT not enabled. Cannot disconnect to remote device.");
2095             if (DBG) log(Log.getStackTraceString(new Throwable()));
2096         } else {
2097             try {
2098                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
2099                 service.disconnectAllEnabledProfiles(this, mAttributionSource, recv);
2100                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2101             } catch (TimeoutException e) {
2102                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2103             } catch (RemoteException e) {
2104                 Log.e(TAG, "", e);
2105                 throw e.rethrowFromSystemServer();
2106             }
2107         }
2108         return defaultValue;
2109     }
2110 
2111     /**
2112      * Returns whether there is an open connection to this device.
2113      *
2114      * @return True if there is at least one open connection to this device.
2115      * @hide
2116      */
2117     @SystemApi
2118     @RequiresLegacyBluetoothPermission
2119     @RequiresBluetoothConnectPermission
2120     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
isConnected()2121     public boolean isConnected() {
2122         if (DBG) log("isConnected()");
2123         final IBluetooth service = getService();
2124         final int defaultValue = CONNECTION_STATE_DISCONNECTED;
2125         if (service == null || !isBluetoothEnabled()) {
2126             Log.w(TAG, "Proxy not attached to service");
2127             if (DBG) log(Log.getStackTraceString(new Throwable()));
2128         } else {
2129             try {
2130                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
2131                 service.getConnectionStateWithAttribution(this, mAttributionSource, recv);
2132                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue)
2133                         != CONNECTION_STATE_DISCONNECTED;
2134             } catch (RemoteException | TimeoutException e) {
2135                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2136             }
2137         }
2138         // BT is not enabled, we cannot be connected.
2139         return false;
2140     }
2141 
2142     /**
2143      * Returns whether there is an open connection to this device
2144      * that has been encrypted.
2145      *
2146      * @return True if there is at least one encrypted connection to this device.
2147      * @hide
2148      */
2149     @SystemApi
2150     @RequiresLegacyBluetoothPermission
2151     @RequiresBluetoothConnectPermission
2152     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
isEncrypted()2153     public boolean isEncrypted() {
2154         if (DBG) log("isEncrypted()");
2155         final IBluetooth service = getService();
2156         final int defaultValue = CONNECTION_STATE_DISCONNECTED;
2157         if (service == null || !isBluetoothEnabled()) {
2158             Log.w(TAG, "Proxy not attached to service");
2159             if (DBG) log(Log.getStackTraceString(new Throwable()));
2160         } else {
2161             try {
2162                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
2163                 service.getConnectionStateWithAttribution(this, mAttributionSource, recv);
2164                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue)
2165                         > CONNECTION_STATE_CONNECTED;
2166             } catch (RemoteException | TimeoutException e) {
2167                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2168             }
2169         }
2170         // BT is not enabled, we cannot be encrypted.
2171         return false;
2172     }
2173 
2174     /**
2175      * Get the Bluetooth class of the remote device.
2176      *
2177      * @return Bluetooth class object, or null on error
2178      */
2179     @RequiresLegacyBluetoothPermission
2180     @RequiresBluetoothConnectPermission
2181     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getBluetoothClass()2182     public BluetoothClass getBluetoothClass() {
2183         if (DBG) log("getBluetoothClass()");
2184         final IBluetooth service = getService();
2185         final int defaultValue = 0;
2186         if (service == null || !isBluetoothEnabled()) {
2187             Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class");
2188             if (DBG) log(Log.getStackTraceString(new Throwable()));
2189         } else {
2190             try {
2191                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
2192                 service.getRemoteClass(this, mAttributionSource, recv);
2193                 int classInt = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2194                 if (classInt == BluetoothClass.ERROR) return null;
2195                 return new BluetoothClass(classInt);
2196             } catch (RemoteException | TimeoutException e) {
2197                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2198             }
2199         }
2200         return null;
2201     }
2202 
2203     /**
2204      * Returns the supported features (UUIDs) of the remote device.
2205      *
2206      * <p>This method does not start a service discovery procedure to retrieve the UUIDs
2207      * from the remote device. Instead, the local cached copy of the service
2208      * UUIDs are returned.
2209      * <p>Use {@link #fetchUuidsWithSdp} if fresh UUIDs are desired.
2210      *
2211      * @return the supported features (UUIDs) of the remote device, or null on error
2212      */
2213     @RequiresLegacyBluetoothPermission
2214     @RequiresBluetoothConnectPermission
2215     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getUuids()2216     public ParcelUuid[] getUuids() {
2217         if (DBG) log("getUuids()");
2218         final IBluetooth service = getService();
2219         final ParcelUuid[] defaultValue = null;
2220         if (service == null || !isBluetoothEnabled()) {
2221             Log.e(TAG, "BT not enabled. Cannot get remote device Uuids");
2222             if (DBG) log(Log.getStackTraceString(new Throwable()));
2223         } else {
2224             try {
2225                 final SynchronousResultReceiver<List<ParcelUuid>> recv =
2226                         SynchronousResultReceiver.get();
2227                 service.getRemoteUuids(this, mAttributionSource, recv);
2228                 List<ParcelUuid> parcels = recv.awaitResultNoInterrupt(getSyncTimeout())
2229                         .getValue(null);
2230                 return parcels != null ? parcels.toArray(new ParcelUuid[parcels.size()]) : null;
2231             } catch (RemoteException | TimeoutException e) {
2232                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2233             }
2234         }
2235         return defaultValue;
2236     }
2237 
2238     /**
2239      * Perform a service discovery on the remote device to get the UUIDs supported.
2240      *
2241      * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent,
2242      * with the UUIDs supported by the remote end. If there is an error
2243      * in getting the SDP records or if the process takes a long time, or the device is bonding and
2244      * we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the UUIDs that is
2245      * currently present in the cache. Clients should use the {@link #getUuids} to get UUIDs
2246      * if service discovery is not to be performed. If there is an ongoing bonding process,
2247      * service discovery or device inquiry, the request will be queued.
2248      *
2249      * @return False if the check fails, True if the process of initiating an ACL connection
2250      * to the remote device was started or cached UUIDs will be broadcast.
2251      */
2252     @RequiresLegacyBluetoothPermission
2253     @RequiresBluetoothConnectPermission
2254     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
fetchUuidsWithSdp()2255     public boolean fetchUuidsWithSdp() {
2256         return fetchUuidsWithSdp(TRANSPORT_AUTO);
2257     }
2258 
2259     /**
2260      * Perform a service discovery on the remote device to get the UUIDs supported with the
2261      * specific transport.
2262      *
2263      * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent,
2264      * with the UUIDs supported by the remote end. If there is an error
2265      * in getting the SDP or GATT records or if the process takes a long time, or the device
2266      * is bonding and we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the
2267      * UUIDs that is currently present in the cache. Clients should use the {@link #getUuids}
2268      * to get UUIDs if service discovery is not to be performed. If there is an ongoing bonding
2269      * process, service discovery or device inquiry, the request will be queued.
2270      *
2271      * @param transport - provide type of transport (e.g. LE or Classic).
2272      * @return False if the check fails, True if the process of initiating an ACL connection
2273      * to the remote device was started or cached UUIDs will be broadcast with the specific
2274      * transport.
2275      *
2276      * @hide
2277      */
2278     @SystemApi
2279     @RequiresPermission(allOf = {
2280             android.Manifest.permission.BLUETOOTH_CONNECT,
2281             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2282     })
fetchUuidsWithSdp(@ransport int transport)2283     public boolean fetchUuidsWithSdp(@Transport int transport) {
2284         if (DBG) log("fetchUuidsWithSdp()");
2285         final IBluetooth service = getService();
2286         final boolean defaultValue = false;
2287         if (service == null || !isBluetoothEnabled()) {
2288             Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp");
2289             if (DBG) log(Log.getStackTraceString(new Throwable()));
2290         } else {
2291             try {
2292                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
2293                 service.fetchRemoteUuidsWithAttribution(this, transport, mAttributionSource, recv);
2294                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2295             } catch (RemoteException | TimeoutException e) {
2296                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2297             }
2298         }
2299         return defaultValue;
2300     }
2301 
2302     /**
2303      * Perform a service discovery on the remote device to get the SDP records associated
2304      * with the specified UUID.
2305      *
2306      * <p>This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent,
2307      * with the SDP records found on the remote end. If there is an error
2308      * in getting the SDP records or if the process takes a long time,
2309      * {@link #ACTION_SDP_RECORD} intent is sent with an status value in
2310      * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0.
2311      * Detailed status error codes can be found by members of the Bluetooth package in
2312      * the AbstractionLayer class.
2313      * <p>The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}.
2314      * The object type will match one of the SdpXxxRecord types, depending on the UUID searched
2315      * for.
2316      *
2317      * @return False if the check fails, True if the process
2318      *               of initiating an ACL connection to the remote device
2319      *               was started.
2320      */
2321     /** @hide */
2322     @RequiresLegacyBluetoothPermission
2323     @RequiresBluetoothConnectPermission
2324     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
sdpSearch(ParcelUuid uuid)2325     public boolean sdpSearch(ParcelUuid uuid) {
2326         if (DBG) log("sdpSearch()");
2327         final IBluetooth service = getService();
2328         final boolean defaultValue = false;
2329         if (service == null || !isBluetoothEnabled()) {
2330             Log.e(TAG, "BT not enabled. Cannot query remote device sdp records");
2331             if (DBG) log(Log.getStackTraceString(new Throwable()));
2332         } else {
2333             try {
2334                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
2335                 service.sdpSearch(this, uuid, mAttributionSource, recv);
2336                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2337             } catch (RemoteException | TimeoutException e) {
2338                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2339             }
2340         }
2341         return defaultValue;
2342     }
2343 
2344     /**
2345      * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
2346      *
2347      * @return true pin has been set false for error
2348      */
2349     @RequiresLegacyBluetoothAdminPermission
2350     @RequiresBluetoothConnectPermission
2351     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
setPin(byte[] pin)2352     public boolean setPin(byte[] pin) {
2353         if (DBG) log("setPin()");
2354         final IBluetooth service = getService();
2355         final boolean defaultValue = false;
2356         if (service == null || !isBluetoothEnabled()) {
2357             Log.e(TAG, "BT not enabled. Cannot set Remote Device pin");
2358             if (DBG) log(Log.getStackTraceString(new Throwable()));
2359         } else {
2360             try {
2361                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
2362                 service.setPin(this, true, pin.length, pin, mAttributionSource, recv);
2363                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2364             } catch (RemoteException | TimeoutException e) {
2365                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2366             }
2367         }
2368         return defaultValue;
2369     }
2370 
2371     /**
2372      * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
2373      *
2374      * @return true pin has been set false for error
2375      * @hide
2376      */
2377     @SystemApi
2378     @RequiresLegacyBluetoothAdminPermission
2379     @RequiresBluetoothConnectPermission
2380     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
setPin(@onNull String pin)2381     public boolean setPin(@NonNull String pin) {
2382         byte[] pinBytes = convertPinToBytes(pin);
2383         if (pinBytes == null) {
2384             return false;
2385         }
2386         return setPin(pinBytes);
2387     }
2388 
2389     /**
2390      * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing.
2391      *
2392      * @return true confirmation has been sent out false for error
2393      */
2394     @RequiresPermission(allOf = {
2395             android.Manifest.permission.BLUETOOTH_CONNECT,
2396             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2397     })
setPairingConfirmation(boolean confirm)2398     public boolean setPairingConfirmation(boolean confirm) {
2399         if (DBG) log("setPairingConfirmation()");
2400         final IBluetooth service = getService();
2401         final boolean defaultValue = false;
2402         if (service == null || !isBluetoothEnabled()) {
2403             Log.e(TAG, "BT not enabled. Cannot set pairing confirmation");
2404             if (DBG) log(Log.getStackTraceString(new Throwable()));
2405         } else {
2406             try {
2407                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
2408                 service.setPairingConfirmation(this, confirm, mAttributionSource, recv);
2409                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2410             } catch (RemoteException | TimeoutException e) {
2411                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2412             }
2413         }
2414         return defaultValue;
2415     }
2416 
isBluetoothEnabled()2417     boolean isBluetoothEnabled() {
2418         boolean ret = false;
2419         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
2420         if (adapter != null && adapter.isEnabled()) {
2421             ret = true;
2422         }
2423         return ret;
2424     }
2425 
2426     /**
2427      * Gets whether the phonebook access is allowed for this bluetooth device
2428      *
2429      * @return Whether the phonebook access is allowed to this device. Can be {@link
2430      * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
2431      * @hide
2432      */
2433     @SystemApi
2434     @RequiresLegacyBluetoothPermission
2435     @RequiresBluetoothConnectPermission
2436     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getPhonebookAccessPermission()2437     public @AccessPermission int getPhonebookAccessPermission() {
2438         if (DBG) log("getPhonebookAccessPermission()");
2439         final IBluetooth service = getService();
2440         final int defaultValue = ACCESS_UNKNOWN;
2441         if (service == null || !isBluetoothEnabled()) {
2442             Log.w(TAG, "Proxy not attached to service");
2443             if (DBG) log(Log.getStackTraceString(new Throwable()));
2444         } else {
2445             try {
2446                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
2447                 service.getPhonebookAccessPermission(this, mAttributionSource, recv);
2448                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2449             } catch (RemoteException | TimeoutException e) {
2450                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2451             }
2452         }
2453         return defaultValue;
2454     }
2455 
2456     /**
2457      * Sets whether the {@link BluetoothDevice} enters silence mode. Audio will not
2458      * be routed to the {@link BluetoothDevice} if set to {@code true}.
2459      *
2460      * When the {@link BluetoothDevice} enters silence mode, and the {@link BluetoothDevice}
2461      * is an active device (for A2DP or HFP), the active device for that profile
2462      * will be set to null.
2463      * If the {@link BluetoothDevice} exits silence mode while the A2DP or HFP
2464      * active device is null, the {@link BluetoothDevice} will be set as the
2465      * active device for that profile.
2466      * If the {@link BluetoothDevice} is disconnected, it exits silence mode.
2467      * If the {@link BluetoothDevice} is set as the active device for A2DP or
2468      * HFP, while silence mode is enabled, then the device will exit silence mode.
2469      * If the {@link BluetoothDevice} is in silence mode, AVRCP position change
2470      * event and HFP AG indicators will be disabled.
2471      * If the {@link BluetoothDevice} is not connected with A2DP or HFP, it cannot
2472      * enter silence mode.
2473      *
2474      * @param silence true to enter silence mode, false to exit
2475      * @return true on success, false on error.
2476      * @throws IllegalStateException if Bluetooth is not turned ON.
2477      * @hide
2478      */
2479     @SystemApi
2480     @RequiresPermission(allOf = {
2481             android.Manifest.permission.BLUETOOTH_CONNECT,
2482             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2483     })
setSilenceMode(boolean silence)2484     public boolean setSilenceMode(boolean silence) {
2485         if (DBG) log("setSilenceMode()");
2486         final IBluetooth service = getService();
2487         final boolean defaultValue = false;
2488         if (service == null || !isBluetoothEnabled()) {
2489             throw new IllegalStateException("Bluetooth is not turned ON");
2490         } else {
2491             try {
2492                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
2493                 service.setSilenceMode(this, silence, mAttributionSource, recv);
2494                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2495             } catch (RemoteException | TimeoutException e) {
2496                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2497             }
2498         }
2499         return defaultValue;
2500     }
2501 
2502     /**
2503      * Check whether the {@link BluetoothDevice} is in silence mode
2504      *
2505      * @return true on device in silence mode, otherwise false.
2506      * @throws IllegalStateException if Bluetooth is not turned ON.
2507      * @hide
2508      */
2509     @SystemApi
2510     @RequiresPermission(allOf = {
2511             android.Manifest.permission.BLUETOOTH_CONNECT,
2512             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2513     })
isInSilenceMode()2514     public boolean isInSilenceMode() {
2515         if (DBG) log("isInSilenceMode()");
2516         final IBluetooth service = getService();
2517         final boolean defaultValue = false;
2518         if (service == null || !isBluetoothEnabled()) {
2519             throw new IllegalStateException("Bluetooth is not turned ON");
2520         } else {
2521             try {
2522                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
2523                 service.getSilenceMode(this, mAttributionSource, recv);
2524                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2525             } catch (RemoteException | TimeoutException e) {
2526                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2527             }
2528         }
2529         return defaultValue;
2530     }
2531 
2532     /**
2533      * Sets whether the phonebook access is allowed to this device.
2534      *
2535      * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link
2536      * #ACCESS_REJECTED}.
2537      * @return Whether the value has been successfully set.
2538      * @hide
2539      */
2540     @SystemApi
2541     @RequiresPermission(allOf = {
2542             android.Manifest.permission.BLUETOOTH_CONNECT,
2543             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2544     })
setPhonebookAccessPermission(@ccessPermission int value)2545     public boolean setPhonebookAccessPermission(@AccessPermission int value) {
2546         if (DBG) log("setPhonebookAccessPermission()");
2547         final IBluetooth service = getService();
2548         final boolean defaultValue = false;
2549         if (service == null || !isBluetoothEnabled()) {
2550             Log.w(TAG, "Proxy not attached to service");
2551             if (DBG) log(Log.getStackTraceString(new Throwable()));
2552         } else {
2553             try {
2554                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
2555                 service.setPhonebookAccessPermission(this, value, mAttributionSource, recv);
2556                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2557             } catch (RemoteException | TimeoutException e) {
2558                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2559             }
2560         }
2561         return defaultValue;
2562     }
2563 
2564     /**
2565      * Gets whether message access is allowed to this bluetooth device
2566      *
2567      * @return Whether the message access is allowed to this device.
2568      * @hide
2569      */
2570     @SystemApi
2571     @RequiresLegacyBluetoothPermission
2572     @RequiresBluetoothConnectPermission
2573     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getMessageAccessPermission()2574     public @AccessPermission int getMessageAccessPermission() {
2575         if (DBG) log("getMessageAccessPermission()");
2576         final IBluetooth service = getService();
2577         final int defaultValue = ACCESS_UNKNOWN;
2578         if (service == null || !isBluetoothEnabled()) {
2579             Log.w(TAG, "Proxy not attached to service");
2580             if (DBG) log(Log.getStackTraceString(new Throwable()));
2581         } else {
2582             try {
2583                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
2584                 service.getMessageAccessPermission(this, mAttributionSource, recv);
2585                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2586             } catch (RemoteException | TimeoutException e) {
2587                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2588             }
2589         }
2590         return defaultValue;
2591     }
2592 
2593     /**
2594      * Sets whether the message access is allowed to this device.
2595      *
2596      * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded,
2597      * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if
2598      * the permission is not being granted.
2599      * @return Whether the value has been successfully set.
2600      * @hide
2601      */
2602     @SystemApi
2603     @RequiresPermission(allOf = {
2604             android.Manifest.permission.BLUETOOTH_CONNECT,
2605             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2606     })
setMessageAccessPermission(@ccessPermission int value)2607     public boolean setMessageAccessPermission(@AccessPermission int value) {
2608         // Validates param value is one of the accepted constants
2609         if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) {
2610             throw new IllegalArgumentException(value + "is not a valid AccessPermission value");
2611         }
2612         if (DBG) log("setMessageAccessPermission()");
2613         final IBluetooth service = getService();
2614         final boolean defaultValue = false;
2615         if (service == null || !isBluetoothEnabled()) {
2616             Log.w(TAG, "Proxy not attached to service");
2617             if (DBG) log(Log.getStackTraceString(new Throwable()));
2618         } else {
2619             try {
2620                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
2621                 service.setMessageAccessPermission(this, value, mAttributionSource, recv);
2622                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2623             } catch (RemoteException | TimeoutException e) {
2624                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2625             }
2626         }
2627         return defaultValue;
2628     }
2629 
2630     /**
2631      * Gets whether sim access is allowed for this bluetooth device
2632      *
2633      * @return Whether the Sim access is allowed to this device.
2634      * @hide
2635      */
2636     @SystemApi
2637     @RequiresLegacyBluetoothPermission
2638     @RequiresBluetoothConnectPermission
2639     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getSimAccessPermission()2640     public @AccessPermission int getSimAccessPermission() {
2641         if (DBG) log("getSimAccessPermission()");
2642         final IBluetooth service = getService();
2643         final int defaultValue = ACCESS_UNKNOWN;
2644         if (service == null || !isBluetoothEnabled()) {
2645             Log.w(TAG, "Proxy not attached to service");
2646             if (DBG) log(Log.getStackTraceString(new Throwable()));
2647         } else {
2648             try {
2649                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
2650                 service.getSimAccessPermission(this, mAttributionSource, recv);
2651                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2652             } catch (RemoteException | TimeoutException e) {
2653                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2654             }
2655         }
2656         return defaultValue;
2657     }
2658 
2659     /**
2660      * Sets whether the Sim access is allowed to this device.
2661      *
2662      * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded,
2663      * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if
2664      * the permission is not being granted.
2665      * @return Whether the value has been successfully set.
2666      * @hide
2667      */
2668     @SystemApi
2669     @RequiresPermission(allOf = {
2670             android.Manifest.permission.BLUETOOTH_CONNECT,
2671             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
2672     })
setSimAccessPermission(int value)2673     public boolean setSimAccessPermission(int value) {
2674         if (DBG) log("setSimAccessPermission()");
2675         final IBluetooth service = getService();
2676         final boolean defaultValue = false;
2677         if (service == null || !isBluetoothEnabled()) {
2678             Log.w(TAG, "Proxy not attached to service");
2679             if (DBG) log(Log.getStackTraceString(new Throwable()));
2680         } else {
2681             try {
2682                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
2683                 service.setSimAccessPermission(this, value, mAttributionSource, recv);
2684                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
2685             } catch (RemoteException | TimeoutException e) {
2686                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
2687             }
2688         }
2689         return defaultValue;
2690     }
2691 
2692     /**
2693      * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
2694      * outgoing connection to this remote device on given channel.
2695      * <p>The remote device will be authenticated and communication on this
2696      * socket will be encrypted.
2697      * <p> Use this socket only if an authenticated socket link is possible.
2698      * Authentication refers to the authentication of the link key to
2699      * prevent person-in-the-middle type of attacks.
2700      * For example, for Bluetooth 2.1 devices, if any of the devices does not
2701      * have an input and output capability or just has the ability to
2702      * display a numeric key, a secure socket connection is not possible.
2703      * In such a case, use {@link createInsecureRfcommSocket}.
2704      * For more details, refer to the Security Model section 5.2 (vol 3) of
2705      * Bluetooth Core Specification version 2.1 + EDR.
2706      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
2707      * connection.
2708      * <p>Valid RFCOMM channels are in range 1 to 30.
2709      *
2710      * @param channel RFCOMM channel to connect to
2711      * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
2712      * @throws IOException on error, for example Bluetooth not available, or insufficient
2713      * permissions
2714      * @hide
2715      */
2716     @UnsupportedAppUsage
2717     @RequiresLegacyBluetoothPermission
2718     @RequiresBluetoothConnectPermission
2719     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2720     @SuppressLint("AndroidFrameworkRequiresPermission")
createRfcommSocket(int channel)2721     public BluetoothSocket createRfcommSocket(int channel) throws IOException {
2722         if (!isBluetoothEnabled()) {
2723             Log.e(TAG, "Bluetooth is not enabled");
2724             throw new IOException();
2725         }
2726         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel,
2727                 null);
2728     }
2729 
2730     /**
2731      * Create an L2cap {@link BluetoothSocket} ready to start a secure
2732      * outgoing connection to this remote device on given channel.
2733      * <p>The remote device will be authenticated and communication on this
2734      * socket will be encrypted.
2735      * <p> Use this socket only if an authenticated socket link is possible.
2736      * Authentication refers to the authentication of the link key to
2737      * prevent person-in-the-middle type of attacks.
2738      * For example, for Bluetooth 2.1 devices, if any of the devices does not
2739      * have an input and output capability or just has the ability to
2740      * display a numeric key, a secure socket connection is not possible.
2741      * In such a case, use {@link createInsecureRfcommSocket}.
2742      * For more details, refer to the Security Model section 5.2 (vol 3) of
2743      * Bluetooth Core Specification version 2.1 + EDR.
2744      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
2745      * connection.
2746      * <p>Valid L2CAP PSM channels are in range 1 to 2^16.
2747      *
2748      * @param channel L2cap PSM/channel to connect to
2749      * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
2750      * @throws IOException on error, for example Bluetooth not available, or insufficient
2751      * permissions
2752      * @hide
2753      */
2754     @RequiresLegacyBluetoothPermission
2755     @RequiresBluetoothConnectPermission
2756     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2757     @SuppressLint("AndroidFrameworkRequiresPermission")
createL2capSocket(int channel)2758     public BluetoothSocket createL2capSocket(int channel) throws IOException {
2759         return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, true, true, this, channel,
2760                 null);
2761     }
2762 
2763     /**
2764      * Create an L2cap {@link BluetoothSocket} ready to start an insecure
2765      * outgoing connection to this remote device on given channel.
2766      * <p>The remote device will be not authenticated and communication on this
2767      * socket will not be encrypted.
2768      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
2769      * connection.
2770      * <p>Valid L2CAP PSM channels are in range 1 to 2^16.
2771      *
2772      * @param channel L2cap PSM/channel to connect to
2773      * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
2774      * @throws IOException on error, for example Bluetooth not available, or insufficient
2775      * permissions
2776      * @hide
2777      */
2778     @RequiresLegacyBluetoothPermission
2779     @RequiresBluetoothConnectPermission
2780     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2781     @SuppressLint("AndroidFrameworkRequiresPermission")
createInsecureL2capSocket(int channel)2782     public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException {
2783         return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel,
2784                 null);
2785     }
2786 
2787     /**
2788      * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
2789      * outgoing connection to this remote device using SDP lookup of uuid.
2790      * <p>This is designed to be used with {@link
2791      * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer
2792      * Bluetooth applications.
2793      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
2794      * connection. This will also perform an SDP lookup of the given uuid to
2795      * determine which channel to connect to.
2796      * <p>The remote device will be authenticated and communication on this
2797      * socket will be encrypted.
2798      * <p> Use this socket only if an authenticated socket link is possible.
2799      * Authentication refers to the authentication of the link key to
2800      * prevent person-in-the-middle type of attacks.
2801      * For example, for Bluetooth 2.1 devices, if any of the devices does not
2802      * have an input and output capability or just has the ability to
2803      * display a numeric key, a secure socket connection is not possible.
2804      * In such a case, use {@link #createInsecureRfcommSocketToServiceRecord}.
2805      * For more details, refer to the Security Model section 5.2 (vol 3) of
2806      * Bluetooth Core Specification version 2.1 + EDR.
2807      * <p>Hint: If you are connecting to a Bluetooth serial board then try
2808      * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.
2809      * However if you are connecting to an Android peer then please generate
2810      * your own unique UUID.
2811      *
2812      * @param uuid service record uuid to lookup RFCOMM channel
2813      * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
2814      * @throws IOException on error, for example Bluetooth not available, or insufficient
2815      * permissions
2816      */
2817     @RequiresLegacyBluetoothPermission
2818     @RequiresBluetoothConnectPermission
2819     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2820     @SuppressLint("AndroidFrameworkRequiresPermission")
createRfcommSocketToServiceRecord(UUID uuid)2821     public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
2822         if (!isBluetoothEnabled()) {
2823             Log.e(TAG, "Bluetooth is not enabled");
2824             throw new IOException();
2825         }
2826 
2827         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
2828                 new ParcelUuid(uuid));
2829     }
2830 
2831     /**
2832      * Create an RFCOMM {@link BluetoothSocket} socket ready to start an insecure
2833      * outgoing connection to this remote device using SDP lookup of uuid.
2834      * <p> The communication channel will not have an authenticated link key
2835      * i.e it will be subject to person-in-the-middle attacks. For Bluetooth 2.1
2836      * devices, the link key will be encrypted, as encryption is mandatory.
2837      * For legacy devices (pre Bluetooth 2.1 devices) the link key will
2838      * be not be encrypted. Use {@link #createRfcommSocketToServiceRecord} if an
2839      * encrypted and authenticated communication channel is desired.
2840      * <p>This is designed to be used with {@link
2841      * BluetoothAdapter#listenUsingInsecureRfcommWithServiceRecord} for peer-peer
2842      * Bluetooth applications.
2843      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
2844      * connection. This will also perform an SDP lookup of the given uuid to
2845      * determine which channel to connect to.
2846      * <p>The remote device will be authenticated and communication on this
2847      * socket will be encrypted.
2848      * <p>Hint: If you are connecting to a Bluetooth serial board then try
2849      * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.
2850      * However if you are connecting to an Android peer then please generate
2851      * your own unique UUID.
2852      *
2853      * @param uuid service record uuid to lookup RFCOMM channel
2854      * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
2855      * @throws IOException on error, for example Bluetooth not available, or insufficient
2856      * permissions
2857      */
2858     @RequiresLegacyBluetoothPermission
2859     @RequiresBluetoothConnectPermission
2860     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2861     @SuppressLint("AndroidFrameworkRequiresPermission")
createInsecureRfcommSocketToServiceRecord(UUID uuid)2862     public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException {
2863         if (!isBluetoothEnabled()) {
2864             Log.e(TAG, "Bluetooth is not enabled");
2865             throw new IOException();
2866         }
2867         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1,
2868                 new ParcelUuid(uuid));
2869     }
2870 
2871     /**
2872      * Construct an insecure RFCOMM socket ready to start an outgoing
2873      * connection.
2874      * Call #connect on the returned #BluetoothSocket to begin the connection.
2875      * The remote device will not be authenticated and communication on this
2876      * socket will not be encrypted.
2877      *
2878      * @param port remote port
2879      * @return An RFCOMM BluetoothSocket
2880      * @throws IOException On error, for example Bluetooth not available, or insufficient
2881      * permissions.
2882      * @hide
2883      */
2884     @UnsupportedAppUsage(publicAlternatives = "Use "
2885             + "{@link #createInsecureRfcommSocketToServiceRecord} instead.")
2886     @RequiresLegacyBluetoothAdminPermission
2887     @RequiresBluetoothConnectPermission
2888     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2889     @SuppressLint("AndroidFrameworkRequiresPermission")
createInsecureRfcommSocket(int port)2890     public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
2891         if (!isBluetoothEnabled()) {
2892             Log.e(TAG, "Bluetooth is not enabled");
2893             throw new IOException();
2894         }
2895         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port,
2896                 null);
2897     }
2898 
2899     /**
2900      * Construct a SCO socket ready to start an outgoing connection.
2901      * Call #connect on the returned #BluetoothSocket to begin the connection.
2902      *
2903      * @return a SCO BluetoothSocket
2904      * @throws IOException on error, for example Bluetooth not available, or insufficient
2905      * permissions.
2906      * @hide
2907      */
2908     @UnsupportedAppUsage
2909     @RequiresLegacyBluetoothAdminPermission
2910     @RequiresBluetoothConnectPermission
2911     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
2912     @SuppressLint("AndroidFrameworkRequiresPermission")
createScoSocket()2913     public BluetoothSocket createScoSocket() throws IOException {
2914         if (!isBluetoothEnabled()) {
2915             Log.e(TAG, "Bluetooth is not enabled");
2916             throw new IOException();
2917         }
2918         return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null);
2919     }
2920 
2921     /**
2922      * Check that a pin is valid and convert to byte array.
2923      *
2924      * Bluetooth pin's are 1 to 16 bytes of UTF-8 characters.
2925      *
2926      * @param pin pin as java String
2927      * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin.
2928      * @hide
2929      */
2930     @UnsupportedAppUsage
convertPinToBytes(String pin)2931     public static byte[] convertPinToBytes(String pin) {
2932         if (pin == null) {
2933             return null;
2934         }
2935         byte[] pinBytes;
2936         try {
2937             pinBytes = pin.getBytes("UTF-8");
2938         } catch (UnsupportedEncodingException uee) {
2939             Log.e(TAG, "UTF-8 not supported?!?");  // this should not happen
2940             return null;
2941         }
2942         if (pinBytes.length <= 0 || pinBytes.length > 16) {
2943             return null;
2944         }
2945         return pinBytes;
2946     }
2947 
2948     /**
2949      * Connect to GATT Server hosted by this device. Caller acts as GATT client.
2950      * The callback is used to deliver results to Caller, such as connection status as well
2951      * as any further GATT client operations.
2952      * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
2953      * GATT client operations.
2954      *
2955      * @param callback GATT callback handler that will receive asynchronous callbacks.
2956      * @param autoConnect Whether to directly connect to the remote device (false) or to
2957      * automatically connect as soon as the remote device becomes available (true).
2958      * @throws IllegalArgumentException if callback is null
2959      */
2960     @RequiresBluetoothConnectPermission
2961     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)2962     public BluetoothGatt connectGatt(Context context, boolean autoConnect,
2963             BluetoothGattCallback callback) {
2964         return (connectGatt(context, autoConnect, callback, TRANSPORT_AUTO));
2965     }
2966 
2967     /**
2968      * Connect to GATT Server hosted by this device. Caller acts as GATT client.
2969      * The callback is used to deliver results to Caller, such as connection status as well
2970      * as any further GATT client operations.
2971      * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
2972      * GATT client operations.
2973      *
2974      * @param callback GATT callback handler that will receive asynchronous callbacks.
2975      * @param autoConnect Whether to directly connect to the remote device (false) or to
2976      * automatically connect as soon as the remote device becomes available (true).
2977      * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
2978      * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
2979      * BluetoothDevice#TRANSPORT_LE}
2980      * @throws IllegalArgumentException if callback is null
2981      */
2982     @RequiresBluetoothConnectPermission
2983     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport)2984     public BluetoothGatt connectGatt(Context context, boolean autoConnect,
2985             BluetoothGattCallback callback, int transport) {
2986         return (connectGatt(context, autoConnect, callback, transport, PHY_LE_1M_MASK));
2987     }
2988 
2989     /**
2990      * Connect to GATT Server hosted by this device. Caller acts as GATT client.
2991      * The callback is used to deliver results to Caller, such as connection status as well
2992      * as any further GATT client operations.
2993      * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
2994      * GATT client operations.
2995      *
2996      * @param callback GATT callback handler that will receive asynchronous callbacks.
2997      * @param autoConnect Whether to directly connect to the remote device (false) or to
2998      * automatically connect as soon as the remote device becomes available (true).
2999      * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
3000      * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
3001      * BluetoothDevice#TRANSPORT_LE}
3002      * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link
3003      * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
3004      * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect}
3005      * is set to true.
3006      * @throws NullPointerException if callback is null
3007      */
3008     @RequiresBluetoothConnectPermission
3009     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy)3010     public BluetoothGatt connectGatt(Context context, boolean autoConnect,
3011             BluetoothGattCallback callback, int transport, int phy) {
3012         return connectGatt(context, autoConnect, callback, transport, phy, null);
3013     }
3014 
3015     /**
3016      * Connect to GATT Server hosted by this device. Caller acts as GATT client.
3017      * The callback is used to deliver results to Caller, such as connection status as well
3018      * as any further GATT client operations.
3019      * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
3020      * GATT client operations.
3021      *
3022      * @param callback GATT callback handler that will receive asynchronous callbacks.
3023      * @param autoConnect Whether to directly connect to the remote device (false) or to
3024      * automatically connect as soon as the remote device becomes available (true).
3025      * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
3026      * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
3027      * BluetoothDevice#TRANSPORT_LE}
3028      * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link
3029      * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link
3030      * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect}
3031      * is set to true.
3032      * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on
3033      * an un-specified background thread.
3034      * @throws NullPointerException if callback is null
3035      */
3036     @RequiresBluetoothConnectPermission
3037     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy, Handler handler)3038     public BluetoothGatt connectGatt(Context context, boolean autoConnect,
3039             BluetoothGattCallback callback, int transport, int phy,
3040             Handler handler) {
3041         return connectGatt(context, autoConnect, callback, transport, false, phy, handler);
3042     }
3043 
3044     /**
3045      * Connect to GATT Server hosted by this device. Caller acts as GATT client.
3046      * The callback is used to deliver results to Caller, such as connection status as well
3047      * as any further GATT client operations.
3048      * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
3049      * GATT client operations.
3050      *
3051      * @param callback GATT callback handler that will receive asynchronous callbacks.
3052      * @param autoConnect Whether to directly connect to the remote device (false) or to
3053      * automatically connect as soon as the remote device becomes available (true).
3054      * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
3055      * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
3056      * BluetoothDevice#TRANSPORT_LE}
3057      * @param opportunistic Whether this GATT client is opportunistic. An opportunistic GATT client
3058      * does not hold a GATT connection. It automatically disconnects when no other GATT connections
3059      * are active for the remote device.
3060      * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link
3061      * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link
3062      * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect}
3063      * is set to true.
3064      * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on
3065      * an un-specified background thread.
3066      * @return A BluetoothGatt instance. You can use BluetoothGatt to conduct GATT client
3067      * operations.
3068      * @hide
3069      */
3070     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
3071     @RequiresBluetoothConnectPermission
3072     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, boolean opportunistic, int phy, Handler handler)3073     public BluetoothGatt connectGatt(Context context, boolean autoConnect,
3074             BluetoothGattCallback callback, int transport,
3075             boolean opportunistic, int phy, Handler handler) {
3076         if (callback == null) {
3077             throw new NullPointerException("callback is null");
3078         }
3079 
3080         // TODO(Bluetooth) check whether platform support BLE
3081         //     Do the check here or in GattServer?
3082         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
3083         IBluetoothManager managerService = adapter.getBluetoothManager();
3084         try {
3085             IBluetoothGatt iGatt = managerService.getBluetoothGatt();
3086             if (iGatt == null) {
3087                 // BLE is not supported
3088                 return null;
3089             }
3090             BluetoothGatt gatt = new BluetoothGatt(
3091                     iGatt, this, transport, opportunistic, phy, mAttributionSource);
3092             gatt.connect(autoConnect, callback, handler);
3093             return gatt;
3094         } catch (RemoteException e) {
3095             Log.e(TAG, "", e);
3096         }
3097         return null;
3098     }
3099 
3100     /**
3101      * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
3102      * be used to start a secure outgoing connection to the remote device with the same dynamic
3103      * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
3104      * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capChannel()} for
3105      * peer-peer Bluetooth applications.
3106      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
3107      * <p>Application using this API is responsible for obtaining PSM value from remote device.
3108      * <p>The remote device will be authenticated and communication on this socket will be
3109      * encrypted.
3110      * <p> Use this socket if an authenticated socket link is possible. Authentication refers
3111      * to the authentication of the link key to prevent person-in-the-middle type of attacks.
3112      *
3113      * @param psm dynamic PSM value from remote device
3114      * @return a CoC #BluetoothSocket ready for an outgoing connection
3115      * @throws IOException on error, for example Bluetooth not available, or insufficient
3116      * permissions
3117      */
3118     @RequiresLegacyBluetoothPermission
3119     @RequiresBluetoothConnectPermission
3120     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
3121     @SuppressLint("AndroidFrameworkRequiresPermission")
createL2capChannel(int psm)3122     public @NonNull BluetoothSocket createL2capChannel(int psm) throws IOException {
3123         if (!isBluetoothEnabled()) {
3124             Log.e(TAG, "createL2capChannel: Bluetooth is not enabled");
3125             throw new IOException();
3126         }
3127         if (DBG) Log.d(TAG, "createL2capChannel: psm=" + psm);
3128         return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm,
3129                 null);
3130     }
3131 
3132     /**
3133      * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
3134      * be used to start a secure outgoing connection to the remote device with the same dynamic
3135      * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
3136      * <p>This is designed to be used with {@link
3137      * BluetoothAdapter#listenUsingInsecureL2capChannel()} for peer-peer Bluetooth applications.
3138      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
3139      * <p>Application using this API is responsible for obtaining PSM value from remote device.
3140      * <p> The communication channel may not have an authenticated link key, i.e. it may be subject
3141      * to person-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and
3142      * authenticated communication channel is possible.
3143      *
3144      * @param psm dynamic PSM value from remote device
3145      * @return a CoC #BluetoothSocket ready for an outgoing connection
3146      * @throws IOException on error, for example Bluetooth not available, or insufficient
3147      * permissions
3148      */
3149     @RequiresLegacyBluetoothPermission
3150     @RequiresBluetoothConnectPermission
3151     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
3152     @SuppressLint("AndroidFrameworkRequiresPermission")
createInsecureL2capChannel(int psm)3153     public @NonNull BluetoothSocket createInsecureL2capChannel(int psm) throws IOException {
3154         if (!isBluetoothEnabled()) {
3155             Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled");
3156             throw new IOException();
3157         }
3158         if (DBG) {
3159             Log.d(TAG, "createInsecureL2capChannel: psm=" + psm);
3160         }
3161         return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
3162                 null);
3163     }
3164 
3165     /**
3166      * Set a keyed metadata of this {@link BluetoothDevice} to a
3167      * {@link String} value.
3168      * Only bonded devices's metadata will be persisted across Bluetooth
3169      * restart.
3170      * Metadata will be removed when the device's bond state is moved to
3171      * {@link #BOND_NONE}.
3172      *
3173      * @param key must be within the list of BluetoothDevice.METADATA_*
3174      * @param value a byte array data to set for key. Must be less than
3175      * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length
3176      * @return true on success, false on error
3177      * @hide
3178     */
3179     @SystemApi
3180     @RequiresPermission(allOf = {
3181             android.Manifest.permission.BLUETOOTH_CONNECT,
3182             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
3183     })
setMetadata(@etadataKey int key, @NonNull byte[] value)3184     public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) {
3185         if (DBG) log("setMetadata()");
3186         final IBluetooth service = getService();
3187         final boolean defaultValue = false;
3188         if (service == null || !isBluetoothEnabled()) {
3189             Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata");
3190             if (DBG) log(Log.getStackTraceString(new Throwable()));
3191         } else if (value.length > METADATA_MAX_LENGTH) {
3192             throw new IllegalArgumentException("value length is " + value.length
3193                     + ", should not over " + METADATA_MAX_LENGTH);
3194         } else {
3195             try {
3196                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
3197                 service.setMetadata(this, key, value, mAttributionSource, recv);
3198                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
3199             } catch (RemoteException | TimeoutException e) {
3200                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
3201             }
3202         }
3203         return defaultValue;
3204     }
3205 
3206     /**
3207      * Get a keyed metadata for this {@link BluetoothDevice} as {@link String}
3208      *
3209      * @param key must be within the list of BluetoothDevice.METADATA_*
3210      * @return Metadata of the key as byte array, null on error or not found
3211      * @hide
3212      */
3213     @SystemApi
3214     @Nullable
3215     @RequiresPermission(allOf = {
3216             android.Manifest.permission.BLUETOOTH_CONNECT,
3217             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
3218     })
getMetadata(@etadataKey int key)3219     public byte[] getMetadata(@MetadataKey int key) {
3220         if (DBG) log("getMetadata()");
3221         final IBluetooth service = getService();
3222         final byte[] defaultValue = null;
3223         if (service == null || !isBluetoothEnabled()) {
3224             Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata");
3225             if (DBG) log(Log.getStackTraceString(new Throwable()));
3226         } else {
3227             try {
3228                 final SynchronousResultReceiver<byte[]> recv = SynchronousResultReceiver.get();
3229                 service.getMetadata(this, key, mAttributionSource, recv);
3230                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
3231             } catch (RemoteException | TimeoutException e) {
3232                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
3233             }
3234         }
3235         return defaultValue;
3236     }
3237 
3238     /**
3239      * Get the maxinum metadata key ID.
3240      *
3241      * @return the last supported metadata key
3242      * @hide
3243      */
getMaxMetadataKey()3244     public static @MetadataKey int getMaxMetadataKey() {
3245         return METADATA_MAX_KEY;
3246     }
3247 
3248     /** @hide */
3249     @Retention(RetentionPolicy.SOURCE)
3250     @IntDef(
3251         prefix = { "FEATURE_" },
3252         value = {
3253             /** Remote support status of audio policy feature is unknown/unconfigured **/
3254             BluetoothStatusCodes.FEATURE_NOT_CONFIGURED,
3255             /** Remote support status of audio policy feature is supported **/
3256             BluetoothStatusCodes.FEATURE_SUPPORTED,
3257             /** Remote support status of audio policy feature is not supported **/
3258             BluetoothStatusCodes.FEATURE_NOT_SUPPORTED,
3259         }
3260     )
3261 
3262     public @interface AudioPolicyRemoteSupport {}
3263 
3264     /** @hide */
3265     @Retention(RetentionPolicy.SOURCE)
3266     @IntDef(value = {
3267             BluetoothStatusCodes.SUCCESS,
3268             BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
3269             BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
3270             BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED,
3271             BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
3272             BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED,
3273     })
3274     public @interface AudioPolicyReturnValues{}
3275 
3276     /**
3277      * Returns whether the audio policy feature is supported by
3278      * both the local and the remote device.
3279      * This is configured during initiating the connection between the devices through
3280      * one of the transport protocols (e.g. HFP Vendor specific protocol). So if the API
3281      * is invoked before this initial configuration is completed, it returns
3282      * {@link BluetoothStatusCodes#FEATURE_NOT_CONFIGURED} to indicate the remote
3283      * device has not yet relayed this information. After the internal configuration,
3284      * the support status will be set to either
3285      * {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} or
3286      * {@link BluetoothStatusCodes#FEATURE_SUPPORTED}.
3287      * The rest of the APIs related to this feature in both {@link BluetoothDevice}
3288      * and {@link BluetoothSinkAudioPolicy} should be invoked  only after getting a
3289      * {@link BluetoothStatusCodes#FEATURE_SUPPORTED} response from this API.
3290      * <p>Note that this API is intended to be used by a client device to send these requests
3291      * to the server represented by this BluetoothDevice object.
3292      *
3293      * @return if call audio policy feature is supported by both local and remote
3294      * device or not
3295      *
3296      * @hide
3297      */
3298     @RequiresBluetoothConnectPermission
3299     @RequiresPermission(allOf = {
3300             android.Manifest.permission.BLUETOOTH_CONNECT,
3301             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
3302     })
isRequestAudioPolicyAsSinkSupported()3303     public @AudioPolicyRemoteSupport int isRequestAudioPolicyAsSinkSupported() {
3304         if (DBG) log("isRequestAudioPolicyAsSinkSupported()");
3305         final IBluetooth service = getService();
3306         final int defaultValue = BluetoothStatusCodes.FEATURE_NOT_CONFIGURED;
3307         if (service == null || !isBluetoothEnabled()) {
3308             Log.e(TAG, "BT not enabled. Cannot retrieve audio policy support status.");
3309             if (DBG) log(Log.getStackTraceString(new Throwable()));
3310         } else {
3311             try {
3312                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
3313                 service.isRequestAudioPolicyAsSinkSupported(this, mAttributionSource, recv);
3314                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
3315             } catch (TimeoutException e) {
3316                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
3317             } catch (RemoteException e) {
3318                 Log.e(TAG, "", e);
3319                 throw e.rethrowFromSystemServer();
3320             }
3321         }
3322         return defaultValue;
3323     }
3324 
3325     /**
3326      * Sets call audio preferences and sends them to the remote device.
3327      * <p>Note that the caller should check if the feature is supported by
3328      * invoking {@link BluetoothDevice#isRequestAudioPolicyAsSinkSupported} first.
3329      * <p>This API will throw an exception if the feature is not supported but still
3330      * invoked.
3331      * <p>Note that this API is intended to be used by a client device to send these requests
3332      * to the server represented by this BluetoothDevice object.
3333      *
3334      * @param policies call audio policy preferences
3335      * @return whether audio policy was requested successfully or not
3336      *
3337      * @hide
3338      */
3339     @RequiresBluetoothConnectPermission
3340     @RequiresPermission(allOf = {
3341             android.Manifest.permission.BLUETOOTH_CONNECT,
3342             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
3343     })
requestAudioPolicyAsSink( @onNull BluetoothSinkAudioPolicy policies)3344     public @AudioPolicyReturnValues int requestAudioPolicyAsSink(
3345             @NonNull BluetoothSinkAudioPolicy policies) {
3346         if (DBG) log("requestAudioPolicyAsSink");
3347         final IBluetooth service = getService();
3348         final int defaultValue = BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
3349         if (service == null || !isBluetoothEnabled()) {
3350             Log.e(TAG, "Bluetooth is not enabled. Cannot set Audio Policy.");
3351             if (DBG) log(Log.getStackTraceString(new Throwable()));
3352         } else {
3353             try {
3354                 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
3355                 service.requestAudioPolicyAsSink(this, policies, mAttributionSource, recv);
3356                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
3357             } catch (RemoteException | TimeoutException e) {
3358                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
3359             }
3360         }
3361         return defaultValue;
3362     }
3363 
3364     /**
3365      * Gets the call audio preferences for the remote device.
3366      * <p>Note that the caller should check if the feature is supported by
3367      * invoking {@link BluetoothDevice#isRequestAudioPolicyAsSinkSupported} first.
3368      * <p>This API will throw an exception if the feature is not supported but still
3369      * invoked.
3370      * <p>This API will return null if
3371      * 1. The bleutooth service is not started yet,
3372      * 2. It is invoked for a device which is not bonded, or
3373      * 3. The used transport, for example, HFP Client profile is not enabled or
3374      * connected yet.
3375      * <p>Note that this API is intended to be used by a client device to send these requests
3376      * to the server represented by this BluetoothDevice object.
3377      *
3378      * @return call audio policy as {@link BluetoothSinkAudioPolicy} object
3379      *
3380      * @hide
3381      */
3382     @RequiresBluetoothConnectPermission
3383     @RequiresPermission(allOf = {
3384             android.Manifest.permission.BLUETOOTH_CONNECT,
3385             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
3386     })
getRequestedAudioPolicyAsSink()3387     public @Nullable BluetoothSinkAudioPolicy getRequestedAudioPolicyAsSink() {
3388         if (DBG) log("getRequestedAudioPolicyAsSink");
3389         final IBluetooth service = getService();
3390         if (service == null || !isBluetoothEnabled()) {
3391             Log.e(TAG, "Bluetooth is not enabled. Cannot get Audio Policy.");
3392             if (DBG) log(Log.getStackTraceString(new Throwable()));
3393         } else {
3394             try {
3395                 final SynchronousResultReceiver<BluetoothSinkAudioPolicy>
3396                         recv = SynchronousResultReceiver.get();
3397                 service.getRequestedAudioPolicyAsSink(this, mAttributionSource, recv);
3398                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
3399             } catch (RemoteException | TimeoutException e) {
3400                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
3401             }
3402         }
3403         return null;
3404     }
3405 
3406     /**
3407      * Enable or disable audio low latency for this {@link BluetoothDevice}.
3408      *
3409      * @param allowed true if low latency is allowed, false if low latency is disallowed.
3410      * @return true if the value is successfully set,
3411      * false if there is a error when setting the value.
3412      * @hide
3413      */
3414     @SystemApi
3415     @RequiresBluetoothConnectPermission
3416     @RequiresPermission(allOf = {
3417             android.Manifest.permission.BLUETOOTH_CONNECT,
3418             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
3419     })
setLowLatencyAudioAllowed(boolean allowed)3420     public boolean setLowLatencyAudioAllowed(boolean allowed) {
3421         if (DBG) log("setLowLatencyAudioAllowed(" + allowed + ")");
3422         final IBluetooth service = getService();
3423         final boolean defaultValue = false;
3424         if (service == null || !isBluetoothEnabled()) {
3425             Log.e(TAG, "Bluetooth is not enabled. Cannot allow low latency");
3426             if (DBG) log(Log.getStackTraceString(new Throwable()));
3427         } else {
3428             try {
3429                 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
3430                 service.allowLowLatencyAudio(allowed, this, recv);
3431                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
3432             } catch (RemoteException | TimeoutException e) {
3433                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
3434             }
3435         }
3436         return defaultValue;
3437     }
3438 
log(String msg)3439     private static void log(String msg) {
3440         Log.d(TAG, msg);
3441     }
3442 }
3443