blob: c65a5d09d20c428c0dea7182c5a297c3b14177c0 [file] [log] [blame]
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright 2018 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 "BluetoothPacsClienServiceJni"
#define LOG_NDEBUG 0
#include "android_runtime/AndroidRuntime.h"
#include "base/logging.h"
#include "com_android_bluetooth.h"
#include "hardware/bt_pacs_client.h"
#include <string.h>
#include <shared_mutex>
using bluetooth::bap::pacs::ConnectionState;
using bluetooth::bap::pacs::PacsClientInterface;
using bluetooth::bap::pacs::PacsClientCallbacks;
namespace android {
static jmethodID method_OnInitialized;
static jmethodID method_onConnectionStateChanged;
static jmethodID method_OnAudioContextAvailable;
static jmethodID method_onServiceDiscovery;
static struct {
jclass clazz;
jmethodID constructor;
jmethodID getCodecType;
jmethodID getCodecPriority;
jmethodID getSampleRate;
jmethodID getBitsPerSample;
jmethodID getChannelMode;
jmethodID getCodecSpecific1;
jmethodID getCodecSpecific2;
jmethodID getCodecSpecific3;
jmethodID getCodecSpecific4;
} android_bluetooth_pacs_record;
static PacsClientInterface* sPacsClientInterface = nullptr;
static std::shared_timed_mutex interface_mutex;
static jobject mCallbacksObj = nullptr;
static std::shared_timed_mutex callbacks_mutex;
class PacsClientCallbacksImpl : public PacsClientCallbacks {
public:
~PacsClientCallbacksImpl() = default;
void OnInitialized(int status,
int client_id) override {
LOG(INFO) << __func__;
std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_OnInitialized,
(jint)status, (jint)client_id);
}
void OnConnectionState(const RawAddress& bd_addr,
ConnectionState state) override {
LOG(INFO) << __func__;
std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
ScopedLocalRef<jbyteArray> addr(
sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
if (!addr.get()) {
LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state";
return;
}
sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
(jbyte*)&bd_addr);
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
addr.get(), (jint)state);
}
void OnAudioContextAvailable(const RawAddress& bd_addr,
uint32_t available_contexts) override {
LOG(INFO) << __func__;
std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
ScopedLocalRef<jbyteArray> addr(
sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
if (!addr.get()) {
LOG(ERROR) << "Failed to new jbyteArray bd addr for available audio context";
return;
}
sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
(jbyte*)&bd_addr);
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_OnAudioContextAvailable,
addr.get(), (jint)available_contexts);
}
void OnSearchComplete(int status, const RawAddress& address,
std::vector<bluetooth::bap::pacs::CodecConfig> sink_pac_records,
std::vector<bluetooth::bap::pacs::CodecConfig> src_pac_records,
uint32_t sink_locations,
uint32_t src_locations,
uint32_t available_contexts,
uint32_t supported_contexts) override {
LOG(INFO) << __func__;
std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
jsize i = 0;
jobjectArray sink_pac_records_array = sCallbackEnv->NewObjectArray(
(jsize)sink_pac_records.size(),
android_bluetooth_pacs_record.clazz, nullptr);
for (auto const& cap : sink_pac_records) {
jobject capObj = sCallbackEnv->NewObject(
android_bluetooth_pacs_record.clazz,
android_bluetooth_pacs_record.constructor,
(jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
(jint)cap.bits_per_sample, (jint)cap.channel_mode,
(jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
(jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
sCallbackEnv->SetObjectArrayElement(sink_pac_records_array, i++, capObj);
sCallbackEnv->DeleteLocalRef(capObj);
}
i = 0;
jobjectArray src_pac_records_array = sCallbackEnv->NewObjectArray(
(jsize)src_pac_records.size(),
android_bluetooth_pacs_record.clazz, nullptr);
for (auto const& cap : src_pac_records) {
jobject capObj = sCallbackEnv->NewObject(
android_bluetooth_pacs_record.clazz,
android_bluetooth_pacs_record.constructor,
(jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
(jint)cap.bits_per_sample, (jint)cap.channel_mode,
(jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
(jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
sCallbackEnv->SetObjectArrayElement(src_pac_records_array, i++,
capObj);
sCallbackEnv->DeleteLocalRef(capObj);
}
ScopedLocalRef<jbyteArray> addr(
sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
if (!addr.get()) {
LOG(ERROR) << "Failed to new jbyteArray bd addr";
return;
}
sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
(jbyte*)&address);
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceDiscovery,
sink_pac_records_array, src_pac_records_array, (jint)sink_locations,
(jint)src_locations, (jint)available_contexts, (jint)supported_contexts,
(jint)status, addr.get());
}
};
static PacsClientCallbacksImpl sPacsClientCallbacks;
static void classInitNative(JNIEnv* env, jclass clazz) {
jclass jniBluetoothCodecConfigClass =
env->FindClass("android/bluetooth/BluetoothCodecConfig");
android_bluetooth_pacs_record.constructor =
env->GetMethodID(jniBluetoothCodecConfigClass, "<init>", "(IIIIIJJJJ)V");
android_bluetooth_pacs_record.getCodecType =
env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecType", "()I");
android_bluetooth_pacs_record.getCodecPriority =
env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecPriority", "()I");
android_bluetooth_pacs_record.getSampleRate =
env->GetMethodID(jniBluetoothCodecConfigClass, "getSampleRate", "()I");
android_bluetooth_pacs_record.getBitsPerSample =
env->GetMethodID(jniBluetoothCodecConfigClass, "getBitsPerSample", "()I");
android_bluetooth_pacs_record.getChannelMode =
env->GetMethodID(jniBluetoothCodecConfigClass, "getChannelMode", "()I");
android_bluetooth_pacs_record.getCodecSpecific1 = env->GetMethodID(
jniBluetoothCodecConfigClass, "getCodecSpecific1", "()J");
android_bluetooth_pacs_record.getCodecSpecific2 = env->GetMethodID(
jniBluetoothCodecConfigClass, "getCodecSpecific2", "()J");
android_bluetooth_pacs_record.getCodecSpecific3 = env->GetMethodID(
jniBluetoothCodecConfigClass, "getCodecSpecific3", "()J");
android_bluetooth_pacs_record.getCodecSpecific4 = env->GetMethodID(
jniBluetoothCodecConfigClass, "getCodecSpecific4", "()J");
method_OnInitialized =
env->GetMethodID(clazz, "OnInitialized", "(II)V");
method_onConnectionStateChanged =
env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V");
method_OnAudioContextAvailable =
env->GetMethodID(clazz, "OnAudioContextAvailable", "([BI)V");
method_onServiceDiscovery =
env->GetMethodID(clazz, "onServiceDiscovery", "([Landroid/bluetooth/BluetoothCodecConfig;"
"[Landroid/bluetooth/BluetoothCodecConfig;"
"IIIII[B)V");
LOG(INFO) << __func__ << ": succeeds";
}
static void initNative(JNIEnv* env, jobject object) {
std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
const bt_interface_t* btInf = getBluetoothInterface();
if (btInf == nullptr) {
LOG(ERROR) << "Bluetooth module is not loaded";
return;
}
if (sPacsClientInterface != nullptr) {
LOG(INFO) << "Cleaning up PacsClient Interface before initializing...";
sPacsClientInterface->Cleanup(0);
sPacsClientInterface = nullptr;
}
if (mCallbacksObj != nullptr) {
LOG(INFO) << "Cleaning up PacsClient callback object";
env->DeleteGlobalRef(mCallbacksObj);
mCallbacksObj = nullptr;
}
if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
LOG(ERROR) << "Failed to allocate Global Ref for pacs client Callbacks";
return;
}
android_bluetooth_pacs_record.clazz = (jclass)env->NewGlobalRef(
env->FindClass("android/bluetooth/BluetoothCodecConfig"));
if (android_bluetooth_pacs_record.clazz == nullptr) {
ALOGE("%s: Failed to allocate Global Ref for BluetoothCodecConfig class",
__func__);
return;
}
sPacsClientInterface = (PacsClientInterface*)btInf->get_profile_interface(
BT_PROFILE_PACS_CLIENT_ID);
if (sPacsClientInterface == nullptr) {
LOG(ERROR) << "Failed to get Bluetooth pacs client Interface";
return;
}
sPacsClientInterface->Init(&sPacsClientCallbacks);
}
static void cleanupNative(JNIEnv* env, jobject object, jint client_id) {
std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
const bt_interface_t* btInf = getBluetoothInterface();
if (btInf == nullptr) {
LOG(ERROR) << "Bluetooth module is not loaded";
return;
}
if (sPacsClientInterface != nullptr) {
sPacsClientInterface->Cleanup(client_id);
sPacsClientInterface = nullptr;
}
if (mCallbacksObj != nullptr) {
env->DeleteGlobalRef(mCallbacksObj);
mCallbacksObj = nullptr;
}
env->DeleteGlobalRef(android_bluetooth_pacs_record.clazz);
android_bluetooth_pacs_record.clazz = nullptr;
}
static jboolean connectPacsClientNative(JNIEnv* env, jobject object,
jint client_id, jbyteArray address) {
LOG(INFO) << __func__;
std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
if (!sPacsClientInterface) return JNI_FALSE;
jbyte* addr = env->GetByteArrayElements(address, nullptr);
if (!addr) {
jniThrowIOException(env, EINVAL);
return JNI_FALSE;
}
RawAddress* tmpraw = (RawAddress*)addr;
sPacsClientInterface->Connect(client_id, *tmpraw);
env->ReleaseByteArrayElements(address, addr, 0);
return JNI_TRUE;
}
static jboolean disconnectPacsClientNative(JNIEnv* env, jobject object,
jint client_id, jbyteArray address) {
LOG(INFO) << __func__;
std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
if (!sPacsClientInterface) return JNI_FALSE;
jbyte* addr = env->GetByteArrayElements(address, nullptr);
if (!addr) {
jniThrowIOException(env, EINVAL);
return JNI_FALSE;
}
RawAddress* tmpraw = (RawAddress*)addr;
sPacsClientInterface->Disconnect(client_id, *tmpraw);
env->ReleaseByteArrayElements(address, addr, 0);
return JNI_TRUE;
}
static jboolean startDiscoveryNative(JNIEnv* env, jobject object,
jint client_id, jbyteArray address) {
std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
if (!sPacsClientInterface) return JNI_FALSE;
jbyte* addr = env->GetByteArrayElements(address, nullptr);
if (!addr) {
jniThrowIOException(env, EINVAL);
return JNI_FALSE;
}
RawAddress* tmpraw = (RawAddress*)addr;
sPacsClientInterface->StartDiscovery(client_id, *tmpraw);
env->ReleaseByteArrayElements(address, addr, 0);
return JNI_TRUE;
}
static void GetAvailableAudioContextsNative(JNIEnv* env, jobject object,
jint client_id, jbyteArray address) {
std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
if (!sPacsClientInterface) return;
jbyte* addr = env->GetByteArrayElements(address, nullptr);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
RawAddress* tmpraw = (RawAddress*)addr;
sPacsClientInterface->GetAvailableAudioContexts(client_id, *tmpraw);
env->ReleaseByteArrayElements(address, addr, 0);
}
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void*)classInitNative},
{"initNative", "()V", (void*)initNative},
{"cleanupNative", "(I)V", (void*)cleanupNative},
{"connectPacsClientNative", "(I[B)Z", (void*)connectPacsClientNative},
{"disconnectPacsClientNative", "(I[B)Z", (void*)disconnectPacsClientNative},
{"startDiscoveryNative", "(I[B)Z", (void*)startDiscoveryNative},
{"GetAvailableAudioContextsNative", "(I[B)Z", (void*)GetAvailableAudioContextsNative},
};
int register_com_android_bluetooth_pacs_client(JNIEnv* env) {
return jniRegisterNativeMethods(
env, "com/android/bluetooth/pc/PacsClientNativeInterface",
sMethods, NELEM(sMethods));
}
} // namespace android