1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "BluetoothHearingAidServiceJni"
18
19 #include <bluetooth/log.h>
20 #include <jni.h>
21 #include <nativehelper/JNIHelp.h>
22 #include <nativehelper/scoped_local_ref.h>
23
24 #include <cerrno>
25 #include <cstdint>
26 #include <cstring>
27 #include <mutex>
28 #include <shared_mutex>
29
30 #include "com_android_bluetooth.h"
31 #include "hardware/bluetooth.h"
32 #include "hardware/bt_hearing_aid.h"
33 #include "types/raw_address.h"
34
35 using bluetooth::hearing_aid::ConnectionState;
36 using bluetooth::hearing_aid::HearingAidCallbacks;
37 using bluetooth::hearing_aid::HearingAidInterface;
38
39 namespace android {
40 static jmethodID method_onConnectionStateChanged;
41 static jmethodID method_onDeviceAvailable;
42
43 static HearingAidInterface* sHearingAidInterface = nullptr;
44 static std::shared_timed_mutex interface_mutex;
45
46 static jobject mCallbacksObj = nullptr;
47 static std::shared_timed_mutex callbacks_mutex;
48
49 class HearingAidCallbacksImpl : public HearingAidCallbacks {
50 public:
51 ~HearingAidCallbacksImpl() = default;
OnConnectionState(ConnectionState state,const RawAddress & bd_addr)52 void OnConnectionState(ConnectionState state, const RawAddress& bd_addr) override {
53 log::info("");
54
55 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
56 CallbackEnv sCallbackEnv(__func__);
57 if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
58 return;
59 }
60
61 ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
62 sCallbackEnv->NewByteArray(sizeof(RawAddress)));
63 if (!addr.get()) {
64 log::error("Failed to new jbyteArray bd addr for connection state");
65 return;
66 }
67
68 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr);
69 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint)state,
70 addr.get());
71 }
72
OnDeviceAvailable(uint8_t capabilities,uint64_t hi_sync_id,const RawAddress & bd_addr)73 void OnDeviceAvailable(uint8_t capabilities, uint64_t hi_sync_id,
74 const RawAddress& bd_addr) override {
75 log::info("capabilities={} hi_sync_id={}", capabilities, hi_sync_id);
76
77 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
78 CallbackEnv sCallbackEnv(__func__);
79 if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
80 return;
81 }
82
83 ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
84 sCallbackEnv->NewByteArray(sizeof(RawAddress)));
85 if (!addr.get()) {
86 log::error("Failed to new jbyteArray bd addr for connection state");
87 return;
88 }
89
90 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr);
91 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDeviceAvailable, (jbyte)capabilities,
92 (jlong)hi_sync_id, addr.get());
93 }
94 };
95
96 static HearingAidCallbacksImpl sHearingAidCallbacks;
97
initNative(JNIEnv * env,jobject object)98 static void initNative(JNIEnv* env, jobject object) {
99 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
100 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
101
102 const bt_interface_t* btInf = getBluetoothInterface();
103 if (btInf == nullptr) {
104 log::error("Bluetooth module is not loaded");
105 return;
106 }
107
108 if (sHearingAidInterface != nullptr) {
109 log::info("Cleaning up HearingAid Interface before initializing...");
110 sHearingAidInterface->Cleanup();
111 sHearingAidInterface = nullptr;
112 }
113
114 if (mCallbacksObj != nullptr) {
115 log::info("Cleaning up HearingAid callback object");
116 env->DeleteGlobalRef(mCallbacksObj);
117 mCallbacksObj = nullptr;
118 }
119
120 if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
121 log::error("Failed to allocate Global Ref for Hearing Aid Callbacks");
122 return;
123 }
124
125 sHearingAidInterface =
126 (HearingAidInterface*)btInf->get_profile_interface(BT_PROFILE_HEARING_AID_ID);
127 if (sHearingAidInterface == nullptr) {
128 log::error("Failed to get Bluetooth Hearing Aid Interface");
129 return;
130 }
131
132 sHearingAidInterface->Init(&sHearingAidCallbacks);
133 }
134
cleanupNative(JNIEnv * env,jobject)135 static void cleanupNative(JNIEnv* env, jobject /* object */) {
136 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
137 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
138
139 const bt_interface_t* btInf = getBluetoothInterface();
140 if (btInf == nullptr) {
141 log::error("Bluetooth module is not loaded");
142 return;
143 }
144
145 if (sHearingAidInterface != nullptr) {
146 sHearingAidInterface->Cleanup();
147 sHearingAidInterface = nullptr;
148 }
149
150 if (mCallbacksObj != nullptr) {
151 env->DeleteGlobalRef(mCallbacksObj);
152 mCallbacksObj = nullptr;
153 }
154 }
155
connectHearingAidNative(JNIEnv * env,jobject,jbyteArray address)156 static jboolean connectHearingAidNative(JNIEnv* env, jobject /* object */, jbyteArray address) {
157 log::info("");
158 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
159 if (!sHearingAidInterface) {
160 return JNI_FALSE;
161 }
162
163 jbyte* addr = env->GetByteArrayElements(address, nullptr);
164 if (!addr) {
165 jniThrowIOException(env, EINVAL);
166 return JNI_FALSE;
167 }
168
169 RawAddress* tmpraw = (RawAddress*)addr;
170 sHearingAidInterface->Connect(*tmpraw);
171 env->ReleaseByteArrayElements(address, addr, 0);
172 return JNI_TRUE;
173 }
174
disconnectHearingAidNative(JNIEnv * env,jobject,jbyteArray address)175 static jboolean disconnectHearingAidNative(JNIEnv* env, jobject /* object */, jbyteArray address) {
176 log::info("");
177 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
178 if (!sHearingAidInterface) {
179 return JNI_FALSE;
180 }
181
182 jbyte* addr = env->GetByteArrayElements(address, nullptr);
183 if (!addr) {
184 jniThrowIOException(env, EINVAL);
185 return JNI_FALSE;
186 }
187
188 RawAddress* tmpraw = (RawAddress*)addr;
189 sHearingAidInterface->Disconnect(*tmpraw);
190 env->ReleaseByteArrayElements(address, addr, 0);
191 return JNI_TRUE;
192 }
193
addToAcceptlistNative(JNIEnv * env,jobject,jbyteArray address)194 static jboolean addToAcceptlistNative(JNIEnv* env, jobject /* object */, jbyteArray address) {
195 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
196 if (!sHearingAidInterface) {
197 return JNI_FALSE;
198 }
199 jbyte* addr = env->GetByteArrayElements(address, nullptr);
200 if (!addr) {
201 jniThrowIOException(env, EINVAL);
202 return JNI_FALSE;
203 }
204
205 RawAddress* tmpraw = (RawAddress*)addr;
206 sHearingAidInterface->AddToAcceptlist(*tmpraw);
207 env->ReleaseByteArrayElements(address, addr, 0);
208 return JNI_TRUE;
209 }
210
setVolumeNative(JNIEnv *,jclass,jint volume)211 static void setVolumeNative(JNIEnv* /* env */, jclass /* clazz */, jint volume) {
212 if (!sHearingAidInterface) {
213 log::error("Failed to get the Bluetooth Hearing Aid Interface");
214 return;
215 }
216 sHearingAidInterface->SetVolume(volume);
217 }
218
register_com_android_bluetooth_hearing_aid(JNIEnv * env)219 int register_com_android_bluetooth_hearing_aid(JNIEnv* env) {
220 const JNINativeMethod methods[] = {
221 {"initNative", "()V", (void*)initNative},
222 {"cleanupNative", "()V", (void*)cleanupNative},
223 {"connectHearingAidNative", "([B)Z", (void*)connectHearingAidNative},
224 {"disconnectHearingAidNative", "([B)Z", (void*)disconnectHearingAidNative},
225 {"addToAcceptlistNative", "([B)Z", (void*)addToAcceptlistNative},
226 {"setVolumeNative", "(I)V", (void*)setVolumeNative},
227 };
228 const int result = REGISTER_NATIVE_METHODS(
229 env, "com/android/bluetooth/hearingaid/HearingAidNativeInterface", methods);
230 if (result != 0) {
231 return result;
232 }
233
234 const JNIJavaMethod javaMethods[] = {
235 {"onConnectionStateChanged", "(I[B)V", &method_onConnectionStateChanged},
236 {"onDeviceAvailable", "(BJ[B)V", &method_onDeviceAvailable},
237 };
238 GET_JAVA_METHODS(env, "com/android/bluetooth/hearingaid/HearingAidNativeInterface", javaMethods);
239
240 return 0;
241 }
242 } // namespace android
243