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.util.Log;
     24 
     25 import com.android.internal.util.State;
     26 import com.android.internal.util.StateMachine;
     27 
     28 /**
     29  * This state machine handles Bluetooth Adapter State.
     30  * States:
     31  *      {@link OnState} : Bluetooth is on at this state
     32  *      {@link OffState}: Bluetooth is off at this state. This is the initial
     33  *      state.
     34  *      {@link PendingCommandState} : An enable / disable operation is pending.
     35  * TODO(BT): Add per process on state.
     36  */
     37 
     38 final class AdapterState extends StateMachine {
     39     private static final boolean DBG = false;
     40     private static final String TAG = "BluetoothAdapterState";
     41 
     42     static final int USER_TURN_ON = 1;
     43     static final int STARTED=2;
     44     static final int ENABLED_READY = 3;
     45 
     46     static final int USER_TURN_OFF = 20;
     47     static final int BEGIN_DISABLE = 21;
     48     static final int ALL_DEVICES_DISCONNECTED = 22;
     49 
     50     static final int DISABLED = 24;
     51     static final int STOPPED=25;
     52 
     53     static final int START_TIMEOUT = 100;
     54     static final int ENABLE_TIMEOUT = 101;
     55     static final int DISABLE_TIMEOUT = 103;
     56     static final int STOP_TIMEOUT = 104;
     57     static final int SET_SCAN_MODE_TIMEOUT = 105;
     58 
     59     static final int USER_TURN_OFF_DELAY_MS=500;
     60 
     61     //TODO: tune me
     62     private static final int ENABLE_TIMEOUT_DELAY = 8000;
     63     private static final int DISABLE_TIMEOUT_DELAY = 8000;
     64     private static final int START_TIMEOUT_DELAY = 5000;
     65     private static final int STOP_TIMEOUT_DELAY = 5000;
     66     private static final int PROPERTY_OP_DELAY =2000;
     67     private AdapterService mAdapterService;
     68     private AdapterProperties mAdapterProperties;
     69     private PendingCommandState mPendingCommandState = new PendingCommandState();
     70     private OnState mOnState = new OnState();
     71     private OffState mOffState = new OffState();
     72 
     73     public boolean isTurningOn() {
     74         boolean isTurningOn=  mPendingCommandState.isTurningOn();
     75         if (DBG) Log.d(TAG,"isTurningOn()=" + isTurningOn);
     76         return isTurningOn;
     77     }
     78 
     79     public boolean isTurningOff() {
     80         boolean isTurningOff= mPendingCommandState.isTurningOff();
     81         if (DBG) Log.d(TAG,"isTurningOff()=" + isTurningOff);
     82         return isTurningOff;
     83     }
     84 
     85     private AdapterState(AdapterService service, AdapterProperties adapterProperties) {
     86         super("BluetoothAdapterState:");
     87         addState(mOnState);
     88         addState(mOffState);
     89         addState(mPendingCommandState);
     90         mAdapterService = service;
     91         mAdapterProperties = adapterProperties;
     92         setInitialState(mOffState);
     93     }
     94 
     95     public static AdapterState make(AdapterService service, AdapterProperties adapterProperties) {
     96         Log.d(TAG, "make");
     97         AdapterState as = new AdapterState(service, adapterProperties);
     98         as.start();
     99         return as;
    100     }
    101 
    102     public void doQuit() {
    103         quitNow();
    104     }
    105 
    106     public void cleanup() {
    107         if(mAdapterProperties != null)
    108             mAdapterProperties = null;
    109         if(mAdapterService != null)
    110             mAdapterService = null;
    111     }
    112 
    113     private class OffState extends State {
    114         @Override
    115         public void enter() {
    116             infoLog("Entering OffState");
    117         }
    118 
    119         @Override
    120         public boolean processMessage(Message msg) {
    121 
    122             switch(msg.what) {
    123                case USER_TURN_ON:
    124                    if (DBG) Log.d(TAG,"CURRENT_STATE=OFF, MESSAGE = USER_TURN_ON");
    125                    notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_ON);
    126                    mPendingCommandState.setTurningOn(true);
    127                    transitionTo(mPendingCommandState);
    128                    sendMessageDelayed(START_TIMEOUT, START_TIMEOUT_DELAY);
    129                    mAdapterService.processStart();
    130                    break;
    131                case USER_TURN_OFF:
    132                    if (DBG) Log.d(TAG,"CURRENT_STATE=OFF, MESSAGE = USER_TURN_OFF");
    133                    //TODO: Handle case of service started and stopped without enable
    134                    break;
    135                default:
    136                    if (DBG) Log.d(TAG,"ERROR: UNEXPECTED MESSAGE: CURRENT_STATE=OFF, MESSAGE = " + msg.what );
    137                    return false;
    138             }
    139             return true;
    140         }
    141     }
    142 
    143     private class OnState extends State {
    144         @Override
    145         public void enter() {
    146             infoLog("Entering On State");
    147             mAdapterService.autoConnect();
    148         }
    149 
    150         @Override
    151         public boolean processMessage(Message msg) {
    152 
    153             switch(msg.what) {
    154                case USER_TURN_OFF:
    155                    if (DBG) Log.d(TAG,"CURRENT_STATE=ON, MESSAGE = USER_TURN_OFF");
    156                    notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_OFF);
    157                    mPendingCommandState.setTurningOff(true);
    158                    transitionTo(mPendingCommandState);
    159 
    160                    // Invoke onBluetoothDisable which shall trigger a
    161                    // setScanMode to SCAN_MODE_NONE
    162                    Message m = obtainMessage(SET_SCAN_MODE_TIMEOUT);
    163                    sendMessageDelayed(m, PROPERTY_OP_DELAY);
    164                    mAdapterProperties.onBluetoothDisable();
    165                    break;
    166 
    167                case USER_TURN_ON:
    168                    if (DBG) Log.d(TAG,"CURRENT_STATE=ON, MESSAGE = USER_TURN_ON");
    169                    Log.i(TAG,"Bluetooth already ON, ignoring USER_TURN_ON");
    170                    break;
    171                default:
    172                    if (DBG) Log.d(TAG,"ERROR: UNEXPECTED MESSAGE: CURRENT_STATE=ON, MESSAGE = " + msg.what );
    173                    return false;
    174             }
    175             return true;
    176         }
    177     }
    178 
    179     private class PendingCommandState extends State {
    180         private boolean mIsTurningOn;
    181         private boolean mIsTurningOff;
    182 
    183         public void enter() {
    184             infoLog("Entering PendingCommandState State: isTurningOn()=" + isTurningOn() + ", isTurningOff()=" + isTurningOff());
    185         }
    186 
    187         public void setTurningOn(boolean isTurningOn) {
    188             mIsTurningOn = isTurningOn;
    189         }
    190 
    191         public boolean isTurningOn() {
    192             return mIsTurningOn;
    193         }
    194 
    195         public void setTurningOff(boolean isTurningOff) {
    196             mIsTurningOff = isTurningOff;
    197         }
    198 
    199         public boolean isTurningOff() {
    200             return mIsTurningOff;
    201         }
    202 
    203         @Override
    204         public boolean processMessage(Message msg) {
    205 
    206             boolean isTurningOn= isTurningOn();
    207             boolean isTurningOff = isTurningOff();
    208 
    209             switch (msg.what) {
    210                 case USER_TURN_ON:
    211                     if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = USER_TURN_ON"
    212                             + ", isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
    213                     if (isTurningOn) {
    214                         Log.i(TAG,"CURRENT_STATE=PENDING: Alreadying turning on bluetooth... Ignoring USER_TURN_ON...");
    215                     } else {
    216                         Log.i(TAG,"CURRENT_STATE=PENDING: Deferring request USER_TURN_ON");
    217                         deferMessage(msg);
    218                     }
    219                     break;
    220                 case USER_TURN_OFF:
    221                     if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = USER_TURN_ON"
    222                             + ", isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
    223                     if (isTurningOff) {
    224                         Log.i(TAG,"CURRENT_STATE=PENDING: Alreadying turning off bluetooth... Ignoring USER_TURN_OFF...");
    225                     } else {
    226                         Log.i(TAG,"CURRENT_STATE=PENDING: Deferring request USER_TURN_OFF");
    227                         deferMessage(msg);
    228                     }
    229                     break;
    230                 case STARTED:   {
    231                     if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STARTED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
    232                     //Remove start timeout
    233                     removeMessages(START_TIMEOUT);
    234 
    235                     //Enable
    236                     boolean ret = mAdapterService.enableNative();
    237                     if (!ret) {
    238                         Log.e(TAG, "Error while turning Bluetooth On");
    239                         notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
    240                         transitionTo(mOffState);
    241                     } else {
    242                         sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
    243                     }
    244                 }
    245                     break;
    246 
    247                 case ENABLED_READY:
    248                     if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = ENABLE_READY, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
    249                     removeMessages(ENABLE_TIMEOUT);
    250                     mAdapterProperties.onBluetoothReady();
    251                     mPendingCommandState.setTurningOn(false);
    252                     transitionTo(mOnState);
    253                     notifyAdapterStateChange(BluetoothAdapter.STATE_ON);
    254                     break;
    255 
    256                 case SET_SCAN_MODE_TIMEOUT:
    257                      Log.w(TAG,"Timeout will setting scan mode..Continuing with disable...");
    258                      //Fall through
    259                 case BEGIN_DISABLE: {
    260                     if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = BEGIN_DISABLE" + isTurningOn + ", isTurningOff=" + isTurningOff);
    261                     removeMessages(SET_SCAN_MODE_TIMEOUT);
    262                     sendMessageDelayed(DISABLE_TIMEOUT, DISABLE_TIMEOUT_DELAY);
    263                     boolean ret = mAdapterService.disableNative();
    264                     if (!ret) {
    265                         removeMessages(DISABLE_TIMEOUT);
    266                         Log.e(TAG, "Error while turning Bluetooth Off");
    267                         //FIXME: what about post enable services
    268                         mPendingCommandState.setTurningOff(false);
    269                         notifyAdapterStateChange(BluetoothAdapter.STATE_ON);
    270                     }
    271                 }
    272                     break;
    273                 case DISABLED:
    274                     if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = DISABLED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
    275                     removeMessages(DISABLE_TIMEOUT);
    276                     sendMessageDelayed(STOP_TIMEOUT, STOP_TIMEOUT_DELAY);
    277                     if (mAdapterService.stopProfileServices()) {
    278                         Log.d(TAG,"Stopping profile services that were post enabled");
    279                         break;
    280                     }
    281                     //Fall through if no services or services already stopped
    282                 case STOPPED:
    283                     if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STOPPED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
    284                     removeMessages(STOP_TIMEOUT);
    285                     setTurningOff(false);
    286                     transitionTo(mOffState);
    287                     notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
    288                     break;
    289                 case START_TIMEOUT:
    290                     if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = START_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
    291                     errorLog("Error enabling Bluetooth");
    292                     mPendingCommandState.setTurningOn(false);
    293                     transitionTo(mOffState);
    294                     notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
    295                     break;
    296                 case ENABLE_TIMEOUT:
    297                     if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = ENABLE_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
    298                     errorLog("Error enabling Bluetooth");
    299                     mPendingCommandState.setTurningOn(false);
    300                     transitionTo(mOffState);
    301                     notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
    302                     break;
    303                 case STOP_TIMEOUT:
    304                     if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STOP_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
    305                     errorLog("Error stopping Bluetooth profiles");
    306                     mPendingCommandState.setTurningOff(false);
    307                     transitionTo(mOffState);
    308                     break;
    309                 case DISABLE_TIMEOUT:
    310                     if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = DISABLE_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
    311                     errorLog("Error disabling Bluetooth");
    312                     mPendingCommandState.setTurningOff(false);
    313                     transitionTo(mOnState);
    314                     break;
    315                 default:
    316                     if (DBG) Log.d(TAG,"ERROR: UNEXPECTED MESSAGE: CURRENT_STATE=PENDING, MESSAGE = " + msg.what );
    317                     return false;
    318             }
    319             return true;
    320         }
    321     }
    322 
    323 
    324     private void notifyAdapterStateChange(int newState) {
    325         int oldState = mAdapterProperties.getState();
    326         mAdapterProperties.setState(newState);
    327         infoLog("Bluetooth adapter state changed: " + oldState + "-> " + newState);
    328         mAdapterService.updateAdapterState(oldState, newState);
    329     }
    330 
    331     void stateChangeCallback(int status) {
    332         if (status == AbstractionLayer.BT_STATE_OFF) {
    333             sendMessage(DISABLED);
    334         } else if (status == AbstractionLayer.BT_STATE_ON) {
    335             // We should have got the property change for adapter and remote devices.
    336             sendMessage(ENABLED_READY);
    337         } else {
    338             errorLog("Incorrect status in stateChangeCallback");
    339         }
    340     }
    341 
    342     private void infoLog(String msg) {
    343         if (DBG) Log.i(TAG, msg);
    344     }
    345 
    346     private void errorLog(String msg) {
    347         Log.e(TAG, msg);
    348     }
    349 
    350 }
    351