1 /*
2  * Copyright 2021 HIMSA II K/S - www.himsa.com.
3  * Represented by EHIMA - www.ehima.com
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #define LOG_TAG "BluetoothHapClientJni"
19 
20 #include <string.h>
21 
22 #include <shared_mutex>
23 
24 #include "com_android_bluetooth.h"
25 #include "hardware/bt_has.h"
26 
27 using bluetooth::has::ConnectionState;
28 using bluetooth::has::ErrorCode;
29 using bluetooth::has::HasClientCallbacks;
30 using bluetooth::has::HasClientInterface;
31 using bluetooth::has::PresetInfo;
32 using bluetooth::has::PresetInfoReason;
33 
34 namespace android {
35 static jmethodID method_onConnectionStateChanged;
36 static jmethodID method_onDeviceAvailable;
37 static jmethodID method_onFeaturesUpdate;
38 static jmethodID method_onActivePresetSelected;
39 static jmethodID method_onGroupActivePresetSelected;
40 static jmethodID method_onActivePresetSelectError;
41 static jmethodID method_onGroupActivePresetSelectError;
42 static jmethodID method_onPresetInfo;
43 static jmethodID method_onGroupPresetInfo;
44 static jmethodID method_onPresetInfoError;
45 static jmethodID method_onGroupPresetInfoError;
46 static jmethodID method_onPresetNameSetError;
47 static jmethodID method_onGroupPresetNameSetError;
48 
49 static HasClientInterface* sHasClientInterface = nullptr;
50 static std::shared_timed_mutex interface_mutex;
51 
52 static jobject mCallbacksObj = nullptr;
53 static std::shared_timed_mutex callbacks_mutex;
54 
55 static struct {
56   jclass clazz;
57   jmethodID constructor;
58   jmethodID getCodecType;
59   jmethodID getCodecPriority;
60   jmethodID getSampleRate;
61   jmethodID getBitsPerSample;
62   jmethodID getChannelMode;
63   jmethodID getCodecSpecific1;
64   jmethodID getCodecSpecific2;
65   jmethodID getCodecSpecific3;
66   jmethodID getCodecSpecific4;
67 } android_bluetooth_BluetoothHapPresetInfo;
68 
69 class HasClientCallbacksImpl : public HasClientCallbacks {
70  public:
71   ~HasClientCallbacksImpl() = default;
72 
OnConnectionState(ConnectionState state,const RawAddress & bd_addr)73   void OnConnectionState(ConnectionState state,
74                          const RawAddress& bd_addr) override {
75     log::info("");
76 
77     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
78     CallbackEnv sCallbackEnv(__func__);
79     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
80 
81     ScopedLocalRef<jbyteArray> addr(
82         sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
83     if (!addr.get()) {
84       log::error("Failed to new bd addr jbyteArray for connection state");
85       return;
86     }
87 
88     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
89                                      (jbyte*)&bd_addr);
90     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
91                                  (jint)state, addr.get());
92   }
93 
OnDeviceAvailable(const RawAddress & bd_addr,uint8_t features)94   void OnDeviceAvailable(const RawAddress& bd_addr, uint8_t features) override {
95     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
96     CallbackEnv sCallbackEnv(__func__);
97     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
98 
99     ScopedLocalRef<jbyteArray> addr(
100         sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
101     if (!addr.get()) {
102       log::error("Failed to new bd addr jbyteArray for device available");
103       return;
104     }
105     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
106                                      (jbyte*)&bd_addr);
107 
108     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDeviceAvailable,
109                                  addr.get(), (jint)features);
110   }
111 
OnFeaturesUpdate(const RawAddress & bd_addr,uint8_t features)112   void OnFeaturesUpdate(const RawAddress& bd_addr, uint8_t features) override {
113     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
114     CallbackEnv sCallbackEnv(__func__);
115     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
116 
117     ScopedLocalRef<jbyteArray> addr(
118         sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
119     if (!addr.get()) {
120       log::error("Failed to new bd addr jbyteArray for device available");
121       return;
122     }
123     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
124                                      (jbyte*)&bd_addr);
125 
126     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onFeaturesUpdate,
127                                  addr.get(), (jint)features);
128   }
129 
OnActivePresetSelected(std::variant<RawAddress,int> addr_or_group_id,uint8_t preset_index)130   void OnActivePresetSelected(std::variant<RawAddress, int> addr_or_group_id,
131                               uint8_t preset_index) override {
132     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
133     CallbackEnv sCallbackEnv(__func__);
134     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
135 
136     if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
137       ScopedLocalRef<jbyteArray> addr(
138           sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
139       if (!addr.get()) {
140         log::error("Failed to new bd addr jbyteArray for preset selected");
141         return;
142       }
143       sCallbackEnv->SetByteArrayRegion(
144           addr.get(), 0, sizeof(RawAddress),
145           (jbyte*)&std::get<RawAddress>(addr_or_group_id));
146 
147       sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onActivePresetSelected,
148                                    addr.get(), (jint)preset_index);
149     } else {
150       sCallbackEnv->CallVoidMethod(
151           mCallbacksObj, method_onGroupActivePresetSelected,
152           std::get<int>(addr_or_group_id), (jint)preset_index);
153     }
154   }
155 
OnActivePresetSelectError(std::variant<RawAddress,int> addr_or_group_id,ErrorCode error_code)156   void OnActivePresetSelectError(std::variant<RawAddress, int> addr_or_group_id,
157                                  ErrorCode error_code) override {
158     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
159     CallbackEnv sCallbackEnv(__func__);
160     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
161 
162     if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
163       ScopedLocalRef<jbyteArray> addr(
164           sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
165       if (!addr.get()) {
166         log::error("Failed to new bd addr jbyteArray for preset select error");
167         return;
168       }
169       sCallbackEnv->SetByteArrayRegion(
170           addr.get(), 0, sizeof(RawAddress),
171           (jbyte*)&std::get<RawAddress>(addr_or_group_id));
172 
173       sCallbackEnv->CallVoidMethod(mCallbacksObj,
174                                    method_onActivePresetSelectError, addr.get(),
175                                    (jint)error_code);
176     } else {
177       sCallbackEnv->CallVoidMethod(
178           mCallbacksObj, method_onGroupActivePresetSelectError,
179           std::get<int>(addr_or_group_id), (jint)error_code);
180     }
181   }
182 
OnPresetInfo(std::variant<RawAddress,int> addr_or_group_id,PresetInfoReason info_reason,std::vector<PresetInfo> detail_records)183   void OnPresetInfo(std::variant<RawAddress, int> addr_or_group_id,
184                     PresetInfoReason info_reason,
185                     std::vector<PresetInfo> detail_records) override {
186     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
187     CallbackEnv sCallbackEnv(__func__);
188     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
189 
190     jsize i = 0;
191     jobjectArray presets_array = sCallbackEnv->NewObjectArray(
192         (jsize)detail_records.size(),
193         android_bluetooth_BluetoothHapPresetInfo.clazz, nullptr);
194 
195     const char null_str[] = "";
196     for (auto const& info : detail_records) {
197       const char* name = info.preset_name.c_str();
198       if (!sCallbackEnv.isValidUtf(name)) {
199         log::error("name is not a valid UTF string.");
200         name = null_str;
201       }
202 
203       ScopedLocalRef<jstring> name_str(sCallbackEnv.get(),
204                                        sCallbackEnv->NewStringUTF(name));
205       if (!name_str.get()) {
206         log::error("Failed to new preset name String for preset name");
207         return;
208       }
209 
210       jobject infoObj = sCallbackEnv->NewObject(
211           android_bluetooth_BluetoothHapPresetInfo.clazz,
212           android_bluetooth_BluetoothHapPresetInfo.constructor,
213           (jint)info.preset_index, name_str.get(), (jboolean)info.writable,
214           (jboolean)info.available);
215       sCallbackEnv->SetObjectArrayElement(presets_array, i++, infoObj);
216       sCallbackEnv->DeleteLocalRef(infoObj);
217     }
218 
219     if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
220       ScopedLocalRef<jbyteArray> addr(
221           sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
222       if (!addr.get()) {
223         log::error("Failed to new bd addr jbyteArray for preset name");
224         return;
225       }
226       sCallbackEnv->SetByteArrayRegion(
227           addr.get(), 0, sizeof(RawAddress),
228           (jbyte*)&std::get<RawAddress>(addr_or_group_id));
229 
230       sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onPresetInfo,
231                                    addr.get(), (jint)info_reason,
232                                    presets_array);
233     } else {
234       sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGroupPresetInfo,
235                                    std::get<int>(addr_or_group_id),
236                                    (jint)info_reason, presets_array);
237     }
238   }
239 
OnPresetInfoError(std::variant<RawAddress,int> addr_or_group_id,uint8_t preset_index,ErrorCode error_code)240   virtual void OnPresetInfoError(std::variant<RawAddress, int> addr_or_group_id,
241                                  uint8_t preset_index,
242                                  ErrorCode error_code) override {
243     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
244     CallbackEnv sCallbackEnv(__func__);
245     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
246 
247     if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
248       ScopedLocalRef<jbyteArray> addr(
249           sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
250       if (!addr.get()) {
251         log::error(
252             "Failed to new bd addr jbyteArray for preset name get error");
253         return;
254       }
255       sCallbackEnv->SetByteArrayRegion(
256           addr.get(), 0, sizeof(RawAddress),
257           (jbyte*)&std::get<RawAddress>(addr_or_group_id));
258 
259       sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onPresetInfoError,
260                                    addr.get(), (jint)preset_index,
261                                    (jint)error_code);
262     } else {
263       sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGroupPresetInfoError,
264                                    std::get<int>(addr_or_group_id),
265                                    (jint)preset_index, (jint)error_code);
266     }
267   }
268 
OnSetPresetNameError(std::variant<RawAddress,int> addr_or_group_id,uint8_t preset_index,ErrorCode error_code)269   void OnSetPresetNameError(std::variant<RawAddress, int> addr_or_group_id,
270                             uint8_t preset_index,
271                             ErrorCode error_code) override {
272     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
273     CallbackEnv sCallbackEnv(__func__);
274     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
275 
276     if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
277       ScopedLocalRef<jbyteArray> addr(
278           sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
279       if (!addr.get()) {
280         log::error(
281             "Failed to new bd addr jbyteArray for preset name set error");
282         return;
283       }
284       sCallbackEnv->SetByteArrayRegion(
285           addr.get(), 0, sizeof(RawAddress),
286           (jbyte*)&std::get<RawAddress>(addr_or_group_id));
287 
288       sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onPresetNameSetError,
289                                    addr.get(), (jint)preset_index,
290                                    (jint)error_code);
291     } else {
292       sCallbackEnv->CallVoidMethod(mCallbacksObj,
293                                    method_onGroupPresetNameSetError,
294                                    std::get<int>(addr_or_group_id),
295                                    (jint)preset_index, (jint)error_code);
296     }
297   }
298 };
299 
300 static HasClientCallbacksImpl sHasClientCallbacks;
301 
initNative(JNIEnv * env,jobject object)302 static void initNative(JNIEnv* env, jobject object) {
303   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
304   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
305 
306   const bt_interface_t* btInf = getBluetoothInterface();
307   if (btInf == nullptr) {
308     log::error("Bluetooth module is not loaded");
309     return;
310   }
311 
312   if (sHasClientInterface != nullptr) {
313     log::info("Cleaning up HearingAid Interface before initializing...");
314     sHasClientInterface->Cleanup();
315     sHasClientInterface = nullptr;
316   }
317 
318   if (mCallbacksObj != nullptr) {
319     log::info("Cleaning up HearingAid callback object");
320     env->DeleteGlobalRef(mCallbacksObj);
321     mCallbacksObj = nullptr;
322   }
323 
324   if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
325     log::error("Failed to allocate Global Ref for Hearing Access Callbacks");
326     return;
327   }
328 
329   android_bluetooth_BluetoothHapPresetInfo.clazz = (jclass)env->NewGlobalRef(
330       env->FindClass("android/bluetooth/BluetoothHapPresetInfo"));
331   if (android_bluetooth_BluetoothHapPresetInfo.clazz == nullptr) {
332     log::error(
333         "Failed to allocate Global Ref for BluetoothHapPresetInfo class");
334     return;
335   }
336 
337   sHasClientInterface = (HasClientInterface*)btInf->get_profile_interface(
338       BT_PROFILE_HAP_CLIENT_ID);
339   if (sHasClientInterface == nullptr) {
340     log::error(
341         "Failed to get Bluetooth Hearing Access Service Client Interface");
342     return;
343   }
344 
345   sHasClientInterface->Init(&sHasClientCallbacks);
346 }
347 
cleanupNative(JNIEnv * env,jobject)348 static void cleanupNative(JNIEnv* env, jobject /* object */) {
349   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
350   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
351 
352   const bt_interface_t* btInf = getBluetoothInterface();
353   if (btInf == nullptr) {
354     log::error("Bluetooth module is not loaded");
355     return;
356   }
357 
358   if (sHasClientInterface != nullptr) {
359     sHasClientInterface->Cleanup();
360     sHasClientInterface = nullptr;
361   }
362 
363   if (mCallbacksObj != nullptr) {
364     env->DeleteGlobalRef(mCallbacksObj);
365     mCallbacksObj = nullptr;
366   }
367 }
368 
connectHapClientNative(JNIEnv * env,jobject,jbyteArray address)369 static jboolean connectHapClientNative(JNIEnv* env, jobject /* object */,
370                                        jbyteArray address) {
371   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
372   if (!sHasClientInterface) {
373     log::error("Failed to get the Bluetooth HAP Interface");
374     return JNI_FALSE;
375   }
376 
377   jbyte* addr = env->GetByteArrayElements(address, nullptr);
378   if (!addr) {
379     jniThrowIOException(env, EINVAL);
380     return JNI_FALSE;
381   }
382 
383   RawAddress* tmpraw = (RawAddress*)addr;
384   sHasClientInterface->Connect(*tmpraw);
385   env->ReleaseByteArrayElements(address, addr, 0);
386   return JNI_TRUE;
387 }
388 
disconnectHapClientNative(JNIEnv * env,jobject,jbyteArray address)389 static jboolean disconnectHapClientNative(JNIEnv* env, jobject /* object */,
390                                           jbyteArray address) {
391   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
392   if (!sHasClientInterface) {
393     log::error("Failed to get the Bluetooth HAP Interface");
394     return JNI_FALSE;
395   }
396 
397   jbyte* addr = env->GetByteArrayElements(address, nullptr);
398   if (!addr) {
399     jniThrowIOException(env, EINVAL);
400     return JNI_FALSE;
401   }
402 
403   RawAddress* tmpraw = (RawAddress*)addr;
404   sHasClientInterface->Disconnect(*tmpraw);
405   env->ReleaseByteArrayElements(address, addr, 0);
406   return JNI_TRUE;
407 }
408 
selectActivePresetNative(JNIEnv * env,jobject,jbyteArray address,jint preset_index)409 static void selectActivePresetNative(JNIEnv* env, jobject /* object */,
410                                      jbyteArray address, jint preset_index) {
411   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
412   if (!sHasClientInterface) {
413     log::error("Failed to get the Bluetooth HAP Interface");
414     return;
415   }
416 
417   jbyte* addr = env->GetByteArrayElements(address, nullptr);
418   if (!addr) {
419     jniThrowIOException(env, EINVAL);
420     return;
421   }
422 
423   RawAddress* tmpraw = (RawAddress*)addr;
424   sHasClientInterface->SelectActivePreset(*tmpraw, preset_index);
425   env->ReleaseByteArrayElements(address, addr, 0);
426 }
427 
groupSelectActivePresetNative(JNIEnv *,jobject,jint group_id,jint preset_index)428 static void groupSelectActivePresetNative(JNIEnv* /* env */,
429                                           jobject /* object */, jint group_id,
430                                           jint preset_index) {
431   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
432   if (!sHasClientInterface) {
433     log::error("Failed to get the Bluetooth HAP Interface");
434     return;
435   }
436 
437   sHasClientInterface->SelectActivePreset(group_id, preset_index);
438 }
439 
nextActivePresetNative(JNIEnv * env,jobject,jbyteArray address)440 static void nextActivePresetNative(JNIEnv* env, jobject /* object */,
441                                    jbyteArray address) {
442   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
443   if (!sHasClientInterface) {
444     log::error("Failed to get the Bluetooth HAP Interface");
445     return;
446   }
447 
448   jbyte* addr = env->GetByteArrayElements(address, nullptr);
449   if (!addr) {
450     jniThrowIOException(env, EINVAL);
451     return;
452   }
453 
454   RawAddress* tmpraw = (RawAddress*)addr;
455   sHasClientInterface->NextActivePreset(*tmpraw);
456   env->ReleaseByteArrayElements(address, addr, 0);
457 }
458 
groupNextActivePresetNative(JNIEnv *,jobject,jint group_id)459 static void groupNextActivePresetNative(JNIEnv* /* env */, jobject /* object */,
460                                         jint group_id) {
461   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
462   if (!sHasClientInterface) {
463     log::error("Failed to get the Bluetooth HAP Interface");
464     return;
465   }
466 
467   sHasClientInterface->NextActivePreset(group_id);
468 }
469 
previousActivePresetNative(JNIEnv * env,jobject,jbyteArray address)470 static void previousActivePresetNative(JNIEnv* env, jobject /* object */,
471                                        jbyteArray address) {
472   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
473   if (!sHasClientInterface) {
474     log::error("Failed to get the Bluetooth HAP Interface");
475     return;
476   }
477 
478   jbyte* addr = env->GetByteArrayElements(address, nullptr);
479   if (!addr) {
480     jniThrowIOException(env, EINVAL);
481     return;
482   }
483 
484   RawAddress* tmpraw = (RawAddress*)addr;
485   sHasClientInterface->PreviousActivePreset(*tmpraw);
486   env->ReleaseByteArrayElements(address, addr, 0);
487 }
488 
groupPreviousActivePresetNative(JNIEnv *,jobject,jint group_id)489 static void groupPreviousActivePresetNative(JNIEnv* /* env */,
490                                             jobject /* object */,
491                                             jint group_id) {
492   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
493   if (!sHasClientInterface) {
494     log::error("Failed to get the Bluetooth HAP Interface");
495     return;
496   }
497 
498   sHasClientInterface->PreviousActivePreset(group_id);
499 }
500 
getPresetInfoNative(JNIEnv * env,jobject,jbyteArray address,jint preset_index)501 static void getPresetInfoNative(JNIEnv* env, jobject /* object */,
502                                 jbyteArray address, jint preset_index) {
503   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
504   if (!sHasClientInterface) {
505     log::error("Failed to get the Bluetooth HAP Interface");
506     return;
507   }
508 
509   jbyte* addr = env->GetByteArrayElements(address, nullptr);
510   if (!addr) {
511     jniThrowIOException(env, EINVAL);
512     return;
513   }
514 
515   RawAddress* tmpraw = (RawAddress*)addr;
516   sHasClientInterface->GetPresetInfo(*tmpraw, preset_index);
517   env->ReleaseByteArrayElements(address, addr, 0);
518 }
519 
setPresetNameNative(JNIEnv * env,jobject,jbyteArray address,jint preset_index,jstring name)520 static void setPresetNameNative(JNIEnv* env, jobject /* object */,
521                                 jbyteArray address, jint preset_index,
522                                 jstring name) {
523   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
524   if (!sHasClientInterface) {
525     log::error("Failed to get the Bluetooth HAP Interface");
526     return;
527   }
528 
529   jbyte* addr = env->GetByteArrayElements(address, nullptr);
530   if (!addr) {
531     jniThrowIOException(env, EINVAL);
532     return;
533   }
534 
535   std::string name_str;
536   if (name != nullptr) {
537     const char* value = env->GetStringUTFChars(name, nullptr);
538     name_str = std::string(value);
539     env->ReleaseStringUTFChars(name, value);
540   }
541 
542   RawAddress* tmpraw = (RawAddress*)addr;
543   sHasClientInterface->SetPresetName(*tmpraw, preset_index,
544                                      std::move(name_str));
545   env->ReleaseByteArrayElements(address, addr, 0);
546 }
547 
groupSetPresetNameNative(JNIEnv * env,jobject,jint group_id,jint preset_index,jstring name)548 static void groupSetPresetNameNative(JNIEnv* env, jobject /* object */,
549                                      jint group_id, jint preset_index,
550                                      jstring name) {
551   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
552   if (!sHasClientInterface) {
553     log::error("Failed to get the Bluetooth HAP Interface");
554     return;
555   }
556 
557   std::string name_str;
558   if (name != nullptr) {
559     const char* value = env->GetStringUTFChars(name, nullptr);
560     name_str = std::string(value);
561     env->ReleaseStringUTFChars(name, value);
562   }
563 
564   sHasClientInterface->SetPresetName(group_id, preset_index,
565                                      std::move(name_str));
566 }
567 
register_com_android_bluetooth_hap_client(JNIEnv * env)568 int register_com_android_bluetooth_hap_client(JNIEnv* env) {
569   const JNINativeMethod methods[] = {
570       {"initNative", "()V", (void*)initNative},
571       {"cleanupNative", "()V", (void*)cleanupNative},
572       {"connectHapClientNative", "([B)Z", (void*)connectHapClientNative},
573       {"disconnectHapClientNative", "([B)Z", (void*)disconnectHapClientNative},
574       {"selectActivePresetNative", "([BI)V", (void*)selectActivePresetNative},
575       {"groupSelectActivePresetNative", "(II)V",
576        (void*)groupSelectActivePresetNative},
577       {"nextActivePresetNative", "([B)V", (void*)nextActivePresetNative},
578       {"groupNextActivePresetNative", "(I)V",
579        (void*)groupNextActivePresetNative},
580       {"previousActivePresetNative", "([B)V",
581        (void*)previousActivePresetNative},
582       {"groupPreviousActivePresetNative", "(I)V",
583        (void*)groupPreviousActivePresetNative},
584       {"getPresetInfoNative", "([BI)V", (void*)getPresetInfoNative},
585       {"setPresetNameNative", "([BILjava/lang/String;)V",
586        (void*)setPresetNameNative},
587       {"groupSetPresetNameNative", "(IILjava/lang/String;)V",
588        (void*)groupSetPresetNameNative},
589   };
590   const int result = REGISTER_NATIVE_METHODS(
591       env, "com/android/bluetooth/hap/HapClientNativeInterface", methods);
592   if (result != 0) {
593     return result;
594   }
595 
596   const JNIJavaMethod javaMethods[] = {
597       {"onConnectionStateChanged", "(I[B)V", &method_onConnectionStateChanged},
598       {"onDeviceAvailable", "([BI)V", &method_onDeviceAvailable},
599       {"onFeaturesUpdate", "([BI)V", &method_onFeaturesUpdate},
600       {"onActivePresetSelected", "([BI)V", &method_onActivePresetSelected},
601       {"onActivePresetGroupSelected", "(II)V",
602        &method_onGroupActivePresetSelected},
603       {"onActivePresetSelectError", "([BI)V",
604        &method_onActivePresetSelectError},
605       {"onActivePresetGroupSelectError", "(II)V",
606        &method_onGroupActivePresetSelectError},
607       {"onPresetInfo", "([BI[Landroid/bluetooth/BluetoothHapPresetInfo;)V",
608        &method_onPresetInfo},
609       {"onGroupPresetInfo", "(II[Landroid/bluetooth/BluetoothHapPresetInfo;)V",
610        &method_onGroupPresetInfo},
611       {"onPresetNameSetError", "([BII)V", &method_onPresetNameSetError},
612       {"onGroupPresetNameSetError", "(III)V",
613        &method_onGroupPresetNameSetError},
614       {"onPresetInfoError", "([BII)V", &method_onPresetInfoError},
615       {"onGroupPresetInfoError", "(III)V", &method_onGroupPresetInfoError},
616   };
617   GET_JAVA_METHODS(env, "com/android/bluetooth/hap/HapClientNativeInterface",
618                    javaMethods);
619 
620   const JNIJavaMethod javaHapPresetMethods[] = {
621       {"<init>", "(ILjava/lang/String;ZZ)V",
622        &android_bluetooth_BluetoothHapPresetInfo.constructor},
623   };
624   GET_JAVA_METHODS(env, "android/bluetooth/BluetoothHapPresetInfo",
625                    javaHapPresetMethods);
626 
627   return 0;
628 }
629 }  // namespace android
630