/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "BluetoothSdpJni" #define LOG_NDEBUG 0 #include "com_android_bluetooth.h" #include "hardware/bt_sdp.h" #include "utils/Log.h" #include "android_runtime/AndroidRuntime.h" #include static const uint8_t UUID_OBEX_OBJECT_PUSH[] = {0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; static const uint8_t UUID_PBAP_PSE[] = {0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; static const uint8_t UUID_MAP_MAS[] = {0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; static const uint8_t UUID_MAP_MNS[] = {0x00, 0x00, 0x11, 0x33, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; static const uint8_t UUID_SPP[] = {0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; static const uint8_t UUID_SAP[] = {0x00, 0x00, 0x11, 0x2D, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; // TODO: // Both the fact that the UUIDs are declared in multiple places, plus the fact // that there is a mess of UUID comparison and shortening methods will have to // be fixed. // The btcore->uuid module should be used for all instances. #define UUID_MAX_LENGTH 16 #define IS_UUID(u1,u2) !memcmp(u1,u2,UUID_MAX_LENGTH) namespace android { static jmethodID method_sdpRecordFoundCallback; static jmethodID method_sdpMasRecordFoundCallback; static jmethodID method_sdpMnsRecordFoundCallback; static jmethodID method_sdpPseRecordFoundCallback; static jmethodID method_sdpOppOpsRecordFoundCallback; static jmethodID method_sdpSapsRecordFoundCallback; static const btsdp_interface_t *sBluetoothSdpInterface = NULL; static void sdp_search_callback(bt_status_t status, bt_bdaddr_t *bd_addr, uint8_t* uuid_in, int record_size, bluetooth_sdp_record* record); btsdp_callbacks_t sBluetoothSdpCallbacks = { sizeof(sBluetoothSdpCallbacks), sdp_search_callback }; static jobject sCallbacksObj = NULL; static JNIEnv *sCallbackEnv = NULL; static bool checkCallbackThread() { sCallbackEnv = getCallbackEnv(); JNIEnv* env = AndroidRuntime::getJNIEnv(); if (sCallbackEnv != env || sCallbackEnv == NULL) { ALOGE("Callback env check fail: env: %p, callback: %p", env, sCallbackEnv); return false; } return true; } static void initializeNative(JNIEnv *env, jobject object) { const bt_interface_t* btInf; bt_status_t status; if ( (btInf = getBluetoothInterface()) == NULL) { ALOGE("Bluetooth module is not loaded"); return; } if (sBluetoothSdpInterface !=NULL) { ALOGW("Cleaning up Bluetooth SDP Interface before initializing..."); sBluetoothSdpInterface->deinit(); sBluetoothSdpInterface = NULL; } if ( (sBluetoothSdpInterface = (btsdp_interface_t *) btInf->get_profile_interface(BT_PROFILE_SDP_CLIENT_ID)) == NULL) { ALOGE("Error getting SDP client interface"); }else{ sBluetoothSdpInterface->init(&sBluetoothSdpCallbacks); } sCallbacksObj = env->NewGlobalRef(object); } static void classInitNative(JNIEnv* env, jclass clazz) { /* generic SDP record (raw data)*/ method_sdpRecordFoundCallback = env->GetMethodID(clazz, "sdpRecordFoundCallback", "(I[B[BI[B)V"); /* MAS SDP record*/ method_sdpMasRecordFoundCallback = env->GetMethodID(clazz, "sdpMasRecordFoundCallback", "(I[B[BIIIIIILjava/lang/String;Z)V"); /* MNS SDP record*/ method_sdpMnsRecordFoundCallback = env->GetMethodID(clazz, "sdpMnsRecordFoundCallback", "(I[B[BIIIILjava/lang/String;Z)V"); /* PBAP PSE record */ method_sdpPseRecordFoundCallback = env->GetMethodID(clazz, "sdpPseRecordFoundCallback", "(I[B[BIIIIILjava/lang/String;Z)V"); /* OPP Server record */ method_sdpOppOpsRecordFoundCallback = env->GetMethodID(clazz, "sdpOppOpsRecordFoundCallback", "(I[B[BIIILjava/lang/String;[BZ)V"); /* SAP Server record */ method_sdpSapsRecordFoundCallback = env->GetMethodID(clazz, "sdpSapsRecordFoundCallback", "(I[B[BIILjava/lang/String;Z)V"); } static jboolean sdpSearchNative(JNIEnv *env, jobject obj, jbyteArray address, jbyteArray uuidObj) { ALOGD("%s:",__FUNCTION__); jbyte *addr = NULL, *uuid = NULL; jboolean result = JNI_FALSE; int ret; if (!sBluetoothSdpInterface) goto Fail; addr = env->GetByteArrayElements(address, NULL); if (addr == NULL) { jniThrowIOException(env, EINVAL); goto Fail; } uuid = env->GetByteArrayElements(uuidObj, NULL); if (!uuid) { ALOGE("failed to get uuid"); goto Fail; } ALOGD("%s UUID %.*X",__FUNCTION__,16, (uint8_t*)uuid); if ((ret = sBluetoothSdpInterface->sdp_search((bt_bdaddr_t *)addr, (const uint8_t*)uuid)) != BT_STATUS_SUCCESS) { ALOGE("SDP Search initialization failed: %d", ret); goto Fail; } result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; Fail: if (addr) env->ReleaseByteArrayElements(address, addr, 0); if (uuid) env->ReleaseByteArrayElements(uuidObj, uuid, 0); return result; } static void sdp_search_callback(bt_status_t status, bt_bdaddr_t *bd_addr, uint8_t* uuid_in, int count, bluetooth_sdp_record* records) { jbyteArray addr = NULL; jbyteArray uuid = NULL; jstring service_name = NULL; int i = 0; bluetooth_sdp_record* record; if (!checkCallbackThread()) { ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); goto clean; } addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); if (addr == NULL) goto clean; uuid = sCallbackEnv->NewByteArray(sizeof(bt_uuid_t)); if (uuid == NULL) goto clean; sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr); sCallbackEnv->SetByteArrayRegion(uuid, 0, sizeof(bt_uuid_t), (jbyte*)uuid_in); ALOGD("%s: Status is: %d, Record count: %d", __FUNCTION__, status, count); // Ensure we run the loop at least once, to also signal errors if they occure for(i = 0; i < count || i==0; i++) { bool more_results = (i<(count-1))?true:false; record = &records[i]; service_name = NULL; if (record->hdr.service_name_length > 0) { ALOGD("%s, ServiceName: %s", __FUNCTION__, record->mas.hdr.service_name); service_name = (jstring)sCallbackEnv->NewStringUTF(record->mas.hdr.service_name); } /* call the right callback according to the uuid*/ if (IS_UUID(UUID_MAP_MAS,uuid_in)){ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpMasRecordFoundCallback, (jint) status, addr, uuid, (jint)record->mas.mas_instance_id, (jint)record->mas.hdr.l2cap_psm, (jint)record->mas.hdr.rfcomm_channel_number, (jint)record->mas.hdr.profile_version, (jint)record->mas.supported_features, (jint)record->mas.supported_message_types, service_name, more_results); }else if (IS_UUID(UUID_MAP_MNS,uuid_in)){ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpMnsRecordFoundCallback, (jint) status, addr, uuid, (jint)record->mns.hdr.l2cap_psm, (jint)record->mns.hdr.rfcomm_channel_number, (jint)record->mns.hdr.profile_version, (jint)record->mns.supported_features, service_name, more_results); } else if (IS_UUID(UUID_PBAP_PSE, uuid_in)) { sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpPseRecordFoundCallback, (jint) status, addr, uuid, (jint)record->pse.hdr.l2cap_psm, (jint)record->pse.hdr.rfcomm_channel_number, (jint)record->pse.hdr.profile_version, (jint)record->pse.supported_features, (jint)record->pse.supported_repositories, service_name, more_results); } else if (IS_UUID(UUID_OBEX_OBJECT_PUSH, uuid_in)) { jint formats_list_size = record->ops.supported_formats_list_len; jbyteArray formats_list = sCallbackEnv->NewByteArray(formats_list_size); if (formats_list == NULL) goto clean; sCallbackEnv->SetByteArrayRegion(formats_list, 0, formats_list_size, (jbyte*)record->ops.supported_formats_list); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpOppOpsRecordFoundCallback, (jint) status, addr, uuid, (jint)record->ops.hdr.l2cap_psm, (jint)record->ops.hdr.rfcomm_channel_number, (jint)record->ops.hdr.profile_version, service_name, formats_list, more_results); sCallbackEnv->DeleteLocalRef(formats_list); } else if (IS_UUID(UUID_SAP, uuid_in)) { sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpSapsRecordFoundCallback, (jint) status, addr, uuid, (jint)record->mas.hdr.rfcomm_channel_number, (jint)record->mas.hdr.profile_version, service_name, more_results); } else { // we don't have a wrapper for this uuid, send as raw data jint record_data_size = record->hdr.user1_ptr_len; jbyteArray record_data = NULL; record_data = sCallbackEnv->NewByteArray(record_data_size); if (record_data == NULL) goto clean; sCallbackEnv->SetByteArrayRegion(record_data, 0, record_data_size, (jbyte*)record->hdr.user1_ptr); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpRecordFoundCallback, (jint) status, addr, uuid, record_data_size, record_data); sCallbackEnv->DeleteLocalRef(record_data); } // Cleanup for each iteration if (service_name != NULL) { sCallbackEnv->DeleteLocalRef(service_name); service_name = NULL; } } // End of for-loop clean: if (service_name != NULL) sCallbackEnv->DeleteLocalRef(service_name); if (addr != NULL) sCallbackEnv->DeleteLocalRef(addr); if (uuid != NULL) sCallbackEnv->DeleteLocalRef(uuid); checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); } static jint sdpCreateMapMasRecordNative(JNIEnv *env, jobject obj, jstring name_str, jint mas_id, jint scn, jint l2cap_psm, jint version, jint msg_types, jint features) { ALOGD("%s:", __FUNCTION__); const char* service_name = NULL; bluetooth_sdp_record record = {}; // Must be zero initialized int handle=-1; int ret = 0; if (!sBluetoothSdpInterface) return handle; record.mas.hdr.type = SDP_TYPE_MAP_MAS; if (name_str != NULL) { service_name = env->GetStringUTFChars(name_str, NULL); record.mas.hdr.service_name = (char *) service_name; record.mas.hdr.service_name_length = strlen(service_name); } else { record.mas.hdr.service_name = NULL; record.mas.hdr.service_name_length = 0; } record.mas.hdr.rfcomm_channel_number = scn; record.mas.hdr.l2cap_psm = l2cap_psm; record.mas.hdr.profile_version = version; record.mas.mas_instance_id = mas_id; record.mas.supported_features = features; record.mas.supported_message_types = msg_types; if ( (ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle)) != BT_STATUS_SUCCESS) { ALOGE("SDP Create record failed: %d", ret); goto Fail; } ALOGD("SDP Create record success - handle: %d", handle); Fail: if (service_name) env->ReleaseStringUTFChars(name_str, service_name); return handle; } static jint sdpCreateMapMnsRecordNative(JNIEnv *env, jobject obj, jstring name_str, jint scn, jint l2cap_psm, jint version, jint features) { ALOGD("%s:",__FUNCTION__); const char* service_name = NULL; bluetooth_sdp_record record = {}; // Must be zero initialized int handle=-1; int ret = 0; if (!sBluetoothSdpInterface) return handle; record.mns.hdr.type = SDP_TYPE_MAP_MNS; if (name_str != NULL) { service_name = env->GetStringUTFChars(name_str, NULL); record.mns.hdr.service_name = (char *) service_name; record.mns.hdr.service_name_length = strlen(service_name); } else { record.mns.hdr.service_name = NULL; record.mns.hdr.service_name_length = 0; } record.mns.hdr.rfcomm_channel_number = scn; record.mns.hdr.l2cap_psm = l2cap_psm; record.mns.hdr.profile_version = version; record.mns.supported_features = features; if ( (ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle)) != BT_STATUS_SUCCESS) { ALOGE("SDP Create record failed: %d", ret); goto Fail; } ALOGD("SDP Create record success - handle: %d", handle); Fail: if (service_name) env->ReleaseStringUTFChars(name_str, service_name); return handle; } static jint sdpCreatePbapPseRecordNative(JNIEnv *env, jobject obj, jstring name_str, jint scn, jint l2cap_psm, jint version, jint supported_repositories, jint features) { ALOGD("%s:",__FUNCTION__); const char* service_name = NULL; bluetooth_sdp_record record = {}; // Must be zero initialized int handle=-1; int ret = 0; if (!sBluetoothSdpInterface) return handle; record.pse.hdr.type = SDP_TYPE_PBAP_PSE; if (name_str != NULL) { service_name = env->GetStringUTFChars(name_str, NULL); record.pse.hdr.service_name = (char *) service_name; record.pse.hdr.service_name_length = strlen(service_name); } else { record.pse.hdr.service_name = NULL; record.pse.hdr.service_name_length = 0; } record.pse.hdr.rfcomm_channel_number = scn; record.pse.hdr.l2cap_psm = l2cap_psm; record.pse.hdr.profile_version = version; record.pse.supported_features = features; record.pse.supported_repositories = supported_repositories; if ( (ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle)) != BT_STATUS_SUCCESS) { ALOGE("SDP Create record failed: %d", ret); goto Fail; } ALOGD("SDP Create record success - handle: %d", handle); Fail: if (service_name) env->ReleaseStringUTFChars(name_str, service_name); return handle; } static jint sdpCreateOppOpsRecordNative(JNIEnv *env, jobject obj, jstring name_str, jint scn, jint l2cap_psm, jint version, jbyteArray supported_formats_list) { ALOGD("%s:",__FUNCTION__); const char* service_name = NULL; bluetooth_sdp_record record = {}; // Must be zero initialized jbyte* formats_list; int formats_list_len = 0; int handle=-1; int ret = 0; if (!sBluetoothSdpInterface) return handle; record.ops.hdr.type = SDP_TYPE_OPP_SERVER; if (name_str != NULL) { service_name = env->GetStringUTFChars(name_str, NULL); record.ops.hdr.service_name = (char *) service_name; record.ops.hdr.service_name_length = strlen(service_name); } else { record.ops.hdr.service_name = NULL; record.ops.hdr.service_name_length = 0; } record.ops.hdr.rfcomm_channel_number = scn; record.ops.hdr.l2cap_psm = l2cap_psm; record.ops.hdr.profile_version = version; formats_list = env->GetByteArrayElements(supported_formats_list, NULL); if (formats_list != NULL) { formats_list_len = env->GetArrayLength(supported_formats_list); if (formats_list_len > SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH) { formats_list_len = SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH; } memcpy(record.ops.supported_formats_list, formats_list, formats_list_len); } record.ops.supported_formats_list_len = formats_list_len; if ( (ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle)) != BT_STATUS_SUCCESS) { ALOGE("SDP Create record failed: %d", ret); goto Fail; } ALOGD("SDP Create record success - handle: %d", handle); Fail: if (service_name) env->ReleaseStringUTFChars(name_str, service_name); if (formats_list) env->ReleaseByteArrayElements(supported_formats_list, formats_list, 0); return handle; } static jint sdpCreateSapsRecordNative(JNIEnv *env, jobject obj, jstring name_str, jint scn, jint version) { ALOGD("%s:",__FUNCTION__); const char* service_name = NULL; bluetooth_sdp_record record = {}; // Must be zero initialized int handle = -1; int ret = 0; if (!sBluetoothSdpInterface) return handle; record.sap.hdr.type = SDP_TYPE_SAP_SERVER; if (name_str != NULL) { service_name = env->GetStringUTFChars(name_str, NULL); record.mas.hdr.service_name = (char *) service_name; record.mas.hdr.service_name_length = strlen(service_name); } else { record.mas.hdr.service_name = NULL; record.mas.hdr.service_name_length = 0; } record.mas.hdr.rfcomm_channel_number = scn; record.mas.hdr.profile_version = version; if ( (ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle)) != BT_STATUS_SUCCESS) { ALOGE("SDP Create record failed: %d", ret); goto Fail; } ALOGD("SDP Create record success - handle: %d", handle); Fail: if (service_name) env->ReleaseStringUTFChars(name_str, service_name); return handle; } static jboolean sdpRemoveSdpRecordNative(JNIEnv *env, jobject obj, jint record_id) { ALOGD("%s:",__FUNCTION__); int ret = 0; if (!sBluetoothSdpInterface) return false; if ( (ret = sBluetoothSdpInterface->remove_sdp_record(record_id)) != BT_STATUS_SUCCESS) { ALOGE("SDP Remove record failed: %d", ret); return false; } ALOGD("SDP Remove record success - handle: %d", record_id); return true; } static void cleanupNative(JNIEnv *env, jobject object) { const bt_interface_t* btInf; bt_status_t status; if ( (btInf = getBluetoothInterface()) == NULL) { ALOGE("Bluetooth module is not loaded"); return; } if (sBluetoothSdpInterface !=NULL) { ALOGW("Cleaning up Bluetooth SDP Interface..."); sBluetoothSdpInterface->deinit(); sBluetoothSdpInterface = NULL; } if (sCallbacksObj != NULL) { ALOGW("Cleaning up Bluetooth Health object"); env->DeleteGlobalRef(sCallbacksObj); sCallbacksObj = NULL; } } static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"classInitNative", "()V", (void *) classInitNative}, {"initializeNative", "()V", (void *) initializeNative}, {"cleanupNative", "()V", (void*) cleanupNative}, {"sdpSearchNative", "([B[B)Z", (void*) sdpSearchNative}, {"sdpCreateMapMasRecordNative", "(Ljava/lang/String;IIIIII)I", (void*) sdpCreateMapMasRecordNative}, {"sdpCreateMapMnsRecordNative", "(Ljava/lang/String;IIII)I", (void*) sdpCreateMapMnsRecordNative}, {"sdpCreatePbapPseRecordNative", "(Ljava/lang/String;IIIII)I", (void*) sdpCreatePbapPseRecordNative}, {"sdpCreateOppOpsRecordNative", "(Ljava/lang/String;III[B)I", (void*) sdpCreateOppOpsRecordNative}, {"sdpCreateSapsRecordNative", "(Ljava/lang/String;II)I", (void*) sdpCreateSapsRecordNative}, {"sdpRemoveSdpRecordNative", "(I)Z", (void*) sdpRemoveSdpRecordNative} }; int register_com_android_bluetooth_sdp(JNIEnv* env) { return jniRegisterNativeMethods(env, "com/android/bluetooth/sdp/SdpManager", sMethods, NELEM(sMethods)); } }