| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.bluetooth.btservice; |
| |
| import android.bluetooth.BluetoothAdapter; |
| import android.os.Message; |
| import android.os.UserManager; |
| import android.util.Log; |
| |
| import com.android.internal.util.State; |
| import com.android.internal.util.StateMachine; |
| |
| /** |
| * This state machine handles Bluetooth Adapter State. |
| * States: |
| * {@link OnState} : Bluetooth is on at this state |
| * {@link OffState}: Bluetooth is off at this state. This is the initial |
| * state. |
| * {@link PendingCommandState} : An enable / disable operation is pending. |
| * TODO(BT): Add per process on state. |
| */ |
| |
| final class AdapterState extends StateMachine { |
| private static final boolean DBG = true; |
| private static final boolean VDBG = true; |
| private static final String TAG = "BluetoothAdapterState"; |
| |
| static final int BLE_TURN_ON = 0; |
| static final int USER_TURN_ON = 1; |
| static final int BREDR_STARTED=2; |
| static final int ENABLED_READY = 3; |
| static final int BLE_STARTED=4; |
| |
| static final int USER_TURN_OFF = 20; |
| static final int BEGIN_DISABLE = 21; |
| static final int ALL_DEVICES_DISCONNECTED = 22; |
| static final int BLE_TURN_OFF = 23; |
| |
| static final int DISABLED = 24; |
| static final int BLE_STOPPED=25; |
| static final int BREDR_STOPPED = 26; |
| |
| static final int BREDR_START_TIMEOUT = 100; |
| static final int ENABLE_TIMEOUT = 101; |
| static final int DISABLE_TIMEOUT = 103; |
| static final int BLE_STOP_TIMEOUT = 104; |
| static final int SET_SCAN_MODE_TIMEOUT = 105; |
| static final int BLE_START_TIMEOUT = 106; |
| static final int BREDR_STOP_TIMEOUT = 107; |
| |
| static final int USER_TURN_OFF_DELAY_MS=500; |
| |
| //TODO: tune me |
| private static final int ENABLE_TIMEOUT_DELAY = 12000; |
| private static final int DISABLE_TIMEOUT_DELAY = 8000; |
| private static final int BREDR_START_TIMEOUT_DELAY = 4000; |
| //BLE_START_TIMEOUT can happen quickly as it just a start gattservice |
| private static final int BLE_START_TIMEOUT_DELAY = 2000; //To start GattService |
| private static final int BLE_STOP_TIMEOUT_DELAY = 2000; |
| //BREDR_STOP_TIMEOUT can < STOP_TIMEOUT |
| private static final int BREDR_STOP_TIMEOUT_DELAY = 4000; |
| private static final int PROPERTY_OP_DELAY =2000; |
| private AdapterService mAdapterService; |
| private AdapterProperties mAdapterProperties; |
| private PendingCommandState mPendingCommandState = new PendingCommandState(); |
| private OnState mOnState = new OnState(); |
| private OffState mOffState = new OffState(); |
| private BleOnState mBleOnState = new BleOnState(); |
| |
| public boolean isTurningOn() { |
| boolean isTurningOn= mPendingCommandState.isTurningOn(); |
| verboseLog("isTurningOn()=" + isTurningOn); |
| return isTurningOn; |
| } |
| |
| public boolean isBleTurningOn() { |
| boolean isBleTurningOn= mPendingCommandState.isBleTurningOn(); |
| verboseLog("isBleTurningOn()=" + isBleTurningOn); |
| return isBleTurningOn; |
| } |
| |
| public boolean isBleTurningOff() { |
| boolean isBleTurningOff = mPendingCommandState.isBleTurningOff(); |
| verboseLog("isBleTurningOff()=" + isBleTurningOff); |
| return isBleTurningOff; |
| } |
| |
| public boolean isTurningOff() { |
| boolean isTurningOff= mPendingCommandState.isTurningOff(); |
| verboseLog("isTurningOff()=" + isTurningOff); |
| return isTurningOff; |
| } |
| |
| private AdapterState(AdapterService service, AdapterProperties adapterProperties) { |
| super("BluetoothAdapterState:"); |
| addState(mOnState); |
| addState(mBleOnState); |
| addState(mOffState); |
| addState(mPendingCommandState); |
| mAdapterService = service; |
| mAdapterProperties = adapterProperties; |
| setInitialState(mOffState); |
| } |
| |
| public static AdapterState make(AdapterService service, AdapterProperties adapterProperties) { |
| Log.d(TAG, "make() - Creating AdapterState"); |
| AdapterState as = new AdapterState(service, adapterProperties); |
| as.start(); |
| return as; |
| } |
| |
| public void doQuit() { |
| quitNow(); |
| } |
| |
| public void cleanup() { |
| if(mAdapterProperties != null) |
| mAdapterProperties = null; |
| if(mAdapterService != null) |
| mAdapterService = null; |
| } |
| |
| private class OffState extends State { |
| @Override |
| public void enter() { |
| infoLog("Entering OffState"); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| AdapterService adapterService = mAdapterService; |
| if (adapterService == null) { |
| errorLog("Received message in OffState after cleanup: " + msg.what); |
| return false; |
| } |
| |
| debugLog("Current state: OFF, message: " + msg.what); |
| |
| switch(msg.what) { |
| case BLE_TURN_ON: |
| notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_TURNING_ON); |
| mPendingCommandState.setBleTurningOn(true); |
| transitionTo(mPendingCommandState); |
| sendMessageDelayed(BLE_START_TIMEOUT, BLE_START_TIMEOUT_DELAY); |
| adapterService.BleOnProcessStart(); |
| break; |
| |
| case USER_TURN_OFF: |
| //TODO: Handle case of service started and stopped without enable |
| break; |
| |
| default: |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| private class BleOnState extends State { |
| @Override |
| public void enter() { |
| infoLog("Entering BleOnState"); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| |
| AdapterService adapterService = mAdapterService; |
| AdapterProperties adapterProperties = mAdapterProperties; |
| if ((adapterService == null) || (adapterProperties == null)) { |
| errorLog("Received message in BleOnState after cleanup: " + msg.what); |
| return false; |
| } |
| |
| debugLog("Current state: BLE ON, message: " + msg.what); |
| |
| switch(msg.what) { |
| case USER_TURN_ON: |
| notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_ON); |
| mPendingCommandState.setTurningOn(true); |
| transitionTo(mPendingCommandState); |
| sendMessageDelayed(BREDR_START_TIMEOUT, BREDR_START_TIMEOUT_DELAY); |
| adapterService.startCoreServices(); |
| break; |
| |
| case USER_TURN_OFF: |
| notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_TURNING_OFF); |
| mPendingCommandState.setBleTurningOff(true); |
| adapterProperties.onBleDisable(); |
| transitionTo(mPendingCommandState); |
| sendMessageDelayed(DISABLE_TIMEOUT, DISABLE_TIMEOUT_DELAY); |
| boolean ret = adapterService.disableNative(); |
| if (!ret) { |
| removeMessages(DISABLE_TIMEOUT); |
| errorLog("Error while calling disableNative"); |
| //FIXME: what about post enable services |
| mPendingCommandState.setBleTurningOff(false); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON); |
| } |
| break; |
| |
| default: |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| private class OnState extends State { |
| @Override |
| public void enter() { |
| infoLog("Entering OnState"); |
| |
| AdapterService adapterService = mAdapterService; |
| if (adapterService == null) { |
| errorLog("Entered OnState after cleanup"); |
| return; |
| } |
| adapterService.updateUuids(); |
| adapterService.autoConnect(); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| AdapterProperties adapterProperties = mAdapterProperties; |
| if (adapterProperties == null) { |
| errorLog("Received message in OnState after cleanup: " + msg.what); |
| return false; |
| } |
| |
| debugLog("Current state: ON, message: " + msg.what); |
| |
| switch(msg.what) { |
| case BLE_TURN_OFF: |
| notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_OFF); |
| mPendingCommandState.setTurningOff(true); |
| transitionTo(mPendingCommandState); |
| |
| // Invoke onBluetoothDisable which shall trigger a |
| // setScanMode to SCAN_MODE_NONE |
| Message m = obtainMessage(SET_SCAN_MODE_TIMEOUT); |
| sendMessageDelayed(m, PROPERTY_OP_DELAY); |
| adapterProperties.onBluetoothDisable(); |
| break; |
| |
| case USER_TURN_ON: |
| break; |
| |
| default: |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| private class PendingCommandState extends State { |
| private boolean mIsTurningOn; |
| private boolean mIsTurningOff; |
| private boolean mIsBleTurningOn; |
| private boolean mIsBleTurningOff; |
| |
| public void enter() { |
| infoLog("Entering PendingCommandState"); |
| } |
| |
| public void setTurningOn(boolean isTurningOn) { |
| mIsTurningOn = isTurningOn; |
| } |
| |
| public boolean isTurningOn() { |
| return mIsTurningOn; |
| } |
| |
| public void setTurningOff(boolean isTurningOff) { |
| mIsTurningOff = isTurningOff; |
| } |
| |
| public boolean isTurningOff() { |
| return mIsTurningOff; |
| } |
| |
| public void setBleTurningOn(boolean isBleTurningOn) { |
| mIsBleTurningOn = isBleTurningOn; |
| } |
| |
| public boolean isBleTurningOn() { |
| return mIsBleTurningOn; |
| } |
| |
| public void setBleTurningOff(boolean isBleTurningOff) { |
| mIsBleTurningOff = isBleTurningOff; |
| } |
| |
| public boolean isBleTurningOff() { |
| return mIsBleTurningOff; |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| |
| boolean isTurningOn= isTurningOn(); |
| boolean isTurningOff = isTurningOff(); |
| boolean isBleTurningOn = isBleTurningOn(); |
| boolean isBleTurningOff = isBleTurningOff(); |
| |
| AdapterService adapterService = mAdapterService; |
| AdapterProperties adapterProperties = mAdapterProperties; |
| if ((adapterService == null) || (adapterProperties == null)) { |
| errorLog("Received message in PendingCommandState after cleanup: " + msg.what); |
| return false; |
| } |
| |
| debugLog("Current state: PENDING_COMMAND, message: " + msg.what); |
| |
| switch (msg.what) { |
| case USER_TURN_ON: |
| if (isBleTurningOff || isTurningOff) { //TODO:do we need to send it after ble turn off also?? |
| infoLog("Deferring USER_TURN_ON request..."); |
| deferMessage(msg); |
| } |
| break; |
| |
| case USER_TURN_OFF: |
| if (isTurningOn || isBleTurningOn) { |
| infoLog("Deferring USER_TURN_OFF request..."); |
| deferMessage(msg); |
| } |
| break; |
| |
| case BLE_TURN_ON: |
| if (isTurningOff || isBleTurningOff) { |
| infoLog("Deferring BLE_TURN_ON request..."); |
| deferMessage(msg); |
| } |
| break; |
| |
| case BLE_TURN_OFF: |
| if (isTurningOn || isBleTurningOn) { |
| infoLog("Deferring BLE_TURN_OFF request..."); |
| deferMessage(msg); |
| } |
| break; |
| |
| case BLE_STARTED: |
| //Remove start timeout |
| removeMessages(BLE_START_TIMEOUT); |
| |
| //Enable |
| boolean isGuest = UserManager.get(mAdapterService).isGuestUser(); |
| if (!adapterService.enableNative(isGuest)) { |
| errorLog("Error while turning Bluetooth on"); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); |
| transitionTo(mOffState); |
| } else { |
| sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY); |
| } |
| break; |
| |
| case BREDR_STARTED: |
| //Remove start timeout |
| removeMessages(BREDR_START_TIMEOUT); |
| adapterProperties.onBluetoothReady(); |
| mPendingCommandState.setTurningOn(false); |
| transitionTo(mOnState); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_ON); |
| break; |
| |
| case ENABLED_READY: |
| removeMessages(ENABLE_TIMEOUT); |
| mPendingCommandState.setBleTurningOn(false); |
| transitionTo(mBleOnState); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON); |
| break; |
| |
| case SET_SCAN_MODE_TIMEOUT: |
| warningLog("Timeout while setting scan mode. Continuing with disable..."); |
| //Fall through |
| case BEGIN_DISABLE: |
| removeMessages(SET_SCAN_MODE_TIMEOUT); |
| sendMessageDelayed(BREDR_STOP_TIMEOUT, BREDR_STOP_TIMEOUT_DELAY); |
| adapterService.stopProfileServices(); |
| break; |
| |
| case DISABLED: |
| if (isTurningOn) { |
| removeMessages(ENABLE_TIMEOUT); |
| errorLog("Error enabling Bluetooth - hardware init failed?"); |
| mPendingCommandState.setTurningOn(false); |
| transitionTo(mOffState); |
| adapterService.stopProfileServices(); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); |
| break; |
| } |
| removeMessages(DISABLE_TIMEOUT); |
| sendMessageDelayed(BLE_STOP_TIMEOUT, BLE_STOP_TIMEOUT_DELAY); |
| if (adapterService.stopGattProfileService()) { |
| debugLog("Stopping Gatt profile services that were post enabled"); |
| break; |
| } |
| //Fall through if no services or services already stopped |
| case BLE_STOPPED: |
| removeMessages(BLE_STOP_TIMEOUT); |
| setBleTurningOff(false); |
| transitionTo(mOffState); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); |
| break; |
| |
| case BREDR_STOPPED: |
| removeMessages(BREDR_STOP_TIMEOUT); |
| setTurningOff(false); |
| transitionTo(mBleOnState); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON); |
| break; |
| |
| case BLE_START_TIMEOUT: |
| errorLog("Error enabling Bluetooth (BLE start timeout)"); |
| mPendingCommandState.setBleTurningOn(false); |
| transitionTo(mOffState); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); |
| break; |
| |
| case BREDR_START_TIMEOUT: |
| errorLog("Error enabling Bluetooth (start timeout)"); |
| mPendingCommandState.setTurningOn(false); |
| adapterService.stopProfileServices(); |
| transitionTo(mBleOnState); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON); |
| break; |
| |
| case ENABLE_TIMEOUT: |
| errorLog("Error enabling Bluetooth (enable timeout)"); |
| mPendingCommandState.setBleTurningOn(false); |
| transitionTo(mOffState); |
| adapterService.stopProfileServices(); |
| adapterService.stopGattProfileService(); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); |
| break; |
| |
| case BREDR_STOP_TIMEOUT: |
| errorLog("Error stopping Bluetooth profiles (stop timeout)"); |
| mPendingCommandState.setTurningOff(false); |
| transitionTo(mBleOnState); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_BLE_ON); |
| break; |
| |
| case BLE_STOP_TIMEOUT: |
| errorLog("Error stopping Bluetooth profiles (BLE stop timeout)"); |
| mPendingCommandState.setTurningOff(false); |
| transitionTo(mOffState); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); |
| break; |
| |
| case DISABLE_TIMEOUT: |
| errorLog("Error disabling Bluetooth (disable timeout)"); |
| if (isTurningOn) |
| mPendingCommandState.setTurningOn(false); |
| adapterService.stopProfileServices(); |
| adapterService.stopGattProfileService(); |
| mPendingCommandState.setTurningOff(false); |
| setBleTurningOff(false); |
| transitionTo(mOffState); |
| notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); |
| break; |
| |
| default: |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| private void notifyAdapterStateChange(int newState) { |
| AdapterService adapterService = mAdapterService; |
| AdapterProperties adapterProperties = mAdapterProperties; |
| if ((adapterService == null) || (adapterProperties == null)) { |
| errorLog("notifyAdapterStateChange after cleanup:" + newState); |
| return; |
| } |
| |
| int oldState = adapterProperties.getState(); |
| adapterProperties.setState(newState); |
| infoLog("Bluetooth adapter state changed: " + oldState + "-> " + newState); |
| adapterService.updateAdapterState(oldState, newState); |
| } |
| |
| void stateChangeCallback(int status) { |
| if (status == AbstractionLayer.BT_STATE_OFF) { |
| sendMessage(DISABLED); |
| |
| } else if (status == AbstractionLayer.BT_STATE_ON) { |
| // We should have got the property change for adapter and remote devices. |
| sendMessage(ENABLED_READY); |
| |
| } else { |
| errorLog("Incorrect status in stateChangeCallback"); |
| } |
| } |
| |
| private void infoLog(String msg) { |
| if (DBG) Log.i(TAG, msg); |
| } |
| |
| private void debugLog(String msg) { |
| if (DBG) Log.d(TAG, msg); |
| } |
| |
| private void warningLog(String msg) { |
| if (DBG) Log.d(TAG, msg); |
| } |
| |
| private void verboseLog(String msg) { |
| if (VDBG) Log.v(TAG, msg); |
| } |
| |
| private void errorLog(String msg) { |
| Log.e(TAG, msg); |
| } |
| |
| } |