1 /* 2 * Copyright (C) 2011 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 android.server; 18 19 import android.bluetooth.BluetoothAdapter; 20 import android.bluetooth.IBluetoothStateChangeCallback; 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.os.Binder; 25 import android.os.Message; 26 import android.os.RemoteException; 27 import android.provider.Settings; 28 import android.util.Log; 29 30 import com.android.internal.util.IState; 31 import com.android.internal.util.State; 32 import com.android.internal.util.StateMachine; 33 34 import java.io.PrintWriter; 35 36 /** 37 * Bluetooth Adapter StateMachine 38 * All the states are at the same level, ie, no hierarchy. 39 * (BluetootOn)<----------------------<- 40 * | ^ -------------------->- | 41 * | | | | 42 * TURN_OFF | | SCAN_MODE_CHANGED m1 | | USER_TURN_ON 43 * AIRPLANE_MODE_ON | | | | 44 * V | | | 45 * (Switching) (PerProcessState) 46 * | ^ | | 47 * POWER_STATE_CHANGED & | | TURN_ON(_CONTINUE) | | 48 * ALL_DEVICES_DISCONNECTED | | m2 | | 49 * V |------------------------< | SCAN_MODE_CHANGED 50 * (HotOff)-------------------------->- PER_PROCESS_TURN_ON 51 * / ^ 52 * / | SERVICE_RECORD_LOADED 53 * | | 54 * TURN_COLD | (Warmup) 55 * \ ^ 56 * \ | TURN_HOT/TURN_ON 57 * | | AIRPLANE_MODE_OFF(when Bluetooth was on before) 58 * V | 59 * (PowerOff) <----- initial state 60 * 61 * Legend: 62 * m1 = TURN_HOT 63 * m2 = Transition to HotOff when number of process wanting BT on is 0. 64 * POWER_STATE_CHANGED will make the transition. 65 */ 66 final class BluetoothAdapterStateMachine extends StateMachine { 67 private static final String TAG = "BluetoothAdapterStateMachine"; 68 private static final boolean DBG = false; 69 70 // Message(what) to take an action 71 // 72 // We get this message when user tries to turn on BT 73 static final int USER_TURN_ON = 1; 74 // We get this message when user tries to turn off BT 75 static final int USER_TURN_OFF = 2; 76 // Per process enable / disable messages 77 static final int PER_PROCESS_TURN_ON = 3; 78 static final int PER_PROCESS_TURN_OFF = 4; 79 80 // Turn on Bluetooth Module, Load firmware, and do all the preparation 81 // needed to get the Bluetooth Module ready but keep it not discoverable 82 // and not connectable. This way the Bluetooth Module can be quickly 83 // switched on if needed 84 static final int TURN_HOT = 5; 85 86 // Message(what) to report a event that the state machine need to respond to 87 // 88 // Event indicates sevice records have been loaded 89 static final int SERVICE_RECORD_LOADED = 51; 90 // Event indicates all the remote Bluetooth devices has been disconnected 91 static final int ALL_DEVICES_DISCONNECTED = 52; 92 // Event indicates the Bluetooth scan mode has changed 93 static final int SCAN_MODE_CHANGED = 53; 94 // Event indicates the powered state has changed 95 static final int POWER_STATE_CHANGED = 54; 96 // Event indicates airplane mode is turned on 97 static final int AIRPLANE_MODE_ON = 55; 98 // Event indicates airplane mode is turned off 99 static final int AIRPLANE_MODE_OFF = 56; 100 101 // private internal messages 102 // 103 // USER_TURN_ON is changed to TURN_ON_CONTINUE after we broadcast the 104 // state change intent so that we will not broadcast the intent again in 105 // other state 106 private static final int TURN_ON_CONTINUE = 101; 107 // Unload firmware, turning off Bluetooth module power 108 private static final int TURN_COLD = 102; 109 // Device disconnecting timeout happens 110 private static final int DEVICES_DISCONNECT_TIMEOUT = 103; 111 // Prepare Bluetooth timeout happens 112 private static final int PREPARE_BLUETOOTH_TIMEOUT = 104; 113 // Bluetooth Powerdown timeout happens 114 private static final int POWER_DOWN_TIMEOUT = 105; 115 116 private Context mContext; 117 private BluetoothService mBluetoothService; 118 private BluetoothEventLoop mEventLoop; 119 120 private BluetoothOn mBluetoothOn; 121 private Switching mSwitching; 122 private HotOff mHotOff; 123 private WarmUp mWarmUp; 124 private PowerOff mPowerOff; 125 private PerProcessState mPerProcessState; 126 127 // this is the BluetoothAdapter state that reported externally 128 private int mPublicState; 129 130 // timeout value waiting for all the devices to be disconnected 131 private static final int DEVICES_DISCONNECT_TIMEOUT_TIME = 3000; 132 133 private static final int PREPARE_BLUETOOTH_TIMEOUT_TIME = 10000; 134 135 private static final int POWER_DOWN_TIMEOUT_TIME = 5000; 136 137 BluetoothAdapterStateMachine(Context context, BluetoothService bluetoothService, 138 BluetoothAdapter bluetoothAdapter) { 139 super(TAG); 140 mContext = context; 141 mBluetoothService = bluetoothService; 142 mEventLoop = new BluetoothEventLoop(context, bluetoothAdapter, bluetoothService, this); 143 144 mBluetoothOn = new BluetoothOn(); 145 mSwitching = new Switching(); 146 mHotOff = new HotOff(); 147 mWarmUp = new WarmUp(); 148 mPowerOff = new PowerOff(); 149 mPerProcessState = new PerProcessState(); 150 151 addState(mBluetoothOn); 152 addState(mSwitching); 153 addState(mHotOff); 154 addState(mWarmUp); 155 addState(mPowerOff); 156 addState(mPerProcessState); 157 158 setInitialState(mPowerOff); 159 mPublicState = BluetoothAdapter.STATE_OFF; 160 } 161 162 /** 163 * Bluetooth module's power is off, firmware is not loaded. 164 */ 165 private class PowerOff extends State { 166 @Override 167 public void enter() { 168 if (DBG) log("Enter PowerOff: " + getCurrentMessage().what); 169 } 170 @Override 171 public boolean processMessage(Message message) { 172 log("PowerOff process message: " + message.what); 173 174 boolean retValue = HANDLED; 175 switch(message.what) { 176 case USER_TURN_ON: 177 // starts turning on BT module, broadcast this out 178 broadcastState(BluetoothAdapter.STATE_TURNING_ON); 179 transitionTo(mWarmUp); 180 if (prepareBluetooth()) { 181 // this is user request, save the setting 182 if ((Boolean) message.obj) { 183 persistSwitchSetting(true); 184 } 185 // We will continue turn the BT on all the way to the BluetoothOn state 186 deferMessage(obtainMessage(TURN_ON_CONTINUE)); 187 } else { 188 Log.e(TAG, "failed to prepare bluetooth, abort turning on"); 189 transitionTo(mPowerOff); 190 broadcastState(BluetoothAdapter.STATE_OFF); 191 } 192 break; 193 case TURN_HOT: 194 if (prepareBluetooth()) { 195 transitionTo(mWarmUp); 196 } 197 break; 198 case AIRPLANE_MODE_OFF: 199 if (getBluetoothPersistedSetting()) { 200 // starts turning on BT module, broadcast this out 201 broadcastState(BluetoothAdapter.STATE_TURNING_ON); 202 transitionTo(mWarmUp); 203 if (prepareBluetooth()) { 204 // We will continue turn the BT on all the way to the BluetoothOn state 205 deferMessage(obtainMessage(TURN_ON_CONTINUE)); 206 transitionTo(mWarmUp); 207 } else { 208 Log.e(TAG, "failed to prepare bluetooth, abort turning on"); 209 transitionTo(mPowerOff); 210 broadcastState(BluetoothAdapter.STATE_OFF); 211 } 212 } else if (mContext.getResources().getBoolean 213 (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { 214 sendMessage(TURN_HOT); 215 } 216 break; 217 case PER_PROCESS_TURN_ON: 218 if (prepareBluetooth()) { 219 transitionTo(mWarmUp); 220 } 221 deferMessage(obtainMessage(PER_PROCESS_TURN_ON)); 222 break; 223 case PER_PROCESS_TURN_OFF: 224 perProcessCallback(false, (IBluetoothStateChangeCallback) message.obj); 225 break; 226 case USER_TURN_OFF: 227 Log.w(TAG, "PowerOff received: " + message.what); 228 case AIRPLANE_MODE_ON: // ignore 229 break; 230 default: 231 return NOT_HANDLED; 232 } 233 return retValue; 234 } 235 236 /** 237 * Turn on Bluetooth Module, Load firmware, and do all the preparation 238 * needed to get the Bluetooth Module ready but keep it not discoverable 239 * and not connectable. 240 * The last step of this method sets up the local service record DB. 241 * There will be a event reporting the status of the SDP setup. 242 */ 243 private boolean prepareBluetooth() { 244 if (mBluetoothService.enableNative() != 0) { 245 return false; 246 } 247 248 // try to start event loop, give 2 attempts 249 int retryCount = 2; 250 boolean eventLoopStarted = false; 251 while ((retryCount-- > 0) && !eventLoopStarted) { 252 mEventLoop.start(); 253 // it may take a moment for the other thread to do its 254 // thing. Check periodically for a while. 255 int pollCount = 5; 256 while ((pollCount-- > 0) && !eventLoopStarted) { 257 if (mEventLoop.isEventLoopRunning()) { 258 eventLoopStarted = true; 259 break; 260 } 261 try { 262 Thread.sleep(100); 263 } catch (InterruptedException e) { 264 log("prepareBluetooth sleep interrupted: " + pollCount); 265 break; 266 } 267 } 268 } 269 270 if (!eventLoopStarted) { 271 mBluetoothService.disableNative(); 272 return false; 273 } 274 275 // get BluetoothService ready 276 if (!mBluetoothService.prepareBluetooth()) { 277 mEventLoop.stop(); 278 mBluetoothService.disableNative(); 279 return false; 280 } 281 282 sendMessageDelayed(PREPARE_BLUETOOTH_TIMEOUT, PREPARE_BLUETOOTH_TIMEOUT_TIME); 283 return true; 284 } 285 } 286 287 /** 288 * Turning on Bluetooth module's power, loading firmware, starting 289 * event loop thread to listen on Bluetooth module event changes. 290 */ 291 private class WarmUp extends State { 292 293 @Override 294 public void enter() { 295 if (DBG) log("Enter WarmUp: " + getCurrentMessage().what); 296 } 297 298 @Override 299 public boolean processMessage(Message message) { 300 log("WarmUp process message: " + message.what); 301 302 boolean retValue = HANDLED; 303 switch(message.what) { 304 case SERVICE_RECORD_LOADED: 305 removeMessages(PREPARE_BLUETOOTH_TIMEOUT); 306 transitionTo(mHotOff); 307 break; 308 case PREPARE_BLUETOOTH_TIMEOUT: 309 Log.e(TAG, "Bluetooth adapter SDP failed to load"); 310 shutoffBluetooth(); 311 transitionTo(mPowerOff); 312 broadcastState(BluetoothAdapter.STATE_OFF); 313 break; 314 case USER_TURN_ON: // handle this at HotOff state 315 case TURN_ON_CONTINUE: // Once in HotOff state, continue turn bluetooth 316 // on to the BluetoothOn state 317 case AIRPLANE_MODE_ON: 318 case AIRPLANE_MODE_OFF: 319 case PER_PROCESS_TURN_ON: 320 case PER_PROCESS_TURN_OFF: 321 deferMessage(message); 322 break; 323 case USER_TURN_OFF: 324 Log.w(TAG, "WarmUp received: " + message.what); 325 break; 326 default: 327 return NOT_HANDLED; 328 } 329 return retValue; 330 } 331 332 } 333 334 /** 335 * Bluetooth Module has powered, firmware loaded, event loop started, 336 * SDP loaded, but the modules stays non-discoverable and 337 * non-connectable. 338 */ 339 private class HotOff extends State { 340 @Override 341 public void enter() { 342 if (DBG) log("Enter HotOff: " + getCurrentMessage().what); 343 } 344 345 @Override 346 public boolean processMessage(Message message) { 347 log("HotOff process message: " + message.what); 348 349 boolean retValue = HANDLED; 350 switch(message.what) { 351 case USER_TURN_ON: 352 broadcastState(BluetoothAdapter.STATE_TURNING_ON); 353 if ((Boolean) message.obj) { 354 persistSwitchSetting(true); 355 } 356 // let it fall to TURN_ON_CONTINUE: 357 //$FALL-THROUGH$ 358 case TURN_ON_CONTINUE: 359 mBluetoothService.switchConnectable(true); 360 transitionTo(mSwitching); 361 break; 362 case AIRPLANE_MODE_ON: 363 case TURN_COLD: 364 shutoffBluetooth(); 365 transitionTo(mPowerOff); 366 broadcastState(BluetoothAdapter.STATE_OFF); 367 break; 368 case AIRPLANE_MODE_OFF: 369 if (getBluetoothPersistedSetting()) { 370 broadcastState(BluetoothAdapter.STATE_TURNING_ON); 371 transitionTo(mSwitching); 372 mBluetoothService.switchConnectable(true); 373 } 374 break; 375 case PER_PROCESS_TURN_ON: 376 transitionTo(mPerProcessState); 377 378 // Resend the PER_PROCESS_TURN_ON message so that the callback 379 // can be sent through. 380 deferMessage(message); 381 382 mBluetoothService.switchConnectable(true); 383 break; 384 case PER_PROCESS_TURN_OFF: 385 perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj); 386 break; 387 case USER_TURN_OFF: // ignore 388 break; 389 case POWER_STATE_CHANGED: 390 if ((Boolean) message.obj) { 391 recoverStateMachine(TURN_HOT, null); 392 } 393 break; 394 default: 395 return NOT_HANDLED; 396 } 397 return retValue; 398 } 399 400 } 401 402 private class Switching extends State { 403 404 @Override 405 public void enter() { 406 if (DBG) log("Enter Switching: " + getCurrentMessage().what); 407 } 408 @Override 409 public boolean processMessage(Message message) { 410 log("Switching process message: " + message.what); 411 412 boolean retValue = HANDLED; 413 switch(message.what) { 414 case SCAN_MODE_CHANGED: 415 // This event matches mBluetoothService.switchConnectable action 416 if (mPublicState == BluetoothAdapter.STATE_TURNING_ON) { 417 // set pairable if it's not 418 mBluetoothService.setPairable(); 419 mBluetoothService.initBluetoothAfterTurningOn(); 420 transitionTo(mBluetoothOn); 421 broadcastState(BluetoothAdapter.STATE_ON); 422 // run bluetooth now that it's turned on 423 // Note runBluetooth should be called only in adapter STATE_ON 424 mBluetoothService.runBluetooth(); 425 } 426 break; 427 case POWER_STATE_CHANGED: 428 removeMessages(POWER_DOWN_TIMEOUT); 429 if (!((Boolean) message.obj)) { 430 if (mPublicState == BluetoothAdapter.STATE_TURNING_OFF) { 431 transitionTo(mHotOff); 432 finishSwitchingOff(); 433 if (!mContext.getResources().getBoolean 434 (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { 435 deferMessage(obtainMessage(TURN_COLD)); 436 } 437 } 438 } else { 439 if (mPublicState != BluetoothAdapter.STATE_TURNING_ON) { 440 if (mContext.getResources().getBoolean 441 (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { 442 recoverStateMachine(TURN_HOT, null); 443 } else { 444 recoverStateMachine(TURN_COLD, null); 445 } 446 } 447 } 448 break; 449 case ALL_DEVICES_DISCONNECTED: 450 removeMessages(DEVICES_DISCONNECT_TIMEOUT); 451 mBluetoothService.switchConnectable(false); 452 sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); 453 break; 454 case DEVICES_DISCONNECT_TIMEOUT: 455 sendMessage(ALL_DEVICES_DISCONNECTED); 456 // reset the hardware for error recovery 457 Log.e(TAG, "Devices failed to disconnect, reseting..."); 458 deferMessage(obtainMessage(TURN_COLD)); 459 if (mContext.getResources().getBoolean 460 (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { 461 deferMessage(obtainMessage(TURN_HOT)); 462 } 463 break; 464 case POWER_DOWN_TIMEOUT: 465 transitionTo(mHotOff); 466 finishSwitchingOff(); 467 // reset the hardware for error recovery 468 Log.e(TAG, "Devices failed to power down, reseting..."); 469 deferMessage(obtainMessage(TURN_COLD)); 470 if (mContext.getResources().getBoolean 471 (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { 472 deferMessage(obtainMessage(TURN_HOT)); 473 } 474 break; 475 case USER_TURN_ON: 476 case AIRPLANE_MODE_OFF: 477 case AIRPLANE_MODE_ON: 478 case PER_PROCESS_TURN_ON: 479 case PER_PROCESS_TURN_OFF: 480 case USER_TURN_OFF: 481 deferMessage(message); 482 break; 483 484 default: 485 return NOT_HANDLED; 486 } 487 return retValue; 488 } 489 } 490 491 private class BluetoothOn extends State { 492 493 @Override 494 public void enter() { 495 if (DBG) log("Enter BluetoothOn: " + getCurrentMessage().what); 496 } 497 @Override 498 public boolean processMessage(Message message) { 499 log("BluetoothOn process message: " + message.what); 500 501 boolean retValue = HANDLED; 502 switch(message.what) { 503 case USER_TURN_OFF: 504 if ((Boolean) message.obj) { 505 persistSwitchSetting(false); 506 } 507 508 if (mBluetoothService.isDiscovering()) { 509 mBluetoothService.cancelDiscovery(); 510 } 511 if (!mBluetoothService.isApplicationStateChangeTrackerEmpty()) { 512 transitionTo(mPerProcessState); 513 deferMessage(obtainMessage(TURN_HOT)); 514 break; 515 } 516 //$FALL-THROUGH$ to AIRPLANE_MODE_ON 517 case AIRPLANE_MODE_ON: 518 broadcastState(BluetoothAdapter.STATE_TURNING_OFF); 519 transitionTo(mSwitching); 520 if (mBluetoothService.getAdapterConnectionState() != 521 BluetoothAdapter.STATE_DISCONNECTED) { 522 mBluetoothService.disconnectDevices(); 523 sendMessageDelayed(DEVICES_DISCONNECT_TIMEOUT, 524 DEVICES_DISCONNECT_TIMEOUT_TIME); 525 } else { 526 mBluetoothService.switchConnectable(false); 527 sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); 528 } 529 530 // we turn all the way to PowerOff with AIRPLANE_MODE_ON 531 if (message.what == AIRPLANE_MODE_ON) { 532 // We inform all the per process callbacks 533 allProcessesCallback(false); 534 deferMessage(obtainMessage(AIRPLANE_MODE_ON)); 535 } 536 break; 537 case AIRPLANE_MODE_OFF: 538 case USER_TURN_ON: 539 Log.w(TAG, "BluetoothOn received: " + message.what); 540 break; 541 case PER_PROCESS_TURN_ON: 542 perProcessCallback(true, (IBluetoothStateChangeCallback)message.obj); 543 break; 544 case PER_PROCESS_TURN_OFF: 545 perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj); 546 break; 547 case POWER_STATE_CHANGED: 548 if ((Boolean) message.obj) { 549 // reset the state machine and send it TURN_ON_CONTINUE message 550 recoverStateMachine(USER_TURN_ON, false); 551 } 552 break; 553 default: 554 return NOT_HANDLED; 555 } 556 return retValue; 557 } 558 559 } 560 561 562 private class PerProcessState extends State { 563 IBluetoothStateChangeCallback mCallback = null; 564 boolean isTurningOn = false; 565 566 @Override 567 public void enter() { 568 int what = getCurrentMessage().what; 569 if (DBG) log("Enter PerProcessState: " + what); 570 571 if (what == PER_PROCESS_TURN_ON) { 572 isTurningOn = true; 573 } else if (what == USER_TURN_OFF) { 574 isTurningOn = false; 575 } else { 576 Log.e(TAG, "enter PerProcessState: wrong msg: " + what); 577 } 578 } 579 580 @Override 581 public boolean processMessage(Message message) { 582 log("PerProcessState process message: " + message.what); 583 584 boolean retValue = HANDLED; 585 switch (message.what) { 586 case PER_PROCESS_TURN_ON: 587 mCallback = (IBluetoothStateChangeCallback)getCurrentMessage().obj; 588 589 // If this is not the first application call the callback. 590 if (mBluetoothService.getNumberOfApplicationStateChangeTrackers() > 1) { 591 perProcessCallback(true, mCallback); 592 } 593 break; 594 case SCAN_MODE_CHANGED: 595 if (isTurningOn) { 596 perProcessCallback(true, mCallback); 597 isTurningOn = false; 598 } 599 break; 600 case POWER_STATE_CHANGED: 601 removeMessages(POWER_DOWN_TIMEOUT); 602 if (!((Boolean) message.obj)) { 603 transitionTo(mHotOff); 604 if (!mContext.getResources().getBoolean 605 (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { 606 deferMessage(obtainMessage(TURN_COLD)); 607 } 608 } else { 609 if (!isTurningOn) { 610 recoverStateMachine(TURN_COLD, null); 611 for (IBluetoothStateChangeCallback c: 612 mBluetoothService.getApplicationStateChangeCallbacks()) { 613 perProcessCallback(false, c); 614 deferMessage(obtainMessage(PER_PROCESS_TURN_ON, c)); 615 } 616 } 617 } 618 break; 619 case POWER_DOWN_TIMEOUT: 620 transitionTo(mHotOff); 621 Log.e(TAG, "Power-down timed out, resetting..."); 622 deferMessage(obtainMessage(TURN_COLD)); 623 if (mContext.getResources().getBoolean 624 (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { 625 deferMessage(obtainMessage(TURN_HOT)); 626 } 627 break; 628 case USER_TURN_ON: 629 broadcastState(BluetoothAdapter.STATE_TURNING_ON); 630 persistSwitchSetting(true); 631 mBluetoothService.initBluetoothAfterTurningOn(); 632 transitionTo(mBluetoothOn); 633 broadcastState(BluetoothAdapter.STATE_ON); 634 // run bluetooth now that it's turned on 635 mBluetoothService.runBluetooth(); 636 break; 637 case TURN_HOT: 638 broadcastState(BluetoothAdapter.STATE_TURNING_OFF); 639 if (mBluetoothService.getAdapterConnectionState() != 640 BluetoothAdapter.STATE_DISCONNECTED) { 641 mBluetoothService.disconnectDevices(); 642 sendMessageDelayed(DEVICES_DISCONNECT_TIMEOUT, 643 DEVICES_DISCONNECT_TIMEOUT_TIME); 644 break; 645 } 646 //$FALL-THROUGH$ all devices are already disconnected 647 case ALL_DEVICES_DISCONNECTED: 648 removeMessages(DEVICES_DISCONNECT_TIMEOUT); 649 finishSwitchingOff(); 650 break; 651 case DEVICES_DISCONNECT_TIMEOUT: 652 finishSwitchingOff(); 653 Log.e(TAG, "Devices fail to disconnect, reseting..."); 654 transitionTo(mHotOff); 655 deferMessage(obtainMessage(TURN_COLD)); 656 for (IBluetoothStateChangeCallback c: 657 mBluetoothService.getApplicationStateChangeCallbacks()) { 658 perProcessCallback(false, c); 659 deferMessage(obtainMessage(PER_PROCESS_TURN_ON, c)); 660 } 661 break; 662 case PER_PROCESS_TURN_OFF: 663 perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj); 664 if (mBluetoothService.isApplicationStateChangeTrackerEmpty()) { 665 mBluetoothService.switchConnectable(false); 666 sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); 667 } 668 break; 669 case AIRPLANE_MODE_ON: 670 mBluetoothService.switchConnectable(false); 671 sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); 672 allProcessesCallback(false); 673 // we turn all the way to PowerOff with AIRPLANE_MODE_ON 674 deferMessage(obtainMessage(AIRPLANE_MODE_ON)); 675 break; 676 case USER_TURN_OFF: 677 Log.w(TAG, "PerProcessState received: " + message.what); 678 break; 679 default: 680 return NOT_HANDLED; 681 } 682 return retValue; 683 } 684 } 685 686 private void finishSwitchingOff() { 687 mBluetoothService.finishDisable(); 688 broadcastState(BluetoothAdapter.STATE_OFF); 689 mBluetoothService.cleanupAfterFinishDisable(); 690 } 691 692 private void shutoffBluetooth() { 693 mBluetoothService.shutoffBluetooth(); 694 mEventLoop.stop(); 695 mBluetoothService.cleanNativeAfterShutoffBluetooth(); 696 } 697 698 private void perProcessCallback(boolean on, IBluetoothStateChangeCallback c) { 699 if (c == null) return; 700 701 try { 702 c.onBluetoothStateChange(on); 703 } catch (RemoteException e) {} 704 } 705 706 private void allProcessesCallback(boolean on) { 707 for (IBluetoothStateChangeCallback c: 708 mBluetoothService.getApplicationStateChangeCallbacks()) { 709 perProcessCallback(on, c); 710 } 711 if (!on) { 712 mBluetoothService.clearApplicationStateChangeTracker(); 713 } 714 } 715 716 /** 717 * Return the public BluetoothAdapter state 718 */ 719 int getBluetoothAdapterState() { 720 return mPublicState; 721 } 722 723 BluetoothEventLoop getBluetoothEventLoop() { 724 return mEventLoop; 725 } 726 727 private void persistSwitchSetting(boolean setOn) { 728 long origCallerIdentityToken = Binder.clearCallingIdentity(); 729 Settings.Secure.putInt(mContext.getContentResolver(), 730 Settings.Secure.BLUETOOTH_ON, 731 setOn ? 1 : 0); 732 Binder.restoreCallingIdentity(origCallerIdentityToken); 733 } 734 735 private boolean getBluetoothPersistedSetting() { 736 ContentResolver contentResolver = mContext.getContentResolver(); 737 return (Settings.Secure.getInt(contentResolver, 738 Settings.Secure.BLUETOOTH_ON, 0) > 0); 739 } 740 741 private void broadcastState(int newState) { 742 743 log("Bluetooth state " + mPublicState + " -> " + newState); 744 if (mPublicState == newState) { 745 return; 746 } 747 748 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); 749 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mPublicState); 750 intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); 751 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 752 mPublicState = newState; 753 754 mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); 755 } 756 757 /** 758 * bluetoothd has crashed and recovered, the adapter state machine has to 759 * reset itself and try to return to previous state 760 */ 761 private void recoverStateMachine(int what, Object obj) { 762 Log.e(TAG, "Get unexpected power on event, reset with: " + what); 763 transitionTo(mHotOff); 764 deferMessage(obtainMessage(TURN_COLD)); 765 deferMessage(obtainMessage(what, obj)); 766 } 767 768 private void dump(PrintWriter pw) { 769 IState currentState = getCurrentState(); 770 if (currentState == mPowerOff) { 771 pw.println("Bluetooth OFF - power down\n"); 772 } else if (currentState == mWarmUp) { 773 pw.println("Bluetooth OFF - warm up\n"); 774 } else if (currentState == mHotOff) { 775 pw.println("Bluetooth OFF - hot but off\n"); 776 } else if (currentState == mSwitching) { 777 pw.println("Bluetooth Switching\n"); 778 } else if (currentState == mBluetoothOn) { 779 pw.println("Bluetooth ON\n"); 780 } else { 781 pw.println("ERROR: Bluetooth UNKNOWN STATE "); 782 } 783 } 784 785 private static void log(String msg) { 786 Log.d(TAG, msg); 787 } 788 } 789