1 /* 2 * Copyright (C) 2015 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 "BluetoothSdpJni" 18 19 #define LOG_NDEBUG 0 20 21 #include "com_android_bluetooth.h" 22 #include "hardware/bt_sdp.h" 23 #include "utils/Log.h" 24 #include "android_runtime/AndroidRuntime.h" 25 26 #include <string.h> 27 28 static const uint8_t UUID_OBEX_OBJECT_PUSH[] = {0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00, 29 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; 30 static const uint8_t UUID_PBAP_PSE[] = {0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00, 31 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; 32 static const uint8_t UUID_MAP_MAS[] = {0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00, 33 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; 34 static const uint8_t UUID_MAP_MNS[] = {0x00, 0x00, 0x11, 0x33, 0x00, 0x00, 0x10, 0x00, 35 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; 36 static const uint8_t UUID_SAP[] = {0x00, 0x00, 0x11, 0x2D, 0x00, 0x00, 0x10, 0x00, 37 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; 38 // TODO: 39 // Both the fact that the UUIDs are declared in multiple places, plus the fact 40 // that there is a mess of UUID comparison and shortening methods will have to 41 // be fixed. 42 // The btcore->uuid module should be used for all instances. 43 44 #define UUID_MAX_LENGTH 16 45 #define IS_UUID(u1,u2) !memcmp(u1,u2,UUID_MAX_LENGTH) 46 47 48 namespace android { 49 static jmethodID method_sdpRecordFoundCallback; 50 static jmethodID method_sdpMasRecordFoundCallback; 51 static jmethodID method_sdpMnsRecordFoundCallback; 52 static jmethodID method_sdpPseRecordFoundCallback; 53 static jmethodID method_sdpOppOpsRecordFoundCallback; 54 static jmethodID method_sdpSapsRecordFoundCallback; 55 56 static const btsdp_interface_t *sBluetoothSdpInterface = NULL; 57 58 static void sdp_search_callback(bt_status_t status, bt_bdaddr_t *bd_addr, uint8_t* uuid_in, 59 int record_size, bluetooth_sdp_record* record); 60 61 btsdp_callbacks_t sBluetoothSdpCallbacks = { 62 sizeof(sBluetoothSdpCallbacks), 63 sdp_search_callback 64 }; 65 66 static jobject sCallbacksObj = NULL; 67 static JNIEnv *sCallbackEnv = NULL; 68 69 static bool checkCallbackThread() { 70 sCallbackEnv = getCallbackEnv(); 71 72 JNIEnv* env = AndroidRuntime::getJNIEnv(); 73 if (sCallbackEnv != env || sCallbackEnv == NULL) { 74 ALOGE("Callback env check fail: env: %p, callback: %p", env, sCallbackEnv); 75 return false; 76 } 77 return true; 78 } 79 80 static void initializeNative(JNIEnv *env, jobject object) { 81 const bt_interface_t* btInf; 82 83 if ( (btInf = getBluetoothInterface()) == NULL) { 84 ALOGE("Bluetooth module is not loaded"); 85 return; 86 } 87 if (sBluetoothSdpInterface !=NULL) { 88 ALOGW("Cleaning up Bluetooth SDP Interface before initializing..."); 89 sBluetoothSdpInterface->deinit(); 90 sBluetoothSdpInterface = NULL; 91 } 92 if ( (sBluetoothSdpInterface = (btsdp_interface_t *) 93 btInf->get_profile_interface(BT_PROFILE_SDP_CLIENT_ID)) == NULL) { 94 ALOGE("Error getting SDP client interface"); 95 }else{ 96 sBluetoothSdpInterface->init(&sBluetoothSdpCallbacks); 97 } 98 99 sCallbacksObj = env->NewGlobalRef(object); 100 } 101 102 static void classInitNative(JNIEnv* env, jclass clazz) { 103 104 /* generic SDP record (raw data)*/ 105 method_sdpRecordFoundCallback = env->GetMethodID(clazz, 106 "sdpRecordFoundCallback", 107 "(I[B[BI[B)V"); 108 109 /* MAS SDP record*/ 110 method_sdpMasRecordFoundCallback = env->GetMethodID(clazz, 111 "sdpMasRecordFoundCallback", 112 "(I[B[BIIIIIILjava/lang/String;Z)V"); 113 /* MNS SDP record*/ 114 method_sdpMnsRecordFoundCallback = env->GetMethodID(clazz, 115 "sdpMnsRecordFoundCallback", 116 "(I[B[BIIIILjava/lang/String;Z)V"); 117 /* PBAP PSE record */ 118 method_sdpPseRecordFoundCallback = env->GetMethodID(clazz, 119 "sdpPseRecordFoundCallback", 120 "(I[B[BIIIIILjava/lang/String;Z)V"); 121 /* OPP Server record */ 122 method_sdpOppOpsRecordFoundCallback = env->GetMethodID(clazz, 123 "sdpOppOpsRecordFoundCallback", 124 "(I[B[BIIILjava/lang/String;[BZ)V"); 125 /* SAP Server record */ 126 method_sdpSapsRecordFoundCallback = env->GetMethodID(clazz, 127 "sdpSapsRecordFoundCallback", 128 "(I[B[BIILjava/lang/String;Z)V"); 129 130 } 131 132 static jboolean sdpSearchNative(JNIEnv *env, jobject obj, jbyteArray address, jbyteArray uuidObj) { 133 ALOGD("%s:",__FUNCTION__); 134 135 jbyte *addr = NULL, *uuid = NULL; 136 jboolean result = JNI_FALSE; 137 int ret; 138 if (!sBluetoothSdpInterface) 139 goto Fail; 140 141 addr = env->GetByteArrayElements(address, NULL); 142 if (addr == NULL) { 143 jniThrowIOException(env, EINVAL); 144 goto Fail; 145 } 146 uuid = env->GetByteArrayElements(uuidObj, NULL); 147 if (!uuid) { 148 ALOGE("failed to get uuid"); 149 goto Fail; 150 } 151 ALOGD("%s UUID %.*s",__FUNCTION__,16, (uint8_t*)uuid); 152 153 154 if ((ret = sBluetoothSdpInterface->sdp_search((bt_bdaddr_t *)addr, 155 (const uint8_t*)uuid)) != BT_STATUS_SUCCESS) { 156 ALOGE("SDP Search initialization failed: %d", ret); 157 goto Fail; 158 } 159 160 result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 161 162 Fail: 163 if (addr) env->ReleaseByteArrayElements(address, addr, 0); 164 if (uuid) env->ReleaseByteArrayElements(uuidObj, uuid, 0); 165 return result; 166 } 167 168 static void sdp_search_callback(bt_status_t status, bt_bdaddr_t *bd_addr, uint8_t* uuid_in, 169 int count, bluetooth_sdp_record* records) 170 { 171 172 jbyteArray addr = NULL; 173 jbyteArray uuid = NULL; 174 jstring service_name = NULL; 175 int i = 0; 176 bluetooth_sdp_record* record; 177 178 if (!checkCallbackThread()) { 179 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); 180 goto clean; 181 } 182 183 addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); 184 if (addr == NULL) goto clean; 185 186 uuid = sCallbackEnv->NewByteArray(sizeof(bt_uuid_t)); 187 if (uuid == NULL) goto clean; 188 189 sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr); 190 sCallbackEnv->SetByteArrayRegion(uuid, 0, sizeof(bt_uuid_t), (jbyte*)uuid_in); 191 192 ALOGD("%s: Status is: %d, Record count: %d", __FUNCTION__, status, count); 193 194 // Ensure we run the loop at least once, to also signal errors if they occure 195 for(i = 0; i < count || i==0; i++) { 196 bool more_results = (i<(count-1))?true:false; 197 record = &records[i]; 198 service_name = NULL; 199 if (record->hdr.service_name_length > 0) { 200 ALOGD("%s, ServiceName: %s", __FUNCTION__, record->mas.hdr.service_name); 201 service_name = (jstring)sCallbackEnv->NewStringUTF(record->mas.hdr.service_name); 202 } 203 204 /* call the right callback according to the uuid*/ 205 if (IS_UUID(UUID_MAP_MAS,uuid_in)){ 206 207 sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpMasRecordFoundCallback, 208 (jint) status, 209 addr, 210 uuid, 211 (jint)record->mas.mas_instance_id, 212 (jint)record->mas.hdr.l2cap_psm, 213 (jint)record->mas.hdr.rfcomm_channel_number, 214 (jint)record->mas.hdr.profile_version, 215 (jint)record->mas.supported_features, 216 (jint)record->mas.supported_message_types, 217 service_name, 218 more_results); 219 220 }else if (IS_UUID(UUID_MAP_MNS,uuid_in)){ 221 222 sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpMnsRecordFoundCallback, 223 (jint) status, 224 addr, 225 uuid, 226 (jint)record->mns.hdr.l2cap_psm, 227 (jint)record->mns.hdr.rfcomm_channel_number, 228 (jint)record->mns.hdr.profile_version, 229 (jint)record->mns.supported_features, 230 service_name, 231 more_results); 232 233 } else if (IS_UUID(UUID_PBAP_PSE, uuid_in)) { 234 235 sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpPseRecordFoundCallback, 236 (jint) status, 237 addr, 238 uuid, 239 (jint)record->pse.hdr.l2cap_psm, 240 (jint)record->pse.hdr.rfcomm_channel_number, 241 (jint)record->pse.hdr.profile_version, 242 (jint)record->pse.supported_features, 243 (jint)record->pse.supported_repositories, 244 service_name, 245 more_results); 246 247 } else if (IS_UUID(UUID_OBEX_OBJECT_PUSH, uuid_in)) { 248 249 jint formats_list_size = record->ops.supported_formats_list_len; 250 jbyteArray formats_list = sCallbackEnv->NewByteArray(formats_list_size); 251 if (formats_list == NULL) goto clean; 252 sCallbackEnv->SetByteArrayRegion(formats_list, 0, formats_list_size, 253 (jbyte*)record->ops.supported_formats_list); 254 255 sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpOppOpsRecordFoundCallback, 256 (jint) status, 257 addr, 258 uuid, 259 (jint)record->ops.hdr.l2cap_psm, 260 (jint)record->ops.hdr.rfcomm_channel_number, 261 (jint)record->ops.hdr.profile_version, 262 service_name, 263 formats_list, 264 more_results); 265 sCallbackEnv->DeleteLocalRef(formats_list); 266 267 } else if (IS_UUID(UUID_SAP, uuid_in)) { 268 sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpSapsRecordFoundCallback, 269 (jint) status, 270 addr, 271 uuid, 272 (jint)record->mas.hdr.rfcomm_channel_number, 273 (jint)record->mas.hdr.profile_version, 274 service_name, 275 more_results); 276 } else { 277 // we don't have a wrapper for this uuid, send as raw data 278 jint record_data_size = record->hdr.user1_ptr_len; 279 jbyteArray record_data = NULL; 280 281 record_data = sCallbackEnv->NewByteArray(record_data_size); 282 if (record_data == NULL) goto clean; 283 284 sCallbackEnv->SetByteArrayRegion(record_data, 0, record_data_size, 285 (jbyte*)record->hdr.user1_ptr); 286 sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpRecordFoundCallback, 287 (jint) status, addr, uuid, record_data_size, record_data); 288 289 sCallbackEnv->DeleteLocalRef(record_data); 290 291 } 292 // Cleanup for each iteration 293 if (service_name != NULL) { 294 sCallbackEnv->DeleteLocalRef(service_name); 295 service_name = NULL; 296 } 297 } // End of for-loop 298 299 clean: 300 if (service_name != NULL) 301 sCallbackEnv->DeleteLocalRef(service_name); 302 if (addr != NULL) sCallbackEnv->DeleteLocalRef(addr); 303 if (uuid != NULL) sCallbackEnv->DeleteLocalRef(uuid); 304 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 305 } 306 307 static jint sdpCreateMapMasRecordNative(JNIEnv *env, jobject obj, jstring name_str, jint mas_id, 308 jint scn, jint l2cap_psm, jint version, 309 jint msg_types, jint features) { 310 ALOGD("%s:", __FUNCTION__); 311 312 const char* service_name = NULL; 313 bluetooth_sdp_record record = {}; // Must be zero initialized 314 int handle=-1; 315 int ret = 0; 316 if (!sBluetoothSdpInterface) return handle; 317 318 record.mas.hdr.type = SDP_TYPE_MAP_MAS; 319 320 if (name_str != NULL) { 321 service_name = env->GetStringUTFChars(name_str, NULL); 322 record.mas.hdr.service_name = (char *) service_name; 323 record.mas.hdr.service_name_length = strlen(service_name); 324 } else { 325 record.mas.hdr.service_name = NULL; 326 record.mas.hdr.service_name_length = 0; 327 } 328 record.mas.hdr.rfcomm_channel_number = scn; 329 record.mas.hdr.l2cap_psm = l2cap_psm; 330 record.mas.hdr.profile_version = version; 331 332 record.mas.mas_instance_id = mas_id; 333 record.mas.supported_features = features; 334 record.mas.supported_message_types = msg_types; 335 336 if ( (ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle)) 337 != BT_STATUS_SUCCESS) { 338 ALOGE("SDP Create record failed: %d", ret); 339 goto Fail; 340 } 341 342 ALOGD("SDP Create record success - handle: %d", handle); 343 344 Fail: 345 if (service_name) env->ReleaseStringUTFChars(name_str, service_name); 346 return handle; 347 } 348 349 static jint sdpCreateMapMnsRecordNative(JNIEnv *env, jobject obj, jstring name_str, 350 jint scn, jint l2cap_psm, jint version, 351 jint features) { 352 ALOGD("%s:",__FUNCTION__); 353 354 const char* service_name = NULL; 355 bluetooth_sdp_record record = {}; // Must be zero initialized 356 int handle=-1; 357 int ret = 0; 358 if (!sBluetoothSdpInterface) return handle; 359 360 record.mns.hdr.type = SDP_TYPE_MAP_MNS; 361 362 if (name_str != NULL) { 363 service_name = env->GetStringUTFChars(name_str, NULL); 364 record.mns.hdr.service_name = (char *) service_name; 365 record.mns.hdr.service_name_length = strlen(service_name); 366 } else { 367 record.mns.hdr.service_name = NULL; 368 record.mns.hdr.service_name_length = 0; 369 } 370 record.mns.hdr.rfcomm_channel_number = scn; 371 record.mns.hdr.l2cap_psm = l2cap_psm; 372 record.mns.hdr.profile_version = version; 373 374 record.mns.supported_features = features; 375 376 if ( (ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle)) 377 != BT_STATUS_SUCCESS) { 378 ALOGE("SDP Create record failed: %d", ret); 379 goto Fail; 380 } 381 382 ALOGD("SDP Create record success - handle: %d", handle); 383 384 Fail: 385 if (service_name) env->ReleaseStringUTFChars(name_str, service_name); 386 return handle; 387 } 388 389 static jint sdpCreatePbapPseRecordNative(JNIEnv *env, jobject obj, jstring name_str, 390 jint scn, jint l2cap_psm, jint version, 391 jint supported_repositories, jint features) { 392 ALOGD("%s:",__FUNCTION__); 393 394 const char* service_name = NULL; 395 bluetooth_sdp_record record = {}; // Must be zero initialized 396 int handle=-1; 397 int ret = 0; 398 if (!sBluetoothSdpInterface) return handle; 399 400 record.pse.hdr.type = SDP_TYPE_PBAP_PSE; 401 402 if (name_str != NULL) { 403 service_name = env->GetStringUTFChars(name_str, NULL); 404 record.pse.hdr.service_name = (char *) service_name; 405 record.pse.hdr.service_name_length = strlen(service_name); 406 } else { 407 record.pse.hdr.service_name = NULL; 408 record.pse.hdr.service_name_length = 0; 409 } 410 record.pse.hdr.rfcomm_channel_number = scn; 411 record.pse.hdr.l2cap_psm = l2cap_psm; 412 record.pse.hdr.profile_version = version; 413 414 record.pse.supported_features = features; 415 record.pse.supported_repositories = supported_repositories; 416 417 if ( (ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle)) 418 != BT_STATUS_SUCCESS) { 419 ALOGE("SDP Create record failed: %d", ret); 420 goto Fail; 421 } 422 423 ALOGD("SDP Create record success - handle: %d", handle); 424 425 Fail: 426 if (service_name) env->ReleaseStringUTFChars(name_str, service_name); 427 return handle; 428 } 429 430 static jint sdpCreateOppOpsRecordNative(JNIEnv *env, jobject obj, jstring name_str, 431 jint scn, jint l2cap_psm, jint version, 432 jbyteArray supported_formats_list) { 433 ALOGD("%s:",__FUNCTION__); 434 435 const char* service_name = NULL; 436 bluetooth_sdp_record record = {}; // Must be zero initialized 437 jbyte* formats_list; 438 int formats_list_len = 0; 439 int handle=-1; 440 int ret = 0; 441 if (!sBluetoothSdpInterface) return handle; 442 443 record.ops.hdr.type = SDP_TYPE_OPP_SERVER; 444 445 if (name_str != NULL) { 446 service_name = env->GetStringUTFChars(name_str, NULL); 447 record.ops.hdr.service_name = (char *) service_name; 448 record.ops.hdr.service_name_length = strlen(service_name); 449 } else { 450 record.ops.hdr.service_name = NULL; 451 record.ops.hdr.service_name_length = 0; 452 } 453 record.ops.hdr.rfcomm_channel_number = scn; 454 record.ops.hdr.l2cap_psm = l2cap_psm; 455 record.ops.hdr.profile_version = version; 456 457 formats_list = env->GetByteArrayElements(supported_formats_list, NULL); 458 if (formats_list != NULL) { 459 formats_list_len = env->GetArrayLength(supported_formats_list); 460 if (formats_list_len > SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH) { 461 formats_list_len = SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH; 462 } 463 memcpy(record.ops.supported_formats_list, formats_list, formats_list_len); 464 } 465 466 record.ops.supported_formats_list_len = formats_list_len; 467 468 if ( (ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle)) 469 != BT_STATUS_SUCCESS) { 470 ALOGE("SDP Create record failed: %d", ret); 471 goto Fail; 472 } 473 474 ALOGD("SDP Create record success - handle: %d", handle); 475 476 Fail: 477 if (service_name) env->ReleaseStringUTFChars(name_str, service_name); 478 if (formats_list) env->ReleaseByteArrayElements(supported_formats_list, formats_list, 0); 479 return handle; 480 } 481 482 static jint sdpCreateSapsRecordNative(JNIEnv *env, jobject obj, jstring name_str, 483 jint scn, jint version) { 484 ALOGD("%s:",__FUNCTION__); 485 486 const char* service_name = NULL; 487 bluetooth_sdp_record record = {}; // Must be zero initialized 488 int handle = -1; 489 int ret = 0; 490 if (!sBluetoothSdpInterface) return handle; 491 492 record.sap.hdr.type = SDP_TYPE_SAP_SERVER; 493 494 if (name_str != NULL) { 495 service_name = env->GetStringUTFChars(name_str, NULL); 496 record.mas.hdr.service_name = (char *) service_name; 497 record.mas.hdr.service_name_length = strlen(service_name); 498 } else { 499 record.mas.hdr.service_name = NULL; 500 record.mas.hdr.service_name_length = 0; 501 } 502 record.mas.hdr.rfcomm_channel_number = scn; 503 record.mas.hdr.profile_version = version; 504 505 if ( (ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle)) 506 != BT_STATUS_SUCCESS) { 507 ALOGE("SDP Create record failed: %d", ret); 508 goto Fail; 509 } 510 511 ALOGD("SDP Create record success - handle: %d", handle); 512 513 Fail: 514 if (service_name) env->ReleaseStringUTFChars(name_str, service_name); 515 return handle; 516 } 517 518 static jboolean sdpRemoveSdpRecordNative(JNIEnv *env, jobject obj, jint record_id) { 519 ALOGD("%s:",__FUNCTION__); 520 521 int ret = 0; 522 if (!sBluetoothSdpInterface) return false; 523 524 if ( (ret = sBluetoothSdpInterface->remove_sdp_record(record_id)) 525 != BT_STATUS_SUCCESS) { 526 ALOGE("SDP Remove record failed: %d", ret); 527 return false; 528 } 529 530 ALOGD("SDP Remove record success - handle: %d", record_id); 531 return true; 532 } 533 534 535 static void cleanupNative(JNIEnv *env, jobject object) { 536 const bt_interface_t* btInf; 537 538 if ( (btInf = getBluetoothInterface()) == NULL) { 539 ALOGE("Bluetooth module is not loaded"); 540 return; 541 } 542 543 if (sBluetoothSdpInterface !=NULL) { 544 ALOGW("Cleaning up Bluetooth SDP Interface..."); 545 sBluetoothSdpInterface->deinit(); 546 sBluetoothSdpInterface = NULL; 547 } 548 549 if (sCallbacksObj != NULL) { 550 ALOGW("Cleaning up Bluetooth SDP object"); 551 env->DeleteGlobalRef(sCallbacksObj); 552 sCallbacksObj = NULL; 553 } 554 } 555 556 static JNINativeMethod sMethods[] = { 557 /* name, signature, funcPtr */ 558 {"classInitNative", "()V", (void *) classInitNative}, 559 {"initializeNative", "()V", (void *) initializeNative}, 560 {"cleanupNative", "()V", (void*) cleanupNative}, 561 {"sdpSearchNative", "([B[B)Z", (void*) sdpSearchNative}, 562 {"sdpCreateMapMasRecordNative", "(Ljava/lang/String;IIIIII)I", 563 (void*) sdpCreateMapMasRecordNative}, 564 {"sdpCreateMapMnsRecordNative", "(Ljava/lang/String;IIII)I", 565 (void*) sdpCreateMapMnsRecordNative}, 566 {"sdpCreatePbapPseRecordNative", "(Ljava/lang/String;IIIII)I", 567 (void*) sdpCreatePbapPseRecordNative}, 568 {"sdpCreateOppOpsRecordNative", "(Ljava/lang/String;III[B)I", 569 (void*) sdpCreateOppOpsRecordNative}, 570 {"sdpCreateSapsRecordNative", "(Ljava/lang/String;II)I", 571 (void*) sdpCreateSapsRecordNative}, 572 {"sdpRemoveSdpRecordNative", "(I)Z", (void*) sdpRemoveSdpRecordNative} 573 }; 574 575 int register_com_android_bluetooth_sdp(JNIEnv* env) 576 { 577 return jniRegisterNativeMethods(env, "com/android/bluetooth/sdp/SdpManager", 578 sMethods, NELEM(sMethods)); 579 } 580 581 582 } 583