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.os.UserManager; 24 import android.util.Log; 25 26 import com.android.internal.util.State; 27 import com.android.internal.util.StateMachine; 28 29 /** 30 * This state machine handles Bluetooth Adapter State. 31 * States: 32 * {@link OnState} : Bluetooth is on at this state 33 * {@link OffState}: Bluetooth is off at this state. This is the initial 34 * state. 35 * {@link PendingCommandState} : An enable / disable operation is pending. 36 * TODO(BT): Add per process on state. 37 */ 38 39 final class AdapterState extends StateMachine { 40 private static final boolean DBG = true; 41 private static final boolean VDBG = true; 42 private static final String TAG = "BluetoothAdapterState"; 43 44 static final int BLE_TURN_ON = 0; 45 static final int USER_TURN_ON = 1; 46 static final int BREDR_STARTED=2; 47 static final int ENABLED_READY = 3; 48 static final int BLE_STARTED=4; 49 50 static final int USER_TURN_OFF = 20; 51 static final int BEGIN_DISABLE = 21; 52 static final int ALL_DEVICES_DISCONNECTED = 22; 53 static final int BLE_TURN_OFF = 23; 54 55 static final int DISABLED = 24; 56 static final int BLE_STOPPED=25; 57 static final int BREDR_STOPPED = 26; 58 59 static final int BREDR_START_TIMEOUT = 100; 60 static final int ENABLE_TIMEOUT = 101; 61 static final int DISABLE_TIMEOUT = 103; 62 static final int BLE_STOP_TIMEOUT = 104; 63 static final int SET_SCAN_MODE_TIMEOUT = 105; 64 static final int BLE_START_TIMEOUT = 106; 65 static final int BREDR_STOP_TIMEOUT = 107; 66 67 static final int USER_TURN_OFF_DELAY_MS=500; 68 69 //TODO: tune me 70 private static final int ENABLE_TIMEOUT_DELAY = 12000; 71 private static final int DISABLE_TIMEOUT_DELAY = 8000; 72 private static final int BREDR_START_TIMEOUT_DELAY = 4000; 73 //BLE_START_TIMEOUT can happen quickly as it just a start gattservice 74 private static final int BLE_START_TIMEOUT_DELAY = 2000; //To start GattService 75 private static final int BLE_STOP_TIMEOUT_DELAY = 2000; 76 //BREDR_STOP_TIMEOUT can < STOP_TIMEOUT 77 private static final int BREDR_STOP_TIMEOUT_DELAY = 4000; 78 private static final int PROPERTY_OP_DELAY =2000; 79 private AdapterService mAdapterService; 80 private AdapterProperties mAdapterProperties; 81 private PendingCommandState mPendingCommandState = new PendingCommandState(); 82 private OnState mOnState = new OnState(); 83 private OffState mOffState = new OffState(); 84 private BleOnState mBleOnState = new BleOnState(); 85 86 public boolean isTurningOn() { 87 boolean isTurningOn= mPendingCommandState.isTurningOn(); 88 verboseLog("isTurningOn()=" + isTurningOn); 89 return isTurningOn; 90 } 91 92 public boolean isBleTurningOn() { 93 boolean isBleTurningOn= mPendingCommandState.isBleTurningOn(); 94 verboseLog("isBleTurningOn()=" + isBleTurningOn); 95 return isBleTurningOn; 96 } 97 98 public boolean isBleTurningOff() { 99 boolean isBleTurningOff = mPendingCommandState.isBleTurningOff(); 100 verboseLog("isBleTurningOff()=" + isBleTurningOff); 101 return isBleTurningOff; 102 } 103 104 public boolean isTurningOff() { 105 boolean isTurningOff= mPendingCommandState.isTurningOff(); 106 verboseLog("isTurningOff()=" + isTurningOff); 107 return isTurningOff; 108 } 109 110 private AdapterState(AdapterService service, AdapterProperties adapterProperties) { 111 super("BluetoothAdapterState:"); 112 addState(mOnState); 113 addState(mBleOnState); 114 addState(mOffState); 115 addState(mPendingCommandState); 116 mAdapterService = service; 117 mAdapterProperties = adapterProperties; 118 setInitialState(mOffState); 119 } 120 121 public static AdapterState make(AdapterService service, AdapterProperties adapterProperties) { 122 Log.d(TAG, "make() - Creating AdapterState"); 123 AdapterState as = new AdapterState(service, adapterProperties); 124 as.start(); 125 return as; 126 } 127 128 public void doQuit() { 129 quitNow(); 130 } 131 132 public void cleanup() { 133 if(mAdapterProperties != null) 134 mAdapterProperties = null; 135 if(mAdapterService != null) 136 mAdapterService = null; 137 } 138 139 private class OffState extends State { 140 @Override 141 public void enter() { 142 infoLog("Entering OffState"); 143 } 144 145 @Override 146 public boolean processMessage(Message msg) { 147 AdapterService adapterService = mAdapterService; 148 if (adapterService == null) { 149 errorLog("Received message in OffState after cleanup: " + msg.what); 150 return false; 151 } 152 153 debugLog("Current state: OFF, message: " + msg.what); 154 155 switch(msg.what) { 156 case BLE_TURN_ON: 157 notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_TURNING_ON); 158 mPendingCommandState.setBleTurningOn(true); 159 transitionTo(mPendingCommandState); 160 sendMessageDelayed(BLE_START_TIMEOUT, BLE_START_TIMEOUT_DELAY); 161 adapterService.BleOnProcessStart(); 162 break; 163 164 case USER_TURN_OFF: 165 //TODO: Handle case of service started and stopped without enable 166 break; 167 168 default: 169 return false; 170 } 171 return true; 172 } 173 } 174 175 private class BleOnState extends State { 176 @Override 177 public void enter() { 178 infoLog("Entering BleOnState"); 179 } 180 181 @Override 182 public boolean processMessage(Message msg) { 183 184 AdapterService adapterService = mAdapterService; 185 AdapterProperties adapterProperties = mAdapterProperties; 186 if ((adapterService == null) || (adapterProperties == null)) { 187 errorLog("Received message in BleOnState after cleanup: " + msg.what); 188 return false; 189 } 190 191 debugLog("Current state: BLE ON, message: " + msg.what); 192 193 switch(msg.what) { 194 case USER_TURN_ON: 195 notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_ON); 196 mPendingCommandState.setTurningOn(true); 197 transitionTo(mPendingCommandState); 198 sendMessageDelayed(BREDR_START_TIMEOUT, BREDR_START_TIMEOUT_DELAY); 199 adapterService.startCoreServices(); 200 break; 201 202 case USER_TURN_OFF: 203 notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_TURNING_OFF); 204 mPendingCommandState.setBleTurningOff(true); 205 adapterProperties.onBleDisable(); 206 transitionTo(mPendingCommandState); 207 sendMessageDelayed(DISABLE_TIMEOUT, DISABLE_TIMEOUT_DELAY); 208 boolean ret = adapterService.disableNative(); 209 if (!ret) { 210 removeMessages(DISABLE_TIMEOUT); 211 errorLog("Error while calling disableNative"); 212 //FIXME: what about post enable services 213 mPendingCommandState.setBleTurningOff(false); 214 notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON); 215 } 216 break; 217 218 default: 219 return false; 220 } 221 return true; 222 } 223 } 224 225 private class OnState extends State { 226 @Override 227 public void enter() { 228 infoLog("Entering OnState"); 229 230 AdapterService adapterService = mAdapterService; 231 if (adapterService == null) { 232 errorLog("Entered OnState after cleanup"); 233 return; 234 } 235 adapterService.updateUuids(); 236 adapterService.autoConnect(); 237 } 238 239 @Override 240 public boolean processMessage(Message msg) { 241 AdapterProperties adapterProperties = mAdapterProperties; 242 if (adapterProperties == null) { 243 errorLog("Received message in OnState after cleanup: " + msg.what); 244 return false; 245 } 246 247 debugLog("Current state: ON, message: " + msg.what); 248 249 switch(msg.what) { 250 case BLE_TURN_OFF: 251 notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_OFF); 252 mPendingCommandState.setTurningOff(true); 253 transitionTo(mPendingCommandState); 254 255 // Invoke onBluetoothDisable which shall trigger a 256 // setScanMode to SCAN_MODE_NONE 257 Message m = obtainMessage(SET_SCAN_MODE_TIMEOUT); 258 sendMessageDelayed(m, PROPERTY_OP_DELAY); 259 adapterProperties.onBluetoothDisable(); 260 break; 261 262 case USER_TURN_ON: 263 break; 264 265 default: 266 return false; 267 } 268 return true; 269 } 270 } 271 272 private class PendingCommandState extends State { 273 private boolean mIsTurningOn; 274 private boolean mIsTurningOff; 275 private boolean mIsBleTurningOn; 276 private boolean mIsBleTurningOff; 277 278 public void enter() { 279 infoLog("Entering PendingCommandState"); 280 } 281 282 public void setTurningOn(boolean isTurningOn) { 283 mIsTurningOn = isTurningOn; 284 } 285 286 public boolean isTurningOn() { 287 return mIsTurningOn; 288 } 289 290 public void setTurningOff(boolean isTurningOff) { 291 mIsTurningOff = isTurningOff; 292 } 293 294 public boolean isTurningOff() { 295 return mIsTurningOff; 296 } 297 298 public void setBleTurningOn(boolean isBleTurningOn) { 299 mIsBleTurningOn = isBleTurningOn; 300 } 301 302 public boolean isBleTurningOn() { 303 return mIsBleTurningOn; 304 } 305 306 public void setBleTurningOff(boolean isBleTurningOff) { 307 mIsBleTurningOff = isBleTurningOff; 308 } 309 310 public boolean isBleTurningOff() { 311 return mIsBleTurningOff; 312 } 313 314 @Override 315 public boolean processMessage(Message msg) { 316 317 boolean isTurningOn= isTurningOn(); 318 boolean isTurningOff = isTurningOff(); 319 boolean isBleTurningOn = isBleTurningOn(); 320 boolean isBleTurningOff = isBleTurningOff(); 321 322 AdapterService adapterService = mAdapterService; 323 AdapterProperties adapterProperties = mAdapterProperties; 324 if ((adapterService == null) || (adapterProperties == null)) { 325 errorLog("Received message in PendingCommandState after cleanup: " + msg.what); 326 return false; 327 } 328 329 debugLog("Current state: PENDING_COMMAND, message: " + msg.what); 330 331 switch (msg.what) { 332 case USER_TURN_ON: 333 if (isBleTurningOff || isTurningOff) { //TODO:do we need to send it after ble turn off also?? 334 infoLog("Deferring USER_TURN_ON request..."); 335 deferMessage(msg); 336 } 337 break; 338 339 case USER_TURN_OFF: 340 if (isTurningOn || isBleTurningOn) { 341 infoLog("Deferring USER_TURN_OFF request..."); 342 deferMessage(msg); 343 } 344 break; 345 346 case BLE_TURN_ON: 347 if (isTurningOff || isBleTurningOff) { 348 infoLog("Deferring BLE_TURN_ON request..."); 349 deferMessage(msg); 350 } 351 break; 352 353 case BLE_TURN_OFF: 354 if (isTurningOn || isBleTurningOn) { 355 infoLog("Deferring BLE_TURN_OFF request..."); 356 deferMessage(msg); 357 } 358 break; 359 360 case BLE_STARTED: 361 //Remove start timeout 362 removeMessages(BLE_START_TIMEOUT); 363 364 //Enable 365 boolean isGuest = UserManager.get(mAdapterService).isGuestUser(); 366 if (!adapterService.enableNative(isGuest)) { 367 errorLog("Error while turning Bluetooth on"); 368 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 369 transitionTo(mOffState); 370 } else { 371 sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY); 372 } 373 break; 374 375 case BREDR_STARTED: 376 //Remove start timeout 377 removeMessages(BREDR_START_TIMEOUT); 378 adapterProperties.onBluetoothReady(); 379 mPendingCommandState.setTurningOn(false); 380 transitionTo(mOnState); 381 notifyAdapterStateChange(BluetoothAdapter.STATE_ON); 382 break; 383 384 case ENABLED_READY: 385 removeMessages(ENABLE_TIMEOUT); 386 mPendingCommandState.setBleTurningOn(false); 387 transitionTo(mBleOnState); 388 notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON); 389 break; 390 391 case SET_SCAN_MODE_TIMEOUT: 392 warningLog("Timeout while setting scan mode. Continuing with disable..."); 393 //Fall through 394 case BEGIN_DISABLE: 395 removeMessages(SET_SCAN_MODE_TIMEOUT); 396 sendMessageDelayed(BREDR_STOP_TIMEOUT, BREDR_STOP_TIMEOUT_DELAY); 397 adapterService.stopProfileServices(); 398 break; 399 400 case DISABLED: 401 if (isTurningOn) { 402 removeMessages(ENABLE_TIMEOUT); 403 errorLog("Error enabling Bluetooth - hardware init failed?"); 404 mPendingCommandState.setTurningOn(false); 405 transitionTo(mOffState); 406 adapterService.stopProfileServices(); 407 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 408 break; 409 } 410 removeMessages(DISABLE_TIMEOUT); 411 sendMessageDelayed(BLE_STOP_TIMEOUT, BLE_STOP_TIMEOUT_DELAY); 412 if (adapterService.stopGattProfileService()) { 413 debugLog("Stopping Gatt profile services that were post enabled"); 414 break; 415 } 416 //Fall through if no services or services already stopped 417 case BLE_STOPPED: 418 removeMessages(BLE_STOP_TIMEOUT); 419 setBleTurningOff(false); 420 transitionTo(mOffState); 421 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 422 break; 423 424 case BREDR_STOPPED: 425 removeMessages(BREDR_STOP_TIMEOUT); 426 setTurningOff(false); 427 transitionTo(mBleOnState); 428 notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON); 429 break; 430 431 case BLE_START_TIMEOUT: 432 errorLog("Error enabling Bluetooth (BLE start timeout)"); 433 mPendingCommandState.setBleTurningOn(false); 434 transitionTo(mOffState); 435 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 436 break; 437 438 case BREDR_START_TIMEOUT: 439 errorLog("Error enabling Bluetooth (start timeout)"); 440 mPendingCommandState.setTurningOn(false); 441 transitionTo(mBleOnState); 442 notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON); 443 break; 444 445 case ENABLE_TIMEOUT: 446 errorLog("Error enabling Bluetooth (enable timeout)"); 447 mPendingCommandState.setBleTurningOn(false); 448 transitionTo(mOffState); 449 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 450 break; 451 452 case BREDR_STOP_TIMEOUT: 453 errorLog("Error stopping Bluetooth profiles (stop timeout)"); 454 mPendingCommandState.setTurningOff(false); 455 transitionTo(mBleOnState); 456 notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON); 457 break; 458 459 case BLE_STOP_TIMEOUT: 460 errorLog("Error stopping Bluetooth profiles (BLE stop timeout)"); 461 mPendingCommandState.setTurningOff(false); 462 transitionTo(mOffState); 463 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 464 break; 465 466 case DISABLE_TIMEOUT: 467 errorLog("Error disabling Bluetooth (disable timeout)"); 468 if (isTurningOn) 469 mPendingCommandState.setTurningOn(false); 470 adapterService.stopProfileServices(); 471 adapterService.stopGattProfileService(); 472 mPendingCommandState.setTurningOff(false); 473 setBleTurningOff(false); 474 transitionTo(mOffState); 475 notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); 476 break; 477 478 default: 479 return false; 480 } 481 return true; 482 } 483 } 484 485 private void notifyAdapterStateChange(int newState) { 486 AdapterService adapterService = mAdapterService; 487 AdapterProperties adapterProperties = mAdapterProperties; 488 if ((adapterService == null) || (adapterProperties == null)) { 489 errorLog("notifyAdapterStateChange after cleanup:" + newState); 490 return; 491 } 492 493 int oldState = adapterProperties.getState(); 494 adapterProperties.setState(newState); 495 infoLog("Bluetooth adapter state changed: " + oldState + "-> " + newState); 496 adapterService.updateAdapterState(oldState, newState); 497 } 498 499 void stateChangeCallback(int status) { 500 if (status == AbstractionLayer.BT_STATE_OFF) { 501 sendMessage(DISABLED); 502 503 } else if (status == AbstractionLayer.BT_STATE_ON) { 504 // We should have got the property change for adapter and remote devices. 505 sendMessage(ENABLED_READY); 506 507 } else { 508 errorLog("Incorrect status in stateChangeCallback"); 509 } 510 } 511 512 private void infoLog(String msg) { 513 if (DBG) Log.i(TAG, msg); 514 } 515 516 private void debugLog(String msg) { 517 if (DBG) Log.d(TAG, msg); 518 } 519 520 private void warningLog(String msg) { 521 if (DBG) Log.d(TAG, msg); 522 } 523 524 private void verboseLog(String msg) { 525 if (VDBG) Log.v(TAG, msg); 526 } 527 528 private void errorLog(String msg) { 529 Log.e(TAG, msg); 530 } 531 532 } 533