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