• 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.BluetoothProfile;
21 import android.bluetooth.BluetoothDevice;
22 import com.android.bluetooth.a2dp.A2dpService;
23 import com.android.bluetooth.hid.HidService;
24 import com.android.bluetooth.hfp.HeadsetService;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.os.Message;
28 import android.os.UserHandle;
29 import android.util.Log;
30 
31 import com.android.bluetooth.Utils;
32 import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
33 import com.android.internal.util.State;
34 import com.android.internal.util.StateMachine;
35 
36 import java.util.ArrayList;
37 
38 /**
39  * This state machine handles Bluetooth Adapter State.
40  * States:
41  *      {@link StableState} :  No device is in bonding / unbonding state.
42  *      {@link PendingCommandState} : Some device is in bonding / unbonding state.
43  * TODO(BT) This class can be removed and this logic moved to the stack.
44  */
45 
46 final class BondStateMachine extends StateMachine {
47     private static final boolean DBG = false;
48     private static final String TAG = "BluetoothBondStateMachine";
49 
50     static final int CREATE_BOND = 1;
51     static final int CANCEL_BOND = 2;
52     static final int REMOVE_BOND = 3;
53     static final int BONDING_STATE_CHANGE = 4;
54 
55     static final int BOND_STATE_NONE = 0;
56     static final int BOND_STATE_BONDING = 1;
57     static final int BOND_STATE_BONDED = 2;
58 
59     private AdapterService mAdapterService;
60     private AdapterProperties mAdapterProperties;
61     private RemoteDevices mRemoteDevices;
62     private BluetoothAdapter mAdapter;
63 
64     private PendingCommandState mPendingCommandState = new PendingCommandState();
65     private StableState mStableState = new StableState();
66 
BondStateMachine(AdapterService service, AdapterProperties prop, RemoteDevices remoteDevices)67     private BondStateMachine(AdapterService service,
68             AdapterProperties prop, RemoteDevices remoteDevices) {
69         super("BondStateMachine:");
70         addState(mStableState);
71         addState(mPendingCommandState);
72         mRemoteDevices = remoteDevices;
73         mAdapterService = service;
74         mAdapterProperties = prop;
75         mAdapter = BluetoothAdapter.getDefaultAdapter();
76         setInitialState(mStableState);
77     }
78 
make(AdapterService service, AdapterProperties prop, RemoteDevices remoteDevices)79     public static BondStateMachine make(AdapterService service,
80             AdapterProperties prop, RemoteDevices remoteDevices) {
81         Log.d(TAG, "make");
82         BondStateMachine bsm = new BondStateMachine(service, prop, remoteDevices);
83         bsm.start();
84         return bsm;
85     }
86 
doQuit()87     public void doQuit() {
88         quitNow();
89     }
90 
cleanup()91     public void cleanup() {
92         mAdapterService = null;
93         mRemoteDevices = null;
94         mAdapterProperties = null;
95     }
96 
97     private class StableState extends State {
98         @Override
enter()99         public void enter() {
100             infoLog("StableState(): Entering Off State");
101         }
102 
103         @Override
processMessage(Message msg)104         public boolean processMessage(Message msg) {
105 
106             BluetoothDevice dev = (BluetoothDevice)msg.obj;
107 
108             switch(msg.what) {
109 
110               case CREATE_BOND:
111                   createBond(dev, true);
112                   break;
113               case REMOVE_BOND:
114                   removeBond(dev, true);
115                   break;
116               case BONDING_STATE_CHANGE:
117                 int newState = msg.arg1;
118                 /* if incoming pairing, transition to pending state */
119                 if (newState == BluetoothDevice.BOND_BONDING)
120                 {
121                     sendIntent(dev, newState, 0);
122                     transitionTo(mPendingCommandState);
123                 }
124                 else
125                 {
126                     Log.e(TAG, "In stable state, received invalid newState: " + newState);
127                 }
128                 break;
129 
130               case CANCEL_BOND:
131               default:
132                    Log.e(TAG, "Received unhandled state: " + msg.what);
133                    return false;
134             }
135             return true;
136         }
137     }
138 
139 
140     private class PendingCommandState extends State {
141         private final ArrayList<BluetoothDevice> mDevices =
142             new ArrayList<BluetoothDevice>();
143 
144         @Override
enter()145         public void enter() {
146             infoLog("Entering PendingCommandState State");
147             BluetoothDevice dev = (BluetoothDevice)getCurrentMessage().obj;
148         }
149 
150         @Override
processMessage(Message msg)151         public boolean processMessage(Message msg) {
152 
153             BluetoothDevice dev = (BluetoothDevice)msg.obj;
154             boolean result = false;
155             if (mDevices.contains(dev) &&
156                     msg.what != CANCEL_BOND && msg.what != BONDING_STATE_CHANGE) {
157                 deferMessage(msg);
158                 return true;
159             }
160 
161             switch (msg.what) {
162                 case CREATE_BOND:
163                     result = createBond(dev, false);
164                     break;
165                 case REMOVE_BOND:
166                     result = removeBond(dev, false);
167                     break;
168                 case CANCEL_BOND:
169                     result = cancelBond(dev);
170                     break;
171                 case BONDING_STATE_CHANGE:
172                     int newState = msg.arg1;
173                     int reason = getUnbondReasonFromHALCode(msg.arg2);
174                     sendIntent(dev, newState, reason);
175                     if(newState != BluetoothDevice.BOND_BONDING )
176                     {
177                         /* this is either none/bonded, remove and transition */
178                         result = !mDevices.remove(dev);
179                         if (mDevices.isEmpty()) {
180                             // Whenever mDevices is empty, then we need to
181                             // set result=false. Else, we will end up adding
182                             // the device to the list again. This prevents us
183                             // from pairing with a device that we just unpaired
184                             result = false;
185                             transitionTo(mStableState);
186                         }
187                         if (newState == BluetoothDevice.BOND_NONE)
188                         {
189                             // Set the profile Priorities to undefined
190                             clearProfilePriorty(dev);
191                         }
192                         else if (newState == BluetoothDevice.BOND_BONDED)
193                         {
194                            // Restore the profile priorty settings
195                            setProfilePriorty(dev);
196                         }
197                     }
198                     else if(!mDevices.contains(dev))
199                         result=true;
200                     break;
201                 default:
202                     Log.e(TAG, "Received unhandled event:" + msg.what);
203                     return false;
204             }
205             if (result) mDevices.add(dev);
206 
207             return true;
208         }
209     }
210 
cancelBond(BluetoothDevice dev)211     private boolean cancelBond(BluetoothDevice dev) {
212         if (dev.getBondState() == BluetoothDevice.BOND_BONDING) {
213             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
214             if (!mAdapterService.cancelBondNative(addr)) {
215                Log.e(TAG, "Unexpected error while cancelling bond:");
216             } else {
217                 return true;
218             }
219         }
220         return false;
221     }
222 
removeBond(BluetoothDevice dev, boolean transition)223     private boolean removeBond(BluetoothDevice dev, boolean transition) {
224         if (dev.getBondState() == BluetoothDevice.BOND_BONDED) {
225             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
226             if (!mAdapterService.removeBondNative(addr)) {
227                Log.e(TAG, "Unexpected error while removing bond:");
228             } else {
229                 if (transition) transitionTo(mPendingCommandState);
230                 return true;
231             }
232 
233         }
234         return false;
235     }
236 
createBond(BluetoothDevice dev, boolean transition)237     private boolean createBond(BluetoothDevice dev, boolean transition) {
238         if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
239             infoLog("Bond address is:" + dev);
240             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
241             if (!mAdapterService.createBondNative(addr)) {
242                 sendIntent(dev, BluetoothDevice.BOND_NONE,
243                            BluetoothDevice.UNBOND_REASON_REMOVED);
244                 return false;
245             } else if (transition) {
246                 transitionTo(mPendingCommandState);
247             }
248             return true;
249         }
250         return false;
251     }
252 
sendIntent(BluetoothDevice device, int newState, int reason)253     private void sendIntent(BluetoothDevice device, int newState, int reason) {
254         DeviceProperties devProp = mRemoteDevices.getDeviceProperties(device);
255         int oldState = BluetoothDevice.BOND_NONE;
256         if (devProp != null) {
257             oldState = devProp.getBondState();
258         }
259         if (oldState == newState) return;
260         mAdapterProperties.onBondStateChanged(device, newState);
261 
262         Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
263         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
264         intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState);
265         intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
266         if (newState == BluetoothDevice.BOND_NONE)
267             intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
268         mAdapterService.sendBroadcastAsUser(intent, UserHandle.ALL,
269                 AdapterService.BLUETOOTH_PERM);
270         infoLog("Bond State Change Intent:" + device + " OldState: " + oldState
271                 + " NewState: " + newState);
272     }
273 
bondStateChangeCallback(int status, byte[] address, int newState)274     void bondStateChangeCallback(int status, byte[] address, int newState) {
275         BluetoothDevice device = mRemoteDevices.getDevice(address);
276 
277         if (device == null) {
278             infoLog("No record of the device:" + device);
279             // This device will be added as part of the BONDING_STATE_CHANGE intent processing
280             // in sendIntent above
281             device = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
282         }
283 
284         infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device
285                 + " newState: " + newState);
286 
287         Message msg = obtainMessage(BONDING_STATE_CHANGE);
288         msg.obj = device;
289 
290         if (newState == BOND_STATE_BONDED)
291             msg.arg1 = BluetoothDevice.BOND_BONDED;
292         else if (newState == BOND_STATE_BONDING)
293             msg.arg1 = BluetoothDevice.BOND_BONDING;
294         else
295             msg.arg1 = BluetoothDevice.BOND_NONE;
296         msg.arg2 = status;
297 
298         sendMessage(msg);
299     }
300 
setProfilePriorty(BluetoothDevice device)301     private void setProfilePriorty (BluetoothDevice device){
302         HidService hidService = HidService.getHidService();
303         A2dpService a2dpService = A2dpService.getA2dpService();
304         HeadsetService headsetService = HeadsetService.getHeadsetService();
305 
306         if ((hidService != null) &&
307             (hidService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){
308             hidService.setPriority(device,BluetoothProfile.PRIORITY_ON);
309         }
310 
311         if ((a2dpService != null) &&
312             (a2dpService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){
313             a2dpService.setPriority(device,BluetoothProfile.PRIORITY_ON);
314         }
315 
316         if ((headsetService != null) &&
317             (headsetService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)){
318             headsetService.setPriority(device,BluetoothProfile.PRIORITY_ON);
319         }
320     }
321 
clearProfilePriorty(BluetoothDevice device)322     private void clearProfilePriorty (BluetoothDevice device){
323         HidService hidService = HidService.getHidService();
324         A2dpService a2dpService = A2dpService.getA2dpService();
325         HeadsetService headsetService = HeadsetService.getHeadsetService();
326 
327         if (hidService != null)
328             hidService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED);
329         if(a2dpService != null)
330             a2dpService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED);
331         if(headsetService != null)
332             headsetService.setPriority(device,BluetoothProfile.PRIORITY_UNDEFINED);
333     }
334 
infoLog(String msg)335     private void infoLog(String msg) {
336         Log.i(TAG, msg);
337     }
338 
errorLog(String msg)339     private void errorLog(String msg) {
340         Log.e(TAG, msg);
341     }
342 
getUnbondReasonFromHALCode(int reason)343     private int getUnbondReasonFromHALCode (int reason) {
344         if (reason == AbstractionLayer.BT_STATUS_SUCCESS)
345             return BluetoothDevice.BOND_SUCCESS;
346         else if (reason == AbstractionLayer.BT_STATUS_RMT_DEV_DOWN)
347             return BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN;
348         else if (reason == AbstractionLayer.BT_STATUS_AUTH_FAILURE)
349             return BluetoothDevice.UNBOND_REASON_AUTH_FAILED;
350 
351         /* default */
352         return BluetoothDevice.UNBOND_REASON_REMOVED;
353     }
354 }
355