| /****************************************************************************** |
| * |
| * 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)); |
| } |
| } |