/*
|
* 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 "BluetoothHearingAidServiceJni"
|
|
#define LOG_NDEBUG 0
|
|
#include "android_runtime/AndroidRuntime.h"
|
#include "base/logging.h"
|
#include "com_android_bluetooth.h"
|
#include "hardware/bt_hearing_aid.h"
|
|
#include <string.h>
|
#include <shared_mutex>
|
|
using bluetooth::hearing_aid::ConnectionState;
|
using bluetooth::hearing_aid::HearingAidInterface;
|
using bluetooth::hearing_aid::HearingAidCallbacks;
|
|
namespace android {
|
static jmethodID method_onConnectionStateChanged;
|
static jmethodID method_onDeviceAvailable;
|
|
static HearingAidInterface* sHearingAidInterface = nullptr;
|
static std::shared_timed_mutex interface_mutex;
|
|
static jobject mCallbacksObj = nullptr;
|
static std::shared_timed_mutex callbacks_mutex;
|
|
class HearingAidCallbacksImpl : public HearingAidCallbacks {
|
public:
|
~HearingAidCallbacksImpl() = default;
|
void OnConnectionState(ConnectionState state,
|
const RawAddress& bd_addr) 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,
|
(jint)state, addr.get());
|
}
|
|
void OnDeviceAvailable(uint8_t capabilities, uint64_t hi_sync_id,
|
const RawAddress& bd_addr) override {
|
LOG(INFO) << __func__ << ": capabilities=" << +capabilities
|
<< " hi_sync_id=" << hi_sync_id;
|
|
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_onDeviceAvailable,
|
(jbyte)capabilities, (jlong)hi_sync_id,
|
addr.get());
|
}
|
};
|
|
static HearingAidCallbacksImpl sHearingAidCallbacks;
|
|
static void classInitNative(JNIEnv* env, jclass clazz) {
|
method_onConnectionStateChanged =
|
env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
|
|
method_onDeviceAvailable =
|
env->GetMethodID(clazz, "onDeviceAvailable", "(BJ[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 (sHearingAidInterface != nullptr) {
|
LOG(INFO) << "Cleaning up HearingAid Interface before initializing...";
|
sHearingAidInterface->Cleanup();
|
sHearingAidInterface = nullptr;
|
}
|
|
if (mCallbacksObj != nullptr) {
|
LOG(INFO) << "Cleaning up HearingAid callback object";
|
env->DeleteGlobalRef(mCallbacksObj);
|
mCallbacksObj = nullptr;
|
}
|
|
if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
|
LOG(ERROR) << "Failed to allocate Global Ref for Hearing Aid Callbacks";
|
return;
|
}
|
|
sHearingAidInterface = (HearingAidInterface*)btInf->get_profile_interface(
|
BT_PROFILE_HEARING_AID_ID);
|
if (sHearingAidInterface == nullptr) {
|
LOG(ERROR) << "Failed to get Bluetooth Hearing Aid Interface";
|
return;
|
}
|
|
sHearingAidInterface->Init(&sHearingAidCallbacks);
|
}
|
|
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) {
|
LOG(ERROR) << "Bluetooth module is not loaded";
|
return;
|
}
|
|
if (sHearingAidInterface != nullptr) {
|
sHearingAidInterface->Cleanup();
|
sHearingAidInterface = nullptr;
|
}
|
|
if (mCallbacksObj != nullptr) {
|
env->DeleteGlobalRef(mCallbacksObj);
|
mCallbacksObj = nullptr;
|
}
|
}
|
|
static jboolean connectHearingAidNative(JNIEnv* env, jobject object,
|
jbyteArray address) {
|
LOG(INFO) << __func__;
|
std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
|
if (!sHearingAidInterface) return JNI_FALSE;
|
|
jbyte* addr = env->GetByteArrayElements(address, nullptr);
|
if (!addr) {
|
jniThrowIOException(env, EINVAL);
|
return JNI_FALSE;
|
}
|
|
RawAddress* tmpraw = (RawAddress*)addr;
|
sHearingAidInterface->Connect(*tmpraw);
|
env->ReleaseByteArrayElements(address, addr, 0);
|
return JNI_TRUE;
|
}
|
|
static jboolean disconnectHearingAidNative(JNIEnv* env, jobject object,
|
jbyteArray address) {
|
LOG(INFO) << __func__;
|
std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
|
if (!sHearingAidInterface) return JNI_FALSE;
|
|
jbyte* addr = env->GetByteArrayElements(address, nullptr);
|
if (!addr) {
|
jniThrowIOException(env, EINVAL);
|
return JNI_FALSE;
|
}
|
|
RawAddress* tmpraw = (RawAddress*)addr;
|
sHearingAidInterface->Disconnect(*tmpraw);
|
env->ReleaseByteArrayElements(address, addr, 0);
|
return JNI_TRUE;
|
}
|
|
static jboolean addToWhiteListNative(JNIEnv* env, jobject object,
|
jbyteArray address) {
|
std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
|
if (!sHearingAidInterface) return JNI_FALSE;
|
jbyte* addr = env->GetByteArrayElements(address, nullptr);
|
if (!addr) {
|
jniThrowIOException(env, EINVAL);
|
return JNI_FALSE;
|
}
|
|
RawAddress* tmpraw = (RawAddress*)addr;
|
sHearingAidInterface->AddToWhiteList(*tmpraw);
|
env->ReleaseByteArrayElements(address, addr, 0);
|
return JNI_TRUE;
|
}
|
|
static void setVolumeNative(JNIEnv* env, jclass clazz, jint volume) {
|
if (!sHearingAidInterface) {
|
LOG(ERROR) << __func__
|
<< ": Failed to get the Bluetooth Hearing Aid Interface";
|
return;
|
}
|
sHearingAidInterface->SetVolume(volume);
|
}
|
|
static JNINativeMethod sMethods[] = {
|
{"classInitNative", "()V", (void*)classInitNative},
|
{"initNative", "()V", (void*)initNative},
|
{"cleanupNative", "()V", (void*)cleanupNative},
|
{"connectHearingAidNative", "([B)Z", (void*)connectHearingAidNative},
|
{"disconnectHearingAidNative", "([B)Z", (void*)disconnectHearingAidNative},
|
{"addToWhiteListNative", "([B)Z", (void*)addToWhiteListNative},
|
{"setVolumeNative", "(I)V", (void*)setVolumeNative},
|
};
|
|
int register_com_android_bluetooth_hearing_aid(JNIEnv* env) {
|
return jniRegisterNativeMethods(
|
env, "com/android/bluetooth/hearingaid/HearingAidNativeInterface",
|
sMethods, NELEM(sMethods));
|
}
|
} // namespace android
|