| /* |
| * Copyright (C) 2012 Google Inc. |
| */ |
| |
| package com.android.bluetooth.btservice; |
| |
| import android.bluetooth.BluetoothAdapter; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.os.Message; |
| 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 String TAG = "BluetoothAdapterState"; |
| |
| static final int USER_TURN_ON = 1; |
| static final int USER_TURN_OFF = 2; |
| static final int AIRPLANE_MODE_ON = 3; |
| static final int AIRPLANE_MODE_OFF = 4; |
| static final int ENABLED_READY = 5; |
| static final int DISABLED = 6; |
| static final int ENABLE_TIMEOUT = 7; |
| |
| private static final int ENABLE_TIMEOUT_DELAY = 6000; // 6 secs |
| |
| private AdapterService mAdapterService; |
| private Context mContext; |
| private AdapterProperties mAdapterProperties; |
| private boolean mPendingPersistEnable; |
| private PendingCommandState mPendingCommandState = new PendingCommandState(); |
| private OnState mOnState = new OnState(); |
| private OffState mOffState = new OffState(); |
| |
| public boolean isCurrentlyOn() { |
| return mOnState == getCurrentState(); |
| } |
| public AdapterState(AdapterService service, Context context, |
| AdapterProperties adapterProperties) { |
| super("BluetoothAdapterState:"); |
| addState(mOnState); |
| addState(mOffState); |
| addState(mPendingCommandState); |
| mAdapterService = service; |
| mContext = context; |
| mAdapterProperties = adapterProperties; |
| setInitialState(mOffState); |
| } |
| |
| public void cleanup() { |
| if(mAdapterProperties != null) |
| mAdapterProperties = null; |
| if(mAdapterService != null) |
| mAdapterService = null; |
| if(mContext != null) |
| mContext = null; |
| } |
| |
| private class OffState extends State { |
| @Override |
| public void enter() { |
| infoLog("Entering Off State"); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| if (msg.what == SM_QUIT_CMD) { |
| Log.d(TAG, "Received quit request..."); |
| return false; |
| } |
| |
| switch(msg.what) { |
| case USER_TURN_ON: |
| int persist = msg.arg1; |
| //if (persist == 1) mAdapterService.persistBluetoothSetting(true); |
| //Persist enable state only once enable completes |
| mPendingPersistEnable = (persist ==1); |
| sendIntent(BluetoothAdapter.STATE_TURNING_ON); |
| boolean ret = mAdapterService.enableNative(); |
| if (!ret) { |
| Log.e(TAG, "Error while turning Bluetooth On"); |
| sendIntent(BluetoothAdapter.STATE_OFF); |
| } else { |
| sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY); |
| transitionTo(mPendingCommandState); |
| } |
| break; |
| case USER_TURN_OFF: |
| break; |
| default: |
| Log.e(TAG, "Received unhandled state: " + msg.what); |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| private class OnState extends State { |
| @Override |
| public void enter() { |
| infoLog("Entering On State"); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| switch(msg.what) { |
| case USER_TURN_OFF: |
| sendIntent(BluetoothAdapter.STATE_TURNING_OFF); |
| // Invoke onBluetoothDisable which shall trigger a |
| // setScanMode to SCAN_MODE_NONE |
| mAdapterProperties.onBluetoothDisable(); |
| |
| // stack takes care of disconnecting profiles, send disable here |
| boolean ret = mAdapterService.disableNative(); |
| if (!ret) { |
| Log.e(TAG, "Error while turning Bluetooth Off"); |
| sendIntent(BluetoothAdapter.STATE_ON); |
| } else { |
| transitionTo(mPendingCommandState); |
| } |
| break; |
| case USER_TURN_ON: |
| //case AIRPLANE_MODE_OFF: |
| //ignore |
| break; |
| default: |
| Log.e(TAG, "Received unhandled state: " + msg.what); |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| private class PendingCommandState extends State { |
| @Override |
| public void enter() { |
| infoLog("Entering PendingCommandState State"); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| switch (msg.what) { |
| case USER_TURN_ON: |
| case USER_TURN_OFF: |
| case AIRPLANE_MODE_ON: |
| case AIRPLANE_MODE_OFF: |
| deferMessage(msg); |
| break; |
| case ENABLED_READY: |
| removeMessages(ENABLE_TIMEOUT); |
| transitionTo(mOnState); |
| mAdapterProperties.onBluetoothReady(); |
| mAdapterService.onBluetoothEnabled(); |
| break; |
| case DISABLED: |
| transitionTo(mOffState); |
| mAdapterService.onBluetoothDisabled(); |
| break; |
| case ENABLE_TIMEOUT: |
| errorLog("Error enabling Bluetooth"); |
| transitionTo(mOffState); |
| mAdapterService.onBluetoothEnableTimeout(); |
| break; |
| default: |
| Log.e(TAG, "Received unhandled event:" + msg.what); |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| |
| private void sendIntent(int newState) { |
| int oldState = mAdapterProperties.getState(); |
| Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); |
| intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, oldState); |
| intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); |
| mAdapterProperties.setState(newState); |
| |
| mContext.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM); |
| infoLog("Bluetooth State Change Intent: " + 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 errorLog(String msg) { |
| Log.e(TAG, msg); |
| } |
| } |