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