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