• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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