| /* |
| * 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 "android_runtime/AndroidRuntime.h" |
| #include "com_android_bluetooth.h" |
| #include "hardware/bt_hf.h" |
| #include "utils/Log.h" |
| |
| #include <string.h> |
| |
| 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_onNoiceReductionEnable; |
| 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 const bthf_interface_t* sBluetoothHfpInterface = NULL; |
| static jobject mCallbacksObj = NULL; |
| |
| static jbyteArray marshall_bda(bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return NULL; |
| |
| jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr"); |
| return NULL; |
| } |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), |
| (jbyte*)bd_addr); |
| return addr; |
| } |
| |
| static void connection_state_callback(bthf_connection_state_t state, |
| bt_bdaddr_t* bd_addr) { |
| ALOGI("%s", __func__); |
| |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); |
| if (!addr.get()) return; |
| |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, |
| (jint)state, addr.get()); |
| } |
| |
| static void audio_state_callback(bthf_audio_state_t state, |
| bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); |
| if (!addr.get()) return; |
| |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, |
| (jint)state, addr.get()); |
| } |
| |
| static void voice_recognition_callback(bthf_vr_state_t state, |
| bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> 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()); |
| } |
| |
| static void answer_call_callback(bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> 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()); |
| } |
| |
| static void hangup_call_callback(bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> 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()); |
| } |
| |
| static void volume_control_callback(bthf_volume_type_t type, int volume, |
| bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> 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()); |
| } |
| |
| static void dial_call_callback(char* number, bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); |
| if (!addr.get()) { |
| ALOGE("Fail to new jbyteArray bd addr for audio state"); |
| return; |
| } |
| |
| ScopedLocalRef<jstring> js_number(sCallbackEnv.get(), |
| sCallbackEnv->NewStringUTF(number)); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDialCall, |
| js_number.get(), addr.get()); |
| } |
| |
| static void dtmf_cmd_callback(char dtmf, bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> 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()); |
| } |
| |
| static void noice_reduction_callback(bthf_nrec_t nrec, bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> 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_onNoiceReductionEnable, |
| nrec == BTHF_NREC_START, addr.get()); |
| } |
| |
| static void wbs_callback(bthf_wbs_config_t wbs_config, bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| |
| ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); |
| if (addr.get() == NULL) return; |
| |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onWBS, wbs_config, |
| addr.get()); |
| } |
| |
| static void at_chld_callback(bthf_chld_type_t chld, bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> addr( |
| sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t))); |
| if (!addr.get()) { |
| ALOGE("Fail to new jbyteArray bd addr for audio state"); |
| return; |
| } |
| |
| sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t), |
| (jbyte*)bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtChld, chld, |
| addr.get()); |
| } |
| |
| static void at_cnum_callback(bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> 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()); |
| } |
| |
| static void at_cind_callback(bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> 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()); |
| } |
| |
| static void at_cops_callback(bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> 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()); |
| } |
| |
| static void at_clcc_callback(bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> 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()); |
| } |
| |
| static void unknown_at_callback(char* at_string, bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); |
| if (!addr.get()) { |
| ALOGE("Fail to new jbyteArray bd addr for audio state"); |
| return; |
| } |
| |
| ScopedLocalRef<jstring> js_at_string(sCallbackEnv.get(), |
| sCallbackEnv->NewStringUTF(at_string)); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onUnknownAt, |
| js_at_string.get(), addr.get()); |
| } |
| |
| static void key_pressed_callback(bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| ScopedLocalRef<jbyteArray> 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()); |
| } |
| |
| static void at_bind_callback(char* at_string, bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| |
| ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); |
| if (addr.get() == NULL) return; |
| |
| ScopedLocalRef<jstring> js_at_string(sCallbackEnv.get(), |
| sCallbackEnv->NewStringUTF(at_string)); |
| |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBind, |
| js_at_string.get(), addr.get()); |
| } |
| |
| static void at_biev_callback(bthf_hf_ind_type_t ind_id, int ind_value, |
| bt_bdaddr_t* bd_addr) { |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbackEnv.valid()) return; |
| |
| ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); |
| if (addr.get() == NULL) return; |
| |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBiev, ind_id, |
| (jint)ind_value, addr.get()); |
| } |
| |
| static bthf_callbacks_t sBluetoothHfpCallbacks = { |
| sizeof(sBluetoothHfpCallbacks), |
| connection_state_callback, |
| audio_state_callback, |
| voice_recognition_callback, |
| answer_call_callback, |
| hangup_call_callback, |
| volume_control_callback, |
| dial_call_callback, |
| dtmf_cmd_callback, |
| noice_reduction_callback, |
| wbs_callback, |
| at_chld_callback, |
| at_cnum_callback, |
| at_cind_callback, |
| at_cops_callback, |
| at_clcc_callback, |
| unknown_at_callback, |
| at_bind_callback, |
| at_biev_callback, |
| key_pressed_callback}; |
| |
| 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_onNoiceReductionEnable = |
| env->GetMethodID(clazz, "onNoiceReductionEnable", "(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"); |
| |
| ALOGI("%s: succeeds", __func__); |
| } |
| |
| static void initializeNative(JNIEnv* env, jobject object, jint max_hf_clients, |
| jboolean inband_ringing_support) { |
| const bt_interface_t* btInf = getBluetoothInterface(); |
| if (btInf == NULL) { |
| ALOGE("Bluetooth module is not loaded"); |
| return; |
| } |
| |
| if (sBluetoothHfpInterface != NULL) { |
| ALOGW("Cleaning up Bluetooth Handsfree Interface before initializing..."); |
| sBluetoothHfpInterface->cleanup(); |
| sBluetoothHfpInterface = NULL; |
| } |
| |
| if (mCallbacksObj != NULL) { |
| ALOGW("Cleaning up Bluetooth Handsfree callback object"); |
| env->DeleteGlobalRef(mCallbacksObj); |
| mCallbacksObj = NULL; |
| } |
| |
| sBluetoothHfpInterface = |
| (bthf_interface_t*)btInf->get_profile_interface(BT_PROFILE_HANDSFREE_ID); |
| if (sBluetoothHfpInterface == NULL) { |
| ALOGE("Failed to get Bluetooth Handsfree Interface"); |
| return; |
| } |
| |
| bt_status_t status = sBluetoothHfpInterface->init( |
| &sBluetoothHfpCallbacks, max_hf_clients, inband_ringing_support); |
| if (status != BT_STATUS_SUCCESS) { |
| ALOGE("Failed to initialize Bluetooth HFP, status: %d", status); |
| sBluetoothHfpInterface = NULL; |
| return; |
| } |
| |
| mCallbacksObj = env->NewGlobalRef(object); |
| } |
| |
| static void cleanupNative(JNIEnv* env, jobject object) { |
| const bt_interface_t* btInf = getBluetoothInterface(); |
| |
| if (btInf == NULL) { |
| ALOGE("Bluetooth module is not loaded"); |
| return; |
| } |
| |
| if (sBluetoothHfpInterface != NULL) { |
| ALOGW("Cleaning up Bluetooth Handsfree Interface..."); |
| sBluetoothHfpInterface->cleanup(); |
| sBluetoothHfpInterface = NULL; |
| } |
| |
| if (mCallbacksObj != NULL) { |
| ALOGW("Cleaning up Bluetooth Handsfree callback object"); |
| env->DeleteGlobalRef(mCallbacksObj); |
| mCallbacksObj = NULL; |
| } |
| } |
| |
| static jboolean connectHfpNative(JNIEnv* env, jobject object, |
| jbyteArray address) { |
| ALOGI("%s: sBluetoothHfpInterface: %p", __func__, sBluetoothHfpInterface); |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| bt_status_t status = sBluetoothHfpInterface->connect((bt_bdaddr_t*)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) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| bt_status_t status = sBluetoothHfpInterface->disconnect((bt_bdaddr_t*)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) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| bt_status_t status = |
| sBluetoothHfpInterface->connect_audio((bt_bdaddr_t*)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) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| bt_status_t status = |
| sBluetoothHfpInterface->disconnect_audio((bt_bdaddr_t*)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 startVoiceRecognitionNative(JNIEnv* env, jobject object, |
| jbyteArray address) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| bt_status_t status = |
| sBluetoothHfpInterface->start_voice_recognition((bt_bdaddr_t*)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) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| bt_status_t status = |
| sBluetoothHfpInterface->stop_voice_recognition((bt_bdaddr_t*)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) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| bt_status_t status = sBluetoothHfpInterface->volume_control( |
| (bthf_volume_type_t)volume_type, volume, (bt_bdaddr_t*)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) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| bt_status_t status = sBluetoothHfpInterface->device_status_notification( |
| (bthf_network_state_t)network_state, (bthf_service_type_t)service_type, |
| signal, battery_charge); |
| 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) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| const char* operator_name = env->GetStringUTFChars(operator_str, NULL); |
| |
| bt_status_t status = |
| sBluetoothHfpInterface->cops_response(operator_name, (bt_bdaddr_t*)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) { |
| ALOGI("%s: sBluetoothHfpInterface: %p", __func__, sBluetoothHfpInterface); |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| bt_status_t status = sBluetoothHfpInterface->cind_response( |
| service, num_active, num_held, (bthf_call_state_t)call_state, signal, |
| roam, battery_charge, (bt_bdaddr_t*)addr); |
| if (status != BT_STATUS_SUCCESS) { |
| ALOGE("Failed cind_response, status: %d", status); |
| } |
| env->ReleaseByteArrayElements(address, addr, 0); |
| return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| static jboolean bindResponseNative(JNIEnv* env, jobject object, jint ind_id, |
| jboolean ind_status, jbyteArray address) { |
| ALOGI("%s: sBluetoothHfpInterface: %p", __func__, sBluetoothHfpInterface); |
| |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| bt_status_t status = sBluetoothHfpInterface->bind_response( |
| (bthf_hf_ind_type_t)ind_id, |
| ind_status ? BTHF_HF_IND_ENABLED : BTHF_HF_IND_DISABLED, |
| (bt_bdaddr_t*)addr); |
| |
| if (status != BT_STATUS_SUCCESS) |
| ALOGE("%s: Failed bind_response, 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) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| const char* response = env->GetStringUTFChars(response_str, NULL); |
| |
| bt_status_t status = sBluetoothHfpInterface->formatted_at_response( |
| response, (bt_bdaddr_t*)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) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| bt_status_t status = sBluetoothHfpInterface->at_response( |
| (bthf_at_response_t)response_code, cmee_code, (bt_bdaddr_t*)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) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| const char* number = NULL; |
| if (number_str) number = env->GetStringUTFChars(number_str, NULL); |
| |
| bt_status_t status = sBluetoothHfpInterface->clcc_response( |
| index, (bthf_call_direction_t)dir, (bthf_call_state_t)callStatus, |
| (bthf_call_mode_t)mode, |
| mpty ? BTHF_CALL_MPTY_TYPE_MULTI : BTHF_CALL_MPTY_TYPE_SINGLE, number, |
| (bthf_call_addrtype_t)type, (bt_bdaddr_t*)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) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| const char* number = env->GetStringUTFChars(number_str, NULL); |
| |
| bt_status_t status = sBluetoothHfpInterface->phone_state_change( |
| num_active, num_held, (bthf_call_state_t)call_state, number, |
| (bthf_call_addrtype_t)type); |
| if (status != BT_STATUS_SUCCESS) { |
| ALOGE("Failed report phone state change, status: %d", status); |
| } |
| env->ReleaseStringUTFChars(number_str, number); |
| return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| static jboolean configureWBSNative(JNIEnv* env, jobject object, |
| jbyteArray address, jint codec_config) { |
| if (!sBluetoothHfpInterface) return JNI_FALSE; |
| |
| jbyte* addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| bt_status_t status = sBluetoothHfpInterface->configure_wbs( |
| (bt_bdaddr_t*)addr, (bthf_wbs_config_t)codec_config); |
| if (status != BT_STATUS_SUCCESS) { |
| ALOGE("Failed HF WBS codec config, 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}, |
| {"startVoiceRecognitionNative", "([B)Z", |
| (void*)startVoiceRecognitionNative}, |
| {"stopVoiceRecognitionNative", "([B)Z", (void*)stopVoiceRecognitionNative}, |
| {"setVolumeNative", "(II[B)Z", (void*)setVolumeNative}, |
| {"notifyDeviceStatusNative", "(IIII)Z", (void*)notifyDeviceStatusNative}, |
| {"copsResponseNative", "(Ljava/lang/String;[B)Z", |
| (void*)copsResponseNative}, |
| {"cindResponseNative", "(IIIIIII[B)Z", (void*)cindResponseNative}, |
| {"bindResponseNative", "(IZ[B)Z", (void*)bindResponseNative}, |
| {"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;I)Z", |
| (void*)phoneStateChangeNative}, |
| {"configureWBSNative", "([BI)Z", (void*)configureWBSNative}, |
| }; |
| |
| int register_com_android_bluetooth_hfp(JNIEnv* env) { |
| return jniRegisterNativeMethods( |
| env, "com/android/bluetooth/hfp/HeadsetStateMachine", sMethods, |
| NELEM(sMethods)); |
| } |
| |
| } /* namespace android */ |