AdapterState.java revision 4f5430babbc5a8f870e5a578a4ea3452f41dd97a
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 public void cleanup() { 84 if(mAdapterProperties != null) 85 mAdapterProperties = null; 86 if(mAdapterService != null) 87 mAdapterService = null; 88 } 89 90 private class OffState extends State { 91 @Override 92 public void enter() { 93 infoLog("Entering OffState"); 94 } 95 96 @Override 97 public boolean processMessage(Message msg) { 98 /* TODO(BT) if (msg.what == SM_QUIT_CMD) { 99 Log.d(TAG, "Received quit request..."); 100 return false; 101 } */ 102 103 switch(msg.what) { 104 case USER_TURN_ON: 105 if (DBG) Log.d(TAG,"CURRENT_STATE=OFF, MESSAGE = USER_TURN_ON"); 106 notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_ON); 107 mPendingCommandState.setTurningOn(true); 108 transitionTo(mPendingCommandState); 109 sendMessageDelayed(START_TIMEOUT, START_TIMEOUT_DELAY); 110 mAdapterService.processStart(); 111 break; 112 case USER_TURN_OFF: 113 if (DBG) Log.d(TAG,"CURRENT_STATE=OFF, MESSAGE = USER_TURN_OFF"); 114 //TODO: Handle case of service started and stopped without enable 115 break; 116 default: 117 if (DBG) Log.d(TAG,"ERROR: UNEXPECTED MESSAGE: CURRENT_STATE=OFF, MESSAGE = " + msg.what ); 118 return false; 119 } 120 return true; 121 } 122 } 123 124 private class OnState extends State { 125 @Override 126 public void enter() { 127 infoLog("Entering On State"); 128 mAdapterService.autoConnect(); 129 } 130 131 @Override 132 public boolean processMessage(Message msg) { 133 switch(msg.what) { 134 case USER_TURN_OFF: 135 if (DBG) Log.d(TAG,"CURRENT_STATE=ON, MESSAGE = USER_TURN_OFF"); 136 notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_OFF); 137 mPendingCommandState.setTurningOff(true); 138 transitionTo(mPendingCommandState); 139 140 // Invoke onBluetoothDisable which shall trigger a 141 // setScanMode to SCAN_MODE_NONE 142 Message m = obtainMessage(SET_SCAN_MODE_TIMEOUT); 143 sendMessageDelayed(m, PROPERTY_OP_DELAY); 144 mAdapterProperties.onBluetoothDisable(); 145 break; 146 147 case USER_TURN_ON: 148 if (DBG) Log.d(TAG,"CURRENT_STATE=ON, MESSAGE = USER_TURN_ON"); 149 Log.i(TAG,"Bluetooth already ON, ignoring USER_TURN_ON"); 150 break; 151 default: 152 if (DBG) Log.d(TAG,"ERROR: UNEXPECTED MESSAGE: CURRENT_STATE=ON, MESSAGE = " + msg.what ); 153 return false; 154 } 155 return true; 156 } 157 } 158 159 private class PendingCommandState extends State { 160 private boolean mIsTurningOn; 161 private boolean mIsTurningOff; 162 163 public void enter() { 164 infoLog("Entering PendingCommandState State: isTurningOn()=" + isTurningOn() + ", isTurningOff()=" + isTurningOff()); 165 } 166 167 public void setTurningOn(boolean isTurningOn) { 168 mIsTurningOn = isTurningOn; 169 } 170 171 public boolean isTurningOn() { 172 return mIsTurningOn; 173 } 174 175 public void setTurningOff(boolean isTurningOff) { 176 mIsTurningOff = isTurningOff; 177 } 178 179 public boolean isTurningOff() { 180 return mIsTurningOff; 181 } 182 183 @Override 184 public boolean processMessage(Message msg) { 185 boolean isTurningOn= isTurningOn(); 186 boolean isTurningOff = isTurningOff(); 187 switch (msg.what) { 188 case USER_TURN_ON: 189 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = USER_TURN_ON" 190 + ", isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 191 if (isTurningOn) { 192 Log.i(TAG,"CURRENT_STATE=PENDING: Alreadying turning on bluetooth... Ignoring USER_TURN_ON..."); 193 } else { 194 Log.i(TAG,"CURRENT_STATE=PENDING: Deferring request USER_TURN_ON"); 195 deferMessage(msg); 196 } 197 break; 198 case USER_TURN_OFF: 199 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = USER_TURN_ON" 200 + ", isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 201 if (isTurningOff) { 202 Log.i(TAG,"CURRENT_STATE=PENDING: Alreadying turning off bluetooth... Ignoring USER_TURN_OFF..."); 203 } else { 204 Log.i(TAG,"CURRENT_STATE=PENDING: Deferring request USER_TURN_OFF"); 205 deferMessage(msg); 206 } 207 break; 208 case STARTED: { 209 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STARTED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 210 //Remove start timeout 211 removeMessages(START_TIMEOUT); 212 213 //Enable 214 boolean ret = mAdapterService.enableNative(); 215 if (!ret) { 216 Log.e(TAG, "Error while turning Bluetooth On"); 217 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 218 transitionTo(mOffState); 219 } else { 220 sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY); 221 } 222 } 223 break; 224 225 case ENABLED_READY: 226 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = ENABLE_READY, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 227 removeMessages(ENABLE_TIMEOUT); 228 mAdapterProperties.onBluetoothReady(); 229 mPendingCommandState.setTurningOn(false); 230 transitionTo(mOnState); 231 notifyAdapterStateChange(BluetoothAdapter.STATE_ON); 232 break; 233 234 case SET_SCAN_MODE_TIMEOUT: 235 Log.w(TAG,"Timeout will setting scan mode..Continuing with disable..."); 236 //Fall through 237 case BEGIN_DISABLE: { 238 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = BEGIN_DISABLE" + isTurningOn + ", isTurningOff=" + isTurningOff); 239 removeMessages(SET_SCAN_MODE_TIMEOUT); 240 sendMessageDelayed(DISABLE_TIMEOUT, DISABLE_TIMEOUT_DELAY); 241 boolean ret = mAdapterService.disableNative(); 242 if (!ret) { 243 removeMessages(DISABLE_TIMEOUT); 244 Log.e(TAG, "Error while turning Bluetooth Off"); 245 //FIXME: what about post enable services 246 mPendingCommandState.setTurningOff(false); 247 notifyAdapterStateChange(BluetoothAdapter.STATE_ON); 248 } 249 } 250 break; 251 case DISABLED: 252 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = DISABLED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 253 removeMessages(DISABLE_TIMEOUT); 254 sendMessageDelayed(STOP_TIMEOUT, STOP_TIMEOUT_DELAY); 255 if (mAdapterService.stopProfileServices()) { 256 Log.d(TAG,"Stopping profile services that were post enabled"); 257 break; 258 } 259 //Fall through if no services or services already stopped 260 case STOPPED: 261 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STOPPED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 262 removeMessages(STOP_TIMEOUT); 263 setTurningOff(false); 264 transitionTo(mOffState); 265 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 266 break; 267 case START_TIMEOUT: 268 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = START_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 269 errorLog("Error enabling Bluetooth"); 270 mPendingCommandState.setTurningOn(false); 271 transitionTo(mOffState); 272 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 273 break; 274 case ENABLE_TIMEOUT: 275 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = ENABLE_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 276 errorLog("Error enabling Bluetooth"); 277 mPendingCommandState.setTurningOn(false); 278 transitionTo(mOffState); 279 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 280 break; 281 case STOP_TIMEOUT: 282 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STOP_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 283 errorLog("Error stopping Bluetooth profiles"); 284 mPendingCommandState.setTurningOff(false); 285 transitionTo(mOffState); 286 break; 287 case DISABLE_TIMEOUT: 288 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = DISABLE_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 289 errorLog("Error disabling Bluetooth"); 290 mPendingCommandState.setTurningOff(false); 291 transitionTo(mOnState); 292 break; 293 default: 294 if (DBG) Log.d(TAG,"ERROR: UNEXPECTED MESSAGE: CURRENT_STATE=PENDING, MESSAGE = " + msg.what ); 295 return false; 296 } 297 return true; 298 } 299 } 300 301 302 private void notifyAdapterStateChange(int newState) { 303 int oldState = mAdapterProperties.getState(); 304 mAdapterProperties.setState(newState); 305 infoLog("Bluetooth adapter state changed: " + oldState + "-> " + newState); 306 mAdapterService.updateAdapterState(oldState, newState); 307 } 308 309 void stateChangeCallback(int status) { 310 if (status == AbstractionLayer.BT_STATE_OFF) { 311 sendMessage(DISABLED); 312 } else if (status == AbstractionLayer.BT_STATE_ON) { 313 // We should have got the property change for adapter and remote devices. 314 sendMessage(ENABLED_READY); 315 } else { 316 errorLog("Incorrect status in stateChangeCallback"); 317 } 318 } 319 320 private void infoLog(String msg) { 321 if (DBG) Log.i(TAG, msg); 322 } 323 324 private void errorLog(String msg) { 325 Log.e(TAG, msg); 326 } 327} 328