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 */
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             BluetoothStatsLog.write(BluetoothStatsLog.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                 BluetoothStatsLog.write(BluetoothStatsLog.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, AdapterService.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         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
390                 mAdapterService.obfuscateAddress(device), 0, device.getType(),
391                 newState, BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN, reason,
392                 mAdapterService.getMetricId(device));
393         BluetoothClass deviceClass = device.getBluetoothClass();
394         int classOfDevice = deviceClass == null ? 0 : deviceClass.getClassOfDevice();
395         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_CLASS_OF_DEVICE_REPORTED,
396                 mAdapterService.obfuscateAddress(device), classOfDevice,
397                 mAdapterService.getMetricId(device));
398         mAdapterProperties.onBondStateChanged(device, newState);
399 
400         if (devProp != null && ((devProp.getDeviceType() == BluetoothDevice.DEVICE_TYPE_CLASSIC
401                 || devProp.getDeviceType() == BluetoothDevice.DEVICE_TYPE_DUAL)
402                 && newState == BluetoothDevice.BOND_BONDED && devProp.getUuids() == null)) {
403             infoLog(device + " is bonded, wait for SDP complete to broadcast bonded intent");
404             if (!mPendingBondedDevices.contains(device)) {
405                 mPendingBondedDevices.add(device);
406             }
407             if (oldState == BluetoothDevice.BOND_NONE) {
408                 // Broadcast NONE->BONDING for NONE->BONDED case.
409                 newState = BluetoothDevice.BOND_BONDING;
410             } else {
411                 return;
412             }
413         }
414 
415         Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
416         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
417         intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState);
418         intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
419         if (newState == BluetoothDevice.BOND_NONE) {
420             intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
421         }
422         mAdapterService.sendBroadcastAsUser(intent, UserHandle.ALL, AdapterService.BLUETOOTH_PERM);
423         infoLog("Bond State Change Intent:" + device + " " + state2str(oldState) + " => "
424                 + state2str(newState));
425     }
426 
bondStateChangeCallback(int status, byte[] address, int newState)427     void bondStateChangeCallback(int status, byte[] address, int newState) {
428         BluetoothDevice device = mRemoteDevices.getDevice(address);
429 
430         if (device == null) {
431             infoLog("No record of the device:" + device);
432             // This device will be added as part of the BONDING_STATE_CHANGE intent processing
433             // in sendIntent above
434             device = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
435         }
436 
437         infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device + " newState: "
438                 + newState);
439 
440         Message msg = obtainMessage(BONDING_STATE_CHANGE);
441         msg.obj = device;
442 
443         if (newState == BOND_STATE_BONDED) {
444             msg.arg1 = BluetoothDevice.BOND_BONDED;
445         } else if (newState == BOND_STATE_BONDING) {
446             msg.arg1 = BluetoothDevice.BOND_BONDING;
447         } else {
448             msg.arg1 = BluetoothDevice.BOND_NONE;
449         }
450         msg.arg2 = status;
451 
452         sendMessage(msg);
453     }
454 
sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant, int passkey)455     void sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant, int passkey) {
456         //TODO(BT): Get wakelock and update name and cod
457         BluetoothDevice bdDevice = mRemoteDevices.getDevice(address);
458         if (bdDevice == null) {
459             mRemoteDevices.addDeviceProperties(address);
460         }
461         infoLog("sspRequestCallback: " + address + " name: " + name + " cod: " + cod
462                 + " pairingVariant " + pairingVariant + " passkey: " + passkey);
463         int variant;
464         boolean displayPasskey = false;
465         switch (pairingVariant) {
466 
467             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION:
468                 variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION;
469                 displayPasskey = true;
470                 break;
471 
472             case AbstractionLayer.BT_SSP_VARIANT_CONSENT:
473                 variant = BluetoothDevice.PAIRING_VARIANT_CONSENT;
474                 break;
475 
476             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY:
477                 variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY;
478                 break;
479 
480             case AbstractionLayer.BT_SSP_VARIANT_PASSKEY_NOTIFICATION:
481                 variant = BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY;
482                 displayPasskey = true;
483                 break;
484 
485             default:
486                 errorLog("SSP Pairing variant not present");
487                 return;
488         }
489         BluetoothDevice device = mRemoteDevices.getDevice(address);
490         if (device == null) {
491             warnLog("Device is not known for:" + Utils.getAddressStringFromByte(address));
492             mRemoteDevices.addDeviceProperties(address);
493             device = Objects.requireNonNull(mRemoteDevices.getDevice(address));
494         }
495 
496         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
497                 mAdapterService.obfuscateAddress(device), 0, device.getType(),
498                 BluetoothDevice.BOND_BONDING,
499                 BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_SSP_REQUESTED, 0);
500 
501         Message msg = obtainMessage(SSP_REQUEST);
502         msg.obj = device;
503         if (displayPasskey) {
504             msg.arg1 = passkey;
505         }
506         msg.arg2 = variant;
507         sendMessage(msg);
508     }
509 
pinRequestCallback(byte[] address, byte[] name, int cod, boolean min16Digits)510     void pinRequestCallback(byte[] address, byte[] name, int cod, boolean min16Digits) {
511         //TODO(BT): Get wakelock and update name and cod
512 
513         BluetoothDevice bdDevice = mRemoteDevices.getDevice(address);
514         if (bdDevice == null) {
515             mRemoteDevices.addDeviceProperties(address);
516             bdDevice = Objects.requireNonNull(mRemoteDevices.getDevice(address));
517         }
518 
519         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
520                 mAdapterService.obfuscateAddress(bdDevice), 0, bdDevice.getType(),
521                 BluetoothDevice.BOND_BONDING,
522                 BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_PIN_REQUESTED, 0);
523 
524         infoLog("pinRequestCallback: " + bdDevice.getAddress()
525                 + " name:" + bdDevice.getName() + " cod:" + new BluetoothClass(cod));
526 
527         Message msg = obtainMessage(PIN_REQUEST);
528         msg.obj = bdDevice;
529         msg.arg2 = min16Digits ? 1 : 0; // Use arg2 to pass the min16Digit boolean
530 
531         sendMessage(msg);
532     }
533 
clearProfilePriority(BluetoothDevice device)534     private void clearProfilePriority(BluetoothDevice device) {
535         HidHostService hidService = HidHostService.getHidHostService();
536         A2dpService a2dpService = A2dpService.getA2dpService();
537         HeadsetService headsetService = HeadsetService.getHeadsetService();
538         HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService();
539         A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
540         PbapClientService pbapClientService = PbapClientService.getPbapClientService();
541 
542         if (hidService != null) {
543             hidService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
544         }
545         if (a2dpService != null) {
546             a2dpService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
547         }
548         if (headsetService != null) {
549             headsetService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
550         }
551         if (headsetClientService != null) {
552             headsetClientService.setConnectionPolicy(device,
553                     BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
554         }
555         if (a2dpSinkService != null) {
556             a2dpSinkService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
557         }
558         if (pbapClientService != null) {
559             pbapClientService.setConnectionPolicy(device,
560                     BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
561         }
562     }
563 
state2str(int state)564     private String state2str(int state) {
565         if (state == BluetoothDevice.BOND_NONE) {
566             return "BOND_NONE";
567         } else if (state == BluetoothDevice.BOND_BONDING) {
568             return "BOND_BONDING";
569         } else if (state == BluetoothDevice.BOND_BONDED) {
570             return "BOND_BONDED";
571         } else return "UNKNOWN(" + state + ")";
572     }
573 
infoLog(String msg)574     private void infoLog(String msg) {
575         Log.i(TAG, msg);
576     }
577 
errorLog(String msg)578     private void errorLog(String msg) {
579         Log.e(TAG, msg);
580     }
581 
warnLog(String msg)582     private void warnLog(String msg) {
583         Log.w(TAG, msg);
584     }
585 
getUnbondReasonFromHALCode(int reason)586     private int getUnbondReasonFromHALCode(int reason) {
587         if (reason == AbstractionLayer.BT_STATUS_SUCCESS) {
588             return BluetoothDevice.BOND_SUCCESS;
589         } else if (reason == AbstractionLayer.BT_STATUS_RMT_DEV_DOWN) {
590             return BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN;
591         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_FAILURE) {
592             return BluetoothDevice.UNBOND_REASON_AUTH_FAILED;
593         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_REJECTED) {
594             return BluetoothDevice.UNBOND_REASON_AUTH_REJECTED;
595         } else if (reason == AbstractionLayer.BT_STATUS_AUTH_TIMEOUT) {
596             return BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT;
597         }
598 
599         /* default */
600         return BluetoothDevice.UNBOND_REASON_REMOVED;
601     }
602 }
603