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