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