Add HID to the state machine and add native call backs.
Change-Id: Ib9f3e476d4176bc04e23e7674dc54aa5a6417308
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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?) - " +
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ static jmethodID method_onAgentAuthorize;
|
||||
static jmethodID method_onAgentCancel;
|
||||
|
||||
static jmethodID method_onInputDevicePropertyChanged;
|
||||
static jmethodID method_onInputDeviceConnectionResult;
|
||||
|
||||
typedef event_loop_native_data_t native_data_t;
|
||||
|
||||
@@ -120,7 +121,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
|
||||
"(Ljava/lang/String;II)V");
|
||||
method_onInputDevicePropertyChanged = env->GetMethodID(clazz, "onInputDevicePropertyChanged",
|
||||
"(Ljava/lang/String;[Ljava/lang/String;)V");
|
||||
|
||||
method_onInputDeviceConnectionResult = env->GetMethodID(clazz, "onInputDeviceConnectionResult",
|
||||
"(Ljava/lang/String;Z)V");
|
||||
|
||||
field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
|
||||
#endif
|
||||
@@ -1245,6 +1247,32 @@ done:
|
||||
env->DeleteLocalRef(addr);
|
||||
free(user);
|
||||
}
|
||||
|
||||
void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *n) {
|
||||
LOGV(__FUNCTION__);
|
||||
|
||||
native_data_t *nat = (native_data_t *)n;
|
||||
const char *path = (const char *)user;
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
JNIEnv *env;
|
||||
nat->vm->GetEnv((void**)&env, nat->envVer);
|
||||
|
||||
bool result = JNI_TRUE;
|
||||
if (dbus_set_error_from_message(&err, msg)) {
|
||||
LOG_AND_FREE_DBUS_ERROR(&err);
|
||||
result = JNI_FALSE;
|
||||
}
|
||||
LOGV("... Device Path = %s, result = %d", path, result);
|
||||
jstring jPath = env->NewStringUTF(path);
|
||||
env->CallVoidMethod(nat->me,
|
||||
method_onInputDeviceConnectionResult,
|
||||
jPath,
|
||||
result);
|
||||
env->DeleteLocalRef(jPath);
|
||||
free(user);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static JNINativeMethod sMethods[] = {
|
||||
|
||||
@@ -70,6 +70,7 @@ extern DBusHandlerResult agent_event_filter(DBusConnection *conn,
|
||||
void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat);
|
||||
void onDiscoverServicesResult(DBusMessage *msg, void *user, void *nat);
|
||||
void onCreateDeviceResult(DBusMessage *msg, void *user, void *nat);
|
||||
void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *nat);
|
||||
|
||||
|
||||
/** Get native data stored in the opaque (Java code maintained) pointer mNativeData
|
||||
@@ -887,12 +888,21 @@ static jboolean connectInputDeviceNative(JNIEnv *env, jobject object, jstring pa
|
||||
LOGV(__FUNCTION__);
|
||||
#ifdef HAVE_BLUETOOTH
|
||||
native_data_t *nat = get_native_data(env, object);
|
||||
if (nat) {
|
||||
jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
|
||||
struct event_loop_native_data_t *eventLoopNat =
|
||||
get_EventLoop_native_data(env, eventLoop);
|
||||
|
||||
if (nat && eventLoopNat) {
|
||||
const char *c_path = env->GetStringUTFChars(path, NULL);
|
||||
|
||||
bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
|
||||
c_path, DBUS_INPUT_IFACE, "Connect",
|
||||
DBUS_TYPE_INVALID);
|
||||
int len = env->GetStringLength(path) + 1;
|
||||
char *context_path = (char *)calloc(len, sizeof(char));
|
||||
strlcpy(context_path, c_path, len); // for callback
|
||||
|
||||
bool ret = dbus_func_args_async(env, nat->conn, -1, onInputDeviceConnectionResult,
|
||||
context_path, eventLoopNat, c_path, DBUS_INPUT_IFACE,
|
||||
"Connect",
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
env->ReleaseStringUTFChars(path, c_path);
|
||||
return ret ? JNI_TRUE : JNI_FALSE;
|
||||
@@ -906,12 +916,21 @@ static jboolean disconnectInputDeviceNative(JNIEnv *env, jobject object,
|
||||
LOGV(__FUNCTION__);
|
||||
#ifdef HAVE_BLUETOOTH
|
||||
native_data_t *nat = get_native_data(env, object);
|
||||
if (nat) {
|
||||
jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
|
||||
struct event_loop_native_data_t *eventLoopNat =
|
||||
get_EventLoop_native_data(env, eventLoop);
|
||||
|
||||
if (nat && eventLoopNat) {
|
||||
const char *c_path = env->GetStringUTFChars(path, NULL);
|
||||
|
||||
bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
|
||||
c_path, DBUS_INPUT_IFACE, "Disconnect",
|
||||
DBUS_TYPE_INVALID);
|
||||
int len = env->GetStringLength(path) + 1;
|
||||
char *context_path = (char *)calloc(len, sizeof(char));
|
||||
strlcpy(context_path, c_path, len); // for callback
|
||||
|
||||
bool ret = dbus_func_args_async(env, nat->conn, -1, onInputDeviceConnectionResult,
|
||||
context_path, eventLoopNat, c_path, DBUS_INPUT_IFACE,
|
||||
"Disconnect",
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
env->ReleaseStringUTFChars(path, c_path);
|
||||
return ret ? JNI_TRUE : JNI_FALSE;
|
||||
|
||||
Reference in New Issue
Block a user