1 /*
2 * Copyright 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 "com_android_bluetooth.h"
20 #include "hardware/bt_hearing_aid.h"
21
22 #include <string.h>
23 #include <shared_mutex>
24
25 using bluetooth::hearing_aid::ConnectionState;
26 using bluetooth::hearing_aid::HearingAidInterface;
27 using bluetooth::hearing_aid::HearingAidCallbacks;
28
29 namespace android {
30 static jmethodID method_onConnectionStateChanged;
31 static jmethodID method_onDeviceAvailable;
32
33 static HearingAidInterface* sHearingAidInterface = nullptr;
34 static std::shared_timed_mutex interface_mutex;
35
36 static jobject mCallbacksObj = nullptr;
37 static std::shared_timed_mutex callbacks_mutex;
38
39 class HearingAidCallbacksImpl : public HearingAidCallbacks {
40 public:
41 ~HearingAidCallbacksImpl() = default;
OnConnectionState(ConnectionState state,const RawAddress & bd_addr)42 void OnConnectionState(ConnectionState state,
43 const RawAddress& bd_addr) override {
44 log::info("");
45
46 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
47 CallbackEnv sCallbackEnv(__func__);
48 if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
49
50 ScopedLocalRef<jbyteArray> addr(
51 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
52 if (!addr.get()) {
53 log::error("Failed to new jbyteArray bd addr for connection state");
54 return;
55 }
56
57 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
58 (jbyte*)&bd_addr);
59 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
60 (jint)state, addr.get());
61 }
62
OnDeviceAvailable(uint8_t capabilities,uint64_t hi_sync_id,const RawAddress & bd_addr)63 void OnDeviceAvailable(uint8_t capabilities, uint64_t hi_sync_id,
64 const RawAddress& bd_addr) override {
65 log::info("capabilities={} hi_sync_id={}", capabilities, hi_sync_id);
66
67 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
68 CallbackEnv sCallbackEnv(__func__);
69 if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
70
71 ScopedLocalRef<jbyteArray> addr(
72 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
73 if (!addr.get()) {
74 log::error("Failed to new jbyteArray bd addr for connection state");
75 return;
76 }
77
78 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
79 (jbyte*)&bd_addr);
80 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDeviceAvailable,
81 (jbyte)capabilities, (jlong)hi_sync_id,
82 addr.get());
83 }
84 };
85
86 static HearingAidCallbacksImpl sHearingAidCallbacks;
87
initNative(JNIEnv * env,jobject object)88 static void initNative(JNIEnv* env, jobject object) {
89 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
90 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
91
92 const bt_interface_t* btInf = getBluetoothInterface();
93 if (btInf == nullptr) {
94 log::error("Bluetooth module is not loaded");
95 return;
96 }
97
98 if (sHearingAidInterface != nullptr) {
99 log::info("Cleaning up HearingAid Interface before initializing...");
100 sHearingAidInterface->Cleanup();
101 sHearingAidInterface = nullptr;
102 }
103
104 if (mCallbacksObj != nullptr) {
105 log::info("Cleaning up HearingAid callback object");
106 env->DeleteGlobalRef(mCallbacksObj);
107 mCallbacksObj = nullptr;
108 }
109
110 if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
111 log::error("Failed to allocate Global Ref for Hearing Aid Callbacks");
112 return;
113 }
114
115 sHearingAidInterface = (HearingAidInterface*)btInf->get_profile_interface(
116 BT_PROFILE_HEARING_AID_ID);
117 if (sHearingAidInterface == nullptr) {
118 log::error("Failed to get Bluetooth Hearing Aid Interface");
119 return;
120 }
121
122 sHearingAidInterface->Init(&sHearingAidCallbacks);
123 }
124
cleanupNative(JNIEnv * env,jobject)125 static void cleanupNative(JNIEnv* env, jobject /* object */) {
126 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
127 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
128
129 const bt_interface_t* btInf = getBluetoothInterface();
130 if (btInf == nullptr) {
131 log::error("Bluetooth module is not loaded");
132 return;
133 }
134
135 if (sHearingAidInterface != nullptr) {
136 sHearingAidInterface->Cleanup();
137 sHearingAidInterface = nullptr;
138 }
139
140 if (mCallbacksObj != nullptr) {
141 env->DeleteGlobalRef(mCallbacksObj);
142 mCallbacksObj = nullptr;
143 }
144 }
145
connectHearingAidNative(JNIEnv * env,jobject,jbyteArray address)146 static jboolean connectHearingAidNative(JNIEnv* env, jobject /* object */,
147 jbyteArray address) {
148 log::info("");
149 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
150 if (!sHearingAidInterface) return JNI_FALSE;
151
152 jbyte* addr = env->GetByteArrayElements(address, nullptr);
153 if (!addr) {
154 jniThrowIOException(env, EINVAL);
155 return JNI_FALSE;
156 }
157
158 RawAddress* tmpraw = (RawAddress*)addr;
159 sHearingAidInterface->Connect(*tmpraw);
160 env->ReleaseByteArrayElements(address, addr, 0);
161 return JNI_TRUE;
162 }
163
disconnectHearingAidNative(JNIEnv * env,jobject,jbyteArray address)164 static jboolean disconnectHearingAidNative(JNIEnv* env, jobject /* object */,
165 jbyteArray address) {
166 log::info("");
167 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
168 if (!sHearingAidInterface) return JNI_FALSE;
169
170 jbyte* addr = env->GetByteArrayElements(address, nullptr);
171 if (!addr) {
172 jniThrowIOException(env, EINVAL);
173 return JNI_FALSE;
174 }
175
176 RawAddress* tmpraw = (RawAddress*)addr;
177 sHearingAidInterface->Disconnect(*tmpraw);
178 env->ReleaseByteArrayElements(address, addr, 0);
179 return JNI_TRUE;
180 }
181
addToAcceptlistNative(JNIEnv * env,jobject,jbyteArray address)182 static jboolean addToAcceptlistNative(JNIEnv* env, jobject /* object */,
183 jbyteArray address) {
184 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
185 if (!sHearingAidInterface) return JNI_FALSE;
186 jbyte* addr = env->GetByteArrayElements(address, nullptr);
187 if (!addr) {
188 jniThrowIOException(env, EINVAL);
189 return JNI_FALSE;
190 }
191
192 RawAddress* tmpraw = (RawAddress*)addr;
193 sHearingAidInterface->AddToAcceptlist(*tmpraw);
194 env->ReleaseByteArrayElements(address, addr, 0);
195 return JNI_TRUE;
196 }
197
setVolumeNative(JNIEnv *,jclass,jint volume)198 static void setVolumeNative(JNIEnv* /* env */, jclass /* clazz */,
199 jint volume) {
200 if (!sHearingAidInterface) {
201 log::error("Failed to get the Bluetooth Hearing Aid Interface");
202 return;
203 }
204 sHearingAidInterface->SetVolume(volume);
205 }
206
register_com_android_bluetooth_hearing_aid(JNIEnv * env)207 int register_com_android_bluetooth_hearing_aid(JNIEnv* env) {
208 const JNINativeMethod methods[] = {
209 {"initNative", "()V", (void*)initNative},
210 {"cleanupNative", "()V", (void*)cleanupNative},
211 {"connectHearingAidNative", "([B)Z", (void*)connectHearingAidNative},
212 {"disconnectHearingAidNative", "([B)Z",
213 (void*)disconnectHearingAidNative},
214 {"addToAcceptlistNative", "([B)Z", (void*)addToAcceptlistNative},
215 {"setVolumeNative", "(I)V", (void*)setVolumeNative},
216 };
217 const int result = REGISTER_NATIVE_METHODS(
218 env, "com/android/bluetooth/hearingaid/HearingAidNativeInterface",
219 methods);
220 if (result != 0) {
221 return result;
222 }
223
224 const JNIJavaMethod javaMethods[] = {
225 {"onConnectionStateChanged", "(I[B)V", &method_onConnectionStateChanged},
226 {"onDeviceAvailable", "(BJ[B)V", &method_onDeviceAvailable},
227 };
228 GET_JAVA_METHODS(env,
229 "com/android/bluetooth/hearingaid/HearingAidNativeInterface",
230 javaMethods);
231
232 return 0;
233 }
234 } // namespace android
235