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