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