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