Add HID to the state machine and add native call backs.

Change-Id: Ib9f3e476d4176bc04e23e7674dc54aa5a6417308
This commit is contained in:
Jaikumar Ganesh
2010-07-19 16:28:27 -07:00
parent c3ee99d9eb
commit de07503a38
6 changed files with 367 additions and 22 deletions

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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?) - " +

View File

@@ -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");

View File

@@ -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[] = {

View File

@@ -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;