Bluetooth: Add support for HID Device Role am: e88fd4b594 am: ee469c6fe9 am: 1ee22b7f70
am: a9c92c2442
Change-Id: I56625e00b1b6c2b7b11e4ce4dc3038072ef6c231
diff --git a/Android.mk b/Android.mk
index 427b888..3915347 100644
--- a/Android.mk
+++ b/Android.mk
@@ -130,6 +130,8 @@
core/java/android/bluetooth/IBluetoothSap.aidl \
core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
core/java/android/bluetooth/IBluetoothHeadsetClient.aidl \
+ core/java/android/bluetooth/IBluetoothHidDevice.aidl \
+ core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl \
core/java/android/bluetooth/IBluetoothGatt.aidl \
core/java/android/bluetooth/IBluetoothGattCallback.aidl \
core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 6c1e2a9..1f7d929 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1940,6 +1940,9 @@
} else if (profile == BluetoothProfile.MAP_CLIENT) {
BluetoothMapClient mapClient = new BluetoothMapClient(context, listener);
return true;
+ } else if (profile == BluetoothProfile.HID_DEVICE) {
+ BluetoothHidDevice hidd = new BluetoothHidDevice(context, listener);
+ return true;
} else {
return false;
}
@@ -2016,6 +2019,10 @@
BluetoothMapClient mapClient = (BluetoothMapClient)proxy;
mapClient.close();
break;
+ case BluetoothProfile.HID_DEVICE:
+ BluetoothHidDevice hidd = (BluetoothHidDevice) proxy;
+ hidd.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
new file mode 100644
index 0000000..3e6a078
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2016 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.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public final class BluetoothHidDevice implements BluetoothProfile {
+
+ private static final String TAG = BluetoothHidDevice.class.getSimpleName();
+
+ public static final String ACTION_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.hid.profile.action.CONNECTION_STATE_CHANGED";
+
+ /**
+ * Constants representing device subclass.
+ *
+ * @see #registerApp(String, String, String, byte, byte[],
+ * BluetoothHidDeviceCallback)
+ */
+ public static final byte SUBCLASS1_NONE = (byte) 0x00;
+ public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40;
+ public static final byte SUBCLASS1_MOUSE = (byte) 0x80;
+ public static final byte SUBCLASS1_COMBO = (byte) 0xC0;
+
+ public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00;
+ public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01;
+ public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02;
+ public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03;
+ public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04;
+ public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05;
+ public static final byte SUBCLASS2_CARD_READER = (byte) 0x06;
+
+ /**
+ * Constants representing report types.
+ *
+ * @see BluetoothHidDeviceCallback#onGetReport(byte, byte, int)
+ * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[])
+ * @see BluetoothHidDeviceCallback#onIntrData(byte, byte[])
+ */
+ public static final byte REPORT_TYPE_INPUT = (byte) 1;
+ public static final byte REPORT_TYPE_OUTPUT = (byte) 2;
+ public static final byte REPORT_TYPE_FEATURE = (byte) 3;
+
+ /**
+ * Constants representing error response for Set Report.
+ *
+ * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[])
+ */
+ public static final byte ERROR_RSP_SUCCESS = (byte) 0;
+ public static final byte ERROR_RSP_NOT_READY = (byte) 1;
+ public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2;
+ public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3;
+ public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4;
+ public static final byte ERROR_RSP_UNKNOWN = (byte) 14;
+
+ /**
+ * Constants representing protocol mode used set by host. Default is always
+ * {@link #PROTOCOL_REPORT_MODE} unless notified otherwise.
+ *
+ * @see BluetoothHidDeviceCallback#onSetProtocol(byte)
+ */
+ public static final byte PROTOCOL_BOOT_MODE = (byte) 0;
+ public static final byte PROTOCOL_REPORT_MODE = (byte) 1;
+
+ private Context mContext;
+
+ private ServiceListener mServiceListener;
+
+ private IBluetoothHidDevice mService;
+
+ private BluetoothAdapter mAdapter;
+
+ private static class BluetoothHidDeviceCallbackWrapper extends IBluetoothHidDeviceCallback.Stub {
+
+ private BluetoothHidDeviceCallback mCallback;
+
+ public BluetoothHidDeviceCallbackWrapper(BluetoothHidDeviceCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onAppStatusChanged(BluetoothDevice pluggedDevice,
+ BluetoothHidDeviceAppConfiguration config, boolean registered) {
+ mCallback.onAppStatusChanged(pluggedDevice, config, registered);
+ }
+
+ @Override
+ public void onConnectionStateChanged(BluetoothDevice device, int state) {
+ mCallback.onConnectionStateChanged(device, state);
+ }
+
+ @Override
+ public void onGetReport(byte type, byte id, int bufferSize) {
+ mCallback.onGetReport(type, id, bufferSize);
+ }
+
+ @Override
+ public void onSetReport(byte type, byte id, byte[] data) {
+ mCallback.onSetReport(type, id, data);
+ }
+
+ @Override
+ public void onSetProtocol(byte protocol) {
+ mCallback.onSetProtocol(protocol);
+ }
+
+ @Override
+ public void onIntrData(byte reportId, byte[] data) {
+ mCallback.onIntrData(reportId, data);
+ }
+
+ @Override
+ public void onVirtualCableUnplug() {
+ mCallback.onVirtualCableUnplug();
+ }
+ }
+
+ final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+
+ public void onBluetoothStateChange(boolean up) {
+ Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ synchronized (mConnection) {
+ if (!up) {
+ Log.d(TAG,"Unbinding service...");
+ if (mService != null) {
+ mService = null;
+ try {
+ mContext.unbindService(mConnection);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"onBluetoothStateChange: could not unbind service:", e);
+ }
+ }
+ } else {
+ try {
+ if (mService == null) {
+ Log.d(TAG,"Binding HID Device service...");
+ doBind();
+ }
+ } catch (IllegalStateException e) {
+ Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e);
+ } catch (SecurityException e) {
+ Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e);
+ }
+ }
+ }
+ }
+ };
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG, "onServiceConnected()");
+
+ mService = IBluetoothHidDevice.Stub.asInterface(service);
+
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE,
+ BluetoothHidDevice.this);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Log.d(TAG, "onServiceDisconnected()");
+
+ mService = null;
+
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE);
+ }
+ }
+ };
+
+ BluetoothHidDevice(Context context, ServiceListener listener) {
+ Log.v(TAG, "BluetoothHidDevice");
+
+ mContext = context;
+ mServiceListener = listener;
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ doBind();
+ }
+
+ boolean doBind() {
+ Intent intent = new Intent(IBluetoothHidDevice.class.getName());
+ ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ intent.setComponent(comp);
+ if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+ android.os.Process.myUserHandle())) {
+ Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent);
+ return false;
+ }
+ Log.d(TAG, "Bound to HID Device Service");
+ return true;
+ }
+
+ void close() {
+ Log.v(TAG, "close()");
+
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ synchronized (mConnection) {
+ if (mService != null) {
+ mService = null;
+ try {
+ mContext.unbindService(mConnection);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"close: could not unbind HID Dev service: ", e);
+ }
+ }
+ }
+
+ mServiceListener = null;
+ }
+
+ @Override
+ public List<BluetoothDevice> getConnectedDevices() {
+ Log.v(TAG, "getConnectedDevices()");
+ return null;
+ }
+
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
+ return null;
+ }
+
+ @Override
+ public int getConnectionState(BluetoothDevice device) {
+ Log.v(TAG, "getConnectionState(): device=" + device.getAddress());
+
+ return STATE_DISCONNECTED;
+ }
+
+ /**
+ * Registers application to be used for HID device. Connections to HID
+ * Device are only possible when application is registered. Only one
+ * application can be registered at time. When no longer used, application
+ * should be unregistered using
+ * {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}.
+ *
+ * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of
+ * HID Device SDP record.
+ * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of
+ * Incoming QoS Settings.
+ * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of
+ * Outgoing QoS Settings.
+ * @param callback {@link BluetoothHidDeviceCallback} object to which
+ * callback messages will be sent.
+ * @return
+ */
+ public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp,
+ BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos,
+ BluetoothHidDeviceCallback callback) {
+ Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos
+ + " callback=" + callback);
+
+ boolean result = false;
+
+ if (sdp == null || callback == null) {
+ return false;
+ }
+
+ if (mService != null) {
+ try {
+ BluetoothHidDeviceAppConfiguration config =
+ new BluetoothHidDeviceAppConfiguration();
+ BluetoothHidDeviceCallbackWrapper cbw =
+ new BluetoothHidDeviceCallbackWrapper(callback);
+ result = mService.registerApp(config, sdp, inQos, outQos, cbw);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return result;
+ }
+
+ /**
+ * Unregisters application. Active connection will be disconnected and no
+ * new connections will be allowed until registered again using
+ * {@link #registerApp(String, String, String, byte, byte[], BluetoothHidDeviceCallback)}
+ *
+ * @param config {@link BluetoothHidDeviceAppConfiguration} object as
+ * obtained from
+ * {@link BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice,
+ * BluetoothHidDeviceAppConfiguration, boolean)}
+ *
+ * @return
+ */
+ public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) {
+ Log.v(TAG, "unregisterApp()");
+
+ boolean result = false;
+
+ if (mService != null) {
+ try {
+ result = mService.unregisterApp(config);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return result;
+ }
+
+ /**
+ * Sends report to remote host using interrupt channel.
+ *
+ * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id
+ * are not defined in descriptor.
+ * @param data Report data, not including Report Id.
+ * @return
+ */
+ public boolean sendReport(int id, byte[] data) {
+ Log.v(TAG, "sendReport(): id=" + id);
+
+ boolean result = false;
+
+ if (mService != null) {
+ try {
+ result = mService.sendReport(id, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return result;
+ }
+
+ /**
+ * Sends report to remote host as reply for GET_REPORT request from
+ * {@link BluetoothHidDeviceCallback#onGetReport(byte, byte, int)}.
+ *
+ * @param type Report Type, as in request.
+ * @param id Report Id, as in request.
+ * @param data Report data, not including Report Id.
+ * @return
+ */
+ public boolean replyReport(byte type, byte id, byte[] data) {
+ Log.v(TAG, "replyReport(): type=" + type + " id=" + id);
+
+ boolean result = false;
+
+ if (mService != null) {
+ try {
+ result = mService.replyReport(type, id, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return result;
+ }
+
+ /**
+ * Sends error handshake message as reply for invalid SET_REPORT request
+ * from {@link BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[])}.
+ *
+ * @param error Error to be sent for SET_REPORT via HANDSHAKE.
+ * @return
+ */
+ public boolean reportError(byte error) {
+ Log.v(TAG, "reportError(): error = " + error);
+
+ boolean result = false;
+
+ if (mService != null) {
+ try {
+ result = mService.reportError(error);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return result;
+ }
+
+ /**
+ * Sends Virtual Cable Unplug to currently connected host.
+ *
+ * @return
+ */
+ public boolean unplug() {
+ Log.v(TAG, "unplug()");
+
+ boolean result = false;
+
+ if (mService != null) {
+ try {
+ result = mService.unplug();
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return result;
+ }
+
+ /**
+ * Initiates connection to host which currently has Virtual Cable
+ * established with device.
+ *
+ * @return
+ */
+ public boolean connect() {
+ Log.v(TAG, "connect()");
+
+ boolean result = false;
+
+ if (mService != null) {
+ try {
+ result = mService.connect();
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return result;
+ }
+
+ /**
+ * Disconnects from currently connected host.
+ *
+ * @return
+ */
+ public boolean disconnect() {
+ Log.v(TAG, "disconnect()");
+
+ boolean result = false;
+
+ if (mService != null) {
+ try {
+ result = mService.disconnect();
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return result;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl
new file mode 100644
index 0000000..283a717
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 2016, 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;
+
+parcelable BluetoothHidDeviceAppConfiguration;
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java
new file mode 100644
index 0000000..05ba64e
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 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.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Random;
+
+/** @hide */
+public final class BluetoothHidDeviceAppConfiguration implements Parcelable {
+ private final long mHash;
+
+ BluetoothHidDeviceAppConfiguration() {
+ Random rnd = new Random();
+ mHash = rnd.nextLong();
+ }
+
+ BluetoothHidDeviceAppConfiguration(long hash) {
+ mHash = hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BluetoothHidDeviceAppConfiguration) {
+ BluetoothHidDeviceAppConfiguration config = (BluetoothHidDeviceAppConfiguration) o;
+ return mHash == config.mHash;
+ }
+ return false;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<BluetoothHidDeviceAppConfiguration> CREATOR =
+ new Parcelable.Creator<BluetoothHidDeviceAppConfiguration>() {
+
+ @Override
+ public BluetoothHidDeviceAppConfiguration createFromParcel(Parcel in) {
+ long hash = in.readLong();
+ return new BluetoothHidDeviceAppConfiguration(hash);
+ }
+
+ @Override
+ public BluetoothHidDeviceAppConfiguration[] newArray(int size) {
+ return new BluetoothHidDeviceAppConfiguration[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mHash);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl
new file mode 100644
index 0000000..14f9114
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 2016, 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;
+
+parcelable BluetoothHidDeviceAppQosSettings;
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
new file mode 100644
index 0000000..0d6530c
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 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.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Random;
+
+/** @hide */
+public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
+
+ final public int serviceType;
+ final public int tokenRate;
+ final public int tokenBucketSize;
+ final public int peakBandwidth;
+ final public int latency;
+ final public int delayVariation;
+
+ final static public int SERVICE_NO_TRAFFIC = 0x00;
+ final static public int SERVICE_BEST_EFFORT = 0x01;
+ final static public int SERVICE_GUARANTEED = 0x02;
+
+ final static public int MAX = (int) 0xffffffff;
+
+ public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize,
+ int peakBandwidth,
+ int latency, int delayVariation) {
+ this.serviceType = serviceType;
+ this.tokenRate = tokenRate;
+ this.tokenBucketSize = tokenBucketSize;
+ this.peakBandwidth = peakBandwidth;
+ this.latency = latency;
+ this.delayVariation = delayVariation;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BluetoothHidDeviceAppQosSettings) {
+ BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o;
+ return false;
+ }
+ return false;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<BluetoothHidDeviceAppQosSettings> CREATOR =
+ new Parcelable.Creator<BluetoothHidDeviceAppQosSettings>() {
+
+ @Override
+ public BluetoothHidDeviceAppQosSettings createFromParcel(Parcel in) {
+
+ return new BluetoothHidDeviceAppQosSettings(in.readInt(), in.readInt(), in.readInt(),
+ in.readInt(),
+ in.readInt(), in.readInt());
+ }
+
+ @Override
+ public BluetoothHidDeviceAppQosSettings[] newArray(int size) {
+ return new BluetoothHidDeviceAppQosSettings[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(serviceType);
+ out.writeInt(tokenRate);
+ out.writeInt(tokenBucketSize);
+ out.writeInt(peakBandwidth);
+ out.writeInt(latency);
+ out.writeInt(delayVariation);
+ }
+
+ public int[] toArray() {
+ return new int[] {
+ serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation
+ };
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl
new file mode 100644
index 0000000..87dd10e
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 2016, 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;
+
+parcelable BluetoothHidDeviceAppSdpSettings;
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
new file mode 100644
index 0000000..f9a2245
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 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.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Random;
+
+/** @hide */
+public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
+
+ final public String name;
+ final public String description;
+ final public String provider;
+ final public byte subclass;
+ final public byte[] descriptors;
+
+ public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider,
+ byte subclass, byte[] descriptors) {
+ this.name = name;
+ this.description = description;
+ this.provider = provider;
+ this.subclass = subclass;
+ this.descriptors = descriptors.clone();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BluetoothHidDeviceAppSdpSettings) {
+ BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o;
+ return false;
+ }
+ return false;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<BluetoothHidDeviceAppSdpSettings> CREATOR =
+ new Parcelable.Creator<BluetoothHidDeviceAppSdpSettings>() {
+
+ @Override
+ public BluetoothHidDeviceAppSdpSettings createFromParcel(Parcel in) {
+
+ return new BluetoothHidDeviceAppSdpSettings(in.readString(), in.readString(),
+ in.readString(), in.readByte(), in.createByteArray());
+ }
+
+ @Override
+ public BluetoothHidDeviceAppSdpSettings[] newArray(int size) {
+ return new BluetoothHidDeviceAppSdpSettings[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(name);
+ out.writeString(description);
+ out.writeString(provider);
+ out.writeByte(subclass);
+ out.writeByteArray(descriptors);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
new file mode 100644
index 0000000..0f0e050
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 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.util.Log;
+
+/** @hide */
+public abstract class BluetoothHidDeviceCallback {
+
+ private static final String TAG = BluetoothHidDeviceCallback.class.getSimpleName();
+
+ /**
+ * Callback called when application registration state changes. Usually it's
+ * called due to either
+ * {@link BluetoothHidDevice#registerApp(String, String, String, byte, byte[],
+ * BluetoothHidDeviceCallback)}
+ * or
+ * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)}
+ * , but can be also unsolicited in case e.g. Bluetooth was turned off in
+ * which case application is unregistered automatically.
+ *
+ * @param pluggedDevice {@link BluetoothDevice} object which represents host
+ * that currently has Virtual Cable established with device. Only
+ * valid when application is registered, can be <code>null</code>
+ * .
+ * @param config {@link BluetoothHidDeviceAppConfiguration} object which
+ * represents token required to unregister application using
+ * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)}
+ * .
+ * @param registered <code>true</code> if application is registered,
+ * <code>false</code> otherwise.
+ */
+ public void onAppStatusChanged(BluetoothDevice pluggedDevice,
+ BluetoothHidDeviceAppConfiguration config, boolean registered) {
+ Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + (pluggedDevice == null ?
+ null : pluggedDevice.toString()) + " registered=" + registered);
+ }
+
+ /**
+ * Callback called when connection state with remote host was changed.
+ * Application can assume than Virtual Cable is established when called with
+ * {@link BluetoothProfile#STATE_CONNECTED} <code>state</code>.
+ *
+ * @param device {@link BluetoothDevice} object representing host device
+ * which connection state was changed.
+ * @param state Connection state as defined in {@link BluetoothProfile}.
+ */
+ public void onConnectionStateChanged(BluetoothDevice device, int state) {
+ Log.d(TAG, "onConnectionStateChanged: device=" + device.toString() + " state=" + state);
+ }
+
+ /**
+ * Callback called when GET_REPORT is received from remote host. Should be
+ * replied by application using
+ * {@link BluetoothHidDevice#replyReport(byte, byte, byte[])}.
+ *
+ * @param type Requested Report Type.
+ * @param id Requested Report Id, can be 0 if no Report Id are defined in
+ * descriptor.
+ * @param bufferSize Requested buffer size, application shall respond with
+ * at least given number of bytes.
+ */
+ public void onGetReport(byte type, byte id, int bufferSize) {
+ Log.d(TAG, "onGetReport: type=" + type + " id=" + id + " bufferSize=" + bufferSize);
+ }
+
+ /**
+ * Callback called when SET_REPORT is received from remote host. In case
+ * received data are invalid, application shall respond with
+ * {@link BluetoothHidDevice#reportError()}.
+ *
+ * @param type Report Type.
+ * @param id Report Id.
+ * @param data Report data.
+ */
+ public void onSetReport(byte type, byte id, byte[] data) {
+ Log.d(TAG, "onSetReport: type=" + type + " id=" + id);
+ }
+
+ /**
+ * Callback called when SET_PROTOCOL is received from remote host.
+ * Application shall use this information to send only reports valid for
+ * given protocol mode. By default,
+ * {@link BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed.
+ *
+ * @param protocol Protocol Mode.
+ */
+ public void onSetProtocol(byte protocol) {
+ Log.d(TAG, "onSetProtocol: protocol=" + protocol);
+ }
+
+ /**
+ * Callback called when report data is received over interrupt channel.
+ * Report Type is assumed to be
+ * {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}.
+ *
+ * @param reportId Report Id.
+ * @param data Report data.
+ */
+ public void onIntrData(byte reportId, byte[] data) {
+ Log.d(TAG, "onIntrData: reportId=" + reportId);
+ }
+
+ /**
+ * Callback called when Virtual Cable is removed. This can be either due to
+ * {@link BluetoothHidDevice#unplug()} or request from remote side. After
+ * this callback is received connection will be disconnected automatically.
+ */
+ public void onVirtualCableUnplug() {
+ Log.d(TAG, "onVirtualCableUnplug");
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index f363607..6a009a9 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -143,11 +143,17 @@
public static final int MAP_CLIENT = 18;
/**
+ * HID device
+ * @hide
+ */
+ static public final int HID_DEVICE = 19;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
* @hide
*/
- public static final int MAX_PROFILE_ID = 17;
+ public static final int MAX_PROFILE_ID = 19;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/IBluetoothHidDevice.aidl b/core/java/android/bluetooth/IBluetoothHidDevice.aidl
new file mode 100644
index 0000000..15f7313
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothHidDevice.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 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.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDeviceAppConfiguration;
+import android.bluetooth.IBluetoothHidDeviceCallback;
+import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
+import android.bluetooth.BluetoothHidDeviceAppQosSettings;
+
+/** @hide */
+interface IBluetoothHidDevice {
+ boolean registerApp(in BluetoothHidDeviceAppConfiguration config,
+ in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos,
+ in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback);
+ boolean unregisterApp(in BluetoothHidDeviceAppConfiguration config);
+ boolean sendReport(in int id, in byte[] data);
+ boolean replyReport(in byte type, in byte id, in byte[] data);
+ boolean reportError(byte error);
+ boolean unplug();
+ boolean connect();
+ boolean disconnect();
+}
diff --git a/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl
new file mode 100644
index 0000000..1252876
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016, 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.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDeviceAppConfiguration;
+
+/** @hide */
+interface IBluetoothHidDeviceCallback {
+ void onAppStatusChanged(in BluetoothDevice device, in BluetoothHidDeviceAppConfiguration config, boolean registered);
+ void onConnectionStateChanged(in BluetoothDevice device, in int state);
+ void onGetReport(in byte type, in byte id, in int bufferSize);
+ void onSetReport(in byte type, in byte id, in byte[] data);
+ void onSetProtocol(in byte protocol);
+ void onIntrData(in byte reportId, in byte[] data);
+ void onVirtualCableUnplug();
+}