1/* 2 * Copyright (C) 2016 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 "BluetoothHidDevServiceJni" 18 19#define LOG_NDEBUG 0 20 21#include "android_runtime/AndroidRuntime.h" 22#include "com_android_bluetooth.h" 23#include "hardware/bt_hd.h" 24#include "utils/Log.h" 25 26#include <string.h> 27 28namespace android { 29 30static jmethodID method_onApplicationStateChanged; 31static jmethodID method_onConnectStateChanged; 32static jmethodID method_onGetReport; 33static jmethodID method_onSetReport; 34static jmethodID method_onSetProtocol; 35static jmethodID method_onIntrData; 36static jmethodID method_onVirtualCableUnplug; 37 38static const bthd_interface_t* sHiddIf = NULL; 39static jobject mCallbacksObj = NULL; 40 41static jbyteArray marshall_bda(RawAddress* bd_addr) { 42 CallbackEnv sCallbackEnv(__func__); 43 if (!sCallbackEnv.valid()) return NULL; 44 45 jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress)); 46 if (!addr) { 47 ALOGE("Fail to new jbyteArray bd addr"); 48 return NULL; 49 } 50 sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress), 51 (jbyte*)bd_addr); 52 return addr; 53} 54 55static void application_state_callback(RawAddress* bd_addr, 56 bthd_application_state_t state) { 57 jboolean registered = JNI_FALSE; 58 59 CallbackEnv sCallbackEnv(__func__); 60 61 if (state == BTHD_APP_STATE_REGISTERED) { 62 registered = JNI_TRUE; 63 } 64 65 ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), NULL); 66 67 if (bd_addr) { 68 addr.reset(marshall_bda(bd_addr)); 69 if (!addr.get()) { 70 ALOGE("%s: failed to allocate storage for bt_addr", __FUNCTION__); 71 return; 72 } 73 } 74 75 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onApplicationStateChanged, 76 addr.get(), registered); 77} 78 79static void connection_state_callback(RawAddress* bd_addr, 80 bthd_connection_state_t state) { 81 CallbackEnv sCallbackEnv(__func__); 82 83 ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); 84 if (!addr.get()) { 85 ALOGE("%s: failed to allocate storage for bt_addr", __FUNCTION__); 86 return; 87 } 88 89 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged, 90 addr.get(), (jint)state); 91} 92 93static void get_report_callback(uint8_t type, uint8_t id, 94 uint16_t buffer_size) { 95 CallbackEnv sCallbackEnv(__func__); 96 97 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetReport, type, id, 98 buffer_size); 99} 100 101static void set_report_callback(uint8_t type, uint8_t id, uint16_t len, 102 uint8_t* p_data) { 103 CallbackEnv sCallbackEnv(__func__); 104 105 ScopedLocalRef<jbyteArray> data(sCallbackEnv.get(), 106 sCallbackEnv->NewByteArray(len)); 107 if (!data.get()) { 108 ALOGE("%s: failed to allocate storage for report data", __FUNCTION__); 109 return; 110 } 111 sCallbackEnv->SetByteArrayRegion(data.get(), 0, len, (jbyte*)p_data); 112 113 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetReport, (jbyte)type, 114 (jbyte)id, data.get()); 115} 116 117static void set_protocol_callback(uint8_t protocol) { 118 CallbackEnv sCallbackEnv(__func__); 119 120 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetProtocol, protocol); 121} 122 123static void intr_data_callback(uint8_t report_id, uint16_t len, 124 uint8_t* p_data) { 125 CallbackEnv sCallbackEnv(__func__); 126 127 ScopedLocalRef<jbyteArray> data(sCallbackEnv.get(), 128 sCallbackEnv->NewByteArray(len)); 129 if (!data.get()) { 130 ALOGE("%s: failed to allocate storage for report data", __FUNCTION__); 131 return; 132 } 133 sCallbackEnv->SetByteArrayRegion(data.get(), 0, len, (jbyte*)p_data); 134 135 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onIntrData, 136 (jbyte)report_id, data.get()); 137} 138 139static void vc_unplug_callback(void) { 140 CallbackEnv sCallbackEnv(__func__); 141 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualCableUnplug); 142} 143 144static bthd_callbacks_t sHiddCb = { 145 sizeof(sHiddCb), 146 147 application_state_callback, 148 connection_state_callback, 149 get_report_callback, 150 set_report_callback, 151 set_protocol_callback, 152 intr_data_callback, 153 vc_unplug_callback, 154}; 155 156static void classInitNative(JNIEnv* env, jclass clazz) { 157 ALOGV("%s: done", __FUNCTION__); 158 159 method_onApplicationStateChanged = 160 env->GetMethodID(clazz, "onApplicationStateChanged", "([BZ)V"); 161 method_onConnectStateChanged = 162 env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V"); 163 method_onGetReport = env->GetMethodID(clazz, "onGetReport", "(BBS)V"); 164 method_onSetReport = env->GetMethodID(clazz, "onSetReport", "(BB[B)V"); 165 method_onSetProtocol = env->GetMethodID(clazz, "onSetProtocol", "(B)V"); 166 method_onIntrData = env->GetMethodID(clazz, "onIntrData", "(B[B)V"); 167 method_onVirtualCableUnplug = 168 env->GetMethodID(clazz, "onVirtualCableUnplug", "()V"); 169} 170 171static void initNative(JNIEnv* env, jobject object) { 172 const bt_interface_t* btif; 173 bt_status_t status; 174 175 ALOGV("%s enter", __FUNCTION__); 176 177 if ((btif = getBluetoothInterface()) == NULL) { 178 ALOGE("Cannot obtain BT interface"); 179 return; 180 } 181 182 if (sHiddIf != NULL) { 183 ALOGW("Cleaning up interface"); 184 sHiddIf->cleanup(); 185 sHiddIf = NULL; 186 } 187 188 if (mCallbacksObj != NULL) { 189 ALOGW("Cleaning up callback object"); 190 env->DeleteGlobalRef(mCallbacksObj); 191 mCallbacksObj = NULL; 192 } 193 194 if ((sHiddIf = (bthd_interface_t*)btif->get_profile_interface( 195 BT_PROFILE_HIDDEV_ID)) == NULL) { 196 ALOGE("Cannot obtain interface"); 197 return; 198 } 199 200 if ((status = sHiddIf->init(&sHiddCb)) != BT_STATUS_SUCCESS) { 201 ALOGE("Failed to initialize interface (%d)", status); 202 sHiddIf = NULL; 203 return; 204 } 205 206 mCallbacksObj = env->NewGlobalRef(object); 207 208 ALOGV("%s done", __FUNCTION__); 209} 210 211static void cleanupNative(JNIEnv* env, jobject object) { 212 ALOGV("%s enter", __FUNCTION__); 213 214 if (sHiddIf != NULL) { 215 ALOGI("Cleaning up interface"); 216 sHiddIf->cleanup(); 217 sHiddIf = NULL; 218 } 219 220 if (mCallbacksObj != NULL) { 221 ALOGI("Cleaning up callback object"); 222 env->DeleteGlobalRef(mCallbacksObj); 223 mCallbacksObj = NULL; 224 } 225 226 ALOGV("%s done", __FUNCTION__); 227} 228 229static void fill_qos(JNIEnv* env, jintArray in, bthd_qos_param_t* out) { 230 // set default values 231 out->service_type = 0x01; // best effort 232 out->token_rate = out->token_bucket_size = out->peak_bandwidth = 233 0; // don't care 234 out->access_latency = out->delay_variation = 0xffffffff; // don't care 235 236 if (in == NULL) return; 237 238 jsize len = env->GetArrayLength(in); 239 240 if (len != 6) return; 241 242 uint32_t* buf = (uint32_t*)calloc(len, sizeof(uint32_t)); 243 244 if (buf == NULL) return; 245 246 env->GetIntArrayRegion(in, 0, len, (jint*)buf); 247 248 out->service_type = (uint8_t)buf[0]; 249 out->token_rate = buf[1]; 250 out->token_bucket_size = buf[2]; 251 out->peak_bandwidth = buf[3]; 252 out->access_latency = buf[4]; 253 out->delay_variation = buf[5]; 254 255 free(buf); 256} 257 258static jboolean registerAppNative(JNIEnv* env, jobject thiz, jstring name, 259 jstring description, jstring provider, 260 jbyte subclass, jbyteArray descriptors, 261 jintArray p_in_qos, jintArray p_out_qos) { 262 ALOGV("%s enter", __FUNCTION__); 263 264 jboolean result = JNI_FALSE; 265 bthd_app_param_t app_param; 266 bthd_qos_param_t in_qos; 267 bthd_qos_param_t out_qos; 268 jsize size; 269 uint8_t* data; 270 271 size = env->GetArrayLength(descriptors); 272 data = (uint8_t*)malloc(size); 273 274 if (data != NULL) { 275 env->GetByteArrayRegion(descriptors, 0, size, (jbyte*)data); 276 277 app_param.name = env->GetStringUTFChars(name, NULL); 278 app_param.description = env->GetStringUTFChars(description, NULL); 279 app_param.provider = env->GetStringUTFChars(provider, NULL); 280 app_param.subclass = subclass; 281 app_param.desc_list = data; 282 app_param.desc_list_len = size; 283 284 fill_qos(env, p_in_qos, &in_qos); 285 fill_qos(env, p_out_qos, &out_qos); 286 287 bt_status_t ret = sHiddIf->register_app(&app_param, &in_qos, &out_qos); 288 289 ALOGV("%s: register_app() returned %d", __FUNCTION__, ret); 290 291 if (ret == BT_STATUS_SUCCESS) { 292 result = JNI_TRUE; 293 } 294 295 env->ReleaseStringUTFChars(name, app_param.name); 296 env->ReleaseStringUTFChars(description, app_param.description); 297 env->ReleaseStringUTFChars(provider, app_param.provider); 298 299 free(data); 300 } 301 302 ALOGV("%s done (%d)", __FUNCTION__, result); 303 304 return result; 305} 306 307static jboolean unregisterAppNative(JNIEnv* env, jobject thiz) { 308 ALOGV("%s enter", __FUNCTION__); 309 310 jboolean result = JNI_FALSE; 311 312 bt_status_t ret = sHiddIf->unregister_app(); 313 314 ALOGV("%s: unregister_app() returned %d", __FUNCTION__, ret); 315 316 if (ret == BT_STATUS_SUCCESS) { 317 result = JNI_TRUE; 318 } 319 320 ALOGV("%s done (%d)", __FUNCTION__, result); 321 322 return result; 323} 324 325static jboolean sendReportNative(JNIEnv* env, jobject thiz, jint id, 326 jbyteArray data) { 327 jboolean result = JNI_FALSE; 328 jsize size; 329 uint8_t* buf; 330 331 size = env->GetArrayLength(data); 332 buf = (uint8_t*)malloc(size); 333 334 if (buf != NULL) { 335 env->GetByteArrayRegion(data, 0, size, (jbyte*)buf); 336 337 bt_status_t ret = 338 sHiddIf->send_report(BTHD_REPORT_TYPE_INTRDATA, id, size, buf); 339 340 if (ret == BT_STATUS_SUCCESS) { 341 result = JNI_TRUE; 342 } 343 344 free(buf); 345 } 346 347 return result; 348} 349 350static jboolean replyReportNative(JNIEnv* env, jobject thiz, jbyte type, 351 jbyte id, jbyteArray data) { 352 ALOGV("%s enter", __FUNCTION__); 353 354 jboolean result = JNI_FALSE; 355 jsize size; 356 uint8_t* buf; 357 358 size = env->GetArrayLength(data); 359 buf = (uint8_t*)malloc(size); 360 361 if (buf != NULL) { 362 int report_type = (type & 0x03); 363 env->GetByteArrayRegion(data, 0, size, (jbyte*)buf); 364 365 bt_status_t ret = 366 sHiddIf->send_report((bthd_report_type_t)report_type, id, size, buf); 367 368 ALOGV("%s: send_report() returned %d", __FUNCTION__, ret); 369 370 if (ret == BT_STATUS_SUCCESS) { 371 result = JNI_TRUE; 372 } 373 374 free(buf); 375 } 376 377 ALOGV("%s done (%d)", __FUNCTION__, result); 378 379 return result; 380} 381 382static jboolean reportErrorNative(JNIEnv* env, jobject thiz, jbyte error) { 383 ALOGV("%s enter", __FUNCTION__); 384 385 jboolean result = JNI_FALSE; 386 387 bt_status_t ret = sHiddIf->report_error(error); 388 389 ALOGV("%s: report_error() returned %d", __FUNCTION__, ret); 390 391 if (ret == BT_STATUS_SUCCESS) { 392 result = JNI_TRUE; 393 } 394 395 ALOGV("%s done (%d)", __FUNCTION__, result); 396 397 return result; 398} 399 400static jboolean unplugNative(JNIEnv* env, jobject thiz) { 401 ALOGV("%s enter", __FUNCTION__); 402 403 jboolean result = JNI_FALSE; 404 405 bt_status_t ret = sHiddIf->virtual_cable_unplug(); 406 407 ALOGV("%s: virtual_cable_unplug() returned %d", __FUNCTION__, ret); 408 409 if (ret == BT_STATUS_SUCCESS) { 410 result = JNI_TRUE; 411 } 412 413 ALOGV("%s done (%d)", __FUNCTION__, result); 414 415 return result; 416} 417 418static jboolean connectNative(JNIEnv* env, jobject thiz, jbyteArray address) { 419 ALOGV("%s enter", __FUNCTION__); 420 421 jboolean result = JNI_FALSE; 422 423 jbyte* addr = env->GetByteArrayElements(address, NULL); 424 if (!addr) { 425 ALOGE("Bluetooth device address null"); 426 return JNI_FALSE; 427 } 428 429 bt_status_t ret = sHiddIf->connect((RawAddress*)addr); 430 431 ALOGV("%s: connect() returned %d", __FUNCTION__, ret); 432 433 if (ret == BT_STATUS_SUCCESS) { 434 result = JNI_TRUE; 435 } 436 437 ALOGV("%s done (%d)", __FUNCTION__, result); 438 439 return result; 440} 441 442static jboolean disconnectNative(JNIEnv* env, jobject thiz) { 443 ALOGV("%s enter", __FUNCTION__); 444 445 jboolean result = JNI_FALSE; 446 447 bt_status_t ret = sHiddIf->disconnect(); 448 449 ALOGV("%s: disconnect() returned %d", __FUNCTION__, ret); 450 451 if (ret == BT_STATUS_SUCCESS) { 452 result = JNI_TRUE; 453 } 454 455 ALOGV("%s done (%d)", __FUNCTION__, result); 456 457 return result; 458} 459 460static JNINativeMethod sMethods[] = { 461 {"classInitNative", "()V", (void*)classInitNative}, 462 {"initNative", "()V", (void*)initNative}, 463 {"cleanupNative", "()V", (void*)cleanupNative}, 464 {"registerAppNative", 465 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;B[B[I[I)Z", 466 (void*)registerAppNative}, 467 {"unregisterAppNative", "()Z", (void*)unregisterAppNative}, 468 {"sendReportNative", "(I[B)Z", (void*)sendReportNative}, 469 {"replyReportNative", "(BB[B)Z", (void*)replyReportNative}, 470 {"reportErrorNative", "(B)Z", (void*)reportErrorNative}, 471 {"unplugNative", "()Z", (void*)unplugNative}, 472 {"connectNative", "([B)Z", (void*)connectNative}, 473 {"disconnectNative", "()Z", (void*)disconnectNative}, 474}; 475 476int register_com_android_bluetooth_hidd(JNIEnv* env) { 477 return jniRegisterNativeMethods(env, 478 "com/android/bluetooth/hid/HidDevService", 479 sMethods, NELEM(sMethods)); 480} 481} 482