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