/*
* 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 <string.h>
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_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;
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 %.*s",__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;
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 SDP 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));
}
}