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