Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "BluetoothHidServiceJni"
     18 
     19 #define LOG_NDEBUG 0
     20 
     21 #define CHECK_CALLBACK_ENV                                                      \
     22    if (!checkCallbackThread()) {                                                \
     23        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);\
     24        return;                                                                  \
     25    }
     26 
     27 #include "com_android_bluetooth.h"
     28 #include "hardware/bt_hh.h"
     29 #include "utils/Log.h"
     30 #include "android_runtime/AndroidRuntime.h"
     31 
     32 #include <string.h>
     33 
     34 namespace android {
     35 
     36 static jmethodID method_onConnectStateChanged;
     37 static jmethodID method_onGetProtocolMode;
     38 static jmethodID method_onGetReport;
     39 static jmethodID method_onVirtualUnplug;
     40 
     41 static const bthh_interface_t *sBluetoothHidInterface = NULL;
     42 static jobject mCallbacksObj = NULL;
     43 static JNIEnv *sCallbackEnv = NULL;
     44 
     45 static bool checkCallbackThread() {
     46 
     47     // Always fetch the latest callbackEnv from AdapterService.
     48     // Caching this could cause this sCallbackEnv to go out-of-sync
     49     // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event
     50     // is received
     51 
     52     sCallbackEnv = getCallbackEnv();
     53 
     54     JNIEnv* env = AndroidRuntime::getJNIEnv();
     55     if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
     56     return true;
     57 }
     58 
     59 static void connection_state_callback(bt_bdaddr_t *bd_addr, bthh_connection_state_t state) {
     60     jbyteArray addr;
     61 
     62     CHECK_CALLBACK_ENV
     63     addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
     64     if (!addr) {
     65         ALOGE("Fail to new jbyteArray bd addr for HID channel state");
     66         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     67         return;
     68     }
     69     sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
     70 
     71     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged, addr, (jint) state);
     72     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     73     sCallbackEnv->DeleteLocalRef(addr);
     74 }
     75 
     76 static void get_protocol_mode_callback(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,bthh_protocol_mode_t mode) {
     77     jbyteArray addr;
     78 
     79     CHECK_CALLBACK_ENV
     80     if (hh_status != BTHH_OK) {
     81         ALOGE("BTHH Status is not OK!");
     82         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     83         return;
     84     }
     85 
     86     addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
     87     if (!addr) {
     88         ALOGE("Fail to new jbyteArray bd addr for get protocal mode callback");
     89         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     90         return;
     91     }
     92     sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
     93 
     94     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetProtocolMode, addr, (jint) mode);
     95     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     96     sCallbackEnv->DeleteLocalRef(addr);
     97 }
     98 
     99 static void virtual_unplug_callback(bt_bdaddr_t *bd_addr, bthh_status_t hh_status) {
    100     ALOGD("call to virtual_unplug_callback");
    101     jbyteArray addr;
    102 
    103     CHECK_CALLBACK_ENV
    104     addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    105     if (!addr) {
    106         ALOGE("Fail to new jbyteArray bd addr for HID channel state");
    107         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    108         return;
    109     }
    110     sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
    111 
    112     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualUnplug, addr, (jint) hh_status);
    113     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    114     sCallbackEnv->DeleteLocalRef(addr);
    115 
    116     /*jbyteArray addr;
    117     jint status = hh_status;
    118     CHECK_CALLBACK_ENV
    119     addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    120     if (!addr) {
    121         ALOGE("Fail to new jbyteArray bd addr for HID report");
    122         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    123         return;
    124     }
    125     sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
    126 
    127     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualUnplug, addr, status);
    128     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    129     sCallbackEnv->DeleteLocalRef(addr);*/
    130 }
    131 
    132 
    133 static bthh_callbacks_t sBluetoothHidCallbacks = {
    134     sizeof(sBluetoothHidCallbacks),
    135     connection_state_callback,
    136     NULL,
    137     get_protocol_mode_callback,
    138     NULL,
    139     NULL,
    140     virtual_unplug_callback
    141 };
    142 
    143 // Define native functions
    144 
    145 static void classInitNative(JNIEnv* env, jclass clazz) {
    146     int err;
    147 //    const bt_interface_t* btInf;
    148 //    bt_status_t status;
    149 
    150     method_onConnectStateChanged = env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V");
    151     method_onGetProtocolMode = env->GetMethodID(clazz, "onGetProtocolMode", "([BI)V");
    152     method_onVirtualUnplug = env->GetMethodID(clazz, "onVirtualUnplug", "([BI)V");
    153 
    154 /*
    155     if ( (btInf = getBluetoothInterface()) == NULL) {
    156         ALOGE("Bluetooth module is not loaded");
    157         return;
    158     }
    159 
    160     if ( (sBluetoothHidInterface = (bthh_interface_t *)
    161           btInf->get_profile_interface(BT_PROFILE_HIDHOST_ID)) == NULL) {
    162         ALOGE("Failed to get Bluetooth Handsfree Interface");
    163         return;
    164     }
    165 
    166     // TODO(BT) do this only once or
    167     //          Do we need to do this every time the BT reenables?
    168     if ( (status = sBluetoothHidInterface->init(&sBluetoothHidCallbacks)) != BT_STATUS_SUCCESS) {
    169         ALOGE("Failed to initialize Bluetooth HID, status: %d", status);
    170         sBluetoothHidInterface = NULL;
    171         return;
    172     }
    173 
    174 */
    175     ALOGI("%s: succeeds", __FUNCTION__);
    176 }
    177 
    178 static void initializeNative(JNIEnv *env, jobject object) {
    179     const bt_interface_t* btInf;
    180     bt_status_t status;
    181 
    182     if ( (btInf = getBluetoothInterface()) == NULL) {
    183         ALOGE("Bluetooth module is not loaded");
    184         return;
    185     }
    186 
    187     if (sBluetoothHidInterface !=NULL) {
    188         ALOGW("Cleaning up Bluetooth HID Interface before initializing...");
    189         sBluetoothHidInterface->cleanup();
    190         sBluetoothHidInterface = NULL;
    191     }
    192 
    193     if (mCallbacksObj != NULL) {
    194         ALOGW("Cleaning up Bluetooth GID callback object");
    195         env->DeleteGlobalRef(mCallbacksObj);
    196         mCallbacksObj = NULL;
    197     }
    198 
    199 
    200     if ( (sBluetoothHidInterface = (bthh_interface_t *)
    201           btInf->get_profile_interface(BT_PROFILE_HIDHOST_ID)) == NULL) {
    202         ALOGE("Failed to get Bluetooth HID Interface");
    203         return;
    204     }
    205 
    206     if ( (status = sBluetoothHidInterface->init(&sBluetoothHidCallbacks)) != BT_STATUS_SUCCESS) {
    207         ALOGE("Failed to initialize Bluetooth HID, status: %d", status);
    208         sBluetoothHidInterface = NULL;
    209         return;
    210     }
    211 
    212 
    213 
    214     mCallbacksObj = env->NewGlobalRef(object);
    215 }
    216 
    217 static void cleanupNative(JNIEnv *env, jobject object) {
    218     const bt_interface_t* btInf;
    219     bt_status_t status;
    220 
    221     if ( (btInf = getBluetoothInterface()) == NULL) {
    222         ALOGE("Bluetooth module is not loaded");
    223         return;
    224     }
    225 
    226     if (sBluetoothHidInterface !=NULL) {
    227         ALOGW("Cleaning up Bluetooth HID Interface...");
    228         sBluetoothHidInterface->cleanup();
    229         sBluetoothHidInterface = NULL;
    230     }
    231 
    232     if (mCallbacksObj != NULL) {
    233         ALOGW("Cleaning up Bluetooth GID callback object");
    234         env->DeleteGlobalRef(mCallbacksObj);
    235         mCallbacksObj = NULL;
    236     }
    237 
    238     env->DeleteGlobalRef(mCallbacksObj);
    239 }
    240 
    241 static jboolean connectHidNative(JNIEnv *env, jobject object, jbyteArray address) {
    242     bt_status_t status;
    243     jbyte *addr;
    244     jboolean ret = JNI_TRUE;
    245     if (!sBluetoothHidInterface) return JNI_FALSE;
    246 
    247     addr = env->GetByteArrayElements(address, NULL);
    248     if (!addr) {
    249         ALOGE("Bluetooth device address null");
    250         return JNI_FALSE;
    251     }
    252 
    253     if ((status = sBluetoothHidInterface->connect((bt_bdaddr_t *) addr)) !=
    254          BT_STATUS_SUCCESS) {
    255         ALOGE("Failed HID channel connection, status: %d", status);
    256         ret = JNI_FALSE;
    257     }
    258     env->ReleaseByteArrayElements(address, addr, 0);
    259 
    260     return ret;
    261 }
    262 
    263 static jboolean disconnectHidNative(JNIEnv *env, jobject object, jbyteArray address) {
    264     bt_status_t status;
    265     jbyte *addr;
    266     jboolean ret = JNI_TRUE;
    267     if (!sBluetoothHidInterface) return JNI_FALSE;
    268 
    269     addr = env->GetByteArrayElements(address, NULL);
    270     if (!addr) {
    271         ALOGE("Bluetooth device address null");
    272         return JNI_FALSE;
    273     }
    274 
    275     if ( (status = sBluetoothHidInterface->disconnect((bt_bdaddr_t *) addr)) !=
    276          BT_STATUS_SUCCESS) {
    277         ALOGE("Failed disconnect hid channel, status: %d", status);
    278         ret = JNI_FALSE;
    279     }
    280     env->ReleaseByteArrayElements(address, addr, 0);
    281 
    282     return ret;
    283 }
    284 
    285 static jboolean getProtocolModeNative(JNIEnv *env, jobject object, jbyteArray address) {
    286     bt_status_t status;
    287     jbyte *addr;
    288     jboolean ret = JNI_TRUE;
    289     bthh_protocol_mode_t protocolMode;
    290     if (!sBluetoothHidInterface) return JNI_FALSE;
    291 
    292     addr = env->GetByteArrayElements(address, NULL);
    293     if (!addr) {
    294         ALOGE("Bluetooth device address null");
    295         return JNI_FALSE;
    296     }
    297 
    298     if ( (status = sBluetoothHidInterface->get_protocol((bt_bdaddr_t *) addr, (bthh_protocol_mode_t) protocolMode)) !=
    299          BT_STATUS_SUCCESS) {
    300         ALOGE("Failed get protocol mode, status: %d", status);
    301         ret = JNI_FALSE;
    302     }
    303     env->ReleaseByteArrayElements(address, addr, 0);
    304 
    305     return ret;
    306 }
    307 
    308 static jboolean virtualUnPlugNative(JNIEnv *env, jobject object, jbyteArray address) {
    309     bt_status_t status;
    310     jbyte *addr;
    311     jboolean ret = JNI_TRUE;
    312     if (!sBluetoothHidInterface) return JNI_FALSE;
    313 
    314     addr = env->GetByteArrayElements(address, NULL);
    315         if (!addr) {
    316             ALOGE("Bluetooth device address null");
    317             return JNI_FALSE;
    318         }
    319     if ( (status = sBluetoothHidInterface->virtual_unplug((bt_bdaddr_t *) addr)) !=
    320              BT_STATUS_SUCCESS) {
    321         ALOGE("Failed virual unplug, status: %d", status);
    322         ret = JNI_FALSE;
    323     }
    324     env->ReleaseByteArrayElements(address, addr, 0);
    325     return ret;
    326 
    327 }
    328 
    329 
    330 static jboolean setProtocolModeNative(JNIEnv *env, jobject object, jbyteArray address, jint protocolMode) {
    331     bt_status_t status;
    332     jbyte *addr;
    333     jboolean ret = JNI_TRUE;
    334     if (!sBluetoothHidInterface) return JNI_FALSE;
    335 
    336     ALOGD("%s: protocolMode = %d", __FUNCTION__, protocolMode);
    337 
    338     addr = env->GetByteArrayElements(address, NULL);
    339     if (!addr) {
    340         ALOGE("Bluetooth device address null");
    341         return JNI_FALSE;
    342     }
    343 
    344     bthh_protocol_mode_t mode;
    345     switch(protocolMode){
    346         case 0:
    347             mode = BTHH_REPORT_MODE;
    348             break;
    349         case 1:
    350             mode = BTHH_BOOT_MODE;
    351             break;
    352         default:
    353             ALOGE("Unknown HID protocol mode");
    354             return JNI_FALSE;
    355     }
    356     if ( (status = sBluetoothHidInterface->set_protocol((bt_bdaddr_t *) addr, mode)) !=
    357              BT_STATUS_SUCCESS) {
    358         ALOGE("Failed set protocol mode, status: %d", status);
    359         ret = JNI_FALSE;
    360     }
    361     env->ReleaseByteArrayElements(address, addr, 0);
    362 
    363     return JNI_TRUE;
    364 }
    365 
    366 static jboolean getReportNative(JNIEnv *env, jobject object, jbyteArray address, jbyte reportType, jbyte reportId, jint bufferSize) {
    367     ALOGD("%s: reportType = %d, reportId = %d, bufferSize = %d", __FUNCTION__, reportType, reportId, bufferSize);
    368 
    369     bt_status_t status;
    370     jbyte *addr;
    371     jboolean ret = JNI_TRUE;
    372     if (!sBluetoothHidInterface) return JNI_FALSE;
    373 
    374     addr = env->GetByteArrayElements(address, NULL);
    375     if (!addr) {
    376         ALOGE("Bluetooth device address null");
    377         return JNI_FALSE;
    378     }
    379 
    380     jint rType = reportType;
    381     jint rId = reportId;
    382 
    383     if ( (status = sBluetoothHidInterface->get_report((bt_bdaddr_t *) addr, (bthh_report_type_t) rType, (uint8_t) rId, bufferSize)) !=
    384              BT_STATUS_SUCCESS) {
    385         ALOGE("Failed get report, status: %d", status);
    386         ret = JNI_FALSE;
    387     }
    388     env->ReleaseByteArrayElements(address, addr, 0);
    389 
    390     return ret;
    391 }
    392 
    393 
    394 static jboolean setReportNative(JNIEnv *env, jobject object, jbyteArray address, jbyte reportType, jstring report) {
    395     ALOGD("%s: reportType = %d", __FUNCTION__, reportType);
    396     bt_status_t status;
    397     jbyte *addr;
    398     jboolean ret = JNI_TRUE;
    399     if (!sBluetoothHidInterface) return JNI_FALSE;
    400 
    401     addr = env->GetByteArrayElements(address, NULL);
    402     if (!addr) {
    403         ALOGE("Bluetooth device address null");
    404         return JNI_FALSE;
    405     }
    406     jint rType = reportType;
    407     const char *c_report = env->GetStringUTFChars(report, NULL);
    408 
    409     if ( (status = sBluetoothHidInterface->set_report((bt_bdaddr_t *) addr, (bthh_report_type_t)rType, (char*) c_report)) !=
    410              BT_STATUS_SUCCESS) {
    411         ALOGE("Failed set report, status: %d", status);
    412         ret = JNI_FALSE;
    413     }
    414     env->ReleaseStringUTFChars(report, c_report);
    415     env->ReleaseByteArrayElements(address, addr, 0);
    416 
    417     return ret;
    418 }
    419 
    420 static jboolean sendDataNative(JNIEnv *env, jobject object, jbyteArray address, jstring report) {
    421     ALOGD("%s", __FUNCTION__);
    422     bt_status_t status;
    423     jbyte *addr;
    424     jboolean ret = JNI_TRUE;
    425     if (!sBluetoothHidInterface) return JNI_FALSE;
    426 
    427     addr = env->GetByteArrayElements(address, NULL);
    428     if (!addr) {
    429         ALOGE("Bluetooth device address null");
    430         return JNI_FALSE;
    431     }
    432     const char *c_report = env->GetStringUTFChars(report, NULL);
    433     if ( (status = sBluetoothHidInterface->send_data((bt_bdaddr_t *) addr, (char*) c_report)) !=
    434              BT_STATUS_SUCCESS) {
    435         ALOGE("Failed set report, status: %d", status);
    436         ret = JNI_FALSE;
    437     }
    438     env->ReleaseStringUTFChars(report, c_report);
    439     env->ReleaseByteArrayElements(address, addr, 0);
    440 
    441     return ret;
    442 
    443 }
    444 
    445 static JNINativeMethod sMethods[] = {
    446     {"classInitNative", "()V", (void *) classInitNative},
    447     {"initializeNative", "()V", (void *) initializeNative},
    448     {"cleanupNative", "()V", (void *) cleanupNative},
    449     {"connectHidNative", "([B)Z", (void *) connectHidNative},
    450     {"disconnectHidNative", "([B)Z", (void *) disconnectHidNative},
    451     {"getProtocolModeNative", "([B)Z", (void *) getProtocolModeNative},
    452     {"virtualUnPlugNative", "([B)Z", (void *) virtualUnPlugNative},
    453     {"setProtocolModeNative", "([BB)Z", (void *) setProtocolModeNative},
    454     {"getReportNative", "([BBBI)Z", (void *) getReportNative},
    455     {"setReportNative", "([BBLjava/lang/String;)Z", (void *) setReportNative},
    456     {"sendDataNative", "([BLjava/lang/String;)Z", (void *) sendDataNative},
    457 };
    458 
    459 int register_com_android_bluetooth_hid(JNIEnv* env)
    460 {
    461     return jniRegisterNativeMethods(env, "com/android/bluetooth/hid/HidService",
    462                                     sMethods, NELEM(sMethods));
    463 }
    464 
    465 }
    466