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