diff options
author | Jaikumar Ganesh <jaikumar@google.com> | 2010-07-19 16:28:27 -0700 |
---|---|---|
committer | Jaikumar Ganesh <jaikumar@google.com> | 2010-07-21 10:43:02 -0700 |
commit | de07503a382e81ba82f4cd4dee81ff2fbf3295bc (patch) | |
tree | fc3d73fe306195e9c4080901d74e635323bd3db0 /core/java | |
parent | c3ee99d9eb7e8b4b20c2b8f1c548373e1017e0d3 (diff) | |
download | frameworks_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.java | 253 | ||||
-rw-r--r-- | core/java/android/bluetooth/BluetoothProfileState.java | 12 | ||||
-rw-r--r-- | core/java/android/server/BluetoothEventLoop.java | 20 | ||||
-rw-r--r-- | core/java/android/server/BluetoothService.java | 39 |
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"); |