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