blob: c3a8f6d68f6c884c116112280bd6d44b8b0efa27 [file] [log] [blame]
/******************************************************************************
*
* Copyright 2021 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 "BluetoothCsipClientJni"
#define LOG_NDEBUG 0
#include "android_runtime/AndroidRuntime.h"
#include "com_android_bluetooth.h"
#include "hardware/bt_csip.h"
#include "utils/Log.h"
#include <shared_mutex>
#include <string.h>
using bluetooth::Uuid;
#define UUID_PARAMS(uuid) uuid_lsb(uuid), uuid_msb(uuid)
static uint64_t uuid_lsb(const Uuid& uuid) {
uint64_t lsb = 0;
auto uu = uuid.To128BitBE();
for (int i = 8; i <= 15; i++) {
lsb <<= 8;
lsb |= uu[i];
}
return lsb;
}
static uint64_t uuid_msb(const Uuid& uuid) {
uint64_t msb = 0;
auto uu = uuid.To128BitBE();
for (int i = 0; i <= 7; i++) {
msb <<= 8;
msb |= uu[i];
}
return msb;
}
static Uuid from_java_uuid(jlong uuid_msb, jlong uuid_lsb) {
std::array<uint8_t, Uuid::kNumBytes128> uu{};
for (int i = 0; i < 8; i++) {
uu[7 - i] = (uuid_msb >> (8 * i)) & 0xFF;
uu[15 - i] = (uuid_lsb >> (8 * i)) & 0xFF;
}
return Uuid::From128BitBE(uu);
}
static RawAddress str2addr(JNIEnv* env, jstring address) {
RawAddress bd_addr;
const char* c_address = env->GetStringUTFChars(address, NULL);
if (!c_address) return bd_addr;
RawAddress::FromString(std::string(c_address), bd_addr);
env->ReleaseStringUTFChars(address, c_address);
return bd_addr;
}
namespace android {
static jmethodID method_onCsipAppRegistered;
static jmethodID method_onConnectionStateChanged;
static jmethodID method_onNewSetFound;
static jmethodID method_onNewSetMemberFound;
static jmethodID method_onLockStatusChanged;
static jmethodID method_onLockAvailable;
static jmethodID method_onSetSirkChanged;
static jmethodID method_onSetSizeChanged;
static const btcsip_interface_t* sBluetoothCsipInterface = NULL;
static jobject mCallbacksObj = NULL;
static std::shared_timed_mutex mCallbacks_mutex;
static jstring bdaddr2newjstr(JNIEnv* env, const RawAddress* bda) {
char c_address[32];
snprintf(c_address, sizeof(c_address), "%02X:%02X:%02X:%02X:%02X:%02X",
bda->address[0], bda->address[1], bda->address[2], bda->address[3],
bda->address[4], bda->address[5]);
return env->NewStringUTF(c_address);
}
static void classInitNative(JNIEnv* env, jclass clazz) {
method_onCsipAppRegistered =
env->GetMethodID(clazz, "onCsipAppRegistered", "(IIJJ)V");
method_onConnectionStateChanged =
env->GetMethodID(clazz, "onConnectionStateChanged", "(ILjava/lang/String;II)V");
method_onNewSetFound =
env->GetMethodID(clazz, "onNewSetFound", "(ILjava/lang/String;I[BJJZ)V");
method_onNewSetMemberFound =
env->GetMethodID(clazz, "onNewSetMemberFound", "(ILjava/lang/String;)V");
method_onLockStatusChanged =
env->GetMethodID(clazz, "onLockStatusChanged", "(IIII[Ljava/lang/String;)V");
method_onLockAvailable =
env->GetMethodID(clazz, "onLockAvailable", "(IILjava/lang/String;)V");
method_onSetSirkChanged =
env->GetMethodID(clazz, "onSetSirkChanged", "(I[BLjava/lang/String;)V");
method_onSetSizeChanged =
env->GetMethodID(clazz, "onSetSizeChanged", "(IILjava/lang/String;)V");
ALOGI("%s: succeeds", __func__);
}
static void csip_app_registered_callback(uint8_t status, uint8_t app_id,
const bluetooth::Uuid& uuid){
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCsipAppRegistered,
status, app_id, UUID_PARAMS(uuid));
}
static void connection_state_changed_callback(uint8_t app_id, RawAddress& addr,
uint8_t state, uint8_t status){
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
// address
ScopedLocalRef<jstring> address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &addr));
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
app_id, address.get(), state, status);
}
static void new_set_found_callback(uint8_t set_id, RawAddress& bd_addr, uint8_t size,
uint8_t* sirk, const bluetooth::Uuid& p_srvc_uuid,
bool lock_support) {
ALOGI("%s: ", __func__);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
// address
ScopedLocalRef<jstring> address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bd_addr));
ALOGI("%s: new_set_found_callback: process sirk", __func__);
ScopedLocalRef<jbyteArray> jb(sCallbackEnv.get(), NULL);
jb.reset(sCallbackEnv->NewByteArray(16));
sCallbackEnv->SetByteArrayRegion(jb.get(), 0, 16, (jbyte*)sirk);
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNewSetFound, set_id, address.get(),
size, jb.get(), UUID_PARAMS(p_srvc_uuid), lock_support);
}
static void new_set_member_found_cb(uint8_t set_id, RawAddress& bd_addr) {
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
// address
ScopedLocalRef<jstring> address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bd_addr));
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNewSetMemberFound, set_id, address.get());
}
/** Callback for lock status changed event from stack
*/
static void lock_state_changed_callback(uint8_t app_id, uint8_t set_id,
uint8_t value, uint8_t status,
std::vector<RawAddress> addr_list) {
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
if (!mCallbacksObj) {
ALOGE("mCallbacksObj is NULL. Return.");
return;
}
int i;
jstring bd_addr;
jobjectArray device_list;
jsize len = addr_list.size();
device_list = sCallbackEnv->NewObjectArray(
len, sCallbackEnv->FindClass("java/lang/String"), 0);
for(i = 0; i < len; i++)
{
bd_addr = sCallbackEnv->NewStringUTF(addr_list[i].ToString().c_str());
sCallbackEnv->SetObjectArrayElement(device_list, i, bd_addr);
}
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onLockStatusChanged,
app_id, set_id, value, status, device_list);
}
/** Callback when lock is available on earlier denying set member
*/
static void lock_available_callback(uint8_t app_id, uint8_t set_id,
RawAddress& bd_addr) {
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
if (!mCallbacksObj) {
ALOGE("mCallbacksObj is NULL. Return.");
return;
}
// address
ScopedLocalRef<jstring> address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bd_addr));
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onLockAvailable, app_id, set_id, address.get());
}
/** Callback when size of coordinated set has been changed
*/
static void set_size_changed_callback(uint8_t set_id, uint8_t size,
RawAddress& bd_addr) {
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
if (!mCallbacksObj) {
ALOGE("mCallbacksObj is NULL. Return.");
return;
}
// address
ScopedLocalRef<jstring> address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bd_addr));
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetSizeChanged, set_id, size, address.get());
}
/** Callback when SIRK of coordinated set has been changed
*/
static void set_sirk_changed_callback(uint8_t set_id, uint8_t* sirk,
RawAddress& bd_addr) {
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
if (!mCallbacksObj) {
ALOGE("mCallbacksObj is NULL. Return.");
return;
}
ScopedLocalRef<jbyteArray> jb(sCallbackEnv.get(), NULL);
jb.reset(sCallbackEnv->NewByteArray(24));
sCallbackEnv->SetByteArrayRegion(jb.get(), 0, 24, (jbyte*)sirk);
// address
ScopedLocalRef<jstring> address(sCallbackEnv.get(),
bdaddr2newjstr(sCallbackEnv.get(), &bd_addr));
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetSirkChanged, set_id, jb.get(), address.get());
}
static btcsip_callbacks_t sBluetoothCsipCallbacks = {
sizeof(sBluetoothCsipCallbacks),
csip_app_registered_callback,
connection_state_changed_callback,
new_set_found_callback,
new_set_member_found_cb,
lock_state_changed_callback,
lock_available_callback,
set_size_changed_callback,
set_sirk_changed_callback,
};
static void initNative(JNIEnv* env, jobject object) {
ALOGI("%s: initNative()", __func__);
std::unique_lock<std::shared_timed_mutex> lock(mCallbacks_mutex);
const bt_interface_t* btInf = getBluetoothInterface();
if (btInf == NULL) {
ALOGE("Bluetooth module is not loaded");
return;
}
if (sBluetoothCsipInterface != NULL) {
ALOGW("Cleaning up Bluetooth CSIP CLIENT Interface before initializing...");
sBluetoothCsipInterface->cleanup();
sBluetoothCsipInterface = NULL;
}
if (mCallbacksObj != NULL) {
ALOGW("Cleaning up Bluetooth CSIP callback object");
env->DeleteGlobalRef(mCallbacksObj);
mCallbacksObj = NULL;
}
sBluetoothCsipInterface =
(btcsip_interface_t*)btInf->get_profile_interface(BT_PROFILE_CSIP_CLIENT_ID);
if (sBluetoothCsipInterface == NULL) {
ALOGE("Failed to get Bluetooth CSIPInterface");
return;
}
bt_status_t status = sBluetoothCsipInterface->init(&sBluetoothCsipCallbacks);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed to initialize Bluetooth CSIP Client, status: %d", status);
sBluetoothCsipInterface = NULL;
return;
}
mCallbacksObj = env->NewGlobalRef(object);
}
static void cleanupNative(JNIEnv* env, jobject object) {
ALOGI("%s: cleanupNative()", __func__);
if (!sBluetoothCsipInterface) return;
sBluetoothCsipInterface->cleanup();
}
static jboolean connectSetDeviceNative(JNIEnv* env, jobject object,
jint app_id, jbyteArray address) {
if (!sBluetoothCsipInterface) return JNI_FALSE;
ALOGI("%s: connectSetDeviceNative()", __func__);
jboolean ret = JNI_TRUE;
jbyte* addr = env->GetByteArrayElements(address, nullptr);
if (!addr) {
jniThrowIOException(env, EINVAL);
return JNI_FALSE;
}
RawAddress bd_addr;
bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
sBluetoothCsipInterface->connect(app_id, &bd_addr);
return ret;
}
static jboolean disconnectSetDeviceNative(JNIEnv* env, jobject object,
jint app_id, jbyteArray address) {
if (!sBluetoothCsipInterface) return JNI_FALSE;
jboolean ret = JNI_TRUE;
ALOGI("%s: disconnectSetDeviceNative()", __func__);
jbyte* addr = env->GetByteArrayElements(address, nullptr);
if (!addr) {
jniThrowIOException(env, EINVAL);
return JNI_FALSE;
}
RawAddress bd_addr;
bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
sBluetoothCsipInterface->disconnect(app_id, &bd_addr);
return ret;
}
static void registerCsipAppNative(JNIEnv* env, jobject object,
jlong app_uuid_lsb, jlong app_uuid_msb) {
if (!sBluetoothCsipInterface) return;
Uuid uuid = from_java_uuid(app_uuid_msb, app_uuid_lsb);
sBluetoothCsipInterface->register_csip_app(uuid);
}
static void unregisterCsipAppNative(JNIEnv* env, jobject object, jint app_id) {
if (!sBluetoothCsipInterface) return;
sBluetoothCsipInterface->unregister_csip_app(app_id);
}
static void setLockValueNative(JNIEnv* env, jobject object, jint app_id,
jint set_id, jint value, jobjectArray devicesList) {
if (!sBluetoothCsipInterface) return;
std::vector<RawAddress> lock_list;
int listCount = env->GetArrayLength(devicesList);
for (int i=0; i < listCount; i++) {
jstring address = (jstring) (env->GetObjectArrayElement(devicesList, i));
RawAddress bd_addr = str2addr(env, address);
lock_list.push_back(bd_addr);
env->DeleteLocalRef(address);
}
sBluetoothCsipInterface->set_lock_value(app_id, set_id, value, lock_list);
}
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void*)classInitNative},
{"initNative", "()V", (void*)initNative},
{"cleanupNative", "()V", (void*)cleanupNative},
{"connectSetDeviceNative", "(I[B)Z", (void*)connectSetDeviceNative},
{"disconnectSetDeviceNative", "(I[B)Z", (void*)disconnectSetDeviceNative},
{"registerCsipAppNative", "(JJ)V", (void*)registerCsipAppNative},
{"unregisterCsipAppNative", "(I)V", (void*)unregisterCsipAppNative},
{"setLockValueNative", "(III[Ljava/lang/String;)V", (void*)setLockValueNative},
};
int register_com_android_bluetooth_csip_client(JNIEnv* env) {
ALOGE("%s", __func__);
return jniRegisterNativeMethods(
env, "com/android/bluetooth/groupclient/GroupClientNativeInterface",
sMethods, NELEM(sMethods));
}
}