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