Home | History | Annotate | Download | only in btservice
      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.content.Context;
     21 import android.content.Intent;
     22 import android.os.Message;
     23 import android.os.UserManager;
     24 import android.util.Log;
     25 
     26 import com.android.internal.util.State;
     27 import 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 
     39 final 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