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 <string.h>
21 
22 #include <shared_mutex>
23 
24 #include "com_android_bluetooth.h"
25 #include "hardware/bt_vc.h"
26 
27 using bluetooth::vc::ConnectionState;
28 using bluetooth::vc::VolumeControlCallbacks;
29 using bluetooth::vc::VolumeControlInterface;
30 
31 namespace android {
32 static jmethodID method_onConnectionStateChanged;
33 static jmethodID method_onVolumeStateChanged;
34 static jmethodID method_onGroupVolumeStateChanged;
35 static jmethodID method_onDeviceAvailable;
36 static jmethodID method_onExtAudioOutVolumeOffsetChanged;
37 static jmethodID method_onExtAudioOutLocationChanged;
38 static jmethodID method_onExtAudioOutDescriptionChanged;
39 
40 static VolumeControlInterface* sVolumeControlInterface = nullptr;
41 static std::shared_timed_mutex interface_mutex;
42 
43 static jobject mCallbacksObj = nullptr;
44 static std::shared_timed_mutex callbacks_mutex;
45 
46 class VolumeControlCallbacksImpl : public VolumeControlCallbacks {
47  public:
48   ~VolumeControlCallbacksImpl() = default;
OnConnectionState(ConnectionState state,const RawAddress & bd_addr)49   void OnConnectionState(ConnectionState state,
50                          const RawAddress& bd_addr) override {
51     log::info("state:{}, addr: {}", int(state),
52               bd_addr.ToRedactedStringForLogging());
53 
54     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
55     CallbackEnv sCallbackEnv(__func__);
56     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
57 
58     ScopedLocalRef<jbyteArray> addr(
59         sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
60     if (!addr.get()) {
61       log::error("Failed to new jbyteArray bd addr for connection state");
62       return;
63     }
64 
65     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
66                                      (jbyte*)&bd_addr);
67     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
68                                  (jint)state, addr.get());
69   }
70 
OnVolumeStateChanged(const RawAddress & bd_addr,uint8_t volume,bool mute,bool isAutonomous)71   void OnVolumeStateChanged(const RawAddress& bd_addr, uint8_t volume,
72                             bool mute, bool isAutonomous) override {
73     log::info("");
74 
75     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
76     CallbackEnv sCallbackEnv(__func__);
77     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
78 
79     ScopedLocalRef<jbyteArray> addr(
80         sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
81     if (!addr.get()) {
82       log::error("Failed to new jbyteArray bd addr for connection state");
83       return;
84     }
85 
86     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
87                                      (jbyte*)&bd_addr);
88     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVolumeStateChanged,
89                                  (jint)volume, (jboolean)mute, addr.get(), (jboolean)isAutonomous);
90   }
91 
OnGroupVolumeStateChanged(int group_id,uint8_t volume,bool mute,bool isAutonomous)92   void OnGroupVolumeStateChanged(int group_id, uint8_t volume,
93                                  bool mute, bool isAutonomous) override {
94     log::info("");
95 
96     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
97     CallbackEnv sCallbackEnv(__func__);
98     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
99 
100     sCallbackEnv->CallVoidMethod(mCallbacksObj,
101                                  method_onGroupVolumeStateChanged, (jint)volume,
102                                  (jboolean)mute, group_id, (jboolean)isAutonomous);
103   }
104 
OnDeviceAvailable(const RawAddress & bd_addr,uint8_t num_offsets)105   void OnDeviceAvailable(const RawAddress& bd_addr,
106                          uint8_t num_offsets) override {
107     log::info("");
108 
109     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
110     CallbackEnv sCallbackEnv(__func__);
111     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
112 
113     ScopedLocalRef<jbyteArray> addr(
114         sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
115     if (!addr.get()) {
116       log::error("Failed to new jbyteArray bd addr for onDeviceAvailable");
117       return;
118     }
119 
120     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
121                                      (jbyte*)&bd_addr);
122     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDeviceAvailable,
123                                  (jint)num_offsets, addr.get());
124   }
125 
OnExtAudioOutVolumeOffsetChanged(const RawAddress & bd_addr,uint8_t ext_output_id,int16_t offset)126   void OnExtAudioOutVolumeOffsetChanged(const RawAddress& bd_addr,
127                                         uint8_t ext_output_id,
128                                         int16_t offset) override {
129     log::info("");
130 
131     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
132     CallbackEnv sCallbackEnv(__func__);
133     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
134 
135     ScopedLocalRef<jbyteArray> addr(
136         sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
137     if (!addr.get()) {
138       log::error(
139           "Failed to new jbyteArray bd addr for "
140           "OnExtAudioOutVolumeOffsetChanged");
141       return;
142     }
143 
144     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
145                                      (jbyte*)&bd_addr);
146     sCallbackEnv->CallVoidMethod(mCallbacksObj,
147                                  method_onExtAudioOutVolumeOffsetChanged,
148                                  (jint)ext_output_id, (jint)offset, addr.get());
149   }
150 
OnExtAudioOutLocationChanged(const RawAddress & bd_addr,uint8_t ext_output_id,uint32_t location)151   void OnExtAudioOutLocationChanged(const RawAddress& bd_addr,
152                                     uint8_t ext_output_id,
153                                     uint32_t location) override {
154     log::info("");
155 
156     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
157     CallbackEnv sCallbackEnv(__func__);
158     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
159 
160     ScopedLocalRef<jbyteArray> addr(
161         sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
162     if (!addr.get()) {
163       log::error(
164           "Failed to new jbyteArray bd addr for OnExtAudioOutLocationChanged");
165       return;
166     }
167 
168     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
169                                      (jbyte*)&bd_addr);
170     sCallbackEnv->CallVoidMethod(
171         mCallbacksObj, method_onExtAudioOutLocationChanged, (jint)ext_output_id,
172         (jint)location, addr.get());
173   }
174 
OnExtAudioOutDescriptionChanged(const RawAddress & bd_addr,uint8_t ext_output_id,std::string descr)175   void OnExtAudioOutDescriptionChanged(const RawAddress& bd_addr,
176                                        uint8_t ext_output_id,
177                                        std::string descr) override {
178     log::info("");
179 
180     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
181     CallbackEnv sCallbackEnv(__func__);
182     if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
183 
184     ScopedLocalRef<jbyteArray> addr(
185         sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
186     if (!addr.get()) {
187       log::error(
188           "Failed to new jbyteArray bd addr for "
189           "OnExtAudioOutDescriptionChanged");
190       return;
191     }
192 
193     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
194                                      (jbyte*)&bd_addr);
195     jstring description = sCallbackEnv->NewStringUTF(descr.c_str());
196     sCallbackEnv->CallVoidMethod(mCallbacksObj,
197                                  method_onExtAudioOutDescriptionChanged,
198                                  (jint)ext_output_id, description, addr.get());
199   }
200 };
201 
202 static VolumeControlCallbacksImpl sVolumeControlCallbacks;
203 
initNative(JNIEnv * env,jobject object)204 static void initNative(JNIEnv* env, jobject object) {
205   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
206   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
207 
208   const bt_interface_t* btInf = getBluetoothInterface();
209   if (btInf == nullptr) {
210     log::error("Bluetooth module is not loaded");
211     return;
212   }
213 
214   if (sVolumeControlInterface != nullptr) {
215     log::info("Cleaning up VolumeControl Interface before initializing...");
216     sVolumeControlInterface->Cleanup();
217     sVolumeControlInterface = nullptr;
218   }
219 
220   if (mCallbacksObj != nullptr) {
221     log::info("Cleaning up VolumeControl callback object");
222     env->DeleteGlobalRef(mCallbacksObj);
223     mCallbacksObj = nullptr;
224   }
225 
226   if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
227     log::error("Failed to allocate Global Ref for Volume control Callbacks");
228     return;
229   }
230 
231   sVolumeControlInterface =
232       (VolumeControlInterface*)btInf->get_profile_interface(BT_PROFILE_VC_ID);
233   if (sVolumeControlInterface == nullptr) {
234     log::error("Failed to get Bluetooth Volume Control Interface");
235     return;
236   }
237 
238   sVolumeControlInterface->Init(&sVolumeControlCallbacks);
239 }
240 
cleanupNative(JNIEnv * env,jobject)241 static void cleanupNative(JNIEnv* env, jobject /* object */) {
242   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
243   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
244 
245   const bt_interface_t* btInf = getBluetoothInterface();
246   if (btInf == nullptr) {
247     log::error("Bluetooth module is not loaded");
248     return;
249   }
250 
251   if (sVolumeControlInterface != nullptr) {
252     sVolumeControlInterface->Cleanup();
253     sVolumeControlInterface = nullptr;
254   }
255 
256   if (mCallbacksObj != nullptr) {
257     env->DeleteGlobalRef(mCallbacksObj);
258     mCallbacksObj = nullptr;
259   }
260 }
261 
connectVolumeControlNative(JNIEnv * env,jobject,jbyteArray address)262 static jboolean connectVolumeControlNative(JNIEnv* env, jobject /* object */,
263                                            jbyteArray address) {
264   log::info("");
265   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
266 
267   if (!sVolumeControlInterface) {
268     log::error("Failed to get the Bluetooth Volume Control Interface");
269     return JNI_FALSE;
270   }
271 
272   jbyte* addr = env->GetByteArrayElements(address, nullptr);
273   if (!addr) {
274     jniThrowIOException(env, EINVAL);
275     return JNI_FALSE;
276   }
277 
278   RawAddress* tmpraw = (RawAddress*)addr;
279   sVolumeControlInterface->Connect(*tmpraw);
280   env->ReleaseByteArrayElements(address, addr, 0);
281   return JNI_TRUE;
282 }
283 
disconnectVolumeControlNative(JNIEnv * env,jobject,jbyteArray address)284 static jboolean disconnectVolumeControlNative(JNIEnv* env, jobject /* object */,
285                                               jbyteArray address) {
286   log::info("");
287   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
288 
289   if (!sVolumeControlInterface) {
290     log::error("Failed to get the Bluetooth Volume Control Interface");
291     return JNI_FALSE;
292   }
293 
294   jbyte* addr = env->GetByteArrayElements(address, nullptr);
295   if (!addr) {
296     jniThrowIOException(env, EINVAL);
297     return JNI_FALSE;
298   }
299 
300   RawAddress* tmpraw = (RawAddress*)addr;
301   sVolumeControlInterface->Disconnect(*tmpraw);
302   env->ReleaseByteArrayElements(address, addr, 0);
303   return JNI_TRUE;
304 }
305 
setVolumeNative(JNIEnv * env,jobject,jbyteArray address,jint volume)306 static void setVolumeNative(JNIEnv* env, jobject /* object */,
307                             jbyteArray address, jint volume) {
308   if (!sVolumeControlInterface) {
309     log::error("Failed to get the Bluetooth Volume Control Interface");
310     return;
311   }
312 
313   jbyte* addr = env->GetByteArrayElements(address, nullptr);
314   if (!addr) {
315     jniThrowIOException(env, EINVAL);
316     return;
317   }
318 
319   RawAddress* tmpraw = (RawAddress*)addr;
320   sVolumeControlInterface->SetVolume(*tmpraw, volume);
321   env->ReleaseByteArrayElements(address, addr, 0);
322 }
323 
setGroupVolumeNative(JNIEnv *,jobject,jint group_id,jint volume)324 static void setGroupVolumeNative(JNIEnv* /* env */, jobject /* object */,
325                                  jint group_id, jint volume) {
326   if (!sVolumeControlInterface) {
327     log::error("Failed to get the Bluetooth Volume Control Interface");
328     return;
329   }
330 
331   sVolumeControlInterface->SetVolume(group_id, volume);
332 }
333 
muteNative(JNIEnv * env,jobject,jbyteArray address)334 static void muteNative(JNIEnv* env, jobject /* object */, jbyteArray address) {
335   if (!sVolumeControlInterface) {
336     log::error("Failed to get the Bluetooth Volume Control Interface");
337     return;
338   }
339 
340   jbyte* addr = env->GetByteArrayElements(address, nullptr);
341   if (!addr) {
342     jniThrowIOException(env, EINVAL);
343     return;
344   }
345 
346   RawAddress* tmpraw = (RawAddress*)addr;
347   sVolumeControlInterface->Mute(*tmpraw);
348   env->ReleaseByteArrayElements(address, addr, 0);
349 }
350 
muteGroupNative(JNIEnv *,jobject,jint group_id)351 static void muteGroupNative(JNIEnv* /* env */, jobject /* object */,
352                             jint group_id) {
353   if (!sVolumeControlInterface) {
354     log::error("Failed to get the Bluetooth Volume Control Interface");
355     return;
356   }
357   sVolumeControlInterface->Mute(group_id);
358 }
359 
unmuteNative(JNIEnv * env,jobject,jbyteArray address)360 static void unmuteNative(JNIEnv* env, jobject /* object */,
361                          jbyteArray address) {
362   if (!sVolumeControlInterface) {
363     log::error("Failed to get the Bluetooth Volume Control Interface");
364     return;
365   }
366 
367   jbyte* addr = env->GetByteArrayElements(address, nullptr);
368   if (!addr) {
369     jniThrowIOException(env, EINVAL);
370     return;
371   }
372 
373   RawAddress* tmpraw = (RawAddress*)addr;
374   sVolumeControlInterface->Unmute(*tmpraw);
375   env->ReleaseByteArrayElements(address, addr, 0);
376 }
377 
unmuteGroupNative(JNIEnv *,jobject,jint group_id)378 static void unmuteGroupNative(JNIEnv* /* env */, jobject /* object */,
379                               jint group_id) {
380   if (!sVolumeControlInterface) {
381     log::error("Failed to get the Bluetooth Volume Control Interface");
382     return;
383   }
384   sVolumeControlInterface->Unmute(group_id);
385 }
386 
387 /* Native methods for exterbak audio outputs */
getExtAudioOutVolumeOffsetNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id)388 static jboolean getExtAudioOutVolumeOffsetNative(JNIEnv* env,
389                                                  jobject /* object */,
390                                                  jbyteArray address,
391                                                  jint ext_output_id) {
392   log::info("");
393   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
394   if (!sVolumeControlInterface) return JNI_FALSE;
395 
396   jbyte* addr = env->GetByteArrayElements(address, nullptr);
397   if (!addr) {
398     jniThrowIOException(env, EINVAL);
399     return JNI_FALSE;
400   }
401 
402   RawAddress* tmpraw = (RawAddress*)addr;
403   sVolumeControlInterface->GetExtAudioOutVolumeOffset(*tmpraw, ext_output_id);
404   env->ReleaseByteArrayElements(address, addr, 0);
405   return JNI_TRUE;
406 }
407 
setExtAudioOutVolumeOffsetNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id,jint offset)408 static jboolean setExtAudioOutVolumeOffsetNative(JNIEnv* env,
409                                                  jobject /* object */,
410                                                  jbyteArray address,
411                                                  jint ext_output_id,
412                                                  jint offset) {
413   log::info("");
414   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
415   if (!sVolumeControlInterface) return JNI_FALSE;
416 
417   jbyte* addr = env->GetByteArrayElements(address, nullptr);
418   if (!addr) {
419     jniThrowIOException(env, EINVAL);
420     return JNI_FALSE;
421   }
422 
423   RawAddress* tmpraw = (RawAddress*)addr;
424   sVolumeControlInterface->SetExtAudioOutVolumeOffset(*tmpraw, ext_output_id,
425                                                       offset);
426   env->ReleaseByteArrayElements(address, addr, 0);
427   return JNI_TRUE;
428 }
429 
getExtAudioOutLocationNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id)430 static jboolean getExtAudioOutLocationNative(JNIEnv* env, jobject /* object */,
431                                              jbyteArray address,
432                                              jint ext_output_id) {
433   log::info("");
434   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
435   if (!sVolumeControlInterface) return JNI_FALSE;
436 
437   jbyte* addr = env->GetByteArrayElements(address, nullptr);
438   if (!addr) {
439     jniThrowIOException(env, EINVAL);
440     return JNI_FALSE;
441   }
442 
443   RawAddress* tmpraw = (RawAddress*)addr;
444   sVolumeControlInterface->GetExtAudioOutLocation(*tmpraw, ext_output_id);
445   env->ReleaseByteArrayElements(address, addr, 0);
446   return JNI_TRUE;
447 }
448 
setExtAudioOutLocationNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id,jint location)449 static jboolean setExtAudioOutLocationNative(JNIEnv* env, jobject /* object */,
450                                              jbyteArray address,
451                                              jint ext_output_id,
452                                              jint location) {
453   log::info("");
454   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
455   if (!sVolumeControlInterface) return JNI_FALSE;
456 
457   jbyte* addr = env->GetByteArrayElements(address, nullptr);
458   if (!addr) {
459     jniThrowIOException(env, EINVAL);
460     return JNI_FALSE;
461   }
462 
463   RawAddress* tmpraw = (RawAddress*)addr;
464   sVolumeControlInterface->SetExtAudioOutLocation(*tmpraw, ext_output_id,
465                                                   location);
466   env->ReleaseByteArrayElements(address, addr, 0);
467   return JNI_TRUE;
468 }
469 
getExtAudioOutDescriptionNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id)470 static jboolean getExtAudioOutDescriptionNative(JNIEnv* env,
471                                                 jobject /* object */,
472                                                 jbyteArray address,
473                                                 jint ext_output_id) {
474   log::info("");
475   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
476   if (!sVolumeControlInterface) return JNI_FALSE;
477 
478   jbyte* addr = env->GetByteArrayElements(address, nullptr);
479   if (!addr) {
480     jniThrowIOException(env, EINVAL);
481     return JNI_FALSE;
482   }
483 
484   RawAddress* tmpraw = (RawAddress*)addr;
485   sVolumeControlInterface->GetExtAudioOutDescription(*tmpraw, ext_output_id);
486   env->ReleaseByteArrayElements(address, addr, 0);
487   return JNI_TRUE;
488 }
489 
setExtAudioOutDescriptionNative(JNIEnv * env,jobject,jbyteArray address,jint ext_output_id,jstring descr)490 static jboolean setExtAudioOutDescriptionNative(JNIEnv* env,
491                                                 jobject /* object */,
492                                                 jbyteArray address,
493                                                 jint ext_output_id,
494                                                 jstring descr) {
495   log::info("");
496   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
497   if (!sVolumeControlInterface) return JNI_FALSE;
498 
499   jbyte* addr = env->GetByteArrayElements(address, nullptr);
500   if (!addr) {
501     jniThrowIOException(env, EINVAL);
502     return JNI_FALSE;
503   }
504 
505   std::string description;
506   if (descr != nullptr) {
507     const char* value = env->GetStringUTFChars(descr, nullptr);
508     description = std::string(value);
509     env->ReleaseStringUTFChars(descr, value);
510   }
511 
512   RawAddress* tmpraw = (RawAddress*)addr;
513   sVolumeControlInterface->SetExtAudioOutDescription(*tmpraw, ext_output_id,
514                                                      description);
515   env->ReleaseByteArrayElements(address, addr, 0);
516   return JNI_TRUE;
517 }
518 
register_com_android_bluetooth_vc(JNIEnv * env)519 int register_com_android_bluetooth_vc(JNIEnv* env) {
520   const JNINativeMethod methods[] = {
521       {"initNative", "()V", (void*)initNative},
522       {"cleanupNative", "()V", (void*)cleanupNative},
523       {"connectVolumeControlNative", "([B)Z",
524        (void*)connectVolumeControlNative},
525       {"disconnectVolumeControlNative", "([B)Z",
526        (void*)disconnectVolumeControlNative},
527       {"setVolumeNative", "([BI)V", (void*)setVolumeNative},
528       {"setGroupVolumeNative", "(II)V", (void*)setGroupVolumeNative},
529       {"muteNative", "([B)V", (void*)muteNative},
530       {"muteGroupNative", "(I)V", (void*)muteGroupNative},
531       {"unmuteNative", "([B)V", (void*)unmuteNative},
532       {"unmuteGroupNative", "(I)V", (void*)unmuteGroupNative},
533       {"getExtAudioOutVolumeOffsetNative", "([BI)Z",
534        (void*)getExtAudioOutVolumeOffsetNative},
535       {"setExtAudioOutVolumeOffsetNative", "([BII)Z",
536        (void*)setExtAudioOutVolumeOffsetNative},
537       {"getExtAudioOutLocationNative", "([BI)Z",
538        (void*)getExtAudioOutLocationNative},
539       {"setExtAudioOutLocationNative", "([BII)Z",
540        (void*)setExtAudioOutLocationNative},
541       {"getExtAudioOutDescriptionNative", "([BI)Z",
542        (void*)getExtAudioOutDescriptionNative},
543       {"setExtAudioOutDescriptionNative", "([BILjava/lang/String;)Z",
544        (void*)setExtAudioOutDescriptionNative},
545   };
546   const int result = REGISTER_NATIVE_METHODS(
547       env, "com/android/bluetooth/vc/VolumeControlNativeInterface", methods);
548   if (result != 0) {
549     return result;
550   }
551 
552   const JNIJavaMethod javaMethods[] = {
553       {"onConnectionStateChanged", "(I[B)V", &method_onConnectionStateChanged},
554       {"onVolumeStateChanged", "(IZ[BZ)V", &method_onVolumeStateChanged},
555       {"onGroupVolumeStateChanged", "(IZIZ)V",
556        &method_onGroupVolumeStateChanged},
557       {"onDeviceAvailable", "(I[B)V", &method_onDeviceAvailable},
558       {"onExtAudioOutVolumeOffsetChanged", "(II[B)V",
559        &method_onExtAudioOutVolumeOffsetChanged},
560       {"onExtAudioOutLocationChanged", "(II[B)V",
561        &method_onExtAudioOutLocationChanged},
562       {"onExtAudioOutDescriptionChanged", "(ILjava/lang/String;[B)V",
563        &method_onExtAudioOutDescriptionChanged},
564   };
565   GET_JAVA_METHODS(env, "com/android/bluetooth/vc/VolumeControlNativeInterface",
566                    javaMethods);
567 
568   return 0;
569 }
570 }  // namespace android
571