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