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