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