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 
30 import com.android.bluetooth.BluetoothStatsLog;
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.bluetooth.statemachine.State;
40 import com.android.bluetooth.statemachine.StateMachine;
41 import com.android.internal.annotations.VisibleForTesting;
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 synchronized 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 synchronized 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 synchronized 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, and also set
218                         // result=false to avoid adding the device to mDevices.
219                         mDevices.remove(dev);
220                         result = false;
221                         if (mDevices.isEmpty()) {
222                             transitionTo(mStableState);
223                         }
224                         if (newState == BluetoothDevice.BOND_NONE) {
225                             mAdapterService.setPhonebookAccessPermission(dev,
226                                     BluetoothDevice.ACCESS_UNKNOWN);
227                             mAdapterService.setMessageAccessPermission(dev,
228                                     BluetoothDevice.ACCESS_UNKNOWN);
229                             mAdapterService.setSimAccessPermission(dev,
230                                     BluetoothDevice.ACCESS_UNKNOWN);
231                             // Set the profile Priorities to undefined
232                             clearProfilePriority(dev);
233                         }
234                     } else if (!mDevices.contains(dev)) {
235                         result = true;
236                     }
237                     break;
238                 case SSP_REQUEST:
239                     int passkey = msg.arg1;
240                     int variant = msg.arg2;
241                     sendDisplayPinIntent(devProp.getAddress(), passkey, variant);
242                     break;
243                 case PIN_REQUEST:
244                     BluetoothClass btClass = dev.getBluetoothClass();
245                     int btDeviceClass = btClass.getDeviceClass();
246                     if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD || btDeviceClass
247                             == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) {
248                         // Its a keyboard. Follow the HID spec recommendation of creating the
249                         // passkey and displaying it to the user. If the keyboard doesn't follow
250                         // the spec recommendation, check if the keyboard has a fixed PIN zero
251                         // and pair.
252                         //TODO: Maintain list of devices that have fixed pin
253                         // Generate a variable 6-digit PIN in range of 100000-999999
254                         // This is not truly random but good enough.
255                         int pin = 100000 + (int) Math.floor((Math.random() * (999999 - 100000)));
256                         sendDisplayPinIntent(devProp.getAddress(), pin,
257                                 BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN);
258                         break;
259                     }
260 
261                     if (msg.arg2 == 1) { // Minimum 16 digit pin required here
262                         sendDisplayPinIntent(devProp.getAddress(), 0,
263                                 BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS);
264                     } else {
265                         // In PIN_REQUEST, there is no passkey to display.So do not send the
266                         // EXTRA_PAIRING_KEY type in the intent( 0 in SendDisplayPinIntent() )
267                         sendDisplayPinIntent(devProp.getAddress(), 0,
268                                 BluetoothDevice.PAIRING_VARIANT_PIN);
269                     }
270 
271                     break;
272                 default:
273                     Log.e(TAG, "Received unhandled event:" + msg.what);
274                     return false;
275             }
276             if (result) {
277                 mDevices.add(dev);
278             }
279 
280             return true;
281         }
282     }
283 
cancelBond(BluetoothDevice dev)284     private boolean cancelBond(BluetoothDevice dev) {
285         if (dev.getBondState() == BluetoothDevice.BOND_BONDING) {
286             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
287             if (!mAdapterService.cancelBondNative(addr)) {
288                 Log.e(TAG, "Unexpected error while cancelling bond:");
289             } else {
290                 return true;
291             }
292         }
293         return false;
294     }
295 
removeBond(BluetoothDevice dev, boolean transition)296     private boolean removeBond(BluetoothDevice dev, boolean transition) {
297         DeviceProperties devProp = mRemoteDevices.getDeviceProperties(dev);
298         if (devProp != null && devProp.getBondState() == BluetoothDevice.BOND_BONDED) {
299             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
300             if (!mAdapterService.removeBondNative(addr)) {
301                 Log.e(TAG, "Unexpected error while removing bond:");
302             } else {
303                 if (transition) {
304                     transitionTo(mPendingCommandState);
305                 }
306                 return true;
307             }
308 
309         }
310         return false;
311     }
312 
createBond(BluetoothDevice dev, int transport, OobData oobData, boolean transition)313     private boolean createBond(BluetoothDevice dev, int transport, OobData oobData,
314             boolean transition) {
315         if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
316             infoLog("Bond address is:" + dev);
317             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
318             boolean result;
319             if (oobData != null) {
320                 result = mAdapterService.createBondOutOfBandNative(addr, transport, oobData);
321             } else {
322                 result = mAdapterService.createBondNative(addr, transport);
323             }
324             BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
325                     mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
326                     BluetoothDevice.BOND_BONDING,
327                     oobData == null ? BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN
328                             : BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_OOB_DATA_PROVIDED,
329                     BluetoothProtoEnums.UNBOND_REASON_UNKNOWN);
330             if (!result) {
331                 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
332                         mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
333                         BluetoothDevice.BOND_NONE, BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN,
334                         BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS);
335                 // Using UNBOND_REASON_REMOVED for legacy reason
336                 sendIntent(dev, BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED);
337                 return false;
338             } else if (transition) {
339                 transitionTo(mPendingCommandState);
340             }
341             return true;
342         }
343         return false;
344     }
345 
sendDisplayPinIntent(byte[] address, int pin, int variant)346     private void sendDisplayPinIntent(byte[] address, int pin, int variant) {
347         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
348         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevices.getDevice(address));
349         if (pin != 0) {
350             intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pin);
351         }
352         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, variant);
353         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
354         // Workaround for Android Auto until pre-accepting pairing requests is added.
355         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
356         mAdapterService.sendOrderedBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM);
357     }
358 
359     @VisibleForTesting
sendIntent(BluetoothDevice device, int newState, int reason)360     void sendIntent(BluetoothDevice device, int newState, int reason) {
361         DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device);
362         int oldState = BluetoothDevice.BOND_NONE;
363         if (newState != BluetoothDevice.BOND_NONE
364                 && newState != BluetoothDevice.BOND_BONDING
365                 && newState != BluetoothDevice.BOND_BONDED) {
366             infoLog("Invalid bond state " + newState);
367             return;
368         }
369         if (devProp != null) {
370             oldState = devProp.getBondState();
371         }
372         if (mPendingBondedDevices.contains(device)) {
373             mPendingBondedDevices.remove(device);
374             if (oldState == BluetoothDevice.BOND_BONDED) {
375                 if (newState == BluetoothDevice.BOND_BONDING) {
376                     mAdapterProperties.onBondStateChanged(device, newState);
377                 }
378                 oldState = BluetoothDevice.BOND_BONDING;
379             } else {
380                 // Should not enter here.
381                 throw new IllegalArgumentException("Invalid old state " + oldState);
382             }
383         }
384         if (oldState == newState) {
385             return;
386         }
387         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
388                 mAdapterService.obfuscateAddress(device), 0, device.getType(),
389                 newState, BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN, reason,
390                 mAdapterService.getMetricId(device));
391         BluetoothClass deviceClass = device.getBluetoothClass();
392         int classOfDevice = deviceClass == null ? 0 : deviceClass.getClassOfDevice();
393         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_CLASS_OF_DEVICE_REPORTED,
394                 mAdapterService.obfuscateAddress(device), classOfDevice,
395                 mAdapterService.getMetricId(device));
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         BluetoothStatsLog.write(BluetoothStatsLog.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         BluetoothStatsLog.write(BluetoothStatsLog.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: " + bdDevice.getAddress()
523                 + " name:" + bdDevice.getName() + " cod:" + new BluetoothClass(cod));
524 
525         Message msg = obtainMessage(PIN_REQUEST);
526         msg.obj = bdDevice;
527         msg.arg2 = min16Digits ? 1 : 0; // Use arg2 to pass the min16Digit boolean
528 
529         sendMessage(msg);
530     }
531 
clearProfilePriority(BluetoothDevice device)532     private void clearProfilePriority(BluetoothDevice device) {
533         HidHostService hidService = HidHostService.getHidHostService();
534         A2dpService a2dpService = A2dpService.getA2dpService();
535         HeadsetService headsetService = HeadsetService.getHeadsetService();
536         HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService();
537         A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
538         PbapClientService pbapClientService = PbapClientService.getPbapClientService();
539 
540         if (hidService != null) {
541             hidService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
542         }
543         if (a2dpService != null) {
544             a2dpService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
545         }
546         if (headsetService != null) {
547             headsetService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
548         }
549         if (headsetClientService != null) {
550             headsetClientService.setConnectionPolicy(device,
551                     BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
552         }
553         if (a2dpSinkService != null) {
554             a2dpSinkService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
555         }
556         if (pbapClientService != null) {
557             pbapClientService.setConnectionPolicy(device,
558                     BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
559         }
560     }
561 
state2str(int state)562     private String state2str(int state) {
563         if (state == BluetoothDevice.BOND_NONE) {
564             return "BOND_NONE";
565         } else if (state == BluetoothDevice.BOND_BONDING) {
566             return "BOND_BONDING";
567         } else if (state == BluetoothDevice.BOND_BONDED) {
568             return "BOND_BONDED";
569         } else return "UNKNOWN(" + state + ")";
570     }
571 
infoLog(String msg)572     private void infoLog(String msg) {
573         Log.i(TAG, msg);
574     }
575 
errorLog(String msg)576     private void errorLog(String msg) {
577         Log.e(TAG, msg);
578     }
579 
warnLog(String msg)580     private void warnLog(String msg) {
581         Log.w(TAG, msg);
582     }
583 
getUnbondReasonFromHALCode(int reason)584     private int getUnbondReasonFromHALCode(int reason) {
585         if (reason == AbstractionLayer.BT_STATUS_SUCCESS) {
586             return BluetoothDevice.BOND_SUCCESS;
587         } else if (reason == AbstractionLayer.BT_STATUS_RMT_DEV_DOWN) {
588             return BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN;
589         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_FAILURE) {
590             return BluetoothDevice.UNBOND_REASON_AUTH_FAILED;
591         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_REJECTED) {
592             return BluetoothDevice.UNBOND_REASON_AUTH_REJECTED;
593         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_TIMEOUT) {
594             return BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT;
595         }
596 
597         /* default */
598         return BluetoothDevice.UNBOND_REASON_REMOVED;
599     }
600 }
601