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.content.Context;
21 import android.content.Intent;
22 import android.os.Message;
23 import android.os.UserManager;
24 import android.util.Log;
25 
26 import com.android.internal.util.State;
27 import com.android.internal.util.StateMachine;
28 
29 /**
30  * This state machine handles Bluetooth Adapter State.
31  * States:
32  *      {@link OnState} : Bluetooth is on at this state
33  *      {@link OffState}: Bluetooth is off at this state. This is the initial
34  *      state.
35  *      {@link PendingCommandState} : An enable / disable operation is pending.
36  * TODO(BT): Add per process on state.
37  */
38 
39 final class AdapterState extends StateMachine {
40     private static final boolean DBG = true;
41     private static final boolean VDBG = true;
42     private static final String TAG = "BluetoothAdapterState";
43 
44     static final int BLE_TURN_ON = 0;
45     static final int USER_TURN_ON = 1;
46     static final int BREDR_STARTED=2;
47     static final int ENABLED_READY = 3;
48     static final int BLE_STARTED=4;
49 
50     static final int USER_TURN_OFF = 20;
51     static final int BEGIN_DISABLE = 21;
52     static final int ALL_DEVICES_DISCONNECTED = 22;
53     static final int BLE_TURN_OFF = 23;
54 
55     static final int DISABLED = 24;
56     static final int BLE_STOPPED=25;
57     static final int BREDR_STOPPED = 26;
58 
59     static final int BREDR_START_TIMEOUT = 100;
60     static final int ENABLE_TIMEOUT = 101;
61     static final int DISABLE_TIMEOUT = 103;
62     static final int BLE_STOP_TIMEOUT = 104;
63     static final int SET_SCAN_MODE_TIMEOUT = 105;
64     static final int BLE_START_TIMEOUT = 106;
65     static final int BREDR_STOP_TIMEOUT = 107;
66 
67     static final int USER_TURN_OFF_DELAY_MS=500;
68 
69     //TODO: tune me
70     private static final int ENABLE_TIMEOUT_DELAY = 12000;
71     private static final int DISABLE_TIMEOUT_DELAY = 8000;
72     private static final int BREDR_START_TIMEOUT_DELAY = 4000;
73     //BLE_START_TIMEOUT can happen quickly as it just a start gattservice
74     private static final int BLE_START_TIMEOUT_DELAY = 2000; //To start GattService
75     private static final int BLE_STOP_TIMEOUT_DELAY = 2000;
76     //BREDR_STOP_TIMEOUT can < STOP_TIMEOUT
77     private static final int BREDR_STOP_TIMEOUT_DELAY = 4000;
78     private static final int PROPERTY_OP_DELAY =2000;
79     private AdapterService mAdapterService;
80     private AdapterProperties mAdapterProperties;
81     private PendingCommandState mPendingCommandState = new PendingCommandState();
82     private OnState mOnState = new OnState();
83     private OffState mOffState = new OffState();
84     private BleOnState mBleOnState = new BleOnState();
85 
isTurningOn()86     public boolean isTurningOn() {
87         boolean isTurningOn=  mPendingCommandState.isTurningOn();
88         verboseLog("isTurningOn()=" + isTurningOn);
89         return isTurningOn;
90     }
91 
isBleTurningOn()92     public boolean isBleTurningOn() {
93         boolean isBleTurningOn=  mPendingCommandState.isBleTurningOn();
94         verboseLog("isBleTurningOn()=" + isBleTurningOn);
95         return isBleTurningOn;
96     }
97 
isBleTurningOff()98     public boolean isBleTurningOff() {
99         boolean isBleTurningOff =  mPendingCommandState.isBleTurningOff();
100         verboseLog("isBleTurningOff()=" + isBleTurningOff);
101         return isBleTurningOff;
102     }
103 
isTurningOff()104     public boolean isTurningOff() {
105         boolean isTurningOff= mPendingCommandState.isTurningOff();
106         verboseLog("isTurningOff()=" + isTurningOff);
107         return isTurningOff;
108     }
109 
AdapterState(AdapterService service, AdapterProperties adapterProperties)110     private AdapterState(AdapterService service, AdapterProperties adapterProperties) {
111         super("BluetoothAdapterState:");
112         addState(mOnState);
113         addState(mBleOnState);
114         addState(mOffState);
115         addState(mPendingCommandState);
116         mAdapterService = service;
117         mAdapterProperties = adapterProperties;
118         setInitialState(mOffState);
119     }
120 
make(AdapterService service, AdapterProperties adapterProperties)121     public static AdapterState make(AdapterService service, AdapterProperties adapterProperties) {
122         Log.d(TAG, "make() - Creating AdapterState");
123         AdapterState as = new AdapterState(service, adapterProperties);
124         as.start();
125         return as;
126     }
127 
doQuit()128     public void doQuit() {
129         quitNow();
130     }
131 
cleanup()132     public void cleanup() {
133         if(mAdapterProperties != null)
134             mAdapterProperties = null;
135         if(mAdapterService != null)
136             mAdapterService = null;
137     }
138 
139     private class OffState extends State {
140         @Override
enter()141         public void enter() {
142             infoLog("Entering OffState");
143         }
144 
145         @Override
processMessage(Message msg)146         public boolean processMessage(Message msg) {
147             AdapterService adapterService = mAdapterService;
148             if (adapterService == null) {
149                 errorLog("Received message in OffState after cleanup: " + msg.what);
150                 return false;
151             }
152 
153             debugLog("Current state: OFF, message: " + msg.what);
154 
155             switch(msg.what) {
156                case BLE_TURN_ON:
157                    notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_TURNING_ON);
158                    mPendingCommandState.setBleTurningOn(true);
159                    transitionTo(mPendingCommandState);
160                    sendMessageDelayed(BLE_START_TIMEOUT, BLE_START_TIMEOUT_DELAY);
161                    adapterService.BleOnProcessStart();
162                    break;
163 
164                case USER_TURN_OFF:
165                    //TODO: Handle case of service started and stopped without enable
166                    break;
167 
168                default:
169                    return false;
170             }
171             return true;
172         }
173     }
174 
175     private class BleOnState extends State {
176         @Override
enter()177         public void enter() {
178             infoLog("Entering BleOnState");
179         }
180 
181         @Override
processMessage(Message msg)182         public boolean processMessage(Message msg) {
183 
184             AdapterService adapterService = mAdapterService;
185             AdapterProperties adapterProperties = mAdapterProperties;
186             if ((adapterService == null) || (adapterProperties == null)) {
187                 errorLog("Received message in BleOnState after cleanup: " + msg.what);
188                 return false;
189             }
190 
191             debugLog("Current state: BLE ON, message: " + msg.what);
192 
193             switch(msg.what) {
194                case USER_TURN_ON:
195                    notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_ON);
196                    mPendingCommandState.setTurningOn(true);
197                    transitionTo(mPendingCommandState);
198                    sendMessageDelayed(BREDR_START_TIMEOUT, BREDR_START_TIMEOUT_DELAY);
199                    adapterService.startCoreServices();
200                    break;
201 
202                case USER_TURN_OFF:
203                    notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_TURNING_OFF);
204                    mPendingCommandState.setBleTurningOff(true);
205                    adapterProperties.onBleDisable();
206                    transitionTo(mPendingCommandState);
207                    sendMessageDelayed(DISABLE_TIMEOUT, DISABLE_TIMEOUT_DELAY);
208                    boolean ret = adapterService.disableNative();
209                    if (!ret) {
210                         removeMessages(DISABLE_TIMEOUT);
211                         errorLog("Error while calling disableNative");
212                         //FIXME: what about post enable services
213                         mPendingCommandState.setBleTurningOff(false);
214                         notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON);
215                    }
216                    break;
217 
218                default:
219                    return false;
220             }
221             return true;
222         }
223     }
224 
225     private class OnState extends State {
226         @Override
enter()227         public void enter() {
228             infoLog("Entering OnState");
229 
230             AdapterService adapterService = mAdapterService;
231             if (adapterService == null) {
232                 errorLog("Entered OnState after cleanup");
233                 return;
234             }
235             adapterService.updateUuids();
236             adapterService.autoConnect();
237         }
238 
239         @Override
processMessage(Message msg)240         public boolean processMessage(Message msg) {
241             AdapterProperties adapterProperties = mAdapterProperties;
242             if (adapterProperties == null) {
243                 errorLog("Received message in OnState after cleanup: " + msg.what);
244                 return false;
245             }
246 
247             debugLog("Current state: ON, message: " + msg.what);
248 
249             switch(msg.what) {
250                case BLE_TURN_OFF:
251                    notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_OFF);
252                    mPendingCommandState.setTurningOff(true);
253                    transitionTo(mPendingCommandState);
254 
255                    // Invoke onBluetoothDisable which shall trigger a
256                    // setScanMode to SCAN_MODE_NONE
257                    Message m = obtainMessage(SET_SCAN_MODE_TIMEOUT);
258                    sendMessageDelayed(m, PROPERTY_OP_DELAY);
259                    adapterProperties.onBluetoothDisable();
260                    break;
261 
262                case USER_TURN_ON:
263                    break;
264 
265                default:
266                    return false;
267             }
268             return true;
269         }
270     }
271 
272     private class PendingCommandState extends State {
273         private boolean mIsTurningOn;
274         private boolean mIsTurningOff;
275         private boolean mIsBleTurningOn;
276         private boolean mIsBleTurningOff;
277 
enter()278         public void enter() {
279             infoLog("Entering PendingCommandState");
280         }
281 
setTurningOn(boolean isTurningOn)282         public void setTurningOn(boolean isTurningOn) {
283             mIsTurningOn = isTurningOn;
284         }
285 
isTurningOn()286         public boolean isTurningOn() {
287             return mIsTurningOn;
288         }
289 
setTurningOff(boolean isTurningOff)290         public void setTurningOff(boolean isTurningOff) {
291             mIsTurningOff = isTurningOff;
292         }
293 
isTurningOff()294         public boolean isTurningOff() {
295             return mIsTurningOff;
296         }
297 
setBleTurningOn(boolean isBleTurningOn)298         public void setBleTurningOn(boolean isBleTurningOn) {
299             mIsBleTurningOn = isBleTurningOn;
300         }
301 
isBleTurningOn()302         public boolean isBleTurningOn() {
303             return mIsBleTurningOn;
304         }
305 
setBleTurningOff(boolean isBleTurningOff)306         public void setBleTurningOff(boolean isBleTurningOff) {
307             mIsBleTurningOff = isBleTurningOff;
308         }
309 
isBleTurningOff()310         public boolean isBleTurningOff() {
311             return mIsBleTurningOff;
312         }
313 
314         @Override
processMessage(Message msg)315         public boolean processMessage(Message msg) {
316 
317             boolean isTurningOn= isTurningOn();
318             boolean isTurningOff = isTurningOff();
319             boolean isBleTurningOn = isBleTurningOn();
320             boolean isBleTurningOff = isBleTurningOff();
321 
322             AdapterService adapterService = mAdapterService;
323             AdapterProperties adapterProperties = mAdapterProperties;
324             if ((adapterService == null) || (adapterProperties == null)) {
325                 errorLog("Received message in PendingCommandState after cleanup: " + msg.what);
326                 return false;
327             }
328 
329             debugLog("Current state: PENDING_COMMAND, message: " + msg.what);
330 
331             switch (msg.what) {
332                 case USER_TURN_ON:
333                     if (isBleTurningOff || isTurningOff) { //TODO:do we need to send it after ble turn off also??
334                         infoLog("Deferring USER_TURN_ON request...");
335                         deferMessage(msg);
336                     }
337                     break;
338 
339                 case USER_TURN_OFF:
340                     if (isTurningOn || isBleTurningOn) {
341                         infoLog("Deferring USER_TURN_OFF request...");
342                         deferMessage(msg);
343                     }
344                     break;
345 
346                 case BLE_TURN_ON:
347                     if (isTurningOff || isBleTurningOff) {
348                         infoLog("Deferring BLE_TURN_ON request...");
349                         deferMessage(msg);
350                     }
351                     break;
352 
353                 case BLE_TURN_OFF:
354                     if (isTurningOn || isBleTurningOn) {
355                         infoLog("Deferring BLE_TURN_OFF request...");
356                         deferMessage(msg);
357                     }
358                     break;
359 
360                 case BLE_STARTED:
361                     //Remove start timeout
362                     removeMessages(BLE_START_TIMEOUT);
363 
364                     //Enable
365                     boolean isGuest = UserManager.get(mAdapterService).isGuestUser();
366                     if (!adapterService.enableNative(isGuest)) {
367                         errorLog("Error while turning Bluetooth on");
368                         notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
369                         transitionTo(mOffState);
370                     } else {
371                         sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
372                     }
373                     break;
374 
375                 case BREDR_STARTED:
376                     //Remove start timeout
377                     removeMessages(BREDR_START_TIMEOUT);
378                     adapterProperties.onBluetoothReady();
379                     mPendingCommandState.setTurningOn(false);
380                     transitionTo(mOnState);
381                     notifyAdapterStateChange(BluetoothAdapter.STATE_ON);
382                     break;
383 
384                 case ENABLED_READY:
385                     removeMessages(ENABLE_TIMEOUT);
386                     mPendingCommandState.setBleTurningOn(false);
387                     transitionTo(mBleOnState);
388                     notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON);
389                     break;
390 
391                 case SET_SCAN_MODE_TIMEOUT:
392                      warningLog("Timeout while setting scan mode. Continuing with disable...");
393                      //Fall through
394                 case BEGIN_DISABLE:
395                     removeMessages(SET_SCAN_MODE_TIMEOUT);
396                     sendMessageDelayed(BREDR_STOP_TIMEOUT, BREDR_STOP_TIMEOUT_DELAY);
397                     adapterService.stopProfileServices();
398                     break;
399 
400                 case DISABLED:
401                     if (isTurningOn) {
402                         removeMessages(ENABLE_TIMEOUT);
403                         errorLog("Error enabling Bluetooth - hardware init failed?");
404                         mPendingCommandState.setTurningOn(false);
405                         transitionTo(mOffState);
406                         adapterService.stopProfileServices();
407                         notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
408                         break;
409                     }
410                     removeMessages(DISABLE_TIMEOUT);
411                     sendMessageDelayed(BLE_STOP_TIMEOUT, BLE_STOP_TIMEOUT_DELAY);
412                     if (adapterService.stopGattProfileService()) {
413                         debugLog("Stopping Gatt profile services that were post enabled");
414                         break;
415                     }
416                     //Fall through if no services or services already stopped
417                 case BLE_STOPPED:
418                     removeMessages(BLE_STOP_TIMEOUT);
419                     setBleTurningOff(false);
420                     transitionTo(mOffState);
421                     notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
422                     break;
423 
424                 case BREDR_STOPPED:
425                     removeMessages(BREDR_STOP_TIMEOUT);
426                     setTurningOff(false);
427                     transitionTo(mBleOnState);
428                     notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON);
429                     break;
430 
431                 case BLE_START_TIMEOUT:
432                     errorLog("Error enabling Bluetooth (BLE start timeout)");
433                     mPendingCommandState.setBleTurningOn(false);
434                     transitionTo(mOffState);
435                     notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
436                     break;
437 
438                 case BREDR_START_TIMEOUT:
439                     errorLog("Error enabling Bluetooth (start timeout)");
440                     mPendingCommandState.setTurningOn(false);
441                     transitionTo(mBleOnState);
442                     notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON);
443                     break;
444 
445                 case ENABLE_TIMEOUT:
446                     errorLog("Error enabling Bluetooth (enable timeout)");
447                     mPendingCommandState.setBleTurningOn(false);
448                     transitionTo(mOffState);
449                     notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
450                     break;
451 
452                 case BREDR_STOP_TIMEOUT:
453                     errorLog("Error stopping Bluetooth profiles (stop timeout)");
454                     mPendingCommandState.setTurningOff(false);
455                     transitionTo(mBleOnState);
456                     notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON);
457                     break;
458 
459                 case BLE_STOP_TIMEOUT:
460                     errorLog("Error stopping Bluetooth profiles (BLE stop timeout)");
461                     mPendingCommandState.setTurningOff(false);
462                     transitionTo(mOffState);
463                     notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
464                     break;
465 
466                 case DISABLE_TIMEOUT:
467                     errorLog("Error disabling Bluetooth (disable timeout)");
468                     if (isTurningOn)
469                         mPendingCommandState.setTurningOn(false);
470                     adapterService.stopProfileServices();
471                     adapterService.stopGattProfileService();
472                     mPendingCommandState.setTurningOff(false);
473                     setBleTurningOff(false);
474                     transitionTo(mOffState);
475                     notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
476                     break;
477 
478                 default:
479                     return false;
480             }
481             return true;
482         }
483     }
484 
notifyAdapterStateChange(int newState)485     private void notifyAdapterStateChange(int newState) {
486         AdapterService adapterService = mAdapterService;
487         AdapterProperties adapterProperties = mAdapterProperties;
488         if ((adapterService == null) || (adapterProperties == null)) {
489             errorLog("notifyAdapterStateChange after cleanup:" + newState);
490             return;
491         }
492 
493         int oldState = adapterProperties.getState();
494         adapterProperties.setState(newState);
495         infoLog("Bluetooth adapter state changed: " + oldState + "-> " + newState);
496         adapterService.updateAdapterState(oldState, newState);
497     }
498 
stateChangeCallback(int status)499     void stateChangeCallback(int status) {
500         if (status == AbstractionLayer.BT_STATE_OFF) {
501             sendMessage(DISABLED);
502 
503         } else if (status == AbstractionLayer.BT_STATE_ON) {
504             // We should have got the property change for adapter and remote devices.
505             sendMessage(ENABLED_READY);
506 
507         } else {
508             errorLog("Incorrect status in stateChangeCallback");
509         }
510     }
511 
infoLog(String msg)512     private void infoLog(String msg) {
513         if (DBG) Log.i(TAG, msg);
514     }
515 
debugLog(String msg)516     private void debugLog(String msg) {
517         if (DBG) Log.d(TAG, msg);
518     }
519 
warningLog(String msg)520     private void warningLog(String msg) {
521         if (DBG) Log.d(TAG, msg);
522     }
523 
verboseLog(String msg)524     private void verboseLog(String msg) {
525         if (VDBG) Log.v(TAG, msg);
526     }
527 
errorLog(String msg)528     private void errorLog(String msg) {
529         Log.e(TAG, msg);
530     }
531 
532 }
533