AdapterState.java revision 3558402aae35c6b01c505be012d6736b0c758802
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.autoConnect();
236        }
237
238        @Override
239        public boolean processMessage(Message msg) {
240            AdapterProperties adapterProperties = mAdapterProperties;
241            if (adapterProperties == null) {
242                errorLog("Received message in OnState after cleanup: " + msg.what);
243                return false;
244            }
245
246            debugLog("Current state: ON, message: " + msg.what);
247
248            switch(msg.what) {
249               case BLE_TURN_OFF:
250                   notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_OFF);
251                   mPendingCommandState.setTurningOff(true);
252                   transitionTo(mPendingCommandState);
253
254                   // Invoke onBluetoothDisable which shall trigger a
255                   // setScanMode to SCAN_MODE_NONE
256                   Message m = obtainMessage(SET_SCAN_MODE_TIMEOUT);
257                   sendMessageDelayed(m, PROPERTY_OP_DELAY);
258                   adapterProperties.onBluetoothDisable();
259                   break;
260
261               case USER_TURN_ON:
262                   break;
263
264               default:
265                   return false;
266            }
267            return true;
268        }
269    }
270
271    private class PendingCommandState extends State {
272        private boolean mIsTurningOn;
273        private boolean mIsTurningOff;
274        private boolean mIsBleTurningOn;
275        private boolean mIsBleTurningOff;
276
277        public void enter() {
278            infoLog("Entering PendingCommandState");
279        }
280
281        public void setTurningOn(boolean isTurningOn) {
282            mIsTurningOn = isTurningOn;
283        }
284
285        public boolean isTurningOn() {
286            return mIsTurningOn;
287        }
288
289        public void setTurningOff(boolean isTurningOff) {
290            mIsTurningOff = isTurningOff;
291        }
292
293        public boolean isTurningOff() {
294            return mIsTurningOff;
295        }
296
297        public void setBleTurningOn(boolean isBleTurningOn) {
298            mIsBleTurningOn = isBleTurningOn;
299        }
300
301        public boolean isBleTurningOn() {
302            return mIsBleTurningOn;
303        }
304
305        public void setBleTurningOff(boolean isBleTurningOff) {
306            mIsBleTurningOff = isBleTurningOff;
307        }
308
309        public boolean isBleTurningOff() {
310            return mIsBleTurningOff;
311        }
312
313        @Override
314        public boolean processMessage(Message msg) {
315
316            boolean isTurningOn= isTurningOn();
317            boolean isTurningOff = isTurningOff();
318            boolean isBleTurningOn = isBleTurningOn();
319            boolean isBleTurningOff = isBleTurningOff();
320
321            AdapterService adapterService = mAdapterService;
322            AdapterProperties adapterProperties = mAdapterProperties;
323            if ((adapterService == null) || (adapterProperties == null)) {
324                errorLog("Received message in PendingCommandState after cleanup: " + msg.what);
325                return false;
326            }
327
328            debugLog("Current state: PENDING_COMMAND, message: " + msg.what);
329
330            switch (msg.what) {
331                case USER_TURN_ON:
332                    if (isBleTurningOff || isTurningOff) { //TODO:do we need to send it after ble turn off also??
333                        infoLog("Deferring USER_TURN_ON request...");
334                        deferMessage(msg);
335                    }
336                    break;
337
338                case USER_TURN_OFF:
339                    if (isTurningOn || isBleTurningOn) {
340                        infoLog("Deferring USER_TURN_OFF request...");
341                        deferMessage(msg);
342                    }
343                    break;
344
345                case BLE_TURN_ON:
346                    if (isTurningOff || isBleTurningOff) {
347                        infoLog("Deferring BLE_TURN_ON request...");
348                        deferMessage(msg);
349                    }
350                    break;
351
352                case BLE_TURN_OFF:
353                    if (isTurningOn || isBleTurningOn) {
354                        infoLog("Deferring BLE_TURN_OFF request...");
355                        deferMessage(msg);
356                    }
357                    break;
358
359                case BLE_STARTED:
360                    //Remove start timeout
361                    removeMessages(BLE_START_TIMEOUT);
362
363                    //Enable
364                    boolean isGuest = UserManager.get(mAdapterService).isGuestUser();
365                    if (!adapterService.enableNative(isGuest)) {
366                        errorLog("Error while turning Bluetooth on");
367                        notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
368                        transitionTo(mOffState);
369                    } else {
370                        sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
371                    }
372                    break;
373
374                case BREDR_STARTED:
375                    //Remove start timeout
376                    removeMessages(BREDR_START_TIMEOUT);
377                    adapterProperties.onBluetoothReady();
378                    mPendingCommandState.setTurningOn(false);
379                    transitionTo(mOnState);
380                    notifyAdapterStateChange(BluetoothAdapter.STATE_ON);
381                    break;
382
383                case ENABLED_READY:
384                    removeMessages(ENABLE_TIMEOUT);
385                    mPendingCommandState.setBleTurningOn(false);
386                    transitionTo(mBleOnState);
387                    notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON);
388                    break;
389
390                case SET_SCAN_MODE_TIMEOUT:
391                     warningLog("Timeout while setting scan mode. Continuing with disable...");
392                     //Fall through
393                case BEGIN_DISABLE:
394                    removeMessages(SET_SCAN_MODE_TIMEOUT);
395                    sendMessageDelayed(BREDR_STOP_TIMEOUT, BREDR_STOP_TIMEOUT_DELAY);
396                    adapterService.stopProfileServices();
397                    break;
398
399                case DISABLED:
400                    if (isTurningOn) {
401                        removeMessages(ENABLE_TIMEOUT);
402                        errorLog("Error enabling Bluetooth - hardware init failed?");
403                        mPendingCommandState.setTurningOn(false);
404                        transitionTo(mOffState);
405                        adapterService.stopProfileServices();
406                        notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
407                        break;
408                    }
409                    removeMessages(DISABLE_TIMEOUT);
410                    sendMessageDelayed(BLE_STOP_TIMEOUT, BLE_STOP_TIMEOUT_DELAY);
411                    if (adapterService.stopGattProfileService()) {
412                        debugLog("Stopping Gatt profile services that were post enabled");
413                        break;
414                    }
415                    //Fall through if no services or services already stopped
416                case BLE_STOPPED:
417                    removeMessages(BLE_STOP_TIMEOUT);
418                    setBleTurningOff(false);
419                    transitionTo(mOffState);
420                    notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
421                    break;
422
423                case BREDR_STOPPED:
424                    removeMessages(BREDR_STOP_TIMEOUT);
425                    setTurningOff(false);
426                    transitionTo(mBleOnState);
427                    notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON);
428                    break;
429
430                case BLE_START_TIMEOUT:
431                    errorLog("Error enabling Bluetooth (BLE start timeout)");
432                    mPendingCommandState.setBleTurningOn(false);
433                    transitionTo(mOffState);
434                    notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
435                    break;
436
437                case BREDR_START_TIMEOUT:
438                    errorLog("Error enabling Bluetooth (start timeout)");
439                    mPendingCommandState.setTurningOn(false);
440                    transitionTo(mBleOnState);
441                    notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON);
442                    break;
443
444                case ENABLE_TIMEOUT:
445                    errorLog("Error enabling Bluetooth (enable timeout)");
446                    mPendingCommandState.setBleTurningOn(false);
447                    transitionTo(mOffState);
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    }
483
484    private void notifyAdapterStateChange(int newState) {
485        AdapterService adapterService = mAdapterService;
486        AdapterProperties adapterProperties = mAdapterProperties;
487        if ((adapterService == null) || (adapterProperties == null)) {
488            errorLog("notifyAdapterStateChange after cleanup:" + newState);
489            return;
490        }
491
492        int oldState = adapterProperties.getState();
493        adapterProperties.setState(newState);
494        infoLog("Bluetooth adapter state changed: " + oldState + "-> " + newState);
495        adapterService.updateAdapterState(oldState, newState);
496    }
497
498    void stateChangeCallback(int status) {
499        if (status == AbstractionLayer.BT_STATE_OFF) {
500            sendMessage(DISABLED);
501
502        } else if (status == AbstractionLayer.BT_STATE_ON) {
503            // We should have got the property change for adapter and remote devices.
504            sendMessage(ENABLED_READY);
505
506        } else {
507            errorLog("Incorrect status in stateChangeCallback");
508        }
509    }
510
511    private void infoLog(String msg) {
512        if (DBG) Log.i(TAG, msg);
513    }
514
515    private void debugLog(String msg) {
516        if (DBG) Log.d(TAG, msg);
517    }
518
519    private void warningLog(String msg) {
520        if (DBG) Log.d(TAG, msg);
521    }
522
523    private void verboseLog(String msg) {
524        if (VDBG) Log.v(TAG, msg);
525    }
526
527    private void errorLog(String msg) {
528        Log.e(TAG, msg);
529    }
530
531}
532