diff options
-rw-r--r-- | core/java/android/bluetooth/BluetoothInputDevice.java | 241 | ||||
-rw-r--r-- | core/java/android/bluetooth/BluetoothUuid.java | 6 | ||||
-rw-r--r-- | core/java/android/bluetooth/IBluetooth.aidl | 9 | ||||
-rw-r--r-- | core/java/android/provider/Settings.java | 12 | ||||
-rw-r--r-- | core/java/android/server/BluetoothEventLoop.java | 52 | ||||
-rw-r--r-- | core/java/android/server/BluetoothService.java | 131 | ||||
-rw-r--r-- | core/jni/android_bluetooth_common.cpp | 14 | ||||
-rw-r--r-- | core/jni/android_bluetooth_common.h | 2 | ||||
-rw-r--r-- | core/jni/android_server_BluetoothEventLoop.cpp | 21 | ||||
-rw-r--r-- | core/jni/android_server_BluetoothService.cpp | 42 |
10 files changed, 521 insertions, 9 deletions
diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java new file mode 100644 index 0000000..1793838 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothInputDevice.java @@ -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); + } +} diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index 4164a3d..1909e03 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -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 * diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index ea71034..75f093c 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -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); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a66c9ed..1ab3931 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -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 */ @@ -2282,6 +2282,14 @@ public final class Settings { } /** + * 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) */ public static final String DATA_ROAMING = "data_roaming"; diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 35a582d..9b7a73d 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -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; diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 31e5a7b..e68632d 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -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); } diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp index 9a8f1b8..53ac625 100644 --- a/core/jni/android_bluetooth_common.cpp +++ b/core/jni/android_bluetooth_common.cpp @@ -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; diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h index 378bb6f..27a00ae 100644 --- a/core/jni/android_bluetooth_common.h +++ b/core/jni/android_bluetooth_common.h @@ -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); diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 01b6711..3c88158 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -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); diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index 4420aca..a52a74c 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -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) { |