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