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