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