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 "BluetoothHealthServiceJni"
     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_hl.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_onAppRegistrationState;
     37 static jmethodID method_onChannelStateChanged;
     38 
     39 static const bthl_interface_t *sBluetoothHdpInterface = NULL;
     40 static jobject mCallbacksObj = NULL;
     41 static JNIEnv *sCallbackEnv = NULL;
     42 
     43 static bool checkCallbackThread() {
     44     sCallbackEnv = getCallbackEnv();
     45 
     46     JNIEnv* env = AndroidRuntime::getJNIEnv();
     47     if (sCallbackEnv != env || sCallbackEnv == NULL) {
     48         ALOGE("Callback env check fail: env: %p, callback: %p", env, sCallbackEnv);
     49         return false;
     50     }
     51     return true;
     52 }
     53 
     54 // Define callback functions
     55 static void app_registration_state_callback(int app_id, bthl_app_reg_state_t state) {
     56     CHECK_CALLBACK_ENV
     57     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAppRegistrationState, app_id,
     58                                  (jint) state);
     59     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     60 }
     61 
     62 static void channel_state_callback(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index,
     63                                    int channel_id, bthl_channel_state_t state, int fd) {
     64     jbyteArray addr;
     65     jobject fileDescriptor = NULL;
     66 
     67     CHECK_CALLBACK_ENV
     68     addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
     69     if (!addr) {
     70         ALOGE("Fail to new jbyteArray bd addr for channel state");
     71         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     72         return;
     73     }
     74 
     75     // TODO(BT) check if fd is only valid for BTHH_CONN_STATE_CONNECTED state
     76     if (state == BTHL_CONN_STATE_CONNECTED) {
     77         fileDescriptor = jniCreateFileDescriptor(sCallbackEnv, fd);
     78         if (!fileDescriptor) {
     79             ALOGE("Failed to convert file descriptor, fd: %d", fd);
     80             sCallbackEnv->DeleteLocalRef(addr);
     81             return;
     82         }
     83     }
     84 
     85     sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
     86     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onChannelStateChanged, app_id, addr,
     87                                  mdep_cfg_index, channel_id, (jint) state, fileDescriptor);
     88     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     89     sCallbackEnv->DeleteLocalRef(addr);
     90 }
     91 
     92 static bthl_callbacks_t sBluetoothHdpCallbacks = {
     93     sizeof(sBluetoothHdpCallbacks),
     94     app_registration_state_callback,
     95     channel_state_callback
     96 };
     97 
     98 // Define native functions
     99 
    100 static void classInitNative(JNIEnv* env, jclass clazz) {
    101     method_onAppRegistrationState = env->GetMethodID(clazz, "onAppRegistrationState", "(II)V");
    102     method_onChannelStateChanged = env->GetMethodID(clazz, "onChannelStateChanged",
    103             "(I[BIIILjava/io/FileDescriptor;)V");
    104     ALOGI("%s: succeeds", __FUNCTION__);
    105 }
    106 
    107 static void initializeNative(JNIEnv *env, jobject object) {
    108     const bt_interface_t* btInf;
    109     bt_status_t status;
    110 
    111     if ( (btInf = getBluetoothInterface()) == NULL) {
    112         ALOGE("Bluetooth module is not loaded");
    113         return;
    114     }
    115 
    116     if (sBluetoothHdpInterface !=NULL) {
    117          ALOGW("Cleaning up Bluetooth Health Interface before initializing...");
    118          sBluetoothHdpInterface->cleanup();
    119          sBluetoothHdpInterface = NULL;
    120     }
    121 
    122     if (mCallbacksObj != NULL) {
    123          ALOGW("Cleaning up Bluetooth Health callback object");
    124          env->DeleteGlobalRef(mCallbacksObj);
    125          mCallbacksObj = NULL;
    126     }
    127 
    128     if ( (sBluetoothHdpInterface = (bthl_interface_t *)
    129           btInf->get_profile_interface(BT_PROFILE_HEALTH_ID)) == NULL) {
    130         ALOGE("Failed to get Bluetooth Health Interface");
    131         return;
    132     }
    133 
    134     if ( (status = sBluetoothHdpInterface->init(&sBluetoothHdpCallbacks)) != BT_STATUS_SUCCESS) {
    135         ALOGE("Failed to initialize Bluetooth HDP, status: %d", status);
    136         sBluetoothHdpInterface = NULL;
    137         return;
    138     }
    139 
    140     mCallbacksObj = env->NewGlobalRef(object);
    141 }
    142 
    143 static void cleanupNative(JNIEnv *env, jobject object) {
    144     const bt_interface_t* btInf;
    145 
    146     if ( (btInf = getBluetoothInterface()) == NULL) {
    147         ALOGE("Bluetooth module is not loaded");
    148         return;
    149     }
    150 
    151     if (sBluetoothHdpInterface !=NULL) {
    152         ALOGW("Cleaning up Bluetooth Health Interface...");
    153         sBluetoothHdpInterface->cleanup();
    154         sBluetoothHdpInterface = NULL;
    155     }
    156 
    157     if (mCallbacksObj != NULL) {
    158         ALOGW("Cleaning up Bluetooth Health object");
    159         env->DeleteGlobalRef(mCallbacksObj);
    160         mCallbacksObj = NULL;
    161     }
    162 }
    163 
    164 static jint registerHealthAppNative(JNIEnv *env, jobject object, jint data_type,
    165                                        jint role, jstring name, jint channel_type) {
    166     bt_status_t status;
    167     bthl_mdep_cfg_t mdep_cfg;
    168     bthl_reg_param_t reg_param;
    169     int app_id;
    170 
    171     if (!sBluetoothHdpInterface) {
    172         ALOGE("Failed to register health app. No Bluetooth Health Interface available");
    173         return -1;
    174     }
    175 
    176     mdep_cfg.mdep_role = (bthl_mdep_role_t) role;
    177     mdep_cfg.data_type = data_type;
    178     mdep_cfg.channel_type = (bthl_channel_type_t) channel_type;
    179     // TODO(BT) pass all the followings in from java instead of reuse name
    180     mdep_cfg.mdep_description = env->GetStringUTFChars(name, NULL);
    181     reg_param.application_name = env->GetStringUTFChars(name, NULL);
    182     reg_param.provider_name = NULL;
    183     reg_param.srv_name = NULL;
    184     reg_param.srv_desp = NULL;
    185     reg_param.number_of_mdeps = 1;
    186     reg_param.mdep_cfg = &mdep_cfg;
    187 
    188     if ( (status = sBluetoothHdpInterface->register_application(&reg_param, &app_id)) !=
    189          BT_STATUS_SUCCESS) {
    190         ALOGE("Failed register health app, status: %d", status);
    191         return -1;
    192     }
    193 
    194     env->ReleaseStringUTFChars(name, mdep_cfg.mdep_description);
    195     env->ReleaseStringUTFChars(name, reg_param.application_name);
    196     return app_id;
    197 }
    198 
    199 static jboolean unregisterHealthAppNative(JNIEnv *env, jobject object, int app_id) {
    200     bt_status_t status;
    201     if (!sBluetoothHdpInterface) return JNI_FALSE;
    202 
    203     if ((status = sBluetoothHdpInterface->unregister_application(app_id)) != BT_STATUS_SUCCESS) {
    204         ALOGE("Failed to unregister app %d, status: %d", app_id, status);
    205     }
    206     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    207 }
    208 
    209 static jint connectChannelNative(JNIEnv *env, jobject object,
    210                                  jbyteArray address, jint app_id) {
    211     bt_status_t status;
    212     jbyte *addr;
    213     jint chan_id;
    214     if (!sBluetoothHdpInterface) return -1;
    215 
    216     addr = env->GetByteArrayElements(address, NULL);
    217     if (!addr) {
    218         ALOGE("Bluetooth device address null");
    219         return -1;
    220     }
    221 
    222     if ( (status = sBluetoothHdpInterface->connect_channel(app_id, (bt_bdaddr_t *) addr,
    223                                                            0, &chan_id)) !=
    224          BT_STATUS_SUCCESS) {
    225         ALOGE("Failed HDP channel connection, status: %d", status);
    226         chan_id = -1;
    227     }
    228     env->ReleaseByteArrayElements(address, addr, 0);
    229 
    230     return chan_id;
    231 }
    232 
    233 static jboolean disconnectChannelNative(JNIEnv *env, jobject object, jint channel_id) {
    234     bt_status_t status;
    235     if (!sBluetoothHdpInterface) return JNI_FALSE;
    236 
    237     if ( (status = sBluetoothHdpInterface->destroy_channel(channel_id)) !=
    238          BT_STATUS_SUCCESS) {
    239         ALOGE("Failed disconnect health channel, status: %d", status);
    240         return JNI_FALSE;
    241     }
    242     return JNI_TRUE;
    243 }
    244 
    245 static JNINativeMethod sMethods[] = {
    246     {"classInitNative", "()V", (void *) classInitNative},
    247     {"initializeNative", "()V", (void *) initializeNative},
    248     {"cleanupNative", "()V", (void *) cleanupNative},
    249     {"registerHealthAppNative", "(IILjava/lang/String;I)I", (void *) registerHealthAppNative},
    250     {"unregisterHealthAppNative", "(I)Z", (void *) unregisterHealthAppNative},
    251     {"connectChannelNative", "([BI)I", (void *) connectChannelNative},
    252     {"disconnectChannelNative", "(I)Z", (void *) disconnectChannelNative},
    253 };
    254 
    255 int register_com_android_bluetooth_hdp(JNIEnv* env)
    256 {
    257     return jniRegisterNativeMethods(env, "com/android/bluetooth/hdp/HealthService",
    258                                     sMethods, NELEM(sMethods));
    259 }
    260 
    261 }
    262