blob: 11bdbfefa2b75dcfc12453aca7c03549379ddd8c [file] [log] [blame]
/*
* 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 ALL_DEVICES_DISCONNECTED = 7;
static final int ENABLE_TIMEOUT = 8;
private static final int DISCONNECT_TIMEOUT = 3000;
private static final int ENABLE_TIMEOUT_DELAY = 6000; // 6 secs
private final AdapterService mAdapterService;
private final Context mContext;
private final AdapterProperties mAdapterProperties;
private PendingCommandState mPendingCommandState = new PendingCommandState();
private OnState mOnState = new OnState();
private OffState mOffState = new OffState();
public AdapterState(AdapterService service, Context context,
AdapterProperties adapterProperties) {
super("BluetoothAdapterState:");
addState(mOnState);
addState(mOffState);
addState(mPendingCommandState);
setInitialState(mOffState);
mAdapterService = service;
mContext = context;
mAdapterProperties = adapterProperties;
}
private class OffState extends State {
@Override
public void enter() {
infoLog("Entering Off State");
}
@Override
public boolean processMessage(Message msg) {
switch(msg.what) {
case USER_TURN_ON:
int persist = msg.arg1;
if (persist == 1) mAdapterService.persistBluetoothSetting(true);
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 AIRPLANE_MODE_OFF:
{
if(mAdapterService.getBluetoothPersistedSetting()) {
Log.i(TAG, "OffState : Turning BT on after Airplane"+
" Mode OFF state");
sendIntent(BluetoothAdapter.STATE_TURNING_ON);
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:
case AIRPLANE_MODE_ON:
//ignore
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:
int persist = msg.arg1;
if (persist == 1) {
mAdapterService.persistBluetoothSetting(false);
}
//Fall Through
case AIRPLANE_MODE_ON:
sendIntent(BluetoothAdapter.STATE_TURNING_OFF);
if (mAdapterProperties.getConnectionState() !=
BluetoothAdapter.STATE_DISCONNECTED) {
sendMessageDelayed(ALL_DEVICES_DISCONNECTED,
DISCONNECT_TIMEOUT);
break;
}
//Fall Through
case ALL_DEVICES_DISCONNECTED:
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);
mAdapterProperties.onBluetoothReady();
sendIntent(BluetoothAdapter.STATE_ON);
transitionTo(mOnState);
break;
case DISABLED:
sendIntent(BluetoothAdapter.STATE_OFF);
transitionTo(mOffState);
break;
case ENABLE_TIMEOUT:
errorLog("Error enabling Bluetooth");
sendIntent(BluetoothAdapter.STATE_OFF);
transitionTo(mOffState);
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);
}
}