diff options
| author | 2016-12-29 19:46:08 +0000 | |
|---|---|---|
| committer | 2016-12-29 19:46:08 +0000 | |
| commit | 1ee22b7f7050db257572d9c1a2cb0355c2881c6c (patch) | |
| tree | 93462dcbc558e1014fd83ff947672b5075b11c28 | |
| parent | 2c29250b443d1ed263889a19453d3d462c7fbc24 (diff) | |
| parent | ee469c6fe9a91877fa9427bc5135cbec20fe1af6 (diff) | |
Bluetooth: Add support for HID Device Role am: e88fd4b594
am: ee469c6fe9
Change-Id: Ic48911d254671d402f5a6b8198109f28e6497036
13 files changed, 1014 insertions, 1 deletions
| diff --git a/Android.mk b/Android.mk index 36c9b596848c..cf4930271576 100644 --- a/Android.mk +++ b/Android.mk @@ -124,6 +124,8 @@ LOCAL_SRC_FILES += \  	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 6c1e2a932700..1f7d92942162 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1940,6 +1940,9 @@ public final class BluetoothAdapter {          } 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 @@ public final class BluetoothAdapter {                  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 000000000000..3e6a07866075 --- /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 000000000000..283a71790d3d --- /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 000000000000..05ba64e981f1 --- /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 000000000000..14f91140af2b --- /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 000000000000..0d6530c11a91 --- /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 000000000000..87dd10ee15f4 --- /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 000000000000..f9a224581953 --- /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 000000000000..0f0e050a1f40 --- /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 f3636070fa5c..6a009a9ba282 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -143,11 +143,17 @@ public interface BluetoothProfile {      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 000000000000..15f73138e2cb --- /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 000000000000..125287696585 --- /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(); +} |