1 /*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "BluetoothA2dpServiceJni"
18
19 #include <string.h>
20
21 #include <shared_mutex>
22
23 #include "btif/include/btif_util.h"
24 #include "com_android_bluetooth.h"
25 #include "hardware/bt_av.h"
26
27 namespace android {
28 static jmethodID method_onConnectionStateChanged;
29 static jmethodID method_onAudioStateChanged;
30 static jmethodID method_onCodecConfigChanged;
31 static jmethodID method_isMandatoryCodecPreferred;
32
33 static struct {
34 jclass clazz;
35 jmethodID constructor;
36 jmethodID getCodecType;
37 jmethodID getCodecPriority;
38 jmethodID getSampleRate;
39 jmethodID getBitsPerSample;
40 jmethodID getChannelMode;
41 jmethodID getCodecSpecific1;
42 jmethodID getCodecSpecific2;
43 jmethodID getCodecSpecific3;
44 jmethodID getCodecSpecific4;
45 } android_bluetooth_BluetoothCodecConfig;
46
47 static const btav_source_interface_t* sBluetoothA2dpInterface = nullptr;
48 static std::vector<btav_a2dp_codec_info_t> supported_codecs;
49 static std::shared_timed_mutex interface_mutex;
50
51 static jobject mCallbacksObj = nullptr;
52 static std::shared_timed_mutex callbacks_mutex;
53
bta2dp_connection_state_callback(const RawAddress & bd_addr,btav_connection_state_t state,const btav_error_t &)54 static void bta2dp_connection_state_callback(const RawAddress& bd_addr,
55 btav_connection_state_t state,
56 const btav_error_t& /* error */) {
57 log::info("{}: state: {}", bd_addr, dump_av_conn_state(state));
58
59 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
60 CallbackEnv sCallbackEnv(__func__);
61 if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
62
63 ScopedLocalRef<jbyteArray> addr(
64 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
65 if (!addr.get()) {
66 log::error("Fail to new jbyteArray bd addr");
67 return;
68 }
69
70 sCallbackEnv->SetByteArrayRegion(
71 addr.get(), 0, sizeof(RawAddress),
72 reinterpret_cast<const jbyte*>(bd_addr.address));
73 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
74 addr.get(), (jint)state);
75 }
76
bta2dp_audio_state_callback(const RawAddress & bd_addr,btav_audio_state_t state)77 static void bta2dp_audio_state_callback(const RawAddress& bd_addr,
78 btav_audio_state_t state) {
79 log::info("{}: state: {}", bd_addr, dump_av_audio_state(state));
80
81 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
82 CallbackEnv sCallbackEnv(__func__);
83 if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
84
85 ScopedLocalRef<jbyteArray> addr(
86 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
87 if (!addr.get()) {
88 log::error("Fail to new jbyteArray bd addr");
89 return;
90 }
91
92 sCallbackEnv->SetByteArrayRegion(
93 addr.get(), 0, sizeof(RawAddress),
94 reinterpret_cast<const jbyte*>(bd_addr.address));
95 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged,
96 addr.get(), (jint)state);
97 }
98
bta2dp_audio_config_callback(const RawAddress & bd_addr,btav_a2dp_codec_config_t codec_config,std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities)99 static void bta2dp_audio_config_callback(
100 const RawAddress& bd_addr, btav_a2dp_codec_config_t codec_config,
101 std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
102 std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities) {
103 log::info(
104 "{}: codec: {}, local codecs: {}, selectable codecs: {}", bd_addr,
105 codec_config.CodecNameStr(),
106 btav_a2dp_codec_config_t::PrintCodecs(codecs_local_capabilities),
107 btav_a2dp_codec_config_t::PrintCodecs(codecs_selectable_capabilities));
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 jobject codecConfigObj = sCallbackEnv->NewObject(
114 android_bluetooth_BluetoothCodecConfig.clazz,
115 android_bluetooth_BluetoothCodecConfig.constructor,
116 (jint)codec_config.codec_type, (jint)codec_config.codec_priority,
117 (jint)codec_config.sample_rate, (jint)codec_config.bits_per_sample,
118 (jint)codec_config.channel_mode, (jlong)codec_config.codec_specific_1,
119 (jlong)codec_config.codec_specific_2,
120 (jlong)codec_config.codec_specific_3,
121 (jlong)codec_config.codec_specific_4);
122
123 jsize i = 0;
124 jobjectArray local_capabilities_array = sCallbackEnv->NewObjectArray(
125 (jsize)codecs_local_capabilities.size(),
126 android_bluetooth_BluetoothCodecConfig.clazz, nullptr);
127 for (auto const& cap : codecs_local_capabilities) {
128 jobject capObj = sCallbackEnv->NewObject(
129 android_bluetooth_BluetoothCodecConfig.clazz,
130 android_bluetooth_BluetoothCodecConfig.constructor,
131 (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
132 (jint)cap.bits_per_sample, (jint)cap.channel_mode,
133 (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
134 (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
135 sCallbackEnv->SetObjectArrayElement(local_capabilities_array, i++, capObj);
136 sCallbackEnv->DeleteLocalRef(capObj);
137 }
138
139 i = 0;
140 jobjectArray selectable_capabilities_array = sCallbackEnv->NewObjectArray(
141 (jsize)codecs_selectable_capabilities.size(),
142 android_bluetooth_BluetoothCodecConfig.clazz, nullptr);
143 for (auto const& cap : codecs_selectable_capabilities) {
144 jobject capObj = sCallbackEnv->NewObject(
145 android_bluetooth_BluetoothCodecConfig.clazz,
146 android_bluetooth_BluetoothCodecConfig.constructor,
147 (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
148 (jint)cap.bits_per_sample, (jint)cap.channel_mode,
149 (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
150 (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
151 sCallbackEnv->SetObjectArrayElement(selectable_capabilities_array, i++,
152 capObj);
153 sCallbackEnv->DeleteLocalRef(capObj);
154 }
155
156 ScopedLocalRef<jbyteArray> addr(
157 sCallbackEnv.get(), sCallbackEnv->NewByteArray(RawAddress::kLength));
158 if (!addr.get()) {
159 log::error("Fail to new jbyteArray bd addr");
160 return;
161 }
162 sCallbackEnv->SetByteArrayRegion(
163 addr.get(), 0, RawAddress::kLength,
164 reinterpret_cast<const jbyte*>(bd_addr.address));
165
166 sCallbackEnv->CallVoidMethod(
167 mCallbacksObj, method_onCodecConfigChanged, addr.get(), codecConfigObj,
168 local_capabilities_array, selectable_capabilities_array);
169 }
170
bta2dp_mandatory_codec_preferred_callback(const RawAddress & bd_addr)171 static bool bta2dp_mandatory_codec_preferred_callback(
172 const RawAddress& bd_addr) {
173 log::info("{}", bd_addr);
174
175 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
176 CallbackEnv sCallbackEnv(__func__);
177 if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return false;
178
179 ScopedLocalRef<jbyteArray> addr(
180 sCallbackEnv.get(), sCallbackEnv->NewByteArray(RawAddress::kLength));
181 if (!addr.get()) {
182 log::error("Fail to new jbyteArray bd addr");
183 return false;
184 }
185 sCallbackEnv->SetByteArrayRegion(
186 addr.get(), 0, RawAddress::kLength,
187 reinterpret_cast<const jbyte*>(bd_addr.address));
188 return sCallbackEnv->CallBooleanMethod(
189 mCallbacksObj, method_isMandatoryCodecPreferred, addr.get());
190 }
191
192 static btav_source_callbacks_t sBluetoothA2dpCallbacks = {
193 sizeof(sBluetoothA2dpCallbacks),
194 bta2dp_connection_state_callback,
195 bta2dp_audio_state_callback,
196 bta2dp_audio_config_callback,
197 bta2dp_mandatory_codec_preferred_callback,
198 };
199
prepareCodecPreferences(JNIEnv * env,jobject,jobjectArray codecConfigArray)200 static std::vector<btav_a2dp_codec_config_t> prepareCodecPreferences(
201 JNIEnv* env, jobject /* object */, jobjectArray codecConfigArray) {
202 std::vector<btav_a2dp_codec_config_t> codec_preferences;
203
204 int numConfigs = env->GetArrayLength(codecConfigArray);
205 for (int i = 0; i < numConfigs; i++) {
206 jobject jcodecConfig = env->GetObjectArrayElement(codecConfigArray, i);
207 if (jcodecConfig == nullptr) continue;
208 if (!env->IsInstanceOf(jcodecConfig,
209 android_bluetooth_BluetoothCodecConfig.clazz)) {
210 log::error("Invalid BluetoothCodecConfig instance");
211 continue;
212 }
213 jint codecType = env->CallIntMethod(
214 jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecType);
215 jint codecPriority = env->CallIntMethod(
216 jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecPriority);
217 jint sampleRate = env->CallIntMethod(
218 jcodecConfig, android_bluetooth_BluetoothCodecConfig.getSampleRate);
219 jint bitsPerSample = env->CallIntMethod(
220 jcodecConfig, android_bluetooth_BluetoothCodecConfig.getBitsPerSample);
221 jint channelMode = env->CallIntMethod(
222 jcodecConfig, android_bluetooth_BluetoothCodecConfig.getChannelMode);
223 jlong codecSpecific1 = env->CallLongMethod(
224 jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific1);
225 jlong codecSpecific2 = env->CallLongMethod(
226 jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific2);
227 jlong codecSpecific3 = env->CallLongMethod(
228 jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific3);
229 jlong codecSpecific4 = env->CallLongMethod(
230 jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific4);
231
232 btav_a2dp_codec_config_t codec_config = {
233 .codec_type = static_cast<btav_a2dp_codec_index_t>(codecType),
234 .codec_priority =
235 static_cast<btav_a2dp_codec_priority_t>(codecPriority),
236 .sample_rate = static_cast<btav_a2dp_codec_sample_rate_t>(sampleRate),
237 .bits_per_sample =
238 static_cast<btav_a2dp_codec_bits_per_sample_t>(bitsPerSample),
239 .channel_mode =
240 static_cast<btav_a2dp_codec_channel_mode_t>(channelMode),
241 .codec_specific_1 = codecSpecific1,
242 .codec_specific_2 = codecSpecific2,
243 .codec_specific_3 = codecSpecific3,
244 .codec_specific_4 = codecSpecific4};
245
246 codec_preferences.push_back(codec_config);
247 }
248 return codec_preferences;
249 }
250
initNative(JNIEnv * env,jobject object,jint maxConnectedAudioDevices,jobjectArray codecConfigArray,jobjectArray codecOffloadingArray)251 static void initNative(JNIEnv* env, jobject object,
252 jint maxConnectedAudioDevices,
253 jobjectArray codecConfigArray,
254 jobjectArray codecOffloadingArray) {
255 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
256 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
257
258 const bt_interface_t* btInf = getBluetoothInterface();
259 if (btInf == nullptr) {
260 log::error("Bluetooth module is not loaded");
261 return;
262 }
263
264 if (sBluetoothA2dpInterface != nullptr) {
265 log::warn("Cleaning up A2DP Interface before initializing...");
266 sBluetoothA2dpInterface->cleanup();
267 sBluetoothA2dpInterface = nullptr;
268 }
269
270 if (mCallbacksObj != nullptr) {
271 log::warn("Cleaning up A2DP callback object");
272 env->DeleteGlobalRef(mCallbacksObj);
273 mCallbacksObj = nullptr;
274 }
275
276 if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
277 log::error("Failed to allocate Global Ref for A2DP Callbacks");
278 return;
279 }
280
281 android_bluetooth_BluetoothCodecConfig.clazz = (jclass)env->NewGlobalRef(
282 env->FindClass("android/bluetooth/BluetoothCodecConfig"));
283 if (android_bluetooth_BluetoothCodecConfig.clazz == nullptr) {
284 log::error("Failed to allocate Global Ref for BluetoothCodecConfig class");
285 return;
286 }
287
288 sBluetoothA2dpInterface =
289 (btav_source_interface_t*)btInf->get_profile_interface(
290 BT_PROFILE_ADVANCED_AUDIO_ID);
291 if (sBluetoothA2dpInterface == nullptr) {
292 log::error("Failed to get Bluetooth A2DP Interface");
293 return;
294 }
295
296 std::vector<btav_a2dp_codec_config_t> codec_priorities =
297 prepareCodecPreferences(env, object, codecConfigArray);
298
299 std::vector<btav_a2dp_codec_config_t> codec_offloading =
300 prepareCodecPreferences(env, object, codecOffloadingArray);
301
302 bt_status_t status = sBluetoothA2dpInterface->init(
303 &sBluetoothA2dpCallbacks, maxConnectedAudioDevices, codec_priorities,
304 codec_offloading, &supported_codecs);
305 if (status != BT_STATUS_SUCCESS) {
306 log::error("Failed to initialize Bluetooth A2DP, status: {}",
307 bt_status_text(status));
308 sBluetoothA2dpInterface = nullptr;
309 return;
310 }
311 }
312
cleanupNative(JNIEnv * env,jobject)313 static void cleanupNative(JNIEnv* env, jobject /* object */) {
314 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
315 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
316
317 const bt_interface_t* btInf = getBluetoothInterface();
318 if (btInf == nullptr) {
319 log::error("Bluetooth module is not loaded");
320 return;
321 }
322
323 if (sBluetoothA2dpInterface != nullptr) {
324 sBluetoothA2dpInterface->cleanup();
325 sBluetoothA2dpInterface = nullptr;
326 }
327
328 env->DeleteGlobalRef(android_bluetooth_BluetoothCodecConfig.clazz);
329 android_bluetooth_BluetoothCodecConfig.clazz = nullptr;
330
331 if (mCallbacksObj != nullptr) {
332 env->DeleteGlobalRef(mCallbacksObj);
333 mCallbacksObj = nullptr;
334 }
335 }
336
getSupportedCodecTypesNative(JNIEnv * env)337 static jobjectArray getSupportedCodecTypesNative(JNIEnv* env) {
338 log::info("{}", fmt::ptr(sBluetoothA2dpInterface));
339
340 jclass android_bluetooth_BluetoothCodecType_clazz = (jclass)env->NewGlobalRef(
341 env->FindClass("android/bluetooth/BluetoothCodecType"));
342 if (android_bluetooth_BluetoothCodecType_clazz == nullptr) {
343 log::error("Failed to allocate Global Ref for BluetoothCodecType class");
344 return nullptr;
345 }
346
347 jmethodID init = env->GetMethodID(android_bluetooth_BluetoothCodecType_clazz,
348 "<init>", "(IJLjava/lang/String;)V");
349
350 if (init == nullptr) {
351 log::error("Failed to find method <init> of BluetoothCodecType class");
352 return nullptr;
353 }
354
355 jobjectArray result =
356 env->NewObjectArray(supported_codecs.size(),
357 android_bluetooth_BluetoothCodecType_clazz, nullptr);
358
359 if (result == nullptr) {
360 log::error("Failed to allocate result array of BluetoothCodecType");
361 return nullptr;
362 }
363
364 for (size_t index = 0; index < supported_codecs.size(); index++) {
365 jobject codec_type = env->NewObject(
366 android_bluetooth_BluetoothCodecType_clazz, init,
367 (jint)supported_codecs[index].codec_type,
368 (jlong)supported_codecs[index].codec_id,
369 env->NewStringUTF(supported_codecs[index].codec_name.c_str()));
370 env->SetObjectArrayElement(result, index, codec_type);
371 }
372
373 return result;
374 }
375
connectA2dpNative(JNIEnv * env,jobject,jbyteArray address)376 static jboolean connectA2dpNative(JNIEnv* env, jobject /* object */,
377 jbyteArray address) {
378 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
379 if (!sBluetoothA2dpInterface) {
380 log::error("Failed to get the Bluetooth A2DP Interface");
381 return JNI_FALSE;
382 }
383
384 jbyte* addr = env->GetByteArrayElements(address, nullptr);
385 if (!addr) {
386 jniThrowIOException(env, EINVAL);
387 return JNI_FALSE;
388 }
389
390 RawAddress bd_addr;
391 bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
392 log::info("sBluetoothA2dpInterface: {}, {}",
393 fmt::ptr(sBluetoothA2dpInterface), bd_addr);
394 bt_status_t status = sBluetoothA2dpInterface->connect(bd_addr);
395 if (status != BT_STATUS_SUCCESS) {
396 log::error("Failed A2DP connection, status: {}", bt_status_text(status));
397 }
398 env->ReleaseByteArrayElements(address, addr, 0);
399 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
400 }
401
disconnectA2dpNative(JNIEnv * env,jobject,jbyteArray address)402 static jboolean disconnectA2dpNative(JNIEnv* env, jobject /* object */,
403 jbyteArray address) {
404 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
405 if (!sBluetoothA2dpInterface) {
406 log::error("Failed to get the Bluetooth A2DP Interface");
407 return JNI_FALSE;
408 }
409
410 jbyte* addr = env->GetByteArrayElements(address, nullptr);
411 if (!addr) {
412 jniThrowIOException(env, EINVAL);
413 return JNI_FALSE;
414 }
415
416 RawAddress bd_addr;
417 bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
418 log::info("sBluetoothA2dpInterface: {}, {}",
419 fmt::ptr(sBluetoothA2dpInterface), bd_addr);
420 bt_status_t status = sBluetoothA2dpInterface->disconnect(bd_addr);
421 if (status != BT_STATUS_SUCCESS) {
422 log::error("Failed A2DP disconnection, status: {}", bt_status_text(status));
423 }
424 env->ReleaseByteArrayElements(address, addr, 0);
425 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
426 }
427
setSilenceDeviceNative(JNIEnv * env,jobject,jbyteArray address,jboolean silence)428 static jboolean setSilenceDeviceNative(JNIEnv* env, jobject /* object */,
429 jbyteArray address, jboolean silence) {
430 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
431 if (!sBluetoothA2dpInterface) {
432 log::error("Failed to get the Bluetooth A2DP Interface");
433 return JNI_FALSE;
434 }
435
436 jbyte* addr = env->GetByteArrayElements(address, nullptr);
437
438 RawAddress bd_addr = RawAddress::kEmpty;
439 if (addr) {
440 bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
441 }
442 if (bd_addr == RawAddress::kEmpty) {
443 return JNI_FALSE;
444 }
445 log::info("sBluetoothA2dpInterface: {}, {}: silence: {}",
446 fmt::ptr(sBluetoothA2dpInterface), bd_addr,
447 static_cast<bool>(silence));
448 bt_status_t status =
449 sBluetoothA2dpInterface->set_silence_device(bd_addr, silence);
450 if (status != BT_STATUS_SUCCESS) {
451 log::error("Failed A2DP set_silence_device, status: {}",
452 bt_status_text(status));
453 }
454 env->ReleaseByteArrayElements(address, addr, 0);
455 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
456 }
457
setActiveDeviceNative(JNIEnv * env,jobject,jbyteArray address)458 static jboolean setActiveDeviceNative(JNIEnv* env, jobject /* object */,
459 jbyteArray address) {
460 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
461 if (!sBluetoothA2dpInterface) {
462 log::error("Failed to get the Bluetooth A2DP Interface");
463 return JNI_FALSE;
464 }
465
466 jbyte* addr = env->GetByteArrayElements(address, nullptr);
467
468 RawAddress bd_addr = RawAddress::kEmpty;
469 if (addr) {
470 bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
471 }
472 log::info("sBluetoothA2dpInterface: {}, {}",
473 fmt::ptr(sBluetoothA2dpInterface), bd_addr);
474 bt_status_t status = sBluetoothA2dpInterface->set_active_device(bd_addr);
475 if (status != BT_STATUS_SUCCESS) {
476 log::error("Failed A2DP set_active_device, status: {}",
477 bt_status_text(status));
478 }
479 env->ReleaseByteArrayElements(address, addr, 0);
480 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
481 }
482
setCodecConfigPreferenceNative(JNIEnv * env,jobject object,jbyteArray address,jobjectArray codecConfigArray)483 static jboolean setCodecConfigPreferenceNative(JNIEnv* env, jobject object,
484 jbyteArray address,
485 jobjectArray codecConfigArray) {
486 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
487 if (!sBluetoothA2dpInterface) {
488 log::error("Failed to get the Bluetooth A2DP Interface");
489 return JNI_FALSE;
490 }
491
492 jbyte* addr = env->GetByteArrayElements(address, nullptr);
493 if (!addr) {
494 jniThrowIOException(env, EINVAL);
495 return JNI_FALSE;
496 }
497
498 RawAddress bd_addr;
499 bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
500 std::vector<btav_a2dp_codec_config_t> codec_preferences =
501 prepareCodecPreferences(env, object, codecConfigArray);
502 log::info("sBluetoothA2dpInterface: {}, {}: {}",
503 fmt::ptr(sBluetoothA2dpInterface), bd_addr,
504 btav_a2dp_codec_config_t::PrintCodecs(codec_preferences));
505 bt_status_t status =
506 sBluetoothA2dpInterface->config_codec(bd_addr, codec_preferences);
507 if (status != BT_STATUS_SUCCESS) {
508 log::error("Failed codec configuration, status: {}",
509 bt_status_text(status));
510 }
511 env->ReleaseByteArrayElements(address, addr, 0);
512 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
513 }
514
register_com_android_bluetooth_a2dp(JNIEnv * env)515 int register_com_android_bluetooth_a2dp(JNIEnv* env) {
516 const JNINativeMethod methods[] = {
517 {"initNative",
518 "(I[Landroid/bluetooth/BluetoothCodecConfig;"
519 "[Landroid/bluetooth/BluetoothCodecConfig;)V",
520 (void*)initNative},
521 {"cleanupNative", "()V", (void*)cleanupNative},
522 {"getSupportedCodecTypesNative",
523 "()[Landroid/bluetooth/BluetoothCodecType;",
524 (void*)getSupportedCodecTypesNative},
525 {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
526 {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
527 {"setSilenceDeviceNative", "([BZ)Z", (void*)setSilenceDeviceNative},
528 {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative},
529 {"setCodecConfigPreferenceNative",
530 "([B[Landroid/bluetooth/BluetoothCodecConfig;)Z",
531 (void*)setCodecConfigPreferenceNative},
532 };
533 const int result = REGISTER_NATIVE_METHODS(
534 env, "com/android/bluetooth/a2dp/A2dpNativeInterface", methods);
535 if (result != 0) {
536 return result;
537 }
538
539 const JNIJavaMethod javaMethods[] = {
540 {"onConnectionStateChanged", "([BI)V", &method_onConnectionStateChanged},
541 {"onAudioStateChanged", "([BI)V", &method_onAudioStateChanged},
542 {"onCodecConfigChanged",
543 "([BLandroid/bluetooth/BluetoothCodecConfig;"
544 "[Landroid/bluetooth/BluetoothCodecConfig;"
545 "[Landroid/bluetooth/BluetoothCodecConfig;)V",
546 &method_onCodecConfigChanged},
547 {"isMandatoryCodecPreferred", "([B)Z", &method_isMandatoryCodecPreferred},
548 };
549 GET_JAVA_METHODS(env, "com/android/bluetooth/a2dp/A2dpNativeInterface",
550 javaMethods);
551
552 const JNIJavaMethod codecConfigCallbacksMethods[] = {
553 {"<init>", "(IIIIIJJJJ)V",
554 &android_bluetooth_BluetoothCodecConfig.constructor},
555 {"getCodecType", "()I",
556 &android_bluetooth_BluetoothCodecConfig.getCodecType},
557 {"getCodecPriority", "()I",
558 &android_bluetooth_BluetoothCodecConfig.getCodecPriority},
559 {"getSampleRate", "()I",
560 &android_bluetooth_BluetoothCodecConfig.getSampleRate},
561 {"getBitsPerSample", "()I",
562 &android_bluetooth_BluetoothCodecConfig.getBitsPerSample},
563 {"getChannelMode", "()I",
564 &android_bluetooth_BluetoothCodecConfig.getChannelMode},
565 {"getCodecSpecific1", "()J",
566 &android_bluetooth_BluetoothCodecConfig.getCodecSpecific1},
567 {"getCodecSpecific2", "()J",
568 &android_bluetooth_BluetoothCodecConfig.getCodecSpecific2},
569 {"getCodecSpecific3", "()J",
570 &android_bluetooth_BluetoothCodecConfig.getCodecSpecific3},
571 {"getCodecSpecific4", "()J",
572 &android_bluetooth_BluetoothCodecConfig.getCodecSpecific4},
573 };
574 GET_JAVA_METHODS(env, "android/bluetooth/BluetoothCodecConfig",
575 codecConfigCallbacksMethods);
576
577 return 0;
578 }
579 }
580