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