• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 com.android.bluetooth.btservice;
18 
19 import static android.Manifest.permission.BLUETOOTH_CONNECT;
20 
21 import android.annotation.RequiresPermission;
22 import android.annotation.Nullable;
23 import android.app.Activity;
24 import android.app.ActivityThread;
25 import android.bluetooth.BluetoothAdapter;
26 import android.bluetooth.BluetoothClass;
27 import android.bluetooth.BluetoothDevice;
28 import android.bluetooth.BluetoothProfile;
29 import android.bluetooth.BluetoothProtoEnums;
30 import android.bluetooth.OobData;
31 import android.content.Attributable;
32 import android.content.BroadcastReceiver;
33 import android.content.Intent;
34 import android.os.Build;
35 import android.os.Bundle;
36 import android.os.Handler;
37 import android.os.Message;
38 import android.os.UserHandle;
39 import android.util.Log;
40 
41 import com.android.bluetooth.BluetoothStatsLog;
42 import com.android.bluetooth.Utils;
43 import com.android.bluetooth.a2dp.A2dpService;
44 import com.android.bluetooth.a2dpsink.A2dpSinkService;
45 import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
46 import com.android.bluetooth.hfp.HeadsetService;
47 import com.android.bluetooth.hfpclient.HeadsetClientService;
48 import com.android.bluetooth.hid.HidHostService;
49 import com.android.bluetooth.pbapclient.PbapClientService;
50 import com.android.bluetooth.statemachine.State;
51 import com.android.bluetooth.statemachine.StateMachine;
52 import com.android.internal.annotations.VisibleForTesting;
53 
54 import java.util.ArrayList;
55 import java.util.HashSet;
56 import java.util.Objects;
57 import java.util.Set;
58 
59 /**
60  * This state machine handles Bluetooth Adapter State.
61  * States:
62  *      {@link StableState} :  No device is in bonding / unbonding state.
63  *      {@link PendingCommandState} : Some device is in bonding / unbonding state.
64  * TODO(BT) This class can be removed and this logic moved to the stack.
65  */
66 
67 final class BondStateMachine extends StateMachine {
68     private static final boolean DBG = false;
69     private static final String TAG = "BluetoothBondStateMachine";
70 
71     static final int CREATE_BOND = 1;
72     static final int CANCEL_BOND = 2;
73     static final int REMOVE_BOND = 3;
74     static final int BONDING_STATE_CHANGE = 4;
75     static final int SSP_REQUEST = 5;
76     static final int PIN_REQUEST = 6;
77     static final int UUID_UPDATE = 10;
78     static final int BOND_STATE_NONE = 0;
79     static final int BOND_STATE_BONDING = 1;
80     static final int BOND_STATE_BONDED = 2;
81 
82     private AdapterService mAdapterService;
83     private AdapterProperties mAdapterProperties;
84     private RemoteDevices mRemoteDevices;
85     private BluetoothAdapter mAdapter;
86 
87     private PendingCommandState mPendingCommandState = new PendingCommandState();
88     private StableState mStableState = new StableState();
89 
90     public static final String OOBDATAP192 = "oobdatap192";
91     public static final String OOBDATAP256 = "oobdatap256";
92 
93     @VisibleForTesting Set<BluetoothDevice> mPendingBondedDevices = new HashSet<>();
94 
BondStateMachine(AdapterService service, AdapterProperties prop, RemoteDevices remoteDevices)95     private BondStateMachine(AdapterService service, AdapterProperties prop,
96             RemoteDevices remoteDevices) {
97         super("BondStateMachine:");
98         addState(mStableState);
99         addState(mPendingCommandState);
100         mRemoteDevices = remoteDevices;
101         mAdapterService = service;
102         mAdapterProperties = prop;
103         mAdapter = BluetoothAdapter.getDefaultAdapter();
104         setInitialState(mStableState);
105     }
106 
make(AdapterService service, AdapterProperties prop, RemoteDevices remoteDevices)107     public static BondStateMachine make(AdapterService service, AdapterProperties prop,
108             RemoteDevices remoteDevices) {
109         Log.d(TAG, "make");
110         BondStateMachine bsm = new BondStateMachine(service, prop, remoteDevices);
111         bsm.start();
112         return bsm;
113     }
114 
doQuit()115     public synchronized void doQuit() {
116         quitNow();
117     }
118 
cleanup()119     private void cleanup() {
120         mAdapterService = null;
121         mRemoteDevices = null;
122         mAdapterProperties = null;
123     }
124 
125     @Override
onQuitting()126     protected void onQuitting() {
127         cleanup();
128     }
129 
130     private class StableState extends State {
131         @Override
enter()132         public void enter() {
133             infoLog("StableState(): Entering Off State");
134         }
135 
136         @Override
processMessage(Message msg)137         public synchronized boolean processMessage(Message msg) {
138 
139             BluetoothDevice dev = (BluetoothDevice) msg.obj;
140             Attributable.setAttributionSource(dev,
141                     ActivityThread.currentAttributionSource());
142 
143             switch (msg.what) {
144 
145                 case CREATE_BOND:
146                     OobData p192Data = (msg.getData() != null)
147                             ? msg.getData().getParcelable(OOBDATAP192) : null;
148                     OobData p256Data = (msg.getData() != null)
149                             ? msg.getData().getParcelable(OOBDATAP256) : null;
150                     createBond(dev, msg.arg1, p192Data, p256Data, true);
151                     break;
152                 case REMOVE_BOND:
153                     removeBond(dev, true);
154                     break;
155                 case BONDING_STATE_CHANGE:
156                     int newState = msg.arg1;
157                 /* if incoming pairing, transition to pending state */
158                     if (newState == BluetoothDevice.BOND_BONDING) {
159                         sendIntent(dev, newState, 0);
160                         transitionTo(mPendingCommandState);
161                     } else if (newState == BluetoothDevice.BOND_NONE) {
162                     /* if the link key was deleted by the stack */
163                         sendIntent(dev, newState, 0);
164                     } else {
165                         Log.e(TAG, "In stable state, received invalid newState: "
166                                 + state2str(newState));
167                     }
168                     break;
169                 case UUID_UPDATE:
170                     if (mPendingBondedDevices.contains(dev)) {
171                         sendIntent(dev, BluetoothDevice.BOND_BONDED, 0);
172                     }
173                     break;
174                 case CANCEL_BOND:
175                 default:
176                     Log.e(TAG, "Received unhandled state: " + msg.what);
177                     return false;
178             }
179             return true;
180         }
181     }
182 
183 
184     private class PendingCommandState extends State {
185         private final ArrayList<BluetoothDevice> mDevices = new ArrayList<BluetoothDevice>();
186 
187         @Override
enter()188         public void enter() {
189             infoLog("Entering PendingCommandState State");
190         }
191 
192         @Override
processMessage(Message msg)193         public synchronized boolean processMessage(Message msg) {
194             BluetoothDevice dev = (BluetoothDevice) msg.obj;
195             Attributable.setAttributionSource(dev,
196                     ActivityThread.currentAttributionSource());
197 
198             DeviceProperties devProp = mRemoteDevices.getDeviceProperties(dev);
199             boolean result = false;
200             if (mDevices.contains(dev) && msg.what != CANCEL_BOND
201                     && msg.what != BONDING_STATE_CHANGE && msg.what != SSP_REQUEST
202                     && msg.what != PIN_REQUEST) {
203                 deferMessage(msg);
204                 return true;
205             }
206 
207             switch (msg.what) {
208                 case CREATE_BOND:
209                     OobData p192Data = (msg.getData() != null)
210                             ? msg.getData().getParcelable(OOBDATAP192) : null;
211                     OobData p256Data = (msg.getData() != null)
212                             ? msg.getData().getParcelable(OOBDATAP256) : null;
213                     result = createBond(dev, msg.arg1, p192Data, p256Data, false);
214                     break;
215                 case REMOVE_BOND:
216                     result = removeBond(dev, false);
217                     break;
218                 case CANCEL_BOND:
219                     result = cancelBond(dev);
220                     break;
221                 case BONDING_STATE_CHANGE:
222                     int newState = msg.arg1;
223                     int reason = getUnbondReasonFromHALCode(msg.arg2);
224                     // Bond is explicitly removed if we are in pending command state
225                     if (newState == BluetoothDevice.BOND_NONE
226                             && reason == BluetoothDevice.BOND_SUCCESS) {
227                         reason = BluetoothDevice.UNBOND_REASON_REMOVED;
228                     }
229                     sendIntent(dev, newState, reason);
230                     if (newState != BluetoothDevice.BOND_BONDING) {
231                         // This is either none/bonded, remove and transition, and also set
232                         // result=false to avoid adding the device to mDevices.
233                         mDevices.remove(dev);
234                         result = false;
235                         if (mDevices.isEmpty()) {
236                             transitionTo(mStableState);
237                         }
238                         if (newState == BluetoothDevice.BOND_NONE) {
239                             mAdapterService.setPhonebookAccessPermission(dev,
240                                     BluetoothDevice.ACCESS_UNKNOWN);
241                             mAdapterService.setMessageAccessPermission(dev,
242                                     BluetoothDevice.ACCESS_UNKNOWN);
243                             mAdapterService.setSimAccessPermission(dev,
244                                     BluetoothDevice.ACCESS_UNKNOWN);
245                             // Set the profile Priorities to undefined
246                             clearProfilePriority(dev);
247                         }
248                     } else if (!mDevices.contains(dev)) {
249                         result = true;
250                     }
251                     break;
252                 case SSP_REQUEST:
253                     int passkey = msg.arg1;
254                     int variant = msg.arg2;
255                     sendDisplayPinIntent(devProp.getAddress(), passkey, variant);
256                     break;
257                 case PIN_REQUEST:
258                     BluetoothClass btClass = dev.getBluetoothClass();
259                     int btDeviceClass = btClass.getDeviceClass();
260                     if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD || btDeviceClass
261                             == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) {
262                         // Its a keyboard. Follow the HID spec recommendation of creating the
263                         // passkey and displaying it to the user. If the keyboard doesn't follow
264                         // the spec recommendation, check if the keyboard has a fixed PIN zero
265                         // and pair.
266                         //TODO: Maintain list of devices that have fixed pin
267                         // Generate a variable 6-digit PIN in range of 100000-999999
268                         // This is not truly random but good enough.
269                         int pin = 100000 + (int) Math.floor((Math.random() * (999999 - 100000)));
270                         sendDisplayPinIntent(devProp.getAddress(), pin,
271                                 BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN);
272                         break;
273                     }
274 
275                     if (msg.arg2 == 1) { // Minimum 16 digit pin required here
276                         sendDisplayPinIntent(devProp.getAddress(), 0,
277                                 BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS);
278                     } else {
279                         // In PIN_REQUEST, there is no passkey to display.So do not send the
280                         // EXTRA_PAIRING_KEY type in the intent( 0 in SendDisplayPinIntent() )
281                         sendDisplayPinIntent(devProp.getAddress(), 0,
282                                 BluetoothDevice.PAIRING_VARIANT_PIN);
283                     }
284 
285                     break;
286                 default:
287                     Log.e(TAG, "Received unhandled event:" + msg.what);
288                     return false;
289             }
290             if (result) {
291                 mDevices.add(dev);
292             }
293 
294             return true;
295         }
296     }
297 
cancelBond(BluetoothDevice dev)298     private boolean cancelBond(BluetoothDevice dev) {
299         if (dev.getBondState() == BluetoothDevice.BOND_BONDING) {
300             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
301             if (!mAdapterService.cancelBondNative(addr)) {
302                 Log.e(TAG, "Unexpected error while cancelling bond:");
303             } else {
304                 return true;
305             }
306         }
307         return false;
308     }
309 
removeBond(BluetoothDevice dev, boolean transition)310     private boolean removeBond(BluetoothDevice dev, boolean transition) {
311         DeviceProperties devProp = mRemoteDevices.getDeviceProperties(dev);
312         if (devProp != null && devProp.getBondState() == BluetoothDevice.BOND_BONDED) {
313             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
314             if (!mAdapterService.removeBondNative(addr)) {
315                 Log.e(TAG, "Unexpected error while removing bond:");
316             } else {
317                 if (transition) {
318                     transitionTo(mPendingCommandState);
319                 }
320                 return true;
321             }
322 
323         }
324         return false;
325     }
326 
createBond(BluetoothDevice dev, int transport, OobData remoteP192Data, OobData remoteP256Data, boolean transition)327     private boolean createBond(BluetoothDevice dev, int transport, OobData remoteP192Data,
328             OobData remoteP256Data, boolean transition) {
329         if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
330             infoLog("Bond address is:" + dev);
331             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
332             boolean result;
333             // If we have some data
334             if (remoteP192Data != null || remoteP256Data != null) {
335                 result = mAdapterService.createBondOutOfBandNative(addr, transport,
336                     remoteP192Data, remoteP256Data);
337             } else {
338                 result = mAdapterService.createBondNative(addr, transport);
339             }
340             BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
341                     mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
342                     BluetoothDevice.BOND_BONDING,
343                     remoteP192Data == null && remoteP256Data == null
344                             ? BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN
345                             : BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_OOB_DATA_PROVIDED,
346                     BluetoothProtoEnums.UNBOND_REASON_UNKNOWN);
347 
348             if (!result) {
349                 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
350                         mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
351                         BluetoothDevice.BOND_NONE, BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN,
352                         BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS);
353                 // Using UNBOND_REASON_REMOVED for legacy reason
354                 sendIntent(dev, BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED);
355                 return false;
356             } else if (transition) {
357                 transitionTo(mPendingCommandState);
358             }
359             return true;
360         }
361         return false;
362     }
363 
sendDisplayPinIntent(byte[] address, int pin, int variant)364     private void sendDisplayPinIntent(byte[] address, int pin, int variant) {
365         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
366         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevices.getDevice(address));
367         if (pin != 0) {
368             intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pin);
369         }
370         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, variant);
371         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
372         // Workaround for Android Auto until pre-accepting pairing requests is added.
373         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
374         mAdapterService.sendOrderedBroadcast(intent, BLUETOOTH_CONNECT,
375                 Utils.getTempAllowlistBroadcastOptions(), null/* resultReceiver */,
376                 null/* scheduler */, Activity.RESULT_OK/* initialCode */, null/* initialData */,
377                 null/* initialExtras */);
378     }
379 
380     @VisibleForTesting
sendIntent(BluetoothDevice device, int newState, int reason)381     void sendIntent(BluetoothDevice device, int newState, int reason) {
382         DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device);
383         int oldState = BluetoothDevice.BOND_NONE;
384         if (newState != BluetoothDevice.BOND_NONE
385                 && newState != BluetoothDevice.BOND_BONDING
386                 && newState != BluetoothDevice.BOND_BONDED) {
387             infoLog("Invalid bond state " + newState);
388             return;
389         }
390         if (devProp != null) {
391             oldState = devProp.getBondState();
392         }
393         if (mPendingBondedDevices.contains(device)) {
394             mPendingBondedDevices.remove(device);
395             if (oldState == BluetoothDevice.BOND_BONDED) {
396                 if (newState == BluetoothDevice.BOND_BONDING) {
397                     mAdapterProperties.onBondStateChanged(device, newState);
398                 }
399                 oldState = BluetoothDevice.BOND_BONDING;
400             } else {
401                 // Should not enter here.
402                 throw new IllegalArgumentException("Invalid old state " + oldState);
403             }
404         }
405         if (oldState == newState) {
406             return;
407         }
408         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
409                 mAdapterService.obfuscateAddress(device), 0, device.getType(),
410                 newState, BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN, reason,
411                 mAdapterService.getMetricId(device));
412         BluetoothClass deviceClass = device.getBluetoothClass();
413         int classOfDevice = deviceClass == null ? 0 : deviceClass.getClassOfDevice();
414         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_CLASS_OF_DEVICE_REPORTED,
415                 mAdapterService.obfuscateAddress(device), classOfDevice,
416                 mAdapterService.getMetricId(device));
417         mAdapterProperties.onBondStateChanged(device, newState);
418 
419         if (devProp != null && ((devProp.getDeviceType() == BluetoothDevice.DEVICE_TYPE_CLASSIC
420                 || devProp.getDeviceType() == BluetoothDevice.DEVICE_TYPE_DUAL)
421                 && newState == BluetoothDevice.BOND_BONDED && devProp.getUuids() == null)) {
422             infoLog(device + " is bonded, wait for SDP complete to broadcast bonded intent");
423             if (!mPendingBondedDevices.contains(device)) {
424                 mPendingBondedDevices.add(device);
425             }
426             if (oldState == BluetoothDevice.BOND_NONE) {
427                 // Broadcast NONE->BONDING for NONE->BONDED case.
428                 newState = BluetoothDevice.BOND_BONDING;
429             } else {
430                 return;
431             }
432         }
433 
434         Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
435         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
436         intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState);
437         intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
438         if (newState == BluetoothDevice.BOND_NONE) {
439             intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
440         }
441         mAdapterService.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT,
442                 Utils.getTempAllowlistBroadcastOptions());
443         infoLog("Bond State Change Intent:" + device + " " + state2str(oldState) + " => "
444                 + state2str(newState));
445     }
446 
bondStateChangeCallback(int status, byte[] address, int newState)447     void bondStateChangeCallback(int status, byte[] address, int newState) {
448         BluetoothDevice device = mRemoteDevices.getDevice(address);
449 
450         if (device == null) {
451             infoLog("No record of the device:" + device);
452             // This device will be added as part of the BONDING_STATE_CHANGE intent processing
453             // in sendIntent above
454             device = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
455         }
456 
457         infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device + " newState: "
458                 + newState);
459 
460         Message msg = obtainMessage(BONDING_STATE_CHANGE);
461         msg.obj = device;
462 
463         if (newState == BOND_STATE_BONDED) {
464             msg.arg1 = BluetoothDevice.BOND_BONDED;
465         } else if (newState == BOND_STATE_BONDING) {
466             msg.arg1 = BluetoothDevice.BOND_BONDING;
467         } else {
468             msg.arg1 = BluetoothDevice.BOND_NONE;
469         }
470         msg.arg2 = status;
471 
472         sendMessage(msg);
473     }
474 
sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant, int passkey)475     void sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant, int passkey) {
476         //TODO(BT): Get wakelock and update name and cod
477         BluetoothDevice bdDevice = mRemoteDevices.getDevice(address);
478         if (bdDevice == null) {
479             mRemoteDevices.addDeviceProperties(address);
480         }
481         infoLog("sspRequestCallback: " + address + " name: " + name + " cod: " + cod
482                 + " pairingVariant " + pairingVariant + " passkey: " + (Build.IS_DEBUGGABLE ? passkey : "******"));
483         int variant;
484         boolean displayPasskey = false;
485         switch (pairingVariant) {
486 
487             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION:
488                 variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION;
489                 displayPasskey = true;
490                 break;
491 
492             case AbstractionLayer.BT_SSP_VARIANT_CONSENT:
493                 variant = BluetoothDevice.PAIRING_VARIANT_CONSENT;
494                 break;
495 
496             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY:
497                 variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY;
498                 break;
499 
500             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_NOTIFICATION:
501                 variant = BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY;
502                 displayPasskey = true;
503                 break;
504 
505             default:
506                 errorLog("SSP Pairing variant not present");
507                 return;
508         }
509         BluetoothDevice device = mRemoteDevices.getDevice(address);
510         if (device == null) {
511             warnLog("Device is not known for:" + Utils.getAddressStringFromByte(address));
512             mRemoteDevices.addDeviceProperties(address);
513             device = Objects.requireNonNull(mRemoteDevices.getDevice(address));
514         }
515 
516         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
517                 mAdapterService.obfuscateAddress(device), 0, device.getType(),
518                 BluetoothDevice.BOND_BONDING,
519                 BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_SSP_REQUESTED, 0);
520 
521         Message msg = obtainMessage(SSP_REQUEST);
522         msg.obj = device;
523         if (displayPasskey) {
524             msg.arg1 = passkey;
525         }
526         msg.arg2 = variant;
527         sendMessage(msg);
528     }
529 
pinRequestCallback(byte[] address, byte[] name, int cod, boolean min16Digits)530     void pinRequestCallback(byte[] address, byte[] name, int cod, boolean min16Digits) {
531         //TODO(BT): Get wakelock and update name and cod
532 
533         BluetoothDevice bdDevice = mRemoteDevices.getDevice(address);
534         if (bdDevice == null) {
535             mRemoteDevices.addDeviceProperties(address);
536             bdDevice = Objects.requireNonNull(mRemoteDevices.getDevice(address));
537         }
538 
539         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
540                 mAdapterService.obfuscateAddress(bdDevice), 0, bdDevice.getType(),
541                 BluetoothDevice.BOND_BONDING,
542                 BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_PIN_REQUESTED, 0);
543 
544         infoLog("pinRequestCallback: " + bdDevice.getAddress()
545                 + " name:" + Utils.getName(bdDevice) + " cod:" + new BluetoothClass(cod));
546 
547         Message msg = obtainMessage(PIN_REQUEST);
548         msg.obj = bdDevice;
549         msg.arg2 = min16Digits ? 1 : 0; // Use arg2 to pass the min16Digit boolean
550 
551         sendMessage(msg);
552     }
553 
554     @RequiresPermission(allOf = {
555             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
556             android.Manifest.permission.MODIFY_PHONE_STATE,
557     })
clearProfilePriority(BluetoothDevice device)558     private void clearProfilePriority(BluetoothDevice device) {
559         HidHostService hidService = HidHostService.getHidHostService();
560         A2dpService a2dpService = A2dpService.getA2dpService();
561         HeadsetService headsetService = HeadsetService.getHeadsetService();
562         HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService();
563         A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
564         PbapClientService pbapClientService = PbapClientService.getPbapClientService();
565 
566         if (hidService != null) {
567             hidService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
568         }
569         if (a2dpService != null) {
570             a2dpService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
571         }
572         if (headsetService != null) {
573             headsetService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
574         }
575         if (headsetClientService != null) {
576             headsetClientService.setConnectionPolicy(device,
577                     BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
578         }
579         if (a2dpSinkService != null) {
580             a2dpSinkService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
581         }
582         if (pbapClientService != null) {
583             pbapClientService.setConnectionPolicy(device,
584                     BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
585         }
586     }
587 
state2str(int state)588     private String state2str(int state) {
589         if (state == BluetoothDevice.BOND_NONE) {
590             return "BOND_NONE";
591         } else if (state == BluetoothDevice.BOND_BONDING) {
592             return "BOND_BONDING";
593         } else if (state == BluetoothDevice.BOND_BONDED) {
594             return "BOND_BONDED";
595         } else return "UNKNOWN(" + state + ")";
596     }
597 
infoLog(String msg)598     private void infoLog(String msg) {
599         Log.i(TAG, msg);
600     }
601 
errorLog(String msg)602     private void errorLog(String msg) {
603         Log.e(TAG, msg);
604     }
605 
warnLog(String msg)606     private void warnLog(String msg) {
607         Log.w(TAG, msg);
608     }
609 
getUnbondReasonFromHALCode(int reason)610     private int getUnbondReasonFromHALCode(int reason) {
611         if (reason == AbstractionLayer.BT_STATUS_SUCCESS) {
612             return BluetoothDevice.BOND_SUCCESS;
613         } else if (reason == AbstractionLayer.BT_STATUS_RMT_DEV_DOWN) {
614             return BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN;
615         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_FAILURE) {
616             return BluetoothDevice.UNBOND_REASON_AUTH_FAILED;
617         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_REJECTED) {
618             return BluetoothDevice.UNBOND_REASON_AUTH_REJECTED;
619         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_TIMEOUT) {
620             return BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT;
621         }
622 
623         /* default */
624         return BluetoothDevice.UNBOND_REASON_REMOVED;
625     }
626 }
627