/* * Copyright (C) 2012 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. */ #define LOG_TAG "BluetoothHidHostServiceJni" #define LOG_NDEBUG 1 #include "android_runtime/AndroidRuntime.h" #include "com_android_bluetooth.h" #include "hardware/bt_hh.h" #include "utils/Log.h" #include <string.h> namespace android { static jmethodID method_onConnectStateChanged; static jmethodID method_onGetProtocolMode; static jmethodID method_onGetReport; static jmethodID method_onHandshake; static jmethodID method_onVirtualUnplug; static jmethodID method_onGetIdleTime; static const bthh_interface_t* sBluetoothHidInterface = NULL; static jobject mCallbacksObj = NULL; static jbyteArray marshall_bda(RawAddress* bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return NULL; jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress)); if (!addr) { ALOGE("Fail to new jbyteArray bd addr"); return NULL; } sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress), (jbyte*)bd_addr); return addr; } static void connection_state_callback(RawAddress* bd_addr, bthh_connection_state_t state) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for HID channel state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged, addr.get(), (jint)state); } static void get_protocol_mode_callback(RawAddress* bd_addr, bthh_status_t hh_status, bthh_protocol_mode_t mode) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } if (hh_status != BTHH_OK) { ALOGE("BTHH Status is not OK!"); return; } ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for get protocal mode callback"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetProtocolMode, addr.get(), (jint)mode); } static void get_report_callback(RawAddress* bd_addr, bthh_status_t hh_status, uint8_t* rpt_data, int rpt_size) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } if (hh_status != BTHH_OK) { ALOGE("BTHH Status is not OK!"); return; } ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for get report callback"); return; } ScopedLocalRef<jbyteArray> data(sCallbackEnv.get(), sCallbackEnv->NewByteArray(rpt_size)); if (!data.get()) { ALOGE("Fail to new jbyteArray data for get report callback"); return; } sCallbackEnv->SetByteArrayRegion(data.get(), 0, rpt_size, (jbyte*)rpt_data); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetReport, addr.get(), data.get(), (jint)rpt_size); } static void virtual_unplug_callback(RawAddress* bd_addr, bthh_status_t hh_status) { ALOGV("call to virtual_unplug_callback"); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for HID channel state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualUnplug, addr.get(), (jint)hh_status); } static void handshake_callback(RawAddress* bd_addr, bthh_status_t hh_status) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for handshake callback"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onHandshake, addr.get(), (jint)hh_status); } static void get_idle_time_callback(RawAddress* bd_addr, bthh_status_t hh_status, int idle_time) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("%s: Fail to new jbyteArray bd addr", __func__); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetIdleTime, addr.get(), (jint)idle_time); } static bthh_callbacks_t sBluetoothHidCallbacks = { sizeof(sBluetoothHidCallbacks), connection_state_callback, NULL, get_protocol_mode_callback, get_idle_time_callback, get_report_callback, virtual_unplug_callback, handshake_callback}; // Define native functions static void classInitNative(JNIEnv* env, jclass clazz) { method_onConnectStateChanged = env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V"); method_onGetProtocolMode = env->GetMethodID(clazz, "onGetProtocolMode", "([BI)V"); method_onGetReport = env->GetMethodID(clazz, "onGetReport", "([B[BI)V"); method_onHandshake = env->GetMethodID(clazz, "onHandshake", "([BI)V"); method_onVirtualUnplug = env->GetMethodID(clazz, "onVirtualUnplug", "([BI)V"); method_onGetIdleTime = env->GetMethodID(clazz, "onGetIdleTime", "([BI)V"); ALOGI("%s: succeeds", __func__); } static void initializeNative(JNIEnv* env, jobject object) { const bt_interface_t* btInf = getBluetoothInterface(); if (btInf == NULL) { ALOGE("Bluetooth module is not loaded"); return; } if (sBluetoothHidInterface != NULL) { ALOGW("Cleaning up Bluetooth HID Interface before initializing..."); sBluetoothHidInterface->cleanup(); sBluetoothHidInterface = NULL; } if (mCallbacksObj != NULL) { ALOGW("Cleaning up Bluetooth GID callback object"); env->DeleteGlobalRef(mCallbacksObj); mCallbacksObj = NULL; } sBluetoothHidInterface = (bthh_interface_t*)btInf->get_profile_interface(BT_PROFILE_HIDHOST_ID); if (sBluetoothHidInterface == NULL) { ALOGE("Failed to get Bluetooth HID Interface"); return; } bt_status_t status = sBluetoothHidInterface->init(&sBluetoothHidCallbacks); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed to initialize Bluetooth HID, status: %d", status); sBluetoothHidInterface = NULL; return; } mCallbacksObj = env->NewGlobalRef(object); } static void cleanupNative(JNIEnv* env, jobject object) { const bt_interface_t* btInf = getBluetoothInterface(); if (btInf == NULL) { ALOGE("Bluetooth module is not loaded"); return; } if (sBluetoothHidInterface != NULL) { ALOGW("Cleaning up Bluetooth HID Interface..."); sBluetoothHidInterface->cleanup(); sBluetoothHidInterface = NULL; } if (mCallbacksObj != NULL) { ALOGW("Cleaning up Bluetooth GID callback object"); env->DeleteGlobalRef(mCallbacksObj); mCallbacksObj = NULL; } } static jboolean connectHidNative(JNIEnv* env, jobject object, jbyteArray address) { if (!sBluetoothHidInterface) return JNI_FALSE; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("Bluetooth device address null"); return JNI_FALSE; } jboolean ret = JNI_TRUE; bt_status_t status = sBluetoothHidInterface->connect((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed HID channel connection, status: %d", status); ret = JNI_FALSE; } env->ReleaseByteArrayElements(address, addr, 0); return ret; } static jboolean disconnectHidNative(JNIEnv* env, jobject object, jbyteArray address) { jbyte* addr; jboolean ret = JNI_TRUE; if (!sBluetoothHidInterface) return JNI_FALSE; addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("Bluetooth device address null"); return JNI_FALSE; } bt_status_t status = sBluetoothHidInterface->disconnect((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed disconnect hid channel, status: %d", status); ret = JNI_FALSE; } env->ReleaseByteArrayElements(address, addr, 0); return ret; } static jboolean getProtocolModeNative(JNIEnv* env, jobject object, jbyteArray address) { if (!sBluetoothHidInterface) return JNI_FALSE; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("Bluetooth device address null"); return JNI_FALSE; } jboolean ret = JNI_TRUE; // TODO: protocolMode is unused by the backend: see b/28908173 bthh_protocol_mode_t protocolMode = BTHH_UNSUPPORTED_MODE; bt_status_t status = sBluetoothHidInterface->get_protocol( (RawAddress*)addr, (bthh_protocol_mode_t)protocolMode); if (status != BT_STATUS_SUCCESS) { ALOGE("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) { if (!sBluetoothHidInterface) return JNI_FALSE; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("Bluetooth device address null"); return JNI_FALSE; } jboolean ret = JNI_TRUE; bt_status_t status = sBluetoothHidInterface->virtual_unplug((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("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) { if (!sBluetoothHidInterface) return JNI_FALSE; ALOGD("%s: protocolMode = %d", __func__, protocolMode); jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("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: ALOGE("Unknown HID protocol mode"); return JNI_FALSE; } jboolean ret = JNI_TRUE; bt_status_t status = sBluetoothHidInterface->set_protocol((RawAddress*)addr, mode); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed set protocol mode, status: %d", status); ret = JNI_FALSE; } env->ReleaseByteArrayElements(address, addr, 0); return ret; } static jboolean getReportNative(JNIEnv* env, jobject object, jbyteArray address, jbyte reportType, jbyte reportId, jint bufferSize) { ALOGV("%s: reportType = %d, reportId = %d, bufferSize = %d", __func__, reportType, reportId, bufferSize); if (!sBluetoothHidInterface) return JNI_FALSE; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("Bluetooth device address null"); return JNI_FALSE; } jint rType = reportType; jint rId = reportId; bt_status_t status = sBluetoothHidInterface->get_report( (RawAddress*)addr, (bthh_report_type_t)rType, (uint8_t)rId, bufferSize); jboolean ret = JNI_TRUE; if (status != BT_STATUS_SUCCESS) { ALOGE("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) { ALOGV("%s: reportType = %d", __func__, reportType); if (!sBluetoothHidInterface) return JNI_FALSE; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("Bluetooth device address null"); return JNI_FALSE; } jint rType = reportType; const char* c_report = env->GetStringUTFChars(report, NULL); jboolean ret = JNI_TRUE; bt_status_t status = sBluetoothHidInterface->set_report( (RawAddress*)addr, (bthh_report_type_t)rType, (char*)c_report); if (status != BT_STATUS_SUCCESS) { ALOGE("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) { ALOGV("%s", __func__); jboolean ret = JNI_TRUE; if (!sBluetoothHidInterface) return JNI_FALSE; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("Bluetooth device address null"); return JNI_FALSE; } const char* c_report = env->GetStringUTFChars(report, NULL); bt_status_t status = sBluetoothHidInterface->send_data((RawAddress*)addr, (char*)c_report); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed set data, status: %d", status); ret = JNI_FALSE; } env->ReleaseStringUTFChars(report, c_report); env->ReleaseByteArrayElements(address, addr, 0); return ret; } static jboolean getIdleTimeNative(JNIEnv* env, jobject object, jbyteArray address) { if (!sBluetoothHidInterface) return JNI_FALSE; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("%s: Bluetooth device address null", __func__); return JNI_FALSE; } bt_status_t status = sBluetoothHidInterface->get_idle_time((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("%s: Failed get idle time, status: %d", __func__, status); } env->ReleaseByteArrayElements(address, addr, 0); return status == BT_STATUS_SUCCESS ? JNI_TRUE : JNI_FALSE; } static jboolean setIdleTimeNative(JNIEnv* env, jobject object, jbyteArray address, jbyte idle_time) { if (!sBluetoothHidInterface) return JNI_FALSE; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("%s: Bluetooth device address null", __func__); return JNI_FALSE; } bt_status_t status = sBluetoothHidInterface->set_idle_time((RawAddress*)addr, idle_time); if (status != BT_STATUS_SUCCESS) { ALOGE("%s: Failed set idle time, status: %d", __func__, status); } env->ReleaseByteArrayElements(address, addr, 0); return status == BT_STATUS_SUCCESS ? JNI_TRUE : JNI_FALSE; } static JNINativeMethod sMethods[] = { {"classInitNative", "()V", (void*)classInitNative}, {"initializeNative", "()V", (void*)initializeNative}, {"cleanupNative", "()V", (void*)cleanupNative}, {"connectHidNative", "([B)Z", (void*)connectHidNative}, {"disconnectHidNative", "([B)Z", (void*)disconnectHidNative}, {"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}, {"getIdleTimeNative", "([B)Z", (void*)getIdleTimeNative}, {"setIdleTimeNative", "([BB)Z", (void*)setIdleTimeNative}, }; int register_com_android_bluetooth_hid_host(JNIEnv* env) { return jniRegisterNativeMethods(env, "com/android/bluetooth/hid/HidHostService", sMethods, NELEM(sMethods)); } } // namespace android