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