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(®_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