summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
authorJaikumar Ganesh <jaikumar@google.com>2010-07-19 16:28:27 -0700
committerJaikumar Ganesh <jaikumar@google.com>2010-07-21 10:43:02 -0700
commitde07503a382e81ba82f4cd4dee81ff2fbf3295bc (patch)
treefc3d73fe306195e9c4080901d74e635323bd3db0 /core/java
parentc3ee99d9eb7e8b4b20c2b8f1c548373e1017e0d3 (diff)
downloadframeworks_base-de07503a382e81ba82f4cd4dee81ff2fbf3295bc.zip
frameworks_base-de07503a382e81ba82f4cd4dee81ff2fbf3295bc.tar.gz
frameworks_base-de07503a382e81ba82f4cd4dee81ff2fbf3295bc.tar.bz2
Add HID to the state machine and add native call backs.
Change-Id: Ib9f3e476d4176bc04e23e7674dc54aa5a6417308
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/bluetooth/BluetoothDeviceProfileState.java253
-rw-r--r--core/java/android/bluetooth/BluetoothProfileState.java12
-rw-r--r--core/java/android/server/BluetoothEventLoop.java20
-rw-r--r--core/java/android/server/BluetoothService.java39
4 files changed, 311 insertions, 13 deletions
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index 8e655e2..1fd7151 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -58,19 +58,24 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
private static final String TAG = "BluetoothDeviceProfileState";
private static final boolean DBG = true; //STOPSHIP - Change to false
+ // TODO(): Restructure the state machine to make it scalable with regard to profiles.
public static final int CONNECT_HFP_OUTGOING = 1;
public static final int CONNECT_HFP_INCOMING = 2;
public static final int CONNECT_A2DP_OUTGOING = 3;
public static final int CONNECT_A2DP_INCOMING = 4;
+ public static final int CONNECT_HID_OUTGOING = 5;
+ public static final int CONNECT_HID_INCOMING = 6;
- public static final int DISCONNECT_HFP_OUTGOING = 5;
- private static final int DISCONNECT_HFP_INCOMING = 6;
- public static final int DISCONNECT_A2DP_OUTGOING = 7;
- public static final int DISCONNECT_A2DP_INCOMING = 8;
+ public static final int DISCONNECT_HFP_OUTGOING = 50;
+ private static final int DISCONNECT_HFP_INCOMING = 51;
+ public static final int DISCONNECT_A2DP_OUTGOING = 52;
+ public static final int DISCONNECT_A2DP_INCOMING = 53;
+ public static final int DISCONNECT_HID_OUTGOING = 54;
+ public static final int DISCONNECT_HID_INCOMING = 55;
- public static final int UNPAIR = 9;
- public static final int AUTO_CONNECT_PROFILES = 10;
- public static final int TRANSITION_TO_STABLE = 11;
+ public static final int UNPAIR = 100;
+ public static final int AUTO_CONNECT_PROFILES = 101;
+ public static final int TRANSITION_TO_STABLE = 102;
private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs
@@ -79,6 +84,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
private IncomingHandsfree mIncomingHandsfree = new IncomingHandsfree();
private IncomingA2dp mIncomingA2dp = new IncomingA2dp();
private OutgoingA2dp mOutgoingA2dp = new OutgoingA2dp();
+ private OutgoingHid mOutgoingHid = new OutgoingHid();
+ private IncomingHid mIncomingHid = new IncomingHid();
private Context mContext;
private BluetoothService mService;
@@ -89,6 +96,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
private BluetoothDevice mDevice;
private int mHeadsetState;
private int mA2dpState;
+ private int mHidState;
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -125,6 +133,19 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
newState == BluetoothA2dp.STATE_DISCONNECTED) {
sendMessage(TRANSITION_TO_STABLE);
}
+ } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0);
+ int oldState =
+ intent.getIntExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, 0);
+ mHidState = newState;
+ if (oldState == BluetoothInputDevice.STATE_CONNECTED &&
+ newState == BluetoothInputDevice.STATE_DISCONNECTED) {
+ sendMessage(DISCONNECT_HID_INCOMING);
+ }
+ if (newState == BluetoothInputDevice.STATE_CONNECTED ||
+ newState == BluetoothInputDevice.STATE_DISCONNECTED) {
+ sendMessage(TRANSITION_TO_STABLE);
+ }
} else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
if (!getCurrentState().equals(mBondedDevice)) {
Log.e(TAG, "State is: " + getCurrentState());
@@ -165,6 +186,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
addState(mIncomingHandsfree);
addState(mIncomingA2dp);
addState(mOutgoingA2dp);
+ addState(mOutgoingHid);
+ addState(mIncomingHid);
setInitialState(mBondedDevice);
IntentFilter filter = new IntentFilter();
@@ -172,6 +195,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+ filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, filter);
@@ -224,6 +248,14 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
case DISCONNECT_A2DP_INCOMING:
transitionTo(mIncomingA2dp);
break;
+ case CONNECT_HID_OUTGOING:
+ case DISCONNECT_HID_OUTGOING:
+ transitionTo(mOutgoingHid);
+ break;
+ case CONNECT_HID_INCOMING:
+ case DISCONNECT_HID_INCOMING:
+ transitionTo(mIncomingHid);
+ break;
case UNPAIR:
if (mHeadsetState != BluetoothHeadset.STATE_DISCONNECTED) {
sendMessage(DISCONNECT_HFP_OUTGOING);
@@ -233,6 +265,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
sendMessage(DISCONNECT_A2DP_OUTGOING);
deferMessage(message);
break;
+ } else if (mHidState != BluetoothInputDevice.STATE_DISCONNECTED) {
+ sendMessage(DISCONNECT_HID_OUTGOING);
+ deferMessage(message);
+ break;
}
processCommand(UNPAIR);
break;
@@ -254,6 +290,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
mA2dpService.getConnectedSinks().length == 0) {
mA2dpService.connectSink(mDevice);
}
+ if (mService.getInputDevicePriority(mDevice) ==
+ BluetoothInputDevice.PRIORITY_AUTO_CONNECT) {
+ mService.connectInputDevice(mDevice);
+ }
}
break;
case TRANSITION_TO_STABLE:
@@ -342,6 +382,23 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
deferMessage(deferMsg);
}
break;
+ case CONNECT_HID_OUTGOING:
+ case DISCONNECT_HID_OUTGOING:
+ deferMessage(message);
+ break;
+ case CONNECT_HID_INCOMING:
+ transitionTo(mIncomingHid);
+ if (mStatus) {
+ deferMsg.what = mCommand;
+ deferMessage(deferMsg);
+ }
+ break;
+ case DISCONNECT_HID_INCOMING:
+ if (mStatus) {
+ deferMsg.what = mCommand;
+ deferMessage(deferMsg);
+ }
+ break; // ignore
case UNPAIR:
case AUTO_CONNECT_PROFILES:
deferMessage(message);
@@ -409,6 +466,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
// If this causes incoming HFP to fail, it is more of a headset problem
// since both connections are incoming ones.
break;
+ case CONNECT_HID_OUTGOING:
+ case DISCONNECT_HID_OUTGOING:
+ deferMessage(message);
+ break;
+ case CONNECT_HID_INCOMING:
+ case DISCONNECT_HID_INCOMING:
+ break; // ignore
case UNPAIR:
case AUTO_CONNECT_PROFILES:
deferMessage(message);
@@ -496,6 +560,23 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
case DISCONNECT_A2DP_INCOMING:
// Ignore, will be handled by Bluez
break;
+ case CONNECT_HID_OUTGOING:
+ case DISCONNECT_HID_OUTGOING:
+ deferMessage(message);
+ break;
+ case CONNECT_HID_INCOMING:
+ transitionTo(mIncomingHid);
+ if (mStatus) {
+ deferMsg.what = mCommand;
+ deferMessage(deferMsg);
+ }
+ break;
+ case DISCONNECT_HID_INCOMING:
+ if (mStatus) {
+ deferMsg.what = mCommand;
+ deferMessage(deferMsg);
+ }
+ break; // ignore
case UNPAIR:
case AUTO_CONNECT_PROFILES:
deferMessage(message);
@@ -561,6 +642,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
case DISCONNECT_A2DP_INCOMING:
// Ignore, will be handled by Bluez
break;
+ case CONNECT_HID_OUTGOING:
+ case DISCONNECT_HID_OUTGOING:
+ deferMessage(message);
+ break;
+ case CONNECT_HID_INCOMING:
+ case DISCONNECT_HID_INCOMING:
+ break; // ignore
case UNPAIR:
case AUTO_CONNECT_PROFILES:
deferMessage(message);
@@ -576,6 +664,143 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
}
+ private class OutgoingHid extends HierarchicalState {
+ private boolean mStatus = false;
+ private int mCommand;
+
+ @Override
+ protected void enter() {
+ log("Entering OutgoingHid state with: " + getCurrentMessage().what);
+ mCommand = getCurrentMessage().what;
+ if (mCommand != CONNECT_HID_OUTGOING &&
+ mCommand != DISCONNECT_HID_OUTGOING) {
+ Log.e(TAG, "Error: OutgoingHid state with command:" + mCommand);
+ }
+ mStatus = processCommand(mCommand);
+ if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+ }
+
+ @Override
+ protected boolean processMessage(Message message) {
+ log("OutgoingHid State->Processing Message: " + message.what);
+ Message deferMsg = new Message();
+ switch(message.what) {
+ // defer all outgoing messages
+ case CONNECT_HFP_OUTGOING:
+ case CONNECT_A2DP_OUTGOING:
+ case CONNECT_HID_OUTGOING:
+ case DISCONNECT_HFP_OUTGOING:
+ case DISCONNECT_A2DP_OUTGOING:
+ case DISCONNECT_HID_OUTGOING:
+ deferMessage(message);
+ break;
+
+ case CONNECT_HFP_INCOMING:
+ transitionTo(mIncomingHandsfree);
+ case CONNECT_A2DP_INCOMING:
+ transitionTo(mIncomingA2dp);
+
+ // Don't cancel HID outgoing as there is no guarantee it
+ // will get canceled.
+ // It might already be connected but we might not have got the
+ // INPUT_DEVICE_STATE_CHANGE. Hence, no point disconnecting here.
+ // The worst case, the connection will fail, retry.
+ if (mStatus) {
+ deferMsg.what = mCommand;
+ deferMessage(deferMsg);
+ }
+ break;
+ case CONNECT_HID_INCOMING:
+ // Bluez will take care of the conflicts
+ transitionTo(mIncomingHid);
+ break;
+
+ case DISCONNECT_HFP_INCOMING:
+ case DISCONNECT_A2DP_INCOMING:
+ // At this point, we are already disconnected
+ // with HFP. Sometimes HID connection can
+ // fail due to the disconnection of HFP. So add a retry
+ // for the HID.
+ if (mStatus) {
+ deferMsg.what = mCommand;
+ deferMessage(deferMsg);
+ }
+ break;
+ case DISCONNECT_HID_INCOMING:
+ // Ignore, will be handled by Bluez
+ break;
+
+ case UNPAIR:
+ case AUTO_CONNECT_PROFILES:
+ deferMessage(message);
+ break;
+ case TRANSITION_TO_STABLE:
+ transitionTo(mBondedDevice);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ private class IncomingHid extends HierarchicalState {
+ private boolean mStatus = false;
+ private int mCommand;
+
+ @Override
+ protected void enter() {
+ log("Entering IncomingHid state with: " + getCurrentMessage().what);
+ mCommand = getCurrentMessage().what;
+ if (mCommand != CONNECT_HID_INCOMING &&
+ mCommand != DISCONNECT_HID_INCOMING) {
+ Log.e(TAG, "Error: IncomingHid state with command:" + mCommand);
+ }
+ mStatus = processCommand(mCommand);
+ if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+ }
+
+ @Override
+ protected boolean processMessage(Message message) {
+ log("IncomingHid State->Processing Message: " + message.what);
+ Message deferMsg = new Message();
+ switch(message.what) {
+ case CONNECT_HFP_OUTGOING:
+ case CONNECT_HFP_INCOMING:
+ case DISCONNECT_HFP_OUTGOING:
+ case CONNECT_A2DP_INCOMING:
+ case CONNECT_A2DP_OUTGOING:
+ case DISCONNECT_A2DP_OUTGOING:
+ case CONNECT_HID_OUTGOING:
+ case CONNECT_HID_INCOMING:
+ case DISCONNECT_HID_OUTGOING:
+ deferMessage(message);
+ break;
+ case DISCONNECT_HFP_INCOMING:
+ // Shouldn't happen but if does, we can handle it.
+ // Depends if the headset can handle it.
+ // Incoming HID will be handled by Bluez, Disconnect HFP
+ // the socket would have already been closed.
+ // ignore
+ break;
+ case DISCONNECT_HID_INCOMING:
+ case DISCONNECT_A2DP_INCOMING:
+ // Ignore, will be handled by Bluez
+ break;
+ case UNPAIR:
+ case AUTO_CONNECT_PROFILES:
+ deferMessage(message);
+ break;
+ case TRANSITION_TO_STABLE:
+ transitionTo(mBondedDevice);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
synchronized void cancelCommand(int command) {
if (command == CONNECT_HFP_OUTGOING ) {
@@ -619,6 +844,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
case CONNECT_A2DP_INCOMING:
// ignore, Bluez takes care
return true;
+ case CONNECT_HID_OUTGOING:
+ return mService.connectInputDeviceInternal(mDevice);
+ case CONNECT_HID_INCOMING:
+ return true;
case DISCONNECT_HFP_OUTGOING:
if (!mHeadsetServiceConnected) {
deferHeadsetMessage(command);
@@ -645,6 +874,15 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
return mA2dpService.disconnectSinkInternal(mDevice);
}
break;
+ case DISCONNECT_HID_INCOMING:
+ // ignore
+ return true;
+ case DISCONNECT_HID_OUTGOING:
+ if (mService.getInputDevicePriority(mDevice) ==
+ BluetoothInputDevice.PRIORITY_AUTO_CONNECT) {
+ mService.setInputDevicePriority(mDevice, BluetoothInputDevice.PRIORITY_ON);
+ }
+ return mService.disconnectInputDeviceInternal(mDevice);
case UNPAIR:
return mService.removeBondInternal(mDevice.getAddress());
default:
@@ -653,6 +891,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
return false;
}
+
/*package*/ BluetoothDevice getDevice() {
return mDevice;
}
diff --git a/core/java/android/bluetooth/BluetoothProfileState.java b/core/java/android/bluetooth/BluetoothProfileState.java
index 946dcaa..ad70d0d 100644
--- a/core/java/android/bluetooth/BluetoothProfileState.java
+++ b/core/java/android/bluetooth/BluetoothProfileState.java
@@ -43,8 +43,9 @@ public class BluetoothProfileState extends HierarchicalStateMachine {
private static final boolean DBG = true; // STOPSHIP - change to false.
private static final String TAG = "BluetoothProfileState";
- public static int HFP = 0;
- public static int A2DP = 1;
+ public static final int HFP = 0;
+ public static final int A2DP = 1;
+ public static final int HID = 2;
private static int TRANSITION_TO_STABLE = 100;
@@ -70,6 +71,12 @@ public class BluetoothProfileState extends HierarchicalStateMachine {
newState == BluetoothA2dp.STATE_DISCONNECTED)) {
sendMessage(TRANSITION_TO_STABLE);
}
+ } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0);
+ if (mProfile == HID && (newState == BluetoothInputDevice.STATE_CONNECTED ||
+ newState == BluetoothInputDevice.STATE_DISCONNECTED)) {
+ sendMessage(TRANSITION_TO_STABLE);
+ }
}
}
};
@@ -84,6 +91,7 @@ public class BluetoothProfileState extends HierarchicalStateMachine {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+ filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
context.registerReceiver(mBroadcastReceiver, filter);
}
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 9b7a73d..eb9b62b 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -693,6 +693,26 @@ class BluetoothEventLoop {
}
}
+ private void onInputDeviceConnectionResult(String path, boolean result) {
+ // Success case gets handled by Property Change signal
+ if (!result) {
+ String address = mBluetoothService.getAddressFromObjectPath(path);
+ if (address == null) return;
+
+ boolean connected = false;
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ int state = mBluetoothService.getInputDeviceState(device);
+ if (state == BluetoothInputDevice.STATE_CONNECTING) {
+ connected = false;
+ } else if (state == BluetoothInputDevice.STATE_DISCONNECTING) {
+ connected = true;
+ } else {
+ Log.e(TAG, "Error onInputDeviceConnectionResult. State is:" + state);
+ }
+ mBluetoothService.handleInputDevicePropertyChange(address, connected);
+ }
+ }
+
private void onRestartRequired() {
if (mBluetoothService.isEnabled()) {
Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index d7cd933..68eba6e 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -132,6 +132,7 @@ public class BluetoothService extends IBluetooth.Stub {
private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
private final BluetoothProfileState mA2dpProfileState;
private final BluetoothProfileState mHfpProfileState;
+ private final BluetoothProfileState mHidProfileState;
private BluetoothA2dpService mA2dpService;
private final HashMap<BluetoothDevice, Integer> mInputDevices;
@@ -196,9 +197,11 @@ public class BluetoothService extends IBluetooth.Stub {
mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
+ mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID);
mHfpProfileState.start();
mA2dpProfileState.start();
+ mHidProfileState.start();
IntentFilter filter = new IntentFilter();
registerForAirplaneMode(filter);
@@ -1241,13 +1244,27 @@ public class BluetoothService extends IBluetooth.Stub {
getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
return false;
}
- if(connectInputDeviceNative(objectPath)) {
- handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
+ if (state != null) {
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING;
+ msg.obj = state;
+ mHidProfileState.sendMessage(msg);
return true;
}
return false;
}
+ public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
+ String objectPath = getObjectPathFromAddress(device.getAddress());
+ handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
+ if (!connectInputDeviceNative(objectPath)) {
+ handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED);
+ return false;
+ }
+ return true;
+ }
+
public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
@@ -1256,13 +1273,27 @@ public class BluetoothService extends IBluetooth.Stub {
if (objectPath == null || getConnectedInputDevices().length == 0) {
return false;
}
- if(disconnectInputDeviceNative(objectPath)) {
- handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
+ BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
+ if (state != null) {
+ Message msg = new Message();
+ msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING;
+ msg.obj = state;
+ mHidProfileState.sendMessage(msg);
return true;
}
return false;
}
+ public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
+ String objectPath = getObjectPathFromAddress(device.getAddress());
+ handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
+ if (!disconnectInputDeviceNative(objectPath)) {
+ handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED);
+ return false;
+ }
+ return true;
+ }
+
public synchronized int getInputDeviceState(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");