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
17package com.android.bluetooth.btservice;
18
19import android.bluetooth.BluetoothAdapter;
20import android.content.Context;
21import android.content.Intent;
22import android.os.Message;
23import android.os.UserManager;
24import android.util.Log;
25
26import com.android.internal.util.State;
27import 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
39final 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
86    public boolean isTurningOn() {
87        boolean isTurningOn=  mPendingCommandState.isTurningOn();
88        verboseLog("isTurningOn()=" + isTurningOn);
89        return isTurningOn;
90    }
91
92    public boolean isBleTurningOn() {
93        boolean isBleTurningOn=  mPendingCommandState.isBleTurningOn();
94        verboseLog("isBleTurningOn()=" + isBleTurningOn);
95        return isBleTurningOn;
96    }
97
98    public boolean isBleTurningOff() {
99        boolean isBleTurningOff =  mPendingCommandState.isBleTurningOff();
100        verboseLog("isBleTurningOff()=" + isBleTurningOff);
101        return isBleTurningOff;
102    }
103
104    public boolean isTurningOff() {
105        boolean isTurningOff= mPendingCommandState.isTurningOff();
106        verboseLog("isTurningOff()=" + isTurningOff);
107        return isTurningOff;
108    }
109
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
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
128    public void doQuit() {
129        quitNow();
130    }
131
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
141        public void enter() {
142            infoLog("Entering OffState");
143        }
144
145        @Override
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
177        public void enter() {
178            infoLog("Entering BleOnState");
179        }
180
181        @Override
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
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
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
278        public void enter() {
279            infoLog("Entering PendingCommandState");
280        }
281
282        public void setTurningOn(boolean isTurningOn) {
283            mIsTurningOn = isTurningOn;
284        }
285
286        public boolean isTurningOn() {
287            return mIsTurningOn;
288        }
289
290        public void setTurningOff(boolean isTurningOff) {
291            mIsTurningOff = isTurningOff;
292        }
293
294        public boolean isTurningOff() {
295            return mIsTurningOff;
296        }
297
298        public void setBleTurningOn(boolean isBleTurningOn) {
299            mIsBleTurningOn = isBleTurningOn;
300        }
301
302        public boolean isBleTurningOn() {
303            return mIsBleTurningOn;
304        }
305
306        public void setBleTurningOff(boolean isBleTurningOff) {
307            mIsBleTurningOff = isBleTurningOff;
308        }
309
310        public boolean isBleTurningOff() {
311            return mIsBleTurningOff;
312        }
313
314        @Override
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
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
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
512    private void infoLog(String msg) {
513        if (DBG) Log.i(TAG, msg);
514    }
515
516    private void debugLog(String msg) {
517        if (DBG) Log.d(TAG, msg);
518    }
519
520    private void warningLog(String msg) {
521        if (DBG) Log.d(TAG, msg);
522    }
523
524    private void verboseLog(String msg) {
525        if (VDBG) Log.v(TAG, msg);
526    }
527
528    private void errorLog(String msg) {
529        Log.e(TAG, msg);
530    }
531
532}
533