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