Merge "HID profile."

This commit is contained in:
Jaikumar Ganesh
2010-06-17 13:58:49 -07:00
committed by Android (Google) Code Review
10 changed files with 521 additions and 9 deletions

View File

@@ -0,0 +1,241 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.bluetooth;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Public API for controlling the Bluetooth HID (Input Device) Profile
*
* BluetoothInputDevice is a proxy object used to make calls to Bluetooth Service
* which handles the HID profile.
*
* Creating a BluetoothInputDevice object will initiate a binding with the
* Bluetooth service. Users of this object should call close() when they
* are finished, so that this proxy object can unbind from the service.
*
* Currently the Bluetooth service runs in the system server and this
* proxy object will be immediately bound to the service on construction.
*
* @hide
*/
public final class BluetoothInputDevice {
private static final String TAG = "BluetoothInputDevice";
private static final boolean DBG = false;
/** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */
public static final String EXTRA_INPUT_DEVICE_STATE =
"android.bluetooth.inputdevice.extra.INPUT_DEVICE_STATE";
/** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */
public static final String EXTRA_PREVIOUS_INPUT_DEVICE_STATE =
"android.bluetooth.inputdevice.extra.PREVIOUS_INPUT_DEVICE_STATE";
/** Indicates the state of an input device has changed.
* This intent will always contain EXTRA_INPUT_DEVICE_STATE,
* EXTRA_PREVIOUS_INPUT_DEVICE_STATE and BluetoothDevice.EXTRA_DEVICE
* extras.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_INPUT_DEVICE_STATE_CHANGED =
"android.bluetooth.inputdevice.action.INPUT_DEVICE_STATE_CHANGED";
public static final int STATE_DISCONNECTED = 0;
public static final int STATE_CONNECTING = 1;
public static final int STATE_CONNECTED = 2;
public static final int STATE_DISCONNECTING = 3;
/**
* Auto connection, incoming and outgoing connection are allowed at this
* priority level.
*/
public static final int PRIORITY_AUTO_CONNECT = 1000;
/**
* Incoming and outgoing connection are allowed at this priority level
*/
public static final int PRIORITY_ON = 100;
/**
* Connections to the device are not allowed at this priority level.
*/
public static final int PRIORITY_OFF = 0;
/**
* Default priority level when the device is unpaired.
*/
public static final int PRIORITY_UNDEFINED = -1;
private final IBluetooth mService;
private final Context mContext;
/**
* Create a BluetoothInputDevice proxy object for interacting with the local
* Bluetooth Service which handle the HID profile.
* @param c Context
*/
public BluetoothInputDevice(Context c) {
mContext = c;
IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
if (b != null) {
mService = IBluetooth.Stub.asInterface(b);
} else {
Log.w(TAG, "Bluetooth Service not available!");
// Instead of throwing an exception which prevents people from going
// into Wireless settings in the emulator. Let it crash later when it is actually used.
mService = null;
}
}
/** Initiate a connection to an Input device.
*
* This function returns false on error and true if the connection
* attempt is being made.
*
* Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when the
* connection is completed.
* @param device Remote BT device.
* @return false on immediate error, true otherwise
* @hide
*/
public boolean connectInputDevice(BluetoothDevice device) {
if (DBG) log("connectInputDevice(" + device + ")");
try {
return mService.connectInputDevice(device);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
}
}
/** Initiate disconnect from an Input Device.
* This function return false on error and true if the disconnection
* attempt is being made.
*
* Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when
* disconnect is completed.
*
* @param device Remote BT device.
* @return false on immediate error, true otherwise
* @hide
*/
public boolean disconnectInputDevice(BluetoothDevice device) {
if (DBG) log("disconnectInputDevice(" + device + ")");
try {
return mService.disconnectInputDevice(device);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
}
}
/** Check if a specified InputDevice is connected.
*
* @param device Remote BT device.
* @return True if connected , false otherwise and on error.
* @hide
*/
public boolean isInputDeviceConnected(BluetoothDevice device) {
if (DBG) log("isInputDeviceConnected(" + device + ")");
int state = getInputDeviceState(device);
if (state == STATE_CONNECTED) return true;
return false;
}
/** Check if any Input Device is connected.
*
* @return a unmodifiable set of connected Input Devices, or null on error.
* @hide
*/
public Set<BluetoothDevice> getConnectedInputDevices() {
if (DBG) log("getConnectedInputDevices()");
try {
return Collections.unmodifiableSet(
new HashSet<BluetoothDevice>(
Arrays.asList(mService.getConnectedInputDevices())));
} catch (RemoteException e) {
Log.e(TAG, "", e);
return null;
}
}
/** Get the state of an Input Device.
*
* @param device Remote BT device.
* @return The current state of the Input Device
* @hide
*/
public int getInputDeviceState(BluetoothDevice device) {
if (DBG) log("getInputDeviceState(" + device + ")");
try {
return mService.getInputDeviceState(device);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return STATE_DISCONNECTED;
}
}
/**
* Set priority of an input device.
*
* Priority is a non-negative integer. Priority can take the following
* values:
* {@link PRIORITY_ON}, {@link PRIORITY_OFF}, {@link PRIORITY_AUTO_CONNECT}
*
* @param device Paired device.
* @param priority Integer priority
* @return true if priority is set, false on error
*/
public boolean setInputDevicePriority(BluetoothDevice device, int priority) {
if (DBG) log("setInputDevicePriority(" + device + ", " + priority + ")");
try {
return mService.setInputDevicePriority(device, priority);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
}
}
/**
* Get the priority associated with an Input Device.
*
* @param device Input Device
* @return non-negative priority, or negative error code on error.
*/
public int getInputDevicePriority(BluetoothDevice device) {
if (DBG) log("getInputDevicePriority(" + device + ")");
try {
return mService.getInputDevicePriority(device);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return PRIORITY_OFF;
}
}
private static void log(String msg) {
Log.d(TAG, msg);
}
}

View File

@@ -49,6 +49,8 @@ public final class BluetoothUuid {
ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
public static final ParcelUuid ObexObjectPush =
ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
public static final ParcelUuid Hid =
ParcelUuid.fromString("00000011-0000-1000-8000-00805f9b34fb");
public static final ParcelUuid[] RESERVED_UUIDS = {
AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
@@ -82,6 +84,10 @@ public final class BluetoothUuid {
return uuid.equals(AvrcpTarget);
}
public static boolean isInputDevice(ParcelUuid uuid) {
return uuid.equals(Hid);
}
/**
* Returns true if ParcelUuid is present in uuidArray
*

View File

@@ -17,6 +17,7 @@
package android.bluetooth;
import android.bluetooth.IBluetoothCallback;
import android.bluetooth.BluetoothDevice;
import android.os.ParcelUuid;
/**
@@ -72,4 +73,12 @@ interface IBluetooth
boolean connectHeadset(String address);
boolean disconnectHeadset(String address);
boolean notifyIncomingConnection(String address);
// HID profile APIs
boolean connectInputDevice(in BluetoothDevice device);
boolean disconnectInputDevice(in BluetoothDevice device);
BluetoothDevice[] getConnectedInputDevices(); // change to Set<> once AIDL supports
int getInputDeviceState(in BluetoothDevice device);
boolean setInputDevicePriority(in BluetoothDevice device, int priority);
int getInputDevicePriority(in BluetoothDevice device);
}

View File

@@ -1001,7 +1001,7 @@ public final class Settings {
public static boolean hasInterestingConfigurationChanges(int changes) {
return (changes&ActivityInfo.CONFIG_FONT_SCALE) != 0;
}
public static boolean getShowGTalkServiceStatus(ContentResolver cr) {
return getInt(cr, SHOW_GTALK_SERVICE_STATUS, 0) != 0;
}
@@ -1208,7 +1208,7 @@ public final class Settings {
public static final String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
/**
* @deprecated Use
* @deprecated Use
* {@link android.provider.Settings.Secure#LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED}
* instead
*/
@@ -2281,6 +2281,14 @@ public final class Settings {
return ("bluetooth_a2dp_sink_priority_" + address.toUpperCase());
}
/**
* Get the key that retrieves a bluetooth Input Device's priority.
* @hide
*/
public static final String getBluetoothInputDevicePriorityKey(String address) {
return ("bluetooth_input_device_priority_" + address.toUpperCase());
}
/**
* Whether or not data roaming is enabled. (0 = false, 1 = true)
*/

View File

@@ -20,6 +20,7 @@ import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.Intent;
@@ -427,6 +428,20 @@ class BluetoothEventLoop {
}
}
private void onInputDevicePropertyChanged(String path, String[] propValues) {
String address = mBluetoothService.getAddressFromObjectPath(path);
if (address == null) {
Log.e(TAG, "onInputDevicePropertyChanged: Address of the remote device in null");
return;
}
log(" Input Device : Name of Property is:" + propValues[0]);
boolean state = false;
if (propValues[1].equals("true")) {
state = true;
}
mBluetoothService.handleInputDevicePropertyChange(address, state);
}
private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
if (address == null) {
@@ -573,6 +588,8 @@ class BluetoothEventLoop {
}
private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
if (!mBluetoothService.isEnabled()) return false;
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
if (address == null) {
Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
@@ -581,15 +598,15 @@ class BluetoothEventLoop {
boolean authorized = false;
ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
BluetoothDevice device = mAdapter.getRemoteDevice(address);
// Bluez sends the UUID of the local service being accessed, _not_ the
// remote service
if (mBluetoothService.isEnabled() &&
(BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
|| BluetoothUuid.isAdvAudioDist(uuid)) &&
!isOtherSinkInNonDisconnectingState(address)) {
BluetoothDevice device = mAdapter.getRemoteDevice(address);
if ((BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
|| BluetoothUuid.isAdvAudioDist(uuid)) &&
!isOtherSinkInNonDisconnectingState(address)) {
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
if (authorized) {
Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
@@ -597,6 +614,15 @@ class BluetoothEventLoop {
} else {
Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
}
} else if (BluetoothUuid.isInputDevice(uuid) && !isOtherInputDeviceConnected(address)) {
BluetoothInputDevice inputDevice = new BluetoothInputDevice(mContext);
authorized = inputDevice.getInputDevicePriority(device) >
BluetoothInputDevice.PRIORITY_OFF;
if (authorized) {
Log.i(TAG, "Allowing incoming HID connection from " + address);
} else {
Log.i(TAG, "Rejecting incoming HID connection from " + address);
}
} else {
Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
}
@@ -604,7 +630,19 @@ class BluetoothEventLoop {
return authorized;
}
boolean isOtherSinkInNonDisconnectingState(String address) {
private boolean isOtherInputDeviceConnected(String address) {
Set<BluetoothDevice> devices =
mBluetoothService.lookupInputDevicesMatchingStates(new int[] {
BluetoothInputDevice.STATE_CONNECTING,
BluetoothInputDevice.STATE_CONNECTED});
for (BluetoothDevice device : devices) {
if (!device.getAddress().equals(address)) return true;
}
return false;
}
private boolean isOtherSinkInNonDisconnectingState(String address) {
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
if (devices.size() == 0) return false;

View File

@@ -24,16 +24,19 @@
package android.server;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothDeviceProfileState;
import android.bluetooth.BluetoothProfileState;
import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothCallback;
import android.bluetooth.IBluetoothHeadset;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -70,8 +73,10 @@ import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class BluetoothService extends IBluetooth.Stub {
private static final String TAG = "BluetoothService";
@@ -129,6 +134,8 @@ public class BluetoothService extends IBluetooth.Stub {
private final BluetoothProfileState mHfpProfileState;
private BluetoothA2dpService mA2dpService;
private final HashMap<BluetoothDevice, Integer> mInputDevices;
private static String mDockAddress;
private String mDockPin;
@@ -198,6 +205,7 @@ public class BluetoothService extends IBluetooth.Stub {
filter.addAction(Intent.ACTION_DOCK_EVENT);
mContext.registerReceiver(mReceiver, filter);
mInputDevices = new HashMap<BluetoothDevice, Integer>();
}
public static synchronized String readDockBluetoothAddress() {
@@ -1220,6 +1228,127 @@ public class BluetoothService extends IBluetooth.Stub {
return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
}
public synchronized boolean connectInputDevice(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
String objectPath = getObjectPathFromAddress(device.getAddress());
if (objectPath == null || getConnectedInputDevices().length != 0 ||
getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
return false;
}
if(connectInputDeviceNative(objectPath)) {
handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
return true;
}
return false;
}
public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
String objectPath = getObjectPathFromAddress(device.getAddress());
if (objectPath == null || getConnectedInputDevices().length == 0) {
return false;
}
if(disconnectInputDeviceNative(objectPath)) {
handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
return true;
}
return false;
}
public synchronized int getInputDeviceState(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (mInputDevices.get(device) == null) {
return BluetoothInputDevice.STATE_DISCONNECTED;
}
return mInputDevices.get(device.getAddress());
}
public synchronized BluetoothDevice[] getConnectedInputDevices() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Set<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
new int[] {BluetoothInputDevice.STATE_CONNECTED});
return devices.toArray(new BluetoothDevice[devices.size()]);
}
public synchronized int getInputDevicePriority(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
BluetoothInputDevice.PRIORITY_UNDEFINED);
}
public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
return false;
}
return Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
priority);
}
/*package*/synchronized Set<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
Set<BluetoothDevice> inputDevices = new HashSet<BluetoothDevice>();
if (mInputDevices.isEmpty()) {
return inputDevices;
}
for (BluetoothDevice device: mInputDevices.keySet()) {
int inputDeviceState = getInputDeviceState(device);
for (int state : states) {
if (state == inputDeviceState) {
inputDevices.add(device);
break;
}
}
}
return inputDevices;
}
private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
int prevState;
if (mInputDevices.get(device) == null) {
prevState = BluetoothInputDevice.STATE_DISCONNECTED;
} else {
prevState = mInputDevices.get(device);
}
if (prevState == state) return;
mInputDevices.put(device, state);
if (getInputDevicePriority(device) >
BluetoothInputDevice.PRIORITY_OFF &&
state == BluetoothInputDevice.STATE_CONNECTING ||
state == BluetoothInputDevice.STATE_CONNECTED) {
// We have connected or attempting to connect.
// Bump priority
setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
}
Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState);
intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
}
/*package*/ void handleInputDevicePropertyChange(String path, boolean connected) {
String address = getAddressFromObjectPath(path);
if (address == null) return;
int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
BluetoothInputDevice.STATE_DISCONNECTED;
BluetoothDevice device = mAdapter.getRemoteDevice(address);
handleInputDeviceStateChange(device, state);
}
/*package*/ boolean isRemoteDeviceInCache(String address) {
return (mDeviceProperties.get(address) != null);
}
@@ -2103,4 +2232,6 @@ public class BluetoothService extends IBluetooth.Stub {
short channel);
private native boolean removeServiceRecordNative(int handle);
private native boolean setLinkTimeoutNative(String path, int num_slots);
private native boolean connectInputDeviceNative(String path);
private native boolean disconnectInputDeviceNative(String path);
}

View File

@@ -68,6 +68,10 @@ static Properties adapter_properties[] = {
{"UUIDs", DBUS_TYPE_ARRAY},
};
static Properties input_properties[] = {
{"Connected", DBUS_TYPE_BOOLEAN},
};
typedef union {
char *str_val;
int int_val;
@@ -698,6 +702,11 @@ jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg)
sizeof(remote_device_properties) / sizeof(Properties));
}
jobjectArray parse_input_property_change(JNIEnv *env, DBusMessage *msg) {
return parse_property_change(env, msg, (Properties *) &input_properties,
sizeof(input_properties) / sizeof(Properties));
}
jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter) {
return parse_properties(env, iter, (Properties *) &adapter_properties,
sizeof(adapter_properties) / sizeof(Properties));
@@ -708,6 +717,11 @@ jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter)
sizeof(remote_device_properties) / sizeof(Properties));
}
jobjectArray parse_input_properties(JNIEnv *env, DBusMessageIter *iter) {
return parse_properties(env, iter, (Properties *) &input_properties,
sizeof(input_properties) / sizeof(Properties));
}
int get_bdaddr(const char *str, bdaddr_t *ba) {
char *d = ((char *)ba) + 5, *endp;
int i;

View File

@@ -162,6 +162,8 @@ jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter);
jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter);
jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg);
jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg);
jobjectArray parse_input_properties(JNIEnv *env, DBusMessageIter *iter);
jobjectArray parse_input_property_change(JNIEnv *env, DBusMessage *msg);
void append_variant(DBusMessageIter *iter, int type, void *val);
int get_bdaddr(const char *str, bdaddr_t *ba);
void get_bdaddr_as_string(const bdaddr_t *ba, char *str);

View File

@@ -64,6 +64,8 @@ static jmethodID method_onDisplayPasskey;
static jmethodID method_onAgentAuthorize;
static jmethodID method_onAgentCancel;
static jmethodID method_onInputDevicePropertyChanged;
typedef event_loop_native_data_t native_data_t;
#define EVENT_LOOP_REFS 10
@@ -116,6 +118,9 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
"(Ljava/lang/String;I)V");
method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey",
"(Ljava/lang/String;II)V");
method_onInputDevicePropertyChanged = env->GetMethodID(clazz, "onInputDevicePropertyChanged",
"(Ljava/lang/String;[Ljava/lang/String;)V");
field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
#endif
@@ -853,6 +858,22 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
method_onDeviceDisconnectRequested,
env->NewStringUTF(remote_device_path));
goto success;
} else if (dbus_message_is_signal(msg,
"org.bluez.Input",
"PropertyChanged")) {
jobjectArray str_array =
parse_input_property_change(env, msg);
if (str_array != NULL) {
const char *c_path = dbus_message_get_path(msg);
env->CallVoidMethod(nat->me,
method_onInputDevicePropertyChanged,
env->NewStringUTF(c_path),
str_array);
} else {
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
}
goto success;
}
ret = a2dp_event_filter(msg, env);

View File

@@ -16,6 +16,8 @@
#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
#define DBUS_INPUT_IFACE BLUEZ_DBUS_BASE_IFC ".Input"
#define LOG_TAG "BluetoothService.cpp"
#include "android_bluetooth_common.h"
@@ -881,6 +883,43 @@ static jboolean setLinkTimeoutNative(JNIEnv *env, jobject object, jstring object
return JNI_FALSE;
}
static jboolean connectInputDeviceNative(JNIEnv *env, jobject object, jstring path) {
LOGV(__FUNCTION__);
#ifdef HAVE_BLUETOOTH
native_data_t *nat = get_native_data(env, object);
if (nat) {
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);
env->ReleaseStringUTFChars(path, c_path);
return ret ? JNI_TRUE : JNI_FALSE;
}
#endif
return JNI_FALSE;
}
static jboolean disconnectInputDeviceNative(JNIEnv *env, jobject object,
jstring path) {
LOGV(__FUNCTION__);
#ifdef HAVE_BLUETOOTH
native_data_t *nat = get_native_data(env, object);
if (nat) {
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);
env->ReleaseStringUTFChars(path, c_path);
return ret ? JNI_TRUE : JNI_FALSE;
}
#endif
return JNI_FALSE;
}
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"classInitNative", "()V", (void*)classInitNative},
@@ -926,6 +965,9 @@ static JNINativeMethod sMethods[] = {
{"addRfcommServiceRecordNative", "(Ljava/lang/String;JJS)I", (void *)addRfcommServiceRecordNative},
{"removeServiceRecordNative", "(I)Z", (void *)removeServiceRecordNative},
{"setLinkTimeoutNative", "(Ljava/lang/String;I)Z", (void *)setLinkTimeoutNative},
// HID functions
{"connectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)connectInputDeviceNative},
{"disconnectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectInputDeviceNative},
};
int register_android_server_BluetoothService(JNIEnv *env) {