AdapterState.java revision 4852c5686229f1014e9851f4e9a3a19547581b45
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 46 static final int USER_TURN_OFF_DELAY_MS=500; 47 48 //TODO: tune me 49 private static final int ENABLE_TIMEOUT_DELAY = 8000; 50 private static final int DISABLE_TIMEOUT_DELAY = 8000; 51 private static final int START_TIMEOUT_DELAY = 5000; 52 private static final int STOP_TIMEOUT_DELAY = 5000; 53 private static final int PROPERTY_OP_DELAY =2000; 54 private AdapterService mAdapterService; 55 private AdapterProperties mAdapterProperties; 56 private PendingCommandState mPendingCommandState = new PendingCommandState(); 57 private OnState mOnState = new OnState(); 58 private OffState mOffState = new OffState(); 59 60 public boolean isTurningOn() { 61 boolean isTurningOn= mPendingCommandState.isTurningOn(); 62 if (DBG) Log.d(TAG,"isTurningOn()=" + isTurningOn); 63 return isTurningOn; 64 } 65 66 public boolean isTurningOff() { 67 boolean isTurningOff= mPendingCommandState.isTurningOff(); 68 if (DBG) Log.d(TAG,"isTurningOff()=" + isTurningOff); 69 return isTurningOff; 70 } 71 72 public AdapterState(AdapterService service,AdapterProperties adapterProperties) { 73 super("BluetoothAdapterState:"); 74 addState(mOnState); 75 addState(mOffState); 76 addState(mPendingCommandState); 77 mAdapterService = service; 78 mAdapterProperties = adapterProperties; 79 setInitialState(mOffState); 80 } 81 82 public void cleanup() { 83 if(mAdapterProperties != null) 84 mAdapterProperties = null; 85 if(mAdapterService != null) 86 mAdapterService = null; 87 } 88 89 private class OffState extends State { 90 @Override 91 public void enter() { 92 infoLog("Entering OffState"); 93 } 94 95 @Override 96 public boolean processMessage(Message msg) { 97 if (msg.what == SM_QUIT_CMD) { 98 Log.d(TAG, "Received quit request..."); 99 return false; 100 } 101 int requestId = msg.arg1; 102 103 switch(msg.what) { 104 case USER_TURN_ON: 105 if (DBG) Log.d(TAG,"CURRENT_STATE=OFF, MESSAGE = USER_TURN_ON, requestId= " + msg.arg1); 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, requestId= " + msg.arg1); 114 //Handle case of service started and stopped without enable 115 mAdapterService.startShutdown(requestId); 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 switch(msg.what) { 134 case USER_TURN_OFF: 135 if (DBG) Log.d(TAG,"CURRENT_STATE=ON, MESSAGE = USER_TURN_OFF, requestId= " + msg.arg1); 136 notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_OFF); 137 mPendingCommandState.setTurningOff(true); 138 mPendingCommandState.setOffRequestId(msg.arg1); 139 transitionTo(mPendingCommandState); 140 141 // Invoke onBluetoothDisable which shall trigger a 142 // setScanMode to SCAN_MODE_NONE 143 Message m = obtainMessage(BEGIN_DISABLE); 144 m.arg1 = msg.arg1; 145 sendMessageDelayed(m, PROPERTY_OP_DELAY); 146 mAdapterProperties.onBluetoothDisable(); 147 break; 148 149 case USER_TURN_ON: 150 if (DBG) Log.d(TAG,"CURRENT_STATE=ON, MESSAGE = USER_TURN_ON, requestId= " + msg.arg1); 151 Log.i(TAG,"Bluetooth already ON, ignoring USER_TURN_ON"); 152 break; 153 default: 154 if (DBG) Log.d(TAG,"ERROR: UNEXPECTED MESSAGE: CURRENT_STATE=ON, MESSAGE = " + msg.what ); 155 return false; 156 } 157 return true; 158 } 159 } 160 161 private class PendingCommandState extends State { 162 private boolean mIsTurningOn; 163 private boolean mIsTurningOff; 164 165 private int mRequestId; 166 167 public void enter() { 168 infoLog("Entering PendingCommandState State: isTurningOn()=" + isTurningOn() + ", isTurningOff()=" + isTurningOff()); 169 } 170 171 public void setTurningOn(boolean isTurningOn) { 172 mIsTurningOn = isTurningOn; 173 } 174 175 public boolean isTurningOn() { 176 return mIsTurningOn; 177 } 178 179 public void setTurningOff(boolean isTurningOff) { 180 mIsTurningOff = isTurningOff; 181 } 182 183 public boolean isTurningOff() { 184 return mIsTurningOff; 185 } 186 187 public void setOffRequestId(int requestId) { 188 mRequestId = requestId; 189 } 190 191 public int getOffRequestId() { 192 return mRequestId; 193 } 194 195 @Override 196 public boolean processMessage(Message msg) { 197 boolean isTurningOn= isTurningOn(); 198 boolean isTurningOff = isTurningOff(); 199 switch (msg.what) { 200 case USER_TURN_ON: 201 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = USER_TURN_ON, requestId= " + msg.arg1 202 + ", isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 203 if (isTurningOn) { 204 Log.i(TAG,"CURRENT_STATE=PENDING: Alreadying turning on bluetooth... Ignoring USER_TURN_ON..."); 205 } else { 206 Log.i(TAG,"CURRENT_STATE=PENDING: Deferring request USER_TURN_ON"); 207 deferMessage(msg); 208 } 209 break; 210 case USER_TURN_OFF: 211 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = USER_TURN_ON, requestId= " + msg.arg1 212 + ", isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 213 if (isTurningOff) { 214 Log.i(TAG,"CURRENT_STATE=PENDING: Alreadying turning off bluetooth... Ignoring USER_TURN_OFF..."); 215 } else { 216 Log.i(TAG,"CURRENT_STATE=PENDING: Deferring request USER_TURN_OFF"); 217 deferMessage(msg); 218 } 219 break; 220 case STARTED: { 221 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STARTED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 222 //Remove start timeout 223 removeMessages(START_TIMEOUT); 224 225 //Enable 226 boolean ret = mAdapterService.enableNative(); 227 if (!ret) { 228 Log.e(TAG, "Error while turning Bluetooth On"); 229 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 230 transitionTo(mOffState); 231 } else { 232 sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY); 233 } 234 } 235 break; 236 237 case ENABLED_READY: 238 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = ENABLE_READY, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 239 removeMessages(ENABLE_TIMEOUT); 240 mAdapterProperties.onBluetoothReady(); 241 mPendingCommandState.setTurningOn(false); 242 transitionTo(mOnState); 243 notifyAdapterStateChange(BluetoothAdapter.STATE_ON); 244 break; 245 246 case BEGIN_DISABLE: { 247 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = BEGIN_DISABLE" + isTurningOn + ", isTurningOff=" + isTurningOff); 248 removeMessages(BEGIN_DISABLE); //Remove extra message we setup in USER_TURN_OFF 249 //Log.d(TAG,"CURRENT_STATE=ON, MESSAGE = BEGIN_DISABLE_ON, requestId= " + msg.arg1); 250 sendMessageDelayed(DISABLE_TIMEOUT, DISABLE_TIMEOUT_DELAY); 251 boolean ret = mAdapterService.disableNative(); 252 if (!ret) { 253 removeMessages(DISABLE_TIMEOUT); 254 Log.e(TAG, "Error while turning Bluetooth Off"); 255 //FIXME: what about post enable services 256 mPendingCommandState.setTurningOff(false); 257 mPendingCommandState.setOffRequestId(-1); 258 notifyAdapterStateChange(BluetoothAdapter.STATE_ON); 259 } 260 } 261 break; 262 case DISABLED: 263 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = DISABLED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 264 removeMessages(DISABLE_TIMEOUT); 265 sendMessageDelayed(STOP_TIMEOUT, STOP_TIMEOUT_DELAY); 266 if (mAdapterService.stopProfileServices()) { 267 Log.d(TAG,"Stopping profile services that were post enabled"); 268 break; 269 } 270 //Fall through if no services or services already stopped 271 case STOPPED: 272 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STOPPED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 273 removeMessages(STOP_TIMEOUT); 274 setTurningOff(false); 275 int requestId= getOffRequestId(); 276 setOffRequestId(-1); 277 transitionTo(mOffState); 278 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 279 mAdapterService.startShutdown(requestId); 280 break; 281 case START_TIMEOUT: 282 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = START_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 283 errorLog("Error enabling Bluetooth"); 284 mPendingCommandState.setTurningOn(false); 285 transitionTo(mOffState); 286 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 287 break; 288 case ENABLE_TIMEOUT: 289 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = ENABLE_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 290 errorLog("Error enabling Bluetooth"); 291 mPendingCommandState.setTurningOn(false); 292 transitionTo(mOffState); 293 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 294 break; 295 case STOP_TIMEOUT: 296 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STOP_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 297 errorLog("Error stopping Bluetooth profiles"); 298 mPendingCommandState.setTurningOff(false); 299 transitionTo(mOffState); 300 break; 301 case DISABLE_TIMEOUT: 302 if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = DISABLE_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); 303 errorLog("Error disabling Bluetooth"); 304 mPendingCommandState.setTurningOff(false); 305 transitionTo(mOnState); 306 break; 307 default: 308 if (DBG) Log.d(TAG,"ERROR: UNEXPECTED MESSAGE: CURRENT_STATE=PENDING, MESSAGE = " + msg.what ); 309 return false; 310 } 311 return true; 312 } 313 } 314 315 316 private void notifyAdapterStateChange(int newState) { 317 int oldState = mAdapterProperties.getState(); 318 mAdapterProperties.setState(newState); 319 infoLog("Bluetooth adapter state changed: " + oldState + "-> " + newState); 320 mAdapterService.updateAdapterState(oldState, newState); 321 } 322 323 void stateChangeCallback(int status) { 324 if (status == AbstractionLayer.BT_STATE_OFF) { 325 sendMessage(DISABLED); 326 } else if (status == AbstractionLayer.BT_STATE_ON) { 327 // We should have got the property change for adapter and remote devices. 328 sendMessage(ENABLED_READY); 329 } else { 330 errorLog("Incorrect status in stateChangeCallback"); 331 } 332 } 333 334 private void infoLog(String msg) { 335 if (DBG) Log.i(TAG, msg); 336 } 337 338 private void errorLog(String msg) { 339 Log.e(TAG, msg); 340 } 341} 342