Loading android/app/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ cc_library_shared { "com_android_bluetooth_avrcp_controller.cpp", "com_android_bluetooth_hid_host.cpp", "com_android_bluetooth_hid_device.cpp", "com_android_bluetooth_hearing_aid.cpp", "com_android_bluetooth_hdp.cpp", "com_android_bluetooth_pan.cpp", "com_android_bluetooth_gatt.cpp", Loading android/app/jni/com_android_bluetooth.h +1 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ int register_com_android_bluetooth_gatt (JNIEnv* env); int register_com_android_bluetooth_sdp (JNIEnv* env); int register_com_android_bluetooth_hearing_aid(JNIEnv* env); } #endif /* COM_ANDROID_BLUETOOTH_H */ android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -1354,5 +1354,11 @@ jint JNI_OnLoad(JavaVM* jvm, void* reserved) { return JNI_ERR; } status = android::register_com_android_bluetooth_hearing_aid(e); if (status < 0) { ALOGE("jni hearing aid registration failure: %d", status); return JNI_ERR; } return JNI_VERSION_1_6; } android/app/jni/com_android_bluetooth_hearing_aid.cpp 0 → 100644 +220 −0 Original line number Diff line number Diff line /* * 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 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}, {"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 android/app/src/com/android/bluetooth/btservice/PhonePolicy.java +10 −1 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.hearingaid.HearingAidService; import com.android.bluetooth.hfp.HeadsetService; import com.android.bluetooth.hid.HidHostService; import com.android.bluetooth.pan.PanService; Loading Loading @@ -211,6 +212,7 @@ class PhonePolicy { A2dpService a2dpService = mFactory.getA2dpService(); HeadsetService headsetService = mFactory.getHeadsetService(); PanService panService = mFactory.getPanService(); HearingAidService hearingAidService = mFactory.getHearingAidService(); // Set profile priorities only for the profiles discovered on the remote device. // This avoids needless auto-connect attempts to profiles non-existent on the remote device Loading Loading @@ -239,6 +241,13 @@ class PhonePolicy { .getBoolean(R.bool.config_bluetooth_pan_enable_autoconnect))) { panService.setPriority(device, BluetoothProfile.PRIORITY_ON); } if ((hearingAidService != null) && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid) && (hearingAidService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)) { debugLog("setting hearing aid profile priority for device " + device); hearingAidService.setPriority(device, BluetoothProfile.PRIORITY_ON); } } private void processProfileStateChanged(BluetoothDevice device, int profileId, int nextState, Loading Loading @@ -463,7 +472,7 @@ class PhonePolicy { private static void debugLog(String msg) { if (DBG) { Log.d(TAG, msg); Log.i(TAG, msg); } } Loading Loading
android/app/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ cc_library_shared { "com_android_bluetooth_avrcp_controller.cpp", "com_android_bluetooth_hid_host.cpp", "com_android_bluetooth_hid_device.cpp", "com_android_bluetooth_hearing_aid.cpp", "com_android_bluetooth_hdp.cpp", "com_android_bluetooth_pan.cpp", "com_android_bluetooth_gatt.cpp", Loading
android/app/jni/com_android_bluetooth.h +1 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ int register_com_android_bluetooth_gatt (JNIEnv* env); int register_com_android_bluetooth_sdp (JNIEnv* env); int register_com_android_bluetooth_hearing_aid(JNIEnv* env); } #endif /* COM_ANDROID_BLUETOOTH_H */
android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -1354,5 +1354,11 @@ jint JNI_OnLoad(JavaVM* jvm, void* reserved) { return JNI_ERR; } status = android::register_com_android_bluetooth_hearing_aid(e); if (status < 0) { ALOGE("jni hearing aid registration failure: %d", status); return JNI_ERR; } return JNI_VERSION_1_6; }
android/app/jni/com_android_bluetooth_hearing_aid.cpp 0 → 100644 +220 −0 Original line number Diff line number Diff line /* * 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 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}, {"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
android/app/src/com/android/bluetooth/btservice/PhonePolicy.java +10 −1 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.hearingaid.HearingAidService; import com.android.bluetooth.hfp.HeadsetService; import com.android.bluetooth.hid.HidHostService; import com.android.bluetooth.pan.PanService; Loading Loading @@ -211,6 +212,7 @@ class PhonePolicy { A2dpService a2dpService = mFactory.getA2dpService(); HeadsetService headsetService = mFactory.getHeadsetService(); PanService panService = mFactory.getPanService(); HearingAidService hearingAidService = mFactory.getHearingAidService(); // Set profile priorities only for the profiles discovered on the remote device. // This avoids needless auto-connect attempts to profiles non-existent on the remote device Loading Loading @@ -239,6 +241,13 @@ class PhonePolicy { .getBoolean(R.bool.config_bluetooth_pan_enable_autoconnect))) { panService.setPriority(device, BluetoothProfile.PRIORITY_ON); } if ((hearingAidService != null) && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid) && (hearingAidService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)) { debugLog("setting hearing aid profile priority for device " + device); hearingAidService.setPriority(device, BluetoothProfile.PRIORITY_ON); } } private void processProfileStateChanged(BluetoothDevice device, int profileId, int nextState, Loading Loading @@ -463,7 +472,7 @@ class PhonePolicy { private static void debugLog(String msg) { if (DBG) { Log.d(TAG, msg); Log.i(TAG, msg); } } Loading