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     int err;
    102 //    const bt_interface_t* btInf;
    103 //    bt_status_t status;
    104 
    105     method_onAppRegistrationState = env->GetMethodID(clazz, "onAppRegistrationState", "(II)V");
    106     method_onChannelStateChanged = env->GetMethodID(clazz, "onChannelStateChanged",
    107                                                     "(I[BIIILjava/io/FileDescriptor;)V");
    108 
    109 /*
    110     if ( (btInf = getBluetoothInterface()) == NULL) {
    111         ALOGE("Bluetooth module is not loaded");
    112         return;
    113     }
    114 
    115     if ( (sBluetoothHdpInterface = (bthl_interface_t *)
    116           btInf->get_profile_interface(BT_PROFILE_HEALTH_ID)) == NULL) {
    117         ALOGE("Failed to get Bluetooth Handsfree Interface");
    118         return;
    119     }
    120 
    121     // TODO(BT) do this only once or
    122     //          Do we need to do this every time the BT reenables?
    123     if ( (status = sBluetoothHdpInterface->init(&sBluetoothHdpCallbacks)) != BT_STATUS_SUCCESS) {
    124         ALOGE("Failed to initialize Bluetooth HDP, status: %d", status);
    125         sBluetoothHdpInterface = NULL;
    126         return;
    127     }
    128 */
    129 
    130     ALOGI("%s: succeeds", __FUNCTION__);
    131 }
    132 
    133 static void initializeNative(JNIEnv *env, jobject object) {
    134     const bt_interface_t* btInf;
    135     bt_status_t status;
    136 
    137     if ( (btInf = getBluetoothInterface()) == NULL) {
    138         ALOGE("Bluetooth module is not loaded");
    139         return;
    140     }
    141 
    142     if (sBluetoothHdpInterface !=NULL) {
    143          ALOGW("Cleaning up Bluetooth Health Interface before initializing...");
    144          sBluetoothHdpInterface->cleanup();
    145          sBluetoothHdpInterface = NULL;
    146     }
    147 
    148     if (mCallbacksObj != NULL) {
    149          ALOGW("Cleaning up Bluetooth Health callback object");
    150          env->DeleteGlobalRef(mCallbacksObj);
    151          mCallbacksObj = NULL;
    152     }
    153 
    154     if ( (sBluetoothHdpInterface = (bthl_interface_t *)
    155           btInf->get_profile_interface(BT_PROFILE_HEALTH_ID)) == NULL) {
    156         ALOGE("Failed to get Bluetooth Health Interface");
    157         return;
    158     }
    159 
    160     if ( (status = sBluetoothHdpInterface->init(&sBluetoothHdpCallbacks)) != BT_STATUS_SUCCESS) {
    161         ALOGE("Failed to initialize Bluetooth HDP, status: %d", status);
    162         sBluetoothHdpInterface = NULL;
    163         return;
    164     }
    165 
    166     mCallbacksObj = env->NewGlobalRef(object);
    167 }
    168 
    169 static void cleanupNative(JNIEnv *env, jobject object) {
    170     const bt_interface_t* btInf;
    171     bt_status_t status;
    172 
    173     if ( (btInf = getBluetoothInterface()) == NULL) {
    174         ALOGE("Bluetooth module is not loaded");
    175         return;
    176     }
    177 
    178     if (sBluetoothHdpInterface !=NULL) {
    179         ALOGW("Cleaning up Bluetooth Health Interface...");
    180         sBluetoothHdpInterface->cleanup();
    181         sBluetoothHdpInterface = NULL;
    182     }
    183 
    184     if (mCallbacksObj != NULL) {
    185         ALOGW("Cleaning up Bluetooth Health object");
    186         env->DeleteGlobalRef(mCallbacksObj);
    187         mCallbacksObj = NULL;
    188     }
    189 }
    190 
    191 static jint registerHealthAppNative(JNIEnv *env, jobject object, jint data_type,
    192                                        jint role, jstring name, jint channel_type) {
    193     bt_status_t status;
    194     bthl_mdep_cfg_t mdep_cfg;
    195     bthl_reg_param_t reg_param;
    196     int app_id;
    197 
    198     if (!sBluetoothHdpInterface) return NULL;
    199 
    200     mdep_cfg.mdep_role = (bthl_mdep_role_t) role;
    201     mdep_cfg.data_type = data_type;
    202     mdep_cfg.channel_type = (bthl_channel_type_t) channel_type;
    203     // TODO(BT) pass all the followings in from java instead of reuse name
    204     mdep_cfg.mdep_description = env->GetStringUTFChars(name, NULL);
    205     reg_param.application_name = env->GetStringUTFChars(name, NULL);
    206     reg_param.provider_name = NULL;
    207     reg_param.srv_name = NULL;
    208     reg_param.srv_desp = NULL;
    209     reg_param.number_of_mdeps = 1;
    210     reg_param.mdep_cfg = &mdep_cfg;
    211 
    212     if ( (status = sBluetoothHdpInterface->register_application(&reg_param, &app_id)) !=
    213          BT_STATUS_SUCCESS) {
    214         ALOGE("Failed register health app, status: %d", status);
    215         return -1;
    216     }
    217 
    218     env->ReleaseStringUTFChars(name, mdep_cfg.mdep_description);
    219     env->ReleaseStringUTFChars(name, reg_param.application_name);
    220     return app_id;
    221 }
    222 
    223 static jboolean unregisterHealthAppNative(JNIEnv *env, jobject object, int app_id) {
    224     bt_status_t status;
    225     if (!sBluetoothHdpInterface) return JNI_FALSE;
    226 
    227     if ((status = sBluetoothHdpInterface->unregister_application(app_id)) != BT_STATUS_SUCCESS) {
    228         ALOGE("Failed to unregister app %d, status: %d", app_id, status);
    229     }
    230     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    231 }
    232 
    233 static jint connectChannelNative(JNIEnv *env, jobject object,
    234                                  jbyteArray address, jint app_id) {
    235     bt_status_t status;
    236     jbyte *addr;
    237     jint chan_id;
    238     if (!sBluetoothHdpInterface) return -1;
    239 
    240     addr = env->GetByteArrayElements(address, NULL);
    241     if (!addr) {
    242         ALOGE("Bluetooth device address null");
    243         return -1;
    244     }
    245 
    246     if ( (status = sBluetoothHdpInterface->connect_channel(app_id, (bt_bdaddr_t *) addr,
    247                                                            0, &chan_id)) !=
    248          BT_STATUS_SUCCESS) {
    249         ALOGE("Failed HDP channel connection, status: %d", status);
    250         chan_id = -1;
    251     }
    252     env->ReleaseByteArrayElements(address, addr, 0);
    253 
    254     return chan_id;
    255 }
    256 
    257 static jboolean disconnectChannelNative(JNIEnv *env, jobject object, jint channel_id) {
    258     bt_status_t status;
    259     if (!sBluetoothHdpInterface) return JNI_FALSE;
    260 
    261     if ( (status = sBluetoothHdpInterface->destroy_channel(channel_id)) !=
    262          BT_STATUS_SUCCESS) {
    263         ALOGE("Failed disconnect health channel, status: %d", status);
    264         return JNI_FALSE;
    265     }
    266     return JNI_TRUE;
    267 }
    268 
    269 static JNINativeMethod sMethods[] = {
    270     {"classInitNative", "()V", (void *) classInitNative},
    271     {"initializeNative", "()V", (void *) initializeNative},
    272     {"cleanupNative", "()V", (void *) cleanupNative},
    273     {"registerHealthAppNative", "(IILjava/lang/String;I)I", (void *) registerHealthAppNative},
    274     {"unregisterHealthAppNative", "(I)Z", (void *) unregisterHealthAppNative},
    275     {"connectChannelNative", "([BI)I", (void *) connectChannelNative},
    276     {"disconnectChannelNative", "(I)Z", (void *) disconnectChannelNative},
    277 };
    278 
    279 int register_com_android_bluetooth_hdp(JNIEnv* env)
    280 {
    281     return jniRegisterNativeMethods(env, "com/android/bluetooth/hdp/HealthService",
    282                                     sMethods, NELEM(sMethods));
    283 }
    284 
    285 }
    286