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