blob: 6ffa2daff652556adb13b4476201048f0cb8b7f5 [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 "BluetoothAPM_Jni"
#define LOG_NDEBUG 0
#include "android_runtime/AndroidRuntime.h"
#include "com_android_bluetooth.h"
#include "hardware/bt_apm.h"
#include "utils/Log.h"
#include <string.h>
#include <shared_mutex>
namespace android {
static jmethodID method_onGetActiveprofileCallback;
static const bt_apm_interface_t* sBluetoothApmInterface = nullptr;
static std::shared_timed_mutex interface_mutex;
static jobject mCallbacksObj = nullptr;
static std::shared_timed_mutex callbacks_mutex;
static int btapm_active_profile_callback(const RawAddress& bd_addr, uint16_t audio_type)
{
ALOGI("%s", __func__);
std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return -1;
ScopedLocalRef<jbyteArray> addr(
sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
if (!addr.get()) {
ALOGE("%s: Fail to new jbyteArray bd addr", __func__);
return -1;
}
sCallbackEnv->SetByteArrayRegion(
addr.get(), 0, sizeof(RawAddress),
reinterpret_cast<const jbyte*>(bd_addr.address));
return sCallbackEnv->CallIntMethod(mCallbacksObj, method_onGetActiveprofileCallback,
addr.get(), (jint)audio_type);
}
static btapm_initiator_callbacks_t sBluetoothApmCallbacks = {
sizeof(sBluetoothApmCallbacks),
btapm_active_profile_callback
};
static void classInitNative(JNIEnv* env, jclass clazz) {
ALOGI("%s: succeeds", __func__);
method_onGetActiveprofileCallback =
env->GetMethodID(clazz, "getActiveProfile", "([BI)I");
}
static bool 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) {
ALOGE("%s: Bluetooth module is not loaded", __func__);
return JNI_FALSE;
}
if (sBluetoothApmInterface != nullptr) {
ALOGW("%s: Cleaning up APM Interface before initializing...", __func__);
sBluetoothApmInterface->cleanup();
sBluetoothApmInterface = nullptr;
}
if (mCallbacksObj != nullptr) {
ALOGW("%s: Cleaning up APM callback object", __func__);
env->DeleteGlobalRef(mCallbacksObj);
mCallbacksObj = nullptr;
}
if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
ALOGE("%s: Failed to allocate Global Ref for APM Callbacks", __func__);
return JNI_FALSE;
}
sBluetoothApmInterface =
(bt_apm_interface_t*)btInf->get_profile_interface(
BT_APM_MODULE_ID);
if (sBluetoothApmInterface == nullptr) {
ALOGE("%s: Failed to get Bluetooth APM Interface", __func__);
return JNI_FALSE;
}
bt_status_t status = sBluetoothApmInterface->init(&sBluetoothApmCallbacks);
if (status != BT_STATUS_SUCCESS) {
ALOGE("%s: Failed to initialize Bluetooth APM, status: %d", __func__,
status);
sBluetoothApmInterface = nullptr;
return JNI_FALSE;
}
return JNI_TRUE;
}
static void cleanupNative(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) {
ALOGE("%s: Bluetooth module is not loaded", __func__);
return;
}
if (sBluetoothApmInterface != nullptr) {
sBluetoothApmInterface->cleanup();
sBluetoothApmInterface = nullptr;
}
if (mCallbacksObj != nullptr) {
env->DeleteGlobalRef(mCallbacksObj);
mCallbacksObj = nullptr;
}
}
static jboolean activeDeviceUpdateNative(JNIEnv* env, jobject object,
jbyteArray address, jint profile, jint audio_type) {
ALOGI("%s: sBluetoothApmInterface: %p", __func__, sBluetoothApmInterface);
std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
if (!sBluetoothApmInterface) {
ALOGE("%s: Failed to get the Bluetooth APM Interface", __func__);
return JNI_FALSE;
}
jbyte* addr = env->GetByteArrayElements(address, nullptr);
RawAddress bd_addr = RawAddress::kEmpty;
if (addr) {
bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
}
bt_status_t status = sBluetoothApmInterface->active_device_change(bd_addr, profile, audio_type);
if (status != BT_STATUS_SUCCESS) {
ALOGE("%s: Failed APM active_device_change, status: %d", __func__, status);
}
env->ReleaseByteArrayElements(address, addr, 0);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
static jboolean setContentControlNative(JNIEnv* env, jobject object,
jint content_control_id, jint profile) {
ALOGI("%s: sBluetoothApmInterface: %p", __func__, sBluetoothApmInterface);
std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
if (!sBluetoothApmInterface) {
ALOGE("%s: Failed to get the Bluetooth APM Interface", __func__);
return JNI_FALSE;
}
bt_status_t status = sBluetoothApmInterface->set_content_control_id(content_control_id, profile);
if (status != BT_STATUS_SUCCESS) {
ALOGE("%s: Failed APM content control update, status: %d", __func__, status);
}
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void*)classInitNative},
{"initNative", "()V", (void*)initNative},
{"cleanupNative", "()V", (void*)cleanupNative},
{"activeDeviceUpdateNative", "([BII)Z", (void*)activeDeviceUpdateNative},
{"setContentControlNative", "(II)Z", (void*)setContentControlNative},
};
int register_com_android_bluetooth_apm(JNIEnv* env) {
return jniRegisterNativeMethods(
env, "com/android/bluetooth/apm/ApmNativeInterface", sMethods,
NELEM(sMethods));
}
}