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