• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "BluetoothVolumeControlServiceJni"
19 
20 #include <aics/api.h>
21 #include <bluetooth/log.h>
22 #include <jni.h>
23 #include <nativehelper/JNIHelp.h>
24 #include <nativehelper/scoped_local_ref.h>
25 
26 #include <cerrno>
27 #include <cstdint>
28 #include <cstring>
29 #include <mutex>
30 #include <shared_mutex>
31 #include <string>
32 
33 #include "com_android_bluetooth.h"
34 #include "hardware/bluetooth.h"
35 #include "hardware/bt_vc.h"
36 #include "types/raw_address.h"
37 
38 using bluetooth::aics::GainMode;
39 using bluetooth::aics::Mute;
40 using bluetooth::vc::ConnectionState;
41 using bluetooth::vc::VolumeControlCallbacks;
42 using bluetooth::vc::VolumeControlInterface;
43 using bluetooth::vc::VolumeInputStatus;
44 using bluetooth::vc::VolumeInputType;
45 
46 namespace android {
47 static jmethodID method_onConnectionStateChanged;
48 static jmethodID method_onVolumeStateChanged;
49 static jmethodID method_onGroupVolumeStateChanged;
50 static jmethodID method_onDeviceAvailable;
51 static jmethodID method_onExtAudioOutVolumeOffsetChanged;
52 static jmethodID method_onExtAudioOutLocationChanged;
53 static jmethodID method_onExtAudioOutDescriptionChanged;
54 static jmethodID method_onExtAudioInStateChanged;
55 static jmethodID method_onExtAudioInSetGainSettingFailed;
56 static jmethodID method_onExtAudioInSetMuteFailed;
57 static jmethodID method_onExtAudioInSetGainModeFailed;
58 static jmethodID method_onExtAudioInStatusChanged;
59 static jmethodID method_onExtAudioInTypeChanged;
60 static jmethodID method_onExtAudioInGainSettingPropertiesChanged;
61 static jmethodID method_onExtAudioInDescriptionChanged;
62 
63 static VolumeControlInterface* sVolumeControlInterface = nullptr;
64 static std::shared_timed_mutex interface_mutex;
65 
66 static jobject mCallbacksObj = nullptr;
67 static std::shared_timed_mutex callbacks_mutex;
68 
69 static jfieldID sCallbacksField;
70 
71 class VolumeControlCallbacksImpl : public VolumeControlCallbacks {
72 public:
73   ~VolumeControlCallbacksImpl() = default;
OnConnectionState(ConnectionState state,const RawAddress & bd_addr)74   void OnConnectionState(ConnectionState state, const RawAddress& bd_addr) override {
75     log::info("state:{}, addr: {}", static_cast<int>(state), bd_addr.ToRedactedStringForLogging());
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),
91                                      reinterpret_cast<const jbyte*>(&bd_addr));
92     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint)state,
93                                  addr.get());
94   }
95 
OnVolumeStateChanged(const RawAddress & bd_addr,uint8_t volume,bool mute,uint8_t flags,bool isAutonomous)96   void OnVolumeStateChanged(const RawAddress& bd_addr, uint8_t volume, bool mute, uint8_t flags,
97                             bool isAutonomous) override {
98     log::info("");
99 
100     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
101     CallbackEnv sCallbackEnv(__func__);
102     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
103       return;
104     }
105 
106     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
107                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
108     if (!addr.get()) {
109       log::error("Failed to new jbyteArray bd addr for connection state");
110       return;
111     }
112 
113     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
114                                      reinterpret_cast<const jbyte*>(&bd_addr));
115     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVolumeStateChanged, (jint)volume,
116                                  (jboolean)mute, (jint)flags, addr.get(), (jboolean)isAutonomous);
117   }
118 
OnGroupVolumeStateChanged(int group_id,uint8_t volume,bool mute,bool isAutonomous)119   void OnGroupVolumeStateChanged(int group_id, uint8_t volume, bool mute,
120                                  bool isAutonomous) override {
121     log::info("");
122 
123     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
124     CallbackEnv sCallbackEnv(__func__);
125     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
126       return;
127     }
128 
129     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGroupVolumeStateChanged, (jint)volume,
130                                  (jboolean)mute, group_id, (jboolean)isAutonomous);
131   }
132 
OnDeviceAvailable(const RawAddress & bd_addr,uint8_t num_offsets,uint8_t num_inputs)133   void OnDeviceAvailable(const RawAddress& bd_addr, uint8_t num_offsets,
134                          uint8_t num_inputs) override {
135     log::info("");
136 
137     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
138     CallbackEnv sCallbackEnv(__func__);
139     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
140       return;
141     }
142 
143     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
144                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
145     if (!addr.get()) {
146       log::error("Failed to get addr for {}", bd_addr);
147       return;
148     }
149 
150     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
151                                      reinterpret_cast<const jbyte*>(&bd_addr));
152     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDeviceAvailable, (jint)num_offsets,
153                                  (jint)num_inputs, addr.get());
154   }
155 
OnExtAudioOutVolumeOffsetChanged(const RawAddress & bd_addr,uint8_t ext_output_id,int16_t offset)156   void OnExtAudioOutVolumeOffsetChanged(const RawAddress& bd_addr, uint8_t ext_output_id,
157                                         int16_t offset) override {
158     log::info("");
159 
160     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
161     CallbackEnv sCallbackEnv(__func__);
162     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
163       return;
164     }
165 
166     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
167                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
168     if (!addr.get()) {
169       log::error(
170               "Failed to new jbyteArray bd addr for "
171               "OnExtAudioOutVolumeOffsetChanged");
172       return;
173     }
174 
175     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
176                                      reinterpret_cast<const jbyte*>(&bd_addr));
177     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioOutVolumeOffsetChanged,
178                                  (jint)ext_output_id, (jint)offset, addr.get());
179   }
180 
OnExtAudioOutLocationChanged(const RawAddress & bd_addr,uint8_t ext_output_id,uint32_t location)181   void OnExtAudioOutLocationChanged(const RawAddress& bd_addr, uint8_t ext_output_id,
182                                     uint32_t location) override {
183     log::info("");
184 
185     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
186     CallbackEnv sCallbackEnv(__func__);
187     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
188       return;
189     }
190 
191     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
192                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
193     if (!addr.get()) {
194       log::error("Failed to new jbyteArray bd addr for OnExtAudioOutLocationChanged");
195       return;
196     }
197 
198     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
199                                      reinterpret_cast<const jbyte*>(&bd_addr));
200     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioOutLocationChanged,
201                                  (jint)ext_output_id, (jint)location, addr.get());
202   }
203 
OnExtAudioOutDescriptionChanged(const RawAddress & bd_addr,uint8_t ext_output_id,std::string descr)204   void OnExtAudioOutDescriptionChanged(const RawAddress& bd_addr, uint8_t ext_output_id,
205                                        std::string descr) override {
206     log::info("");
207 
208     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
209     CallbackEnv sCallbackEnv(__func__);
210     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
211       return;
212     }
213 
214     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
215                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
216     if (!addr.get()) {
217       log::error(
218               "Failed to new jbyteArray bd addr for "
219               "OnExtAudioOutDescriptionChanged");
220       return;
221     }
222 
223     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
224                                      reinterpret_cast<const jbyte*>(&bd_addr));
225     jstring description = sCallbackEnv->NewStringUTF(descr.c_str());
226     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioOutDescriptionChanged,
227                                  (jint)ext_output_id, description, addr.get());
228   }
229 
OnExtAudioInStateChanged(const RawAddress & bd_addr,uint8_t ext_input_id,int8_t gain_setting,Mute mute,GainMode gain_mode)230   void OnExtAudioInStateChanged(const RawAddress& bd_addr, uint8_t ext_input_id,
231                                 int8_t gain_setting, Mute mute, GainMode gain_mode) override {
232     log::info("");
233 
234     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
235     CallbackEnv sCallbackEnv(__func__);
236     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
237       return;
238     }
239 
240     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
241                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
242     if (!addr.get()) {
243       log::error("Failed to get addr for {}", bd_addr);
244       return;
245     }
246 
247     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
248                                      reinterpret_cast<const jbyte*>(&bd_addr));
249     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioInStateChanged, (jint)ext_input_id,
250                                  (jint)gain_setting, (jint)mute, (jint)gain_mode, addr.get());
251   }
252 
OnExtAudioInSetGainSettingFailed(const RawAddress & bd_addr,uint8_t ext_input_id)253   void OnExtAudioInSetGainSettingFailed(const RawAddress& bd_addr, uint8_t ext_input_id) override {
254     log::info("");
255 
256     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
257     CallbackEnv sCallbackEnv(__func__);
258     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
259       return;
260     }
261 
262     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
263                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
264     if (!addr.get()) {
265       log::error("Failed to get addr for {}", bd_addr);
266       return;
267     }
268 
269     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
270                                      reinterpret_cast<const jbyte*>(&bd_addr));
271     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioInSetGainSettingFailed,
272                                  (jint)ext_input_id, addr.get());
273   }
274 
OnExtAudioInSetMuteFailed(const RawAddress & bd_addr,uint8_t ext_input_id)275   void OnExtAudioInSetMuteFailed(const RawAddress& bd_addr, uint8_t ext_input_id) override {
276     log::info("");
277 
278     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
279     CallbackEnv sCallbackEnv(__func__);
280     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
281       return;
282     }
283 
284     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
285                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
286     if (!addr.get()) {
287       log::error("Failed to get addr for {}", bd_addr);
288       return;
289     }
290 
291     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
292                                      reinterpret_cast<const jbyte*>(&bd_addr));
293     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioInSetMuteFailed,
294                                  (jint)ext_input_id, addr.get());
295   }
OnExtAudioInSetGainModeFailed(const RawAddress & bd_addr,uint8_t ext_input_id)296   void OnExtAudioInSetGainModeFailed(const RawAddress& bd_addr, uint8_t ext_input_id) override {
297     log::info("");
298 
299     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
300     CallbackEnv sCallbackEnv(__func__);
301     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
302       return;
303     }
304 
305     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
306                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
307     if (!addr.get()) {
308       log::error("Failed to get addr for {}", bd_addr);
309       return;
310     }
311 
312     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
313                                      reinterpret_cast<const jbyte*>(&bd_addr));
314     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioInSetGainModeFailed,
315                                  (jint)ext_input_id, addr.get());
316   }
317 
OnExtAudioInStatusChanged(const RawAddress & bd_addr,uint8_t ext_input_id,VolumeInputStatus status)318   void OnExtAudioInStatusChanged(const RawAddress& bd_addr, uint8_t ext_input_id,
319                                  VolumeInputStatus status) override {
320     log::info("");
321 
322     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
323     CallbackEnv sCallbackEnv(__func__);
324     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
325       return;
326     }
327 
328     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
329                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
330     if (!addr.get()) {
331       log::error("Failed to get addr for {}", bd_addr);
332       return;
333     }
334 
335     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
336                                      reinterpret_cast<const jbyte*>(&bd_addr));
337     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioInStatusChanged,
338                                  (jint)ext_input_id, (jint)status, addr.get());
339   }
340 
OnExtAudioInTypeChanged(const RawAddress & bd_addr,uint8_t ext_input_id,VolumeInputType type)341   void OnExtAudioInTypeChanged(const RawAddress& bd_addr, uint8_t ext_input_id,
342                                VolumeInputType type) override {
343     log::info("");
344 
345     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
346     CallbackEnv sCallbackEnv(__func__);
347     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
348       return;
349     }
350 
351     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
352                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
353     if (!addr.get()) {
354       log::error("Failed to get addr for {}", bd_addr);
355       return;
356     }
357 
358     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
359                                      reinterpret_cast<const jbyte*>(&bd_addr));
360     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioInTypeChanged, (jint)ext_input_id,
361                                  (jint)type, addr.get());
362   }
363 
OnExtAudioInGainSettingPropertiesChanged(const RawAddress & bd_addr,uint8_t ext_input_id,uint8_t unit,int8_t min,int8_t max)364   void OnExtAudioInGainSettingPropertiesChanged(const RawAddress& bd_addr, uint8_t ext_input_id,
365                                                 uint8_t unit, int8_t min, int8_t max) override {
366     log::info("");
367 
368     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
369     CallbackEnv sCallbackEnv(__func__);
370     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
371       return;
372     }
373 
374     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
375                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
376     if (!addr.get()) {
377       log::error("Failed to get addr for {}", bd_addr);
378       return;
379     }
380 
381     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
382                                      reinterpret_cast<const jbyte*>(&bd_addr));
383     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioInGainSettingPropertiesChanged,
384                                  (jint)ext_input_id, (jint)unit, (jint)min, (jint)max, addr.get());
385   }
386 
OnExtAudioInDescriptionChanged(const RawAddress & bd_addr,uint8_t ext_input_id,std::string description,bool is_writable)387   void OnExtAudioInDescriptionChanged(const RawAddress& bd_addr, uint8_t ext_input_id,
388                                       std::string description, bool is_writable) override {
389     log::info("");
390 
391     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
392     CallbackEnv sCallbackEnv(__func__);
393     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) {
394       return;
395     }
396 
397     ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
398                                     sCallbackEnv->NewByteArray(sizeof(RawAddress)));
399     if (!addr.get()) {
400       log::error("Failed to get addr for {}", bd_addr);
401       return;
402     }
403 
404     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
405                                      reinterpret_cast<const jbyte*>(&bd_addr));
406     jstring jdescription = sCallbackEnv->NewStringUTF(description.c_str());
407     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioInDescriptionChanged,
408                                  (jint)ext_input_id, jdescription, (jboolean)is_writable,
409                                  addr.get());
410   }
411 };
412 
413 static VolumeControlCallbacksImpl sVolumeControlCallbacks;
414 
initNative(JNIEnv * env,jobject object)415 static void initNative(JNIEnv* env, jobject object) {
416   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
417   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
418 
419   const bt_interface_t* btInf = getBluetoothInterface();
420   if (btInf == nullptr) {
421     log::error("Bluetooth module is not loaded");
422     return;
423   }
424 
425   if (sVolumeControlInterface != nullptr) {
426     log::info("Cleaning up VolumeControl Interface before initializing...");
427     sVolumeControlInterface->Cleanup();
428     sVolumeControlInterface = nullptr;
429   }
430 
431   if (mCallbacksObj != nullptr) {
432     log::info("Cleaning up VolumeControl callback object");
433     env->DeleteGlobalRef(mCallbacksObj);
434     mCallbacksObj = nullptr;
435   }
436 
437   if ((mCallbacksObj = env->NewGlobalRef(env->GetObjectField(object, sCallbacksField))) ==
438       nullptr) {
439     log::error("Failed to allocate Global Ref for Volume control Callbacks");
440     return;
441   }
442 
443   sVolumeControlInterface =
444           const_cast<VolumeControlInterface*>(reinterpret_cast<const VolumeControlInterface*>(
445                   btInf->get_profile_interface(BT_PROFILE_VC_ID)));
446 
447   if (sVolumeControlInterface == nullptr) {
448     log::error("Failed to get Bluetooth Volume Control Interface");
449     return;
450   }
451 
452   sVolumeControlInterface->Init(&sVolumeControlCallbacks);
453 }
454 
cleanupNative(JNIEnv * env,jobject)455 static void cleanupNative(JNIEnv* env, jobject /* object */) {
456   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
457   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
458 
459   const bt_interface_t* btInf = getBluetoothInterface();
460   if (btInf == nullptr) {
461     log::error("Bluetooth module is not loaded");
462     return;
463   }
464 
465   if (sVolumeControlInterface != nullptr) {
466     sVolumeControlInterface->Cleanup();
467     sVolumeControlInterface = nullptr;
468   }
469 
470   if (mCallbacksObj != nullptr) {
471     env->DeleteGlobalRef(mCallbacksObj);
472     mCallbacksObj = nullptr;
473   }
474 }
475 
connectVolumeControlNative(JNIEnv * env,jobject,jbyteArray address)476 static jboolean connectVolumeControlNative(JNIEnv* env, jobject /* object */, jbyteArray address) {
477   log::info("");
478   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
479 
480   if (!sVolumeControlInterface) {
481     log::error("Failed to get the Bluetooth Volume Control Interface");
482     return JNI_FALSE;
483   }
484 
485   jbyte* addr = env->GetByteArrayElements(address, nullptr);
486   if (!addr) {
487     jniThrowIOException(env, EINVAL);
488     return JNI_FALSE;
489   }
490 
491   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
492   sVolumeControlInterface->Connect(*tmpraw);
493   env->ReleaseByteArrayElements(address, addr, 0);
494   return JNI_TRUE;
495 }
496 
disconnectVolumeControlNative(JNIEnv * env,jobject,jbyteArray address)497 static jboolean disconnectVolumeControlNative(JNIEnv* env, jobject /* object */,
498                                               jbyteArray address) {
499   log::info("");
500   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
501 
502   if (!sVolumeControlInterface) {
503     log::error("Failed to get the Bluetooth Volume Control Interface");
504     return JNI_FALSE;
505   }
506 
507   jbyte* addr = env->GetByteArrayElements(address, nullptr);
508   if (!addr) {
509     jniThrowIOException(env, EINVAL);
510     return JNI_FALSE;
511   }
512 
513   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
514   sVolumeControlInterface->Disconnect(*tmpraw);
515   env->ReleaseByteArrayElements(address, addr, 0);
516   return JNI_TRUE;
517 }
518 
setVolumeNative(JNIEnv * env,jobject,jbyteArray address,jint volume)519 static void setVolumeNative(JNIEnv* env, jobject /* object */, jbyteArray address, jint volume) {
520   if (!sVolumeControlInterface) {
521     log::error("Failed to get the Bluetooth Volume Control Interface");
522     return;
523   }
524 
525   jbyte* addr = env->GetByteArrayElements(address, nullptr);
526   if (!addr) {
527     jniThrowIOException(env, EINVAL);
528     return;
529   }
530 
531   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
532   sVolumeControlInterface->SetVolume(*tmpraw, volume);
533   env->ReleaseByteArrayElements(address, addr, 0);
534 }
535 
setGroupVolumeNative(JNIEnv *,jobject,jint group_id,jint volume)536 static void setGroupVolumeNative(JNIEnv* /* env */, jobject /* object */, jint group_id,
537                                  jint volume) {
538   if (!sVolumeControlInterface) {
539     log::error("Failed to get the Bluetooth Volume Control Interface");
540     return;
541   }
542 
543   sVolumeControlInterface->SetVolume(group_id, volume);
544 }
545 
muteNative(JNIEnv * env,jobject,jbyteArray address)546 static void muteNative(JNIEnv* env, jobject /* object */, jbyteArray address) {
547   if (!sVolumeControlInterface) {
548     log::error("Failed to get the Bluetooth Volume Control Interface");
549     return;
550   }
551 
552   jbyte* addr = env->GetByteArrayElements(address, nullptr);
553   if (!addr) {
554     jniThrowIOException(env, EINVAL);
555     return;
556   }
557 
558   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
559   sVolumeControlInterface->Mute(*tmpraw);
560   env->ReleaseByteArrayElements(address, addr, 0);
561 }
562 
muteGroupNative(JNIEnv *,jobject,jint group_id)563 static void muteGroupNative(JNIEnv* /* env */, jobject /* object */, jint group_id) {
564   if (!sVolumeControlInterface) {
565     log::error("Failed to get the Bluetooth Volume Control Interface");
566     return;
567   }
568   sVolumeControlInterface->Mute(group_id);
569 }
570 
unmuteNative(JNIEnv * env,jobject,jbyteArray address)571 static void unmuteNative(JNIEnv* env, jobject /* object */, jbyteArray address) {
572   if (!sVolumeControlInterface) {
573     log::error("Failed to get the Bluetooth Volume Control Interface");
574     return;
575   }
576 
577   jbyte* addr = env->GetByteArrayElements(address, nullptr);
578   if (!addr) {
579     jniThrowIOException(env, EINVAL);
580     return;
581   }
582 
583   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
584   sVolumeControlInterface->Unmute(*tmpraw);
585   env->ReleaseByteArrayElements(address, addr, 0);
586 }
587 
unmuteGroupNative(JNIEnv *,jobject,jint group_id)588 static void unmuteGroupNative(JNIEnv* /* env */, jobject /* object */, jint group_id) {
589   if (!sVolumeControlInterface) {
590     log::error("Failed to get the Bluetooth Volume Control Interface");
591     return;
592   }
593   sVolumeControlInterface->Unmute(group_id);
594 }
595 
596 /* Native methods for exterbak audio outputs */
getExtAudioOutVolumeOffsetNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id)597 static jboolean getExtAudioOutVolumeOffsetNative(JNIEnv* env, jobject /* object */,
598                                                  jbyteArray address, jint ext_output_id) {
599   log::info("");
600   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
601   if (!sVolumeControlInterface) {
602     return JNI_FALSE;
603   }
604 
605   jbyte* addr = env->GetByteArrayElements(address, nullptr);
606   if (!addr) {
607     jniThrowIOException(env, EINVAL);
608     return JNI_FALSE;
609   }
610 
611   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
612   sVolumeControlInterface->GetExtAudioOutVolumeOffset(*tmpraw, ext_output_id);
613   env->ReleaseByteArrayElements(address, addr, 0);
614   return JNI_TRUE;
615 }
616 
setExtAudioOutVolumeOffsetNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id,jint offset)617 static jboolean setExtAudioOutVolumeOffsetNative(JNIEnv* env, jobject /* object */,
618                                                  jbyteArray address, jint ext_output_id,
619                                                  jint offset) {
620   log::info("");
621   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
622   if (!sVolumeControlInterface) {
623     return JNI_FALSE;
624   }
625 
626   jbyte* addr = env->GetByteArrayElements(address, nullptr);
627   if (!addr) {
628     jniThrowIOException(env, EINVAL);
629     return JNI_FALSE;
630   }
631 
632   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
633   sVolumeControlInterface->SetExtAudioOutVolumeOffset(*tmpraw, ext_output_id, offset);
634   env->ReleaseByteArrayElements(address, addr, 0);
635   return JNI_TRUE;
636 }
637 
getExtAudioOutLocationNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id)638 static jboolean getExtAudioOutLocationNative(JNIEnv* env, jobject /* object */, jbyteArray address,
639                                              jint ext_output_id) {
640   log::info("");
641   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
642   if (!sVolumeControlInterface) {
643     return JNI_FALSE;
644   }
645 
646   jbyte* addr = env->GetByteArrayElements(address, nullptr);
647   if (!addr) {
648     jniThrowIOException(env, EINVAL);
649     return JNI_FALSE;
650   }
651 
652   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
653   sVolumeControlInterface->GetExtAudioOutLocation(*tmpraw, ext_output_id);
654   env->ReleaseByteArrayElements(address, addr, 0);
655   return JNI_TRUE;
656 }
657 
setExtAudioOutLocationNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id,jint location)658 static jboolean setExtAudioOutLocationNative(JNIEnv* env, jobject /* object */, jbyteArray address,
659                                              jint ext_output_id, jint location) {
660   log::info("");
661   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
662   if (!sVolumeControlInterface) {
663     return JNI_FALSE;
664   }
665 
666   jbyte* addr = env->GetByteArrayElements(address, nullptr);
667   if (!addr) {
668     jniThrowIOException(env, EINVAL);
669     return JNI_FALSE;
670   }
671 
672   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
673   sVolumeControlInterface->SetExtAudioOutLocation(*tmpraw, ext_output_id, location);
674   env->ReleaseByteArrayElements(address, addr, 0);
675   return JNI_TRUE;
676 }
677 
getExtAudioOutDescriptionNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id)678 static jboolean getExtAudioOutDescriptionNative(JNIEnv* env, jobject /* object */,
679                                                 jbyteArray address, jint ext_output_id) {
680   log::info("");
681   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
682   if (!sVolumeControlInterface) {
683     return JNI_FALSE;
684   }
685 
686   jbyte* addr = env->GetByteArrayElements(address, nullptr);
687   if (!addr) {
688     jniThrowIOException(env, EINVAL);
689     return JNI_FALSE;
690   }
691 
692   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
693   sVolumeControlInterface->GetExtAudioOutDescription(*tmpraw, ext_output_id);
694   env->ReleaseByteArrayElements(address, addr, 0);
695   return JNI_TRUE;
696 }
697 
setExtAudioOutDescriptionNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id,jstring descr)698 static jboolean setExtAudioOutDescriptionNative(JNIEnv* env, jobject /* object */,
699                                                 jbyteArray address, jint ext_output_id,
700                                                 jstring descr) {
701   log::info("");
702   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
703   if (!sVolumeControlInterface) {
704     return JNI_FALSE;
705   }
706 
707   jbyte* addr = env->GetByteArrayElements(address, nullptr);
708   if (!addr) {
709     jniThrowIOException(env, EINVAL);
710     return JNI_FALSE;
711   }
712 
713   std::string description;
714   if (descr != nullptr) {
715     const char* value = env->GetStringUTFChars(descr, nullptr);
716     description = std::string(value);
717     env->ReleaseStringUTFChars(descr, value);
718   }
719 
720   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
721   sVolumeControlInterface->SetExtAudioOutDescription(*tmpraw, ext_output_id, description);
722   env->ReleaseByteArrayElements(address, addr, 0);
723   return JNI_TRUE;
724 }
725 
726 /* Native methods for external audio inputs */
getExtAudioInStateNative(JNIEnv * env,jobject,jbyteArray address,jint ext_input_id)727 static jboolean getExtAudioInStateNative(JNIEnv* env, jobject /* object */, jbyteArray address,
728                                          jint ext_input_id) {
729   log::info("");
730   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
731   if (!sVolumeControlInterface) {
732     return JNI_FALSE;
733   }
734 
735   jbyte* addr = env->GetByteArrayElements(address, nullptr);
736   if (!addr) {
737     jniThrowIOException(env, EINVAL);
738     return JNI_FALSE;
739   }
740 
741   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
742   sVolumeControlInterface->GetExtAudioInState(*tmpraw, ext_input_id);
743   env->ReleaseByteArrayElements(address, addr, 0);
744   return JNI_TRUE;
745 }
746 
getExtAudioInStatusNative(JNIEnv * env,jobject,jbyteArray address,jint ext_input_id)747 static jboolean getExtAudioInStatusNative(JNIEnv* env, jobject /* object */, jbyteArray address,
748                                           jint ext_input_id) {
749   log::info("");
750   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
751   if (!sVolumeControlInterface) {
752     return JNI_FALSE;
753   }
754 
755   jbyte* addr = env->GetByteArrayElements(address, nullptr);
756   if (!addr) {
757     jniThrowIOException(env, EINVAL);
758     return JNI_FALSE;
759   }
760 
761   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
762   sVolumeControlInterface->GetExtAudioInStatus(*tmpraw, ext_input_id);
763   env->ReleaseByteArrayElements(address, addr, 0);
764   return JNI_TRUE;
765 }
766 
getExtAudioInTypeNative(JNIEnv * env,jobject,jbyteArray address,jint ext_input_id)767 static jboolean getExtAudioInTypeNative(JNIEnv* env, jobject /* object */, jbyteArray address,
768                                         jint ext_input_id) {
769   log::info("");
770   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
771   if (!sVolumeControlInterface) {
772     return JNI_FALSE;
773   }
774 
775   jbyte* addr = env->GetByteArrayElements(address, nullptr);
776   if (!addr) {
777     jniThrowIOException(env, EINVAL);
778     return JNI_FALSE;
779   }
780 
781   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
782   sVolumeControlInterface->GetExtAudioInType(*tmpraw, ext_input_id);
783   env->ReleaseByteArrayElements(address, addr, 0);
784   return JNI_TRUE;
785 }
786 
getExtAudioInGainPropsNative(JNIEnv * env,jobject,jbyteArray address,jint ext_input_id)787 static jboolean getExtAudioInGainPropsNative(JNIEnv* env, jobject /* object */, jbyteArray address,
788                                              jint ext_input_id) {
789   log::info("");
790   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
791   if (!sVolumeControlInterface) {
792     return JNI_FALSE;
793   }
794 
795   jbyte* addr = env->GetByteArrayElements(address, nullptr);
796   if (!addr) {
797     jniThrowIOException(env, EINVAL);
798     return JNI_FALSE;
799   }
800 
801   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
802   sVolumeControlInterface->GetExtAudioInGainProps(*tmpraw, ext_input_id);
803   env->ReleaseByteArrayElements(address, addr, 0);
804   return JNI_TRUE;
805 }
806 
getExtAudioInDescriptionNative(JNIEnv * env,jobject,jbyteArray address,jint ext_input_id)807 static jboolean getExtAudioInDescriptionNative(JNIEnv* env, jobject /* object */,
808                                                jbyteArray address, jint ext_input_id) {
809   log::info("");
810   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
811   if (!sVolumeControlInterface) {
812     return JNI_FALSE;
813   }
814 
815   jbyte* addr = env->GetByteArrayElements(address, nullptr);
816   if (!addr) {
817     jniThrowIOException(env, EINVAL);
818     return JNI_FALSE;
819   }
820 
821   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
822   sVolumeControlInterface->GetExtAudioInDescription(*tmpraw, ext_input_id);
823   env->ReleaseByteArrayElements(address, addr, 0);
824   return JNI_TRUE;
825 }
826 
setExtAudioInDescriptionNative(JNIEnv * env,jobject,jbyteArray address,jint ext_input_id,jstring descr)827 static jboolean setExtAudioInDescriptionNative(JNIEnv* env, jobject /* object */,
828                                                jbyteArray address, jint ext_input_id,
829                                                jstring descr) {
830   log::info("");
831   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
832   if (!sVolumeControlInterface) {
833     return JNI_FALSE;
834   }
835 
836   jbyte* addr = env->GetByteArrayElements(address, nullptr);
837   if (!addr) {
838     jniThrowIOException(env, EINVAL);
839     return JNI_FALSE;
840   }
841 
842   std::string description;
843   if (descr != nullptr) {
844     const char* value = env->GetStringUTFChars(descr, nullptr);
845     description = std::string(value);
846     env->ReleaseStringUTFChars(descr, value);
847   }
848 
849   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
850   bool ret = sVolumeControlInterface->SetExtAudioInDescription(*tmpraw, ext_input_id, description);
851   env->ReleaseByteArrayElements(address, addr, 0);
852   return ret ? JNI_TRUE : JNI_FALSE;
853 }
854 
setExtAudioInGainSettingNative(JNIEnv * env,jobject,jbyteArray address,jint ext_input_id,jint gain_setting)855 static jboolean setExtAudioInGainSettingNative(JNIEnv* env, jobject /* object */,
856                                                jbyteArray address, jint ext_input_id,
857                                                jint gain_setting) {
858   log::info("");
859   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
860   if (!sVolumeControlInterface) {
861     return JNI_FALSE;
862   }
863 
864   jbyte* addr = env->GetByteArrayElements(address, nullptr);
865   if (!addr) {
866     jniThrowIOException(env, EINVAL);
867     return JNI_FALSE;
868   }
869 
870   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
871   bool ret = sVolumeControlInterface->SetExtAudioInGainSetting(*tmpraw, ext_input_id, gain_setting);
872   env->ReleaseByteArrayElements(address, addr, 0);
873   return ret ? JNI_TRUE : JNI_FALSE;
874 }
875 
setExtAudioInGainModeNative(JNIEnv * env,jobject,jbyteArray address,jint ext_input_id,jint gain_mode)876 static jboolean setExtAudioInGainModeNative(JNIEnv* env, jobject /* object */, jbyteArray address,
877                                             jint ext_input_id, jint gain_mode) {
878   log::info("");
879   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
880   if (!sVolumeControlInterface) {
881     return JNI_FALSE;
882   }
883 
884   jbyte* addr = env->GetByteArrayElements(address, nullptr);
885   if (!addr) {
886     jniThrowIOException(env, EINVAL);
887     return JNI_FALSE;
888   }
889 
890   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
891   bool ret = sVolumeControlInterface->SetExtAudioInGainMode(
892           *tmpraw, ext_input_id, bluetooth::aics::parseGainModeField(gain_mode));
893   env->ReleaseByteArrayElements(address, addr, 0);
894   return ret ? JNI_TRUE : JNI_FALSE;
895 }
896 
setExtAudioInMuteNative(JNIEnv * env,jobject,jbyteArray address,jint ext_input_id,jint mute)897 static jboolean setExtAudioInMuteNative(JNIEnv* env, jobject /* object */, jbyteArray address,
898                                         jint ext_input_id, jint mute) {
899   log::info("");
900   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
901   if (!sVolumeControlInterface) {
902     return JNI_FALSE;
903   }
904 
905   jbyte* addr = env->GetByteArrayElements(address, nullptr);
906   if (!addr) {
907     jniThrowIOException(env, EINVAL);
908     return JNI_FALSE;
909   }
910 
911   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
912   bool ret = sVolumeControlInterface->SetExtAudioInMute(*tmpraw, ext_input_id,
913                                                         bluetooth::aics::parseMuteField(mute));
914   env->ReleaseByteArrayElements(address, addr, 0);
915   return ret ? JNI_TRUE : JNI_FALSE;
916 }
917 
register_com_android_bluetooth_vc(JNIEnv * env)918 int register_com_android_bluetooth_vc(JNIEnv* env) {
919   const JNINativeMethod methods[] = {
920           {"initNative", "()V", reinterpret_cast<void*>(initNative)},
921           {"cleanupNative", "()V", reinterpret_cast<void*>(cleanupNative)},
922           {"connectVolumeControlNative", "([B)Z",
923            reinterpret_cast<void*>(connectVolumeControlNative)},
924           {"disconnectVolumeControlNative", "([B)Z",
925            reinterpret_cast<void*>(disconnectVolumeControlNative)},
926           {"setVolumeNative", "([BI)V", reinterpret_cast<void*>(setVolumeNative)},
927           {"setGroupVolumeNative", "(II)V", reinterpret_cast<void*>(setGroupVolumeNative)},
928           {"muteNative", "([B)V", reinterpret_cast<void*>(muteNative)},
929           {"muteGroupNative", "(I)V", reinterpret_cast<void*>(muteGroupNative)},
930           {"unmuteNative", "([B)V", reinterpret_cast<void*>(unmuteNative)},
931           {"unmuteGroupNative", "(I)V", reinterpret_cast<void*>(unmuteGroupNative)},
932           {"getExtAudioOutVolumeOffsetNative", "([BI)Z",
933            reinterpret_cast<void*>(getExtAudioOutVolumeOffsetNative)},
934           {"setExtAudioOutVolumeOffsetNative", "([BII)Z",
935            reinterpret_cast<void*>(setExtAudioOutVolumeOffsetNative)},
936           {"getExtAudioOutLocationNative", "([BI)Z",
937            reinterpret_cast<void*>(getExtAudioOutLocationNative)},
938           {"setExtAudioOutLocationNative", "([BII)Z",
939            reinterpret_cast<void*>(setExtAudioOutLocationNative)},
940           {"getExtAudioOutDescriptionNative", "([BI)Z",
941            reinterpret_cast<void*>(getExtAudioOutDescriptionNative)},
942           {"setExtAudioOutDescriptionNative", "([BILjava/lang/String;)Z",
943            reinterpret_cast<void*>(setExtAudioOutDescriptionNative)},
944           {"getExtAudioInStateNative", "([BI)Z", reinterpret_cast<void*>(getExtAudioInStateNative)},
945           {"getExtAudioInStatusNative", "([BI)Z",
946            reinterpret_cast<void*>(getExtAudioInStatusNative)},
947           {"getExtAudioInTypeNative", "([BI)Z", reinterpret_cast<void*>(getExtAudioInTypeNative)},
948           {"getExtAudioInGainPropsNative", "([BI)Z",
949            reinterpret_cast<void*>(getExtAudioInGainPropsNative)},
950           {"getExtAudioInDescriptionNative", "([BI)Z",
951            reinterpret_cast<void*>(getExtAudioInDescriptionNative)},
952           {"setExtAudioInDescriptionNative", "([BILjava/lang/String;)Z",
953            reinterpret_cast<void*>(setExtAudioInDescriptionNative)},
954           {"setExtAudioInGainSettingNative", "([BII)Z",
955            reinterpret_cast<void*>(setExtAudioInGainSettingNative)},
956           {"setExtAudioInGainModeNative", "([BII)Z",
957            reinterpret_cast<void*>(setExtAudioInGainModeNative)},
958           {"setExtAudioInMuteNative", "([BII)Z", reinterpret_cast<void*>(setExtAudioInMuteNative)},
959   };
960   const int result = REGISTER_NATIVE_METHODS(
961           env, "com/android/bluetooth/vc/VolumeControlNativeInterface", methods);
962   if (result != 0) {
963     return result;
964   }
965 
966   jclass jniVolumeControlNativeInterfaceClass =
967           env->FindClass("com/android/bluetooth/vc/VolumeControlNativeInterface");
968   sCallbacksField = env->GetFieldID(jniVolumeControlNativeInterfaceClass, "mNativeCallback",
969                                     "Lcom/android/bluetooth/vc/VolumeControlNativeCallback;");
970   env->DeleteLocalRef(jniVolumeControlNativeInterfaceClass);
971 
972   const JNIJavaMethod javaMethods[] = {
973           {"onConnectionStateChanged", "(I[B)V", &method_onConnectionStateChanged},
974           {"onVolumeStateChanged", "(IZI[BZ)V", &method_onVolumeStateChanged},
975           {"onGroupVolumeStateChanged", "(IZIZ)V", &method_onGroupVolumeStateChanged},
976           {"onDeviceAvailable", "(II[B)V", &method_onDeviceAvailable},
977           {"onExtAudioOutVolumeOffsetChanged", "(II[B)V", &method_onExtAudioOutVolumeOffsetChanged},
978           {"onExtAudioOutLocationChanged", "(II[B)V", &method_onExtAudioOutLocationChanged},
979           {"onExtAudioOutDescriptionChanged", "(ILjava/lang/String;[B)V",
980            &method_onExtAudioOutDescriptionChanged},
981           {"onExtAudioInStateChanged", "(IIII[B)V", &method_onExtAudioInStateChanged},
982           {"onExtAudioInSetGainSettingFailed", "(I[B)V", &method_onExtAudioInSetGainSettingFailed},
983           {"onExtAudioInSetMuteFailed", "(I[B)V", &method_onExtAudioInSetMuteFailed},
984           {"onExtAudioInSetGainModeFailed", "(I[B)V", &method_onExtAudioInSetGainModeFailed},
985           {"onExtAudioInStatusChanged", "(II[B)V", &method_onExtAudioInStatusChanged},
986           {"onExtAudioInTypeChanged", "(II[B)V", &method_onExtAudioInTypeChanged},
987           {"onExtAudioInGainSettingPropertiesChanged", "(IIII[B)V",
988            &method_onExtAudioInGainSettingPropertiesChanged},
989           {"onExtAudioInDescriptionChanged", "(ILjava/lang/String;Z[B)V",
990            &method_onExtAudioInDescriptionChanged},
991   };
992   GET_JAVA_METHODS(env, "com/android/bluetooth/vc/VolumeControlNativeCallback", javaMethods);
993 
994   return 0;
995 }
996 }  // namespace android
997