HID-PTS:Included API and calbabkcs for hid pts tests

Change-Id: I7f3b19dbcda24dbb0cd4700a54f7cd05e84d3745
diff --git a/jni/com_android_bluetooth_hid.cpp b/jni/com_android_bluetooth_hid.cpp
index cae1056..d0c178d 100755
--- a/jni/com_android_bluetooth_hid.cpp
+++ b/jni/com_android_bluetooth_hid.cpp
@@ -22,6 +22,9 @@
 namespace android {
 
 static jmethodID method_onConnectStateChanged;
+static jmethodID method_onGetProtocolMode;
+static jmethodID method_onGetReport;
+static jmethodID method_onVirtualUnplug;
 
 static const bthh_interface_t *sBluetoothHidInterface = NULL;
 static jobject mCallbacksObj = NULL;
@@ -58,14 +61,71 @@
     sCallbackEnv->DeleteLocalRef(addr);
 }
 
+static void get_protocol_mode_callback(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,bthh_protocol_mode_t mode) {
+    jbyteArray addr;
+
+    CHECK_CALLBACK_ENV
+    if (hh_status != BTHH_OK) {
+        LOGE("BTHH Status is not OK!");
+        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+        return;
+    }
+
+    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+    if (!addr) {
+        LOGE("Fail to new jbyteArray bd addr for get protocal mode callback");
+        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+        return;
+    }
+    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
+
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetProtocolMode, addr, (jint) mode);
+    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+    sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void virtual_unplug_callback(bt_bdaddr_t *bd_addr, bthh_status_t hh_status) {
+    LOGD("call to virtual_unplug_callback");
+    jbyteArray addr;
+
+    CHECK_CALLBACK_ENV
+    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+    if (!addr) {
+        LOGE("Fail to new jbyteArray bd addr for HID channel state");
+        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+        return;
+    }
+    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
+
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualUnplug, addr, (jint) hh_status);
+    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+    sCallbackEnv->DeleteLocalRef(addr);
+
+    /*jbyteArray addr;
+    jint status = hh_status;
+    CHECK_CALLBACK_ENV
+    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+    if (!addr) {
+        LOGE("Fail to new jbyteArray bd addr for HID report");
+        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+        return;
+    }
+    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
+
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualUnplug, addr, status);
+    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+    sCallbackEnv->DeleteLocalRef(addr);*/
+}
+
+
 static bthh_callbacks_t sBluetoothHidCallbacks = {
     sizeof(sBluetoothHidCallbacks),
     connection_state_callback,
     NULL,
+    get_protocol_mode_callback,
     NULL,
     NULL,
-    NULL,
-    NULL
+    virtual_unplug_callback
 };
 
 // Define native functions
@@ -75,8 +135,9 @@
     const bt_interface_t* btInf;
     bt_status_t status;
 
-    method_onConnectStateChanged = env->GetMethodID(clazz, "onConnectStateChanged",
-                                                    "([BI)V");
+    method_onConnectStateChanged = env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V");
+    method_onGetProtocolMode = env->GetMethodID(clazz, "onGetProtocolMode", "([BI)V");
+    method_onVirtualUnplug = env->GetMethodID(clazz, "onVirtualUnplug", "([BI)V");
 
     if ( (btInf = getBluetoothInterface()) == NULL) {
         LOGE("Bluetooth module is not loaded");
@@ -149,12 +210,177 @@
     return ret;
 }
 
+static jboolean getProtocolModeNative(JNIEnv *env, jobject object, jbyteArray address) {
+    bt_status_t status;
+    jbyte *addr;
+    jboolean ret = JNI_TRUE;
+    bthh_protocol_mode_t protocolMode;
+    if (!sBluetoothHidInterface) return JNI_FALSE;
+
+    addr = env->GetByteArrayElements(address, NULL);
+    if (!addr) {
+        LOGE("Bluetooth device address null");
+        return JNI_FALSE;
+    }
+
+    if ( (status = sBluetoothHidInterface->get_protocol((bt_bdaddr_t *) addr, (bthh_protocol_mode_t) protocolMode)) !=
+         BT_STATUS_SUCCESS) {
+        LOGE("Failed get protocol mode, status: %d", status);
+        ret = JNI_FALSE;
+    }
+    env->ReleaseByteArrayElements(address, addr, 0);
+
+    return ret;
+}
+
+static jboolean virtualUnPlugNative(JNIEnv *env, jobject object, jbyteArray address) {
+    bt_status_t status;
+    jbyte *addr;
+    jboolean ret = JNI_TRUE;
+    if (!sBluetoothHidInterface) return JNI_FALSE;
+
+    addr = env->GetByteArrayElements(address, NULL);
+        if (!addr) {
+            LOGE("Bluetooth device address null");
+            return JNI_FALSE;
+        }
+    if ( (status = sBluetoothHidInterface->virtual_unplug((bt_bdaddr_t *) addr)) !=
+             BT_STATUS_SUCCESS) {
+        LOGE("Failed virual unplug, status: %d", status);
+        ret = JNI_FALSE;
+    }
+    env->ReleaseByteArrayElements(address, addr, 0);
+    return ret;
+
+}
+
+
+static jboolean setProtocolModeNative(JNIEnv *env, jobject object, jbyteArray address, jint protocolMode) {
+    bt_status_t status;
+    jbyte *addr;
+    jboolean ret = JNI_TRUE;
+    if (!sBluetoothHidInterface) return JNI_FALSE;
+
+    LOGD("%s: protocolMode = %d", __FUNCTION__, protocolMode);
+
+    addr = env->GetByteArrayElements(address, NULL);
+    if (!addr) {
+        LOGE("Bluetooth device address null");
+        return JNI_FALSE;
+    }
+
+    bthh_protocol_mode_t mode;
+    switch(protocolMode){
+        case 0:
+            mode = BTHH_REPORT_MODE;
+            break;
+        case 1:
+            mode = BTHH_BOOT_MODE;
+            break;
+        default:
+            LOGE("Unknown HID protocol mode");
+            return JNI_FALSE;
+    }
+    if ( (status = sBluetoothHidInterface->set_protocol((bt_bdaddr_t *) addr, mode)) !=
+             BT_STATUS_SUCCESS) {
+        LOGE("Failed set protocol mode, status: %d", status);
+        ret = JNI_FALSE;
+    }
+    env->ReleaseByteArrayElements(address, addr, 0);
+
+    return JNI_TRUE;
+}
+
+static jboolean getReportNative(JNIEnv *env, jobject object, jbyteArray address, jbyte reportType, jbyte reportId, jint bufferSize) {
+    LOGD("%s: reportType = %d, reportId = %d, bufferSize = %d", __FUNCTION__, reportType, reportId, bufferSize);
+
+    bt_status_t status;
+    jbyte *addr;
+    jboolean ret = JNI_TRUE;
+    if (!sBluetoothHidInterface) return JNI_FALSE;
+
+    addr = env->GetByteArrayElements(address, NULL);
+    if (!addr) {
+        LOGE("Bluetooth device address null");
+        return JNI_FALSE;
+    }
+
+    jint rType = reportType;
+    jint rId = reportId;
+
+    if ( (status = sBluetoothHidInterface->get_report((bt_bdaddr_t *) addr, (bthh_report_type_t) rType, (uint8_t) rId, bufferSize)) !=
+             BT_STATUS_SUCCESS) {
+        LOGE("Failed get report, status: %d", status);
+        ret = JNI_FALSE;
+    }
+    env->ReleaseByteArrayElements(address, addr, 0);
+
+    return ret;
+}
+
+
+static jboolean setReportNative(JNIEnv *env, jobject object, jbyteArray address, jbyte reportType, jstring report) {
+    LOGD("%s: reportType = %d", __FUNCTION__, reportType);
+    bt_status_t status;
+    jbyte *addr;
+    jboolean ret = JNI_TRUE;
+    if (!sBluetoothHidInterface) return JNI_FALSE;
+
+    addr = env->GetByteArrayElements(address, NULL);
+    if (!addr) {
+        LOGE("Bluetooth device address null");
+        return JNI_FALSE;
+    }
+    jint rType = reportType;
+    const char *c_report = env->GetStringUTFChars(report, NULL);
+
+    if ( (status = sBluetoothHidInterface->set_report((bt_bdaddr_t *) addr, (bthh_report_type_t)rType, (char*) c_report)) !=
+             BT_STATUS_SUCCESS) {
+        LOGE("Failed set report, status: %d", status);
+        ret = JNI_FALSE;
+    }
+    env->ReleaseStringUTFChars(report, c_report);
+    env->ReleaseByteArrayElements(address, addr, 0);
+
+    return ret;
+}
+
+static jboolean sendDataNative(JNIEnv *env, jobject object, jbyteArray address, jstring report) {
+    LOGD("%s", __FUNCTION__);
+    bt_status_t status;
+    jbyte *addr;
+    jboolean ret = JNI_TRUE;
+    if (!sBluetoothHidInterface) return JNI_FALSE;
+
+    addr = env->GetByteArrayElements(address, NULL);
+    if (!addr) {
+        LOGE("Bluetooth device address null");
+        return JNI_FALSE;
+    }
+    const char *c_report = env->GetStringUTFChars(report, NULL);
+    if ( (status = sBluetoothHidInterface->send_data((bt_bdaddr_t *) addr, (char*) c_report)) !=
+             BT_STATUS_SUCCESS) {
+        LOGE("Failed set report, status: %d", status);
+        ret = JNI_FALSE;
+    }
+    env->ReleaseStringUTFChars(report, c_report);
+    env->ReleaseByteArrayElements(address, addr, 0);
+
+    return ret;
+
+}
+
 static JNINativeMethod sMethods[] = {
     {"classInitNative", "()V", (void *) classInitNative},
     {"initializeNativeDataNative", "()V", (void *) initializeNativeDataNative},
     {"connectHidNative", "([B)Z", (void *) connectHidNative},
     {"disconnectHidNative", "([B)Z", (void *) disconnectHidNative},
-    // TBD
+    {"getProtocolModeNative", "([B)Z", (void *) getProtocolModeNative},
+    {"virtualUnPlugNative", "([B)Z", (void *) virtualUnPlugNative},
+    {"setProtocolModeNative", "([BB)Z", (void *) setProtocolModeNative},
+    {"getReportNative", "([BBBI)Z", (void *) getReportNative},
+    {"setReportNative", "([BBLjava/lang/String;)Z", (void *) setReportNative},
+    {"sendDataNative", "([BLjava/lang/String;)Z", (void *) sendDataNative},
 };
 
 int register_com_android_bluetooth_hid(JNIEnv* env)
diff --git a/src/com/android/bluetooth/hid/HidService.java b/src/com/android/bluetooth/hid/HidService.java
old mode 100644
new mode 100755
index bc432be..2bf580f
--- a/src/com/android/bluetooth/hid/HidService.java
+++ b/src/com/android/bluetooth/hid/HidService.java
@@ -12,6 +12,7 @@
 import android.bluetooth.IBluetooth;
 import android.bluetooth.IBluetoothInputDevice;
 import android.content.Intent;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Handler;
 import android.os.Message;
@@ -46,6 +47,15 @@
     private static final int MESSAGE_CONNECT = 1;
     private static final int MESSAGE_DISCONNECT = 2;
     private static final int MESSAGE_CONNECT_STATE_CHANGED = 3;
+    private static final int MESSAGE_GET_PROTOCOL_MODE = 4;
+    private static final int MESSAGE_VIRTUAL_UNPLUG = 5;
+    private static final int MESSAGE_ON_GET_PROTOCOL_MODE = 6;
+    private static final int MESSAGE_SET_PROTOCOL_MODE = 7;
+    private static final int MESSAGE_GET_REPORT = 8;
+    private static final int MESSAGE_ON_GET_REPORT = 9;
+    private static final int MESSAGE_SET_REPORT = 10;
+    private static final int MESSAGE_SEND_DATA = 11;
+    private static final int MESSAGE_ON_VIRTUAL_UNPLUG = 12;
 
     static {
         classInitNative();
@@ -113,6 +123,80 @@
                     broadcastConnectionState(device, convertHalState(halState));
                 }
                     break;
+                case MESSAGE_GET_PROTOCOL_MODE:
+                {
+                    BluetoothDevice device = (BluetoothDevice) msg.obj;
+                    if(!getProtocolModeNative(getByteAddress(device)) ) {
+                        Log.e(TAG, "Error: get protocol mode native returns false");
+                    }
+                }
+                break;
+
+                case MESSAGE_ON_GET_PROTOCOL_MODE:
+                {
+                    BluetoothDevice device = getDevice((byte[]) msg.obj);
+                    int protocolMode = msg.arg1;
+                    broadcastProtocolMode(device, protocolMode);
+                }
+                break;
+                case MESSAGE_VIRTUAL_UNPLUG:
+                {
+                    BluetoothDevice device = (BluetoothDevice) msg.obj;
+                    if(!virtualUnPlugNative(getByteAddress(device))) {
+                        Log.e(TAG, "Error: virtual unplug native returns false");
+                    }
+                }
+                break;
+                case MESSAGE_SET_PROTOCOL_MODE:
+                {
+                    BluetoothDevice device = (BluetoothDevice) msg.obj;
+                    byte protocolMode = (byte) msg.arg1;
+                    log("sending set protocol mode(" + protocolMode + ")");
+                    if(!setProtocolModeNative(getByteAddress(device), protocolMode)) {
+                        Log.e(TAG, "Error: set protocol mode native returns false");
+                    }
+                }
+                break;
+                case MESSAGE_GET_REPORT:
+                {
+                    BluetoothDevice device = (BluetoothDevice) msg.obj;
+                    Bundle data = msg.getData();
+                    byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE);
+                    byte reportId = data.getByte(BluetoothInputDevice.EXTRA_REPORT_ID);
+                    int bufferSize = data.getInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE);
+                    if(!getReportNative(getByteAddress(device), reportType, reportId, bufferSize)) {
+                        Log.e(TAG, "Error: get report native returns false");
+                    }
+                }
+                break;
+                case MESSAGE_SET_REPORT:
+                {
+                    BluetoothDevice device = (BluetoothDevice) msg.obj;
+                    Bundle data = msg.getData();
+                    byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE);
+                    String report = data.getString(BluetoothInputDevice.EXTRA_REPORT);
+                    if(!setReportNative(getByteAddress(device), reportType, report)) {
+                        Log.e(TAG, "Error: set report native returns false");
+                    }
+                }
+                break;
+                case MESSAGE_SEND_DATA:
+                {
+                    BluetoothDevice device = (BluetoothDevice) msg.obj;
+                    Bundle data = msg.getData();
+                    String report = data.getString(BluetoothInputDevice.EXTRA_REPORT);
+                    if(!sendDataNative(getByteAddress(device), report)) {
+                        Log.e(TAG, "Error: send data native returns false");
+                    }
+                }
+                break;
+                case MESSAGE_ON_VIRTUAL_UNPLUG:
+                {
+                    BluetoothDevice device = getDevice((byte[]) msg.obj);
+                    int status = msg.arg1;
+                    broadcastVirtualUnplugStatus(device, status);
+                }
+                break;
             }
         }
     };
@@ -195,8 +279,118 @@
             return priority;
         }
 
+        public boolean getProtocolMode(BluetoothDevice device) {
+            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                           "Need BLUETOOTH_ADMIN permission");
+            int state = this.getConnectionState(device);
+            if (state != BluetoothInputDevice.STATE_CONNECTED) {
+                return false;
+            }
+            Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE);
+            msg.obj = device;
+            mHandler.sendMessage(msg);
+            return true;
+            /* String objectPath = getObjectPathFromAddress(device.getAddress());
+                return getProtocolModeInputDeviceNative(objectPath);*/
+        }
+
+        public boolean virtualUnplug(BluetoothDevice device) {
+            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                           "Need BLUETOOTH_ADMIN permission");
+            int state = this.getConnectionState(device);
+            if (state != BluetoothInputDevice.STATE_CONNECTED) {
+                return false;
+            }
+            Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG);
+            msg.obj = device;
+            mHandler.sendMessage(msg);
+
+            return true;
+        }
+
+        public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
+            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                           "Need BLUETOOTH_ADMIN permission");
+            int state = this.getConnectionState(device);
+            if (state != BluetoothInputDevice.STATE_CONNECTED) {
+                return false;
+            }
+            Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE);
+            msg.obj = device;
+            msg.arg1 = protocolMode;
+            mHandler.sendMessage(msg);
+            return true ;
+        }
+
+        public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
+            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                           "Need BLUETOOTH_ADMIN permission");
+            int state = this.getConnectionState(device);
+            if (state != BluetoothInputDevice.STATE_CONNECTED) {
+                return false;
+            }
+            Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
+            msg.obj = device;
+            Bundle data = new Bundle();
+            data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType);
+            data.putByte(BluetoothInputDevice.EXTRA_REPORT_ID, reportId);
+            data.putInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE, bufferSize);
+            msg.setData(data);
+            mHandler.sendMessage(msg);
+            return true ;
+        }
+
+        public boolean setReport(BluetoothDevice device, byte reportType, String report) {
+            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                       "Need BLUETOOTH_ADMIN permission");
+            int state = this.getConnectionState(device);
+            if (state != BluetoothInputDevice.STATE_CONNECTED) {
+                return false;
+            }
+            Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
+            msg.obj = device;
+            Bundle data = new Bundle();
+            data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType);
+            data.putString(BluetoothInputDevice.EXTRA_REPORT, report);
+            msg.setData(data);
+            mHandler.sendMessage(msg);
+            return true ;
+
+        }
+
+        public boolean sendData(BluetoothDevice device, String report) {
+            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                       "Need BLUETOOTH_ADMIN permission");
+            int state = this.getConnectionState(device);
+            if (state != BluetoothInputDevice.STATE_CONNECTED) {
+                return false;
+            }
+
+            return sendDataNative(getByteAddress(device), report);
+            /*Message msg = mHandler.obtainMessage(MESSAGE_SEND_DATA);
+            msg.obj = device;
+            Bundle data = new Bundle();
+            data.putString(BluetoothInputDevice.EXTRA_REPORT, report);
+            msg.setData(data);
+            mHandler.sendMessage(msg);
+            return true ;*/
+        }
     };
 
+    private void onGetProtocolMode(byte[] address, int mode) {
+        Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_PROTOCOL_MODE);
+        msg.obj = address;
+        msg.arg1 = mode;
+        mHandler.sendMessage(msg);
+    }
+
+    private void onVirtualUnplug(byte[] address, int status) {
+        Message msg = mHandler.obtainMessage(MESSAGE_ON_VIRTUAL_UNPLUG);
+        msg.obj = address;
+        msg.arg1 = status;
+        mHandler.sendMessage(msg);
+    }
+
     private void onConnectStateChanged(byte[] address, int state) {
         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
         msg.obj = address;
@@ -230,6 +424,23 @@
         }
     }
 
+    private void broadcastProtocolMode(BluetoothDevice device, int protocolMode) {
+        Intent intent = new Intent(BluetoothInputDevice.ACTION_PROTOCOL_MODE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.putExtra(BluetoothInputDevice.EXTRA_PROTOCOL_MODE, protocolMode);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        sendBroadcast(intent, BLUETOOTH_PERM);
+        if (DBG) log("Protocol Mode (" + device + "): " + protocolMode);
+    }
+
+    private void broadcastVirtualUnplugStatus(BluetoothDevice device, int status) {
+        Intent intent = new Intent(BluetoothInputDevice.ACTION_VIRTUAL_UNPLUG_STATUS);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.putExtra(BluetoothInputDevice.EXTRA_VIRTUAL_UNPLUG_STATUS, status);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        sendBroadcast(intent, BLUETOOTH_PERM);
+    }
+
     private BluetoothDevice getDevice(byte[] address) {
         return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
     }
@@ -269,4 +480,10 @@
     private native void initializeNativeDataNative();
     private native boolean connectHidNative(byte[] btAddress);
     private native boolean disconnectHidNative(byte[] btAddress);
+    private native boolean getProtocolModeNative(byte[] btAddress);
+    private native boolean virtualUnPlugNative(byte[] btAddress);
+    private native boolean setProtocolModeNative(byte[] btAddress, byte protocolMode);
+    private native boolean getReportNative(byte[]btAddress, byte reportType, byte reportId, int bufferSize);
+    private native boolean setReportNative(byte[] btAddress, byte reportType, String report);
+    private native boolean sendDataNative(byte[] btAddress, String report);
 }