/* * Copyright (C) 2012 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 "BluetoothHeadsetServiceJni" #define LOG_NDEBUG 0 #include "com_android_bluetooth.h" #include "hardware/bluetooth_headset_callbacks.h" #include "hardware/bluetooth_headset_interface.h" #include "hardware/bt_hf.h" #include "utils/Log.h" #include #include namespace android { static jmethodID method_onConnectionStateChanged; static jmethodID method_onAudioStateChanged; static jmethodID method_onVrStateChanged; static jmethodID method_onAnswerCall; static jmethodID method_onHangupCall; static jmethodID method_onVolumeChanged; static jmethodID method_onDialCall; static jmethodID method_onSendDtmf; static jmethodID method_onNoiseReductionEnable; static jmethodID method_onWBS; static jmethodID method_onAtChld; static jmethodID method_onAtCnum; static jmethodID method_onAtCind; static jmethodID method_onAtCops; static jmethodID method_onAtClcc; static jmethodID method_onUnknownAt; static jmethodID method_onKeyPressed; static jmethodID method_onAtBind; static jmethodID method_onAtBiev; static jmethodID method_onAtBia; static bluetooth::headset::Interface* sBluetoothHfpInterface = nullptr; static std::shared_timed_mutex interface_mutex; static jobject mCallbacksObj = nullptr; static std::shared_timed_mutex callbacks_mutex; static jbyteArray marshall_bda(RawAddress* bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return nullptr; jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress)); if (!addr) { ALOGE("Fail to new jbyteArray bd addr"); return nullptr; } sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress), (jbyte*)bd_addr); return addr; } class JniHeadsetCallbacks : bluetooth::headset::Callbacks { public: static bluetooth::headset::Callbacks* GetInstance() { static bluetooth::headset::Callbacks* instance = new JniHeadsetCallbacks(); return instance; } void ConnectionStateCallback( bluetooth::headset::bthf_connection_state_t state, RawAddress* bd_addr) override { ALOGI("%s %d for %s", __func__, state, bd_addr->ToString().c_str()); std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) return; sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint)state, addr.get()); } void AudioStateCallback(bluetooth::headset::bthf_audio_state_t state, RawAddress* bd_addr) override { ALOGI("%s, %d for %s", __func__, state, bd_addr->ToString().c_str()); std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) return; sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint)state, addr.get()); } void VoiceRecognitionCallback(bluetooth::headset::bthf_vr_state_t state, RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVrStateChanged, (jint)state, addr.get()); } void AnswerCallCallback(RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAnswerCall, addr.get()); } void HangupCallCallback(RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onHangupCall, addr.get()); } void VolumeControlCallback(bluetooth::headset::bthf_volume_type_t type, int volume, RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVolumeChanged, (jint)type, (jint)volume, addr.get()); } void DialCallCallback(char* number, RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } char null_str[] = ""; if (!sCallbackEnv.isValidUtf(number)) { android_errorWriteLog(0x534e4554, "109838537"); ALOGE("%s: number is not a valid UTF string.", __func__); number = null_str; } ScopedLocalRef js_number(sCallbackEnv.get(), sCallbackEnv->NewStringUTF(number)); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDialCall, js_number.get(), addr.get()); } void DtmfCmdCallback(char dtmf, RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } // TBD dtmf has changed from int to char sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSendDtmf, dtmf, addr.get()); } void NoiseReductionCallback(bluetooth::headset::bthf_nrec_t nrec, RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNoiseReductionEnable, nrec == bluetooth::headset::BTHF_NREC_START, addr.get()); } void WbsCallback(bluetooth::headset::bthf_wbs_config_t wbs_config, RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (addr.get() == nullptr) return; sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onWBS, wbs_config, addr.get()); } void AtChldCallback(bluetooth::headset::bthf_chld_type_t chld, RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtChld, chld, addr.get()); } void AtCnumCallback(RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtCnum, addr.get()); } void AtCindCallback(RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtCind, addr.get()); } void AtCopsCallback(RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtCops, addr.get()); } void AtClccCallback(RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtClcc, addr.get()); } void UnknownAtCallback(char* at_string, RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } char null_str[] = ""; if (!sCallbackEnv.isValidUtf(at_string)) { android_errorWriteLog(0x534e4554, "109838537"); ALOGE("%s: at_string is not a valid UTF string.", __func__); at_string = null_str; } ScopedLocalRef js_at_string(sCallbackEnv.get(), sCallbackEnv->NewStringUTF(at_string)); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onUnknownAt, js_at_string.get(), addr.get()); } void KeyPressedCallback(RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for audio state"); return; } sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onKeyPressed, addr.get()); } void AtBindCallback(char* at_string, RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (addr.get() == nullptr) return; char null_str[] = ""; if (!sCallbackEnv.isValidUtf(at_string)) { android_errorWriteLog(0x534e4554, "109838537"); ALOGE("%s: at_string is not a valid UTF string.", __func__); at_string = null_str; } ScopedLocalRef js_at_string(sCallbackEnv.get(), sCallbackEnv->NewStringUTF(at_string)); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBind, js_at_string.get(), addr.get()); } void AtBievCallback(bluetooth::headset::bthf_hf_ind_type_t ind_id, int ind_value, RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (addr.get() == nullptr) return; sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBiev, ind_id, (jint)ind_value, addr.get()); } void AtBiaCallback(bool service, bool roam, bool signal, bool battery, RawAddress* bd_addr) override { std::shared_lock lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || !mCallbacksObj) return; ScopedLocalRef addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (addr.get() == nullptr) return; sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBia, service, roam, signal, battery, addr.get()); } }; static void classInitNative(JNIEnv* env, jclass clazz) { method_onConnectionStateChanged = env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); method_onAudioStateChanged = env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V"); method_onVrStateChanged = env->GetMethodID(clazz, "onVrStateChanged", "(I[B)V"); method_onAnswerCall = env->GetMethodID(clazz, "onAnswerCall", "([B)V"); method_onHangupCall = env->GetMethodID(clazz, "onHangupCall", "([B)V"); method_onVolumeChanged = env->GetMethodID(clazz, "onVolumeChanged", "(II[B)V"); method_onDialCall = env->GetMethodID(clazz, "onDialCall", "(Ljava/lang/String;[B)V"); method_onSendDtmf = env->GetMethodID(clazz, "onSendDtmf", "(I[B)V"); method_onNoiseReductionEnable = env->GetMethodID(clazz, "onNoiseReductionEnable", "(Z[B)V"); method_onWBS = env->GetMethodID(clazz, "onWBS", "(I[B)V"); method_onAtChld = env->GetMethodID(clazz, "onAtChld", "(I[B)V"); method_onAtCnum = env->GetMethodID(clazz, "onAtCnum", "([B)V"); method_onAtCind = env->GetMethodID(clazz, "onAtCind", "([B)V"); method_onAtCops = env->GetMethodID(clazz, "onAtCops", "([B)V"); method_onAtClcc = env->GetMethodID(clazz, "onAtClcc", "([B)V"); method_onUnknownAt = env->GetMethodID(clazz, "onUnknownAt", "(Ljava/lang/String;[B)V"); method_onKeyPressed = env->GetMethodID(clazz, "onKeyPressed", "([B)V"); method_onAtBind = env->GetMethodID(clazz, "onATBind", "(Ljava/lang/String;[B)V"); method_onAtBiev = env->GetMethodID(clazz, "onATBiev", "(II[B)V"); method_onAtBia = env->GetMethodID(clazz, "onAtBia", "(ZZZZ[B)V"); ALOGI("%s: succeeds", __func__); } static void initializeNative(JNIEnv* env, jobject object, jint max_hf_clients, jboolean inband_ringing_enabled) { std::unique_lock interface_lock(interface_mutex); std::unique_lock callbacks_lock(callbacks_mutex); const bt_interface_t* btInf = getBluetoothInterface(); if (!btInf) { ALOGE("%s: Bluetooth module is not loaded", __func__); jniThrowIOException(env, EINVAL); return; } if (sBluetoothHfpInterface) { ALOGI("%s: Cleaning up Bluetooth Handsfree Interface before initializing", __func__); sBluetoothHfpInterface->Cleanup(); sBluetoothHfpInterface = nullptr; } if (mCallbacksObj) { ALOGI("%s: Cleaning up Bluetooth Handsfree callback object", __func__); env->DeleteGlobalRef(mCallbacksObj); mCallbacksObj = nullptr; } sBluetoothHfpInterface = (bluetooth::headset::Interface*)btInf->get_profile_interface( BT_PROFILE_HANDSFREE_ID); if (!sBluetoothHfpInterface) { ALOGW("%s: Failed to get Bluetooth Handsfree Interface", __func__); jniThrowIOException(env, EINVAL); return; } bt_status_t status = sBluetoothHfpInterface->Init(JniHeadsetCallbacks::GetInstance(), max_hf_clients, inband_ringing_enabled); if (status != BT_STATUS_SUCCESS) { ALOGE("%s: Failed to initialize Bluetooth Handsfree Interface, status: %d", __func__, status); sBluetoothHfpInterface = nullptr; return; } mCallbacksObj = env->NewGlobalRef(object); } static void cleanupNative(JNIEnv* env, jobject object) { std::unique_lock interface_lock(interface_mutex); std::unique_lock callbacks_lock(callbacks_mutex); const bt_interface_t* btInf = getBluetoothInterface(); if (!btInf) { ALOGW("%s: Bluetooth module is not loaded", __func__); return; } if (sBluetoothHfpInterface) { ALOGI("%s: Cleaning up Bluetooth Handsfree Interface", __func__); sBluetoothHfpInterface->Cleanup(); sBluetoothHfpInterface = nullptr; } if (mCallbacksObj) { ALOGI("%s: Cleaning up Bluetooth Handsfree callback object", __func__); env->DeleteGlobalRef(mCallbacksObj); mCallbacksObj = nullptr; } } static jboolean connectHfpNative(JNIEnv* env, jobject object, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } ALOGI("%s: device %s", __func__, ((RawAddress*)addr)->ToString().c_str()); bt_status_t status = sBluetoothHfpInterface->Connect((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed HF connection, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean disconnectHfpNative(JNIEnv* env, jobject object, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } ALOGI("%s: device %s", __func__, ((RawAddress*)addr)->ToString().c_str()); bt_status_t status = sBluetoothHfpInterface->Disconnect((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed HF disconnection, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean connectAudioNative(JNIEnv* env, jobject object, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } ALOGI("%s: device %s", __func__, ((RawAddress*)addr)->ToString().c_str()); bt_status_t status = sBluetoothHfpInterface->ConnectAudio((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed HF audio connection, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean disconnectAudioNative(JNIEnv* env, jobject object, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } ALOGI("%s: device %s", __func__, ((RawAddress*)addr)->ToString().c_str()); bt_status_t status = sBluetoothHfpInterface->DisconnectAudio((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed HF audio disconnection, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean isNoiseReductionSupportedNative(JNIEnv* env, jobject object, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } bt_status_t status = sBluetoothHfpInterface->isNoiseReductionSupported((RawAddress*)addr); env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean isVoiceRecognitionSupportedNative(JNIEnv* env, jobject object, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } bt_status_t status = sBluetoothHfpInterface->isVoiceRecognitionSupported((RawAddress*)addr); env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean startVoiceRecognitionNative(JNIEnv* env, jobject object, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } bt_status_t status = sBluetoothHfpInterface->StartVoiceRecognition((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed to start voice recognition, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean stopVoiceRecognitionNative(JNIEnv* env, jobject object, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } bt_status_t status = sBluetoothHfpInterface->StopVoiceRecognition((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed to stop voice recognition, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean setVolumeNative(JNIEnv* env, jobject object, jint volume_type, jint volume, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } bt_status_t status = sBluetoothHfpInterface->VolumeControl( (bluetooth::headset::bthf_volume_type_t)volume_type, volume, (RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("FAILED to control volume, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean notifyDeviceStatusNative(JNIEnv* env, jobject object, jint network_state, jint service_type, jint signal, jint battery_charge, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } bt_status_t status = sBluetoothHfpInterface->DeviceStatusNotification( (bluetooth::headset::bthf_network_state_t)network_state, (bluetooth::headset::bthf_service_type_t)service_type, signal, battery_charge, (RawAddress*)addr); env->ReleaseByteArrayElements(address, addr, 0); if (status != BT_STATUS_SUCCESS) { ALOGE("FAILED to notify device status, status: %d", status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean copsResponseNative(JNIEnv* env, jobject object, jstring operator_str, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } const char* operator_name = env->GetStringUTFChars(operator_str, nullptr); bt_status_t status = sBluetoothHfpInterface->CopsResponse(operator_name, (RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending cops response, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); env->ReleaseStringUTFChars(operator_str, operator_name); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean cindResponseNative(JNIEnv* env, jobject object, jint service, jint num_active, jint num_held, jint call_state, jint signal, jint roam, jint battery_charge, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } bt_status_t status = sBluetoothHfpInterface->CindResponse( service, num_active, num_held, (bluetooth::headset::bthf_call_state_t)call_state, signal, roam, battery_charge, (RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("%s: failed, status: %d", __func__, status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean atResponseStringNative(JNIEnv* env, jobject object, jstring response_str, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } const char* response = env->GetStringUTFChars(response_str, nullptr); bt_status_t status = sBluetoothHfpInterface->FormattedAtResponse(response, (RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed formatted AT response, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); env->ReleaseStringUTFChars(response_str, response); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean atResponseCodeNative(JNIEnv* env, jobject object, jint response_code, jint cmee_code, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } bt_status_t status = sBluetoothHfpInterface->AtResponse( (bluetooth::headset::bthf_at_response_t)response_code, cmee_code, (RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed AT response, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean clccResponseNative(JNIEnv* env, jobject object, jint index, jint dir, jint callStatus, jint mode, jboolean mpty, jstring number_str, jint type, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } const char* number = nullptr; if (number_str) { number = env->GetStringUTFChars(number_str, nullptr); } bt_status_t status = sBluetoothHfpInterface->ClccResponse( index, (bluetooth::headset::bthf_call_direction_t)dir, (bluetooth::headset::bthf_call_state_t)callStatus, (bluetooth::headset::bthf_call_mode_t)mode, mpty ? bluetooth::headset::BTHF_CALL_MPTY_TYPE_MULTI : bluetooth::headset::BTHF_CALL_MPTY_TYPE_SINGLE, number, (bluetooth::headset::bthf_call_addrtype_t)type, (RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending CLCC response, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); if (number) { env->ReleaseStringUTFChars(number_str, number); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean phoneStateChangeNative(JNIEnv* env, jobject object, jint num_active, jint num_held, jint call_state, jstring number_str, jint type, jstring name_str, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, nullptr); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } const char* number = env->GetStringUTFChars(number_str, nullptr); const char* name = nullptr; if (name_str != nullptr) { name = env->GetStringUTFChars(name_str, nullptr); } bt_status_t status = sBluetoothHfpInterface->PhoneStateChange( num_active, num_held, (bluetooth::headset::bthf_call_state_t)call_state, number, (bluetooth::headset::bthf_call_addrtype_t)type, name, (RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed report phone state change, status: %d", status); } env->ReleaseStringUTFChars(number_str, number); if (name != nullptr) { env->ReleaseStringUTFChars(name_str, name); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean setScoAllowedNative(JNIEnv* env, jobject object, jboolean value) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } bt_status_t status = sBluetoothHfpInterface->SetScoAllowed(value == JNI_TRUE); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed HF set sco allowed, status: %d", status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean sendBsirNative(JNIEnv* env, jobject object, jboolean value, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } bt_status_t status = sBluetoothHfpInterface->SendBsir(value == JNI_TRUE, (RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending BSIR, value=%d, status=%d", value, status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean setActiveDeviceNative(JNIEnv* env, jobject object, jbyteArray address) { std::shared_lock lock(interface_mutex); if (!sBluetoothHfpInterface) { ALOGW("%s: sBluetoothHfpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("%s: failed to get device address", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } bt_status_t status = sBluetoothHfpInterface->SetActiveDevice((RawAddress*)addr); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed to set active device, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static JNINativeMethod sMethods[] = { {"classInitNative", "()V", (void*)classInitNative}, {"initializeNative", "(IZ)V", (void*)initializeNative}, {"cleanupNative", "()V", (void*)cleanupNative}, {"connectHfpNative", "([B)Z", (void*)connectHfpNative}, {"disconnectHfpNative", "([B)Z", (void*)disconnectHfpNative}, {"connectAudioNative", "([B)Z", (void*)connectAudioNative}, {"disconnectAudioNative", "([B)Z", (void*)disconnectAudioNative}, {"isNoiseReductionSupportedNative", "([B)Z", (void*)isNoiseReductionSupportedNative}, {"isVoiceRecognitionSupportedNative", "([B)Z", (void*)isVoiceRecognitionSupportedNative}, {"startVoiceRecognitionNative", "([B)Z", (void*)startVoiceRecognitionNative}, {"stopVoiceRecognitionNative", "([B)Z", (void*)stopVoiceRecognitionNative}, {"setVolumeNative", "(II[B)Z", (void*)setVolumeNative}, {"notifyDeviceStatusNative", "(IIII[B)Z", (void*)notifyDeviceStatusNative}, {"copsResponseNative", "(Ljava/lang/String;[B)Z", (void*)copsResponseNative}, {"cindResponseNative", "(IIIIIII[B)Z", (void*)cindResponseNative}, {"atResponseStringNative", "(Ljava/lang/String;[B)Z", (void*)atResponseStringNative}, {"atResponseCodeNative", "(II[B)Z", (void*)atResponseCodeNative}, {"clccResponseNative", "(IIIIZLjava/lang/String;I[B)Z", (void*)clccResponseNative}, {"phoneStateChangeNative", "(IIILjava/lang/String;ILjava/lang/String;[B)Z", (void*)phoneStateChangeNative}, {"setScoAllowedNative", "(Z)Z", (void*)setScoAllowedNative}, {"sendBsirNative", "(Z[B)Z", (void*)sendBsirNative}, {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative}, }; int register_com_android_bluetooth_hfp(JNIEnv* env) { return jniRegisterNativeMethods( env, "com/android/bluetooth/hfp/HeadsetNativeInterface", sMethods, NELEM(sMethods)); } } /* namespace android */