• 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 "BluetoothA2dpServiceJni"
18 
19 #define LOG_NDEBUG 0
20 
21 #include "android_runtime/AndroidRuntime.h"
22 #include "com_android_bluetooth.h"
23 #include "hardware/bt_av.h"
24 #include "utils/Log.h"
25 
26 #include <string.h>
27 
28 namespace android {
29 static jmethodID method_onConnectionStateChanged;
30 static jmethodID method_onAudioStateChanged;
31 static jmethodID method_onCodecConfigChanged;
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 = NULL;
48 static jobject mCallbacksObj = NULL;
49 
bta2dp_connection_state_callback(btav_connection_state_t state,bt_bdaddr_t * bd_addr)50 static void bta2dp_connection_state_callback(btav_connection_state_t state,
51                                              bt_bdaddr_t* bd_addr) {
52   ALOGI("%s", __func__);
53   CallbackEnv sCallbackEnv(__func__);
54   if (!sCallbackEnv.valid()) return;
55 
56   ScopedLocalRef<jbyteArray> addr(
57       sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
58   if (!addr.get()) {
59     ALOGE("Fail to new jbyteArray bd addr for connection state");
60     return;
61   }
62 
63   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
64                                    (jbyte*)bd_addr);
65   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
66                                (jint)state, addr.get());
67 }
68 
bta2dp_audio_state_callback(btav_audio_state_t state,bt_bdaddr_t * bd_addr)69 static void bta2dp_audio_state_callback(btav_audio_state_t state,
70                                         bt_bdaddr_t* bd_addr) {
71   ALOGI("%s", __func__);
72   CallbackEnv sCallbackEnv(__func__);
73   if (!sCallbackEnv.valid()) return;
74 
75   ScopedLocalRef<jbyteArray> addr(
76       sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
77   if (!addr.get()) {
78     ALOGE("Fail to new jbyteArray bd addr for connection state");
79     return;
80   }
81 
82   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
83                                    (jbyte*)bd_addr);
84   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged,
85                                (jint)state, addr.get());
86 }
87 
bta2dp_audio_config_callback(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)88 static void bta2dp_audio_config_callback(
89     btav_a2dp_codec_config_t codec_config,
90     std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
91     std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities) {
92   ALOGI("%s", __func__);
93   CallbackEnv sCallbackEnv(__func__);
94   if (!sCallbackEnv.valid()) return;
95 
96   jobject codecConfigObj = sCallbackEnv->NewObject(
97       android_bluetooth_BluetoothCodecConfig.clazz,
98       android_bluetooth_BluetoothCodecConfig.constructor,
99       (jint)codec_config.codec_type, (jint)codec_config.codec_priority,
100       (jint)codec_config.sample_rate, (jint)codec_config.bits_per_sample,
101       (jint)codec_config.channel_mode, (jlong)codec_config.codec_specific_1,
102       (jlong)codec_config.codec_specific_2,
103       (jlong)codec_config.codec_specific_3,
104       (jlong)codec_config.codec_specific_4);
105 
106   jsize i = 0;
107   jobjectArray local_capabilities_array = sCallbackEnv->NewObjectArray(
108       (jsize)codecs_local_capabilities.size(),
109       android_bluetooth_BluetoothCodecConfig.clazz, NULL);
110   for (auto const& cap : codecs_local_capabilities) {
111     jobject capObj = sCallbackEnv->NewObject(
112         android_bluetooth_BluetoothCodecConfig.clazz,
113         android_bluetooth_BluetoothCodecConfig.constructor,
114         (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
115         (jint)cap.bits_per_sample, (jint)cap.channel_mode,
116         (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
117         (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
118     sCallbackEnv->SetObjectArrayElement(local_capabilities_array, i++, capObj);
119     sCallbackEnv->DeleteLocalRef(capObj);
120   }
121 
122   i = 0;
123   jobjectArray selectable_capabilities_array = sCallbackEnv->NewObjectArray(
124       (jsize)codecs_selectable_capabilities.size(),
125       android_bluetooth_BluetoothCodecConfig.clazz, NULL);
126   for (auto const& cap : codecs_selectable_capabilities) {
127     jobject capObj = sCallbackEnv->NewObject(
128         android_bluetooth_BluetoothCodecConfig.clazz,
129         android_bluetooth_BluetoothCodecConfig.constructor,
130         (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
131         (jint)cap.bits_per_sample, (jint)cap.channel_mode,
132         (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
133         (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
134     sCallbackEnv->SetObjectArrayElement(selectable_capabilities_array, i++,
135                                         capObj);
136     sCallbackEnv->DeleteLocalRef(capObj);
137   }
138 
139   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCodecConfigChanged,
140                                codecConfigObj, local_capabilities_array,
141                                selectable_capabilities_array);
142 }
143 
144 static btav_source_callbacks_t sBluetoothA2dpCallbacks = {
145     sizeof(sBluetoothA2dpCallbacks), bta2dp_connection_state_callback,
146     bta2dp_audio_state_callback, bta2dp_audio_config_callback,
147 };
148 
classInitNative(JNIEnv * env,jclass clazz)149 static void classInitNative(JNIEnv* env, jclass clazz) {
150   jclass jniBluetoothCodecConfigClass =
151       env->FindClass("android/bluetooth/BluetoothCodecConfig");
152   android_bluetooth_BluetoothCodecConfig.constructor =
153       env->GetMethodID(jniBluetoothCodecConfigClass, "<init>", "(IIIIIJJJJ)V");
154   android_bluetooth_BluetoothCodecConfig.getCodecType =
155       env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecType", "()I");
156   android_bluetooth_BluetoothCodecConfig.getCodecPriority =
157       env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecPriority", "()I");
158   android_bluetooth_BluetoothCodecConfig.getSampleRate =
159       env->GetMethodID(jniBluetoothCodecConfigClass, "getSampleRate", "()I");
160   android_bluetooth_BluetoothCodecConfig.getBitsPerSample =
161       env->GetMethodID(jniBluetoothCodecConfigClass, "getBitsPerSample", "()I");
162   android_bluetooth_BluetoothCodecConfig.getChannelMode =
163       env->GetMethodID(jniBluetoothCodecConfigClass, "getChannelMode", "()I");
164   android_bluetooth_BluetoothCodecConfig.getCodecSpecific1 = env->GetMethodID(
165       jniBluetoothCodecConfigClass, "getCodecSpecific1", "()J");
166   android_bluetooth_BluetoothCodecConfig.getCodecSpecific2 = env->GetMethodID(
167       jniBluetoothCodecConfigClass, "getCodecSpecific2", "()J");
168   android_bluetooth_BluetoothCodecConfig.getCodecSpecific3 = env->GetMethodID(
169       jniBluetoothCodecConfigClass, "getCodecSpecific3", "()J");
170   android_bluetooth_BluetoothCodecConfig.getCodecSpecific4 = env->GetMethodID(
171       jniBluetoothCodecConfigClass, "getCodecSpecific4", "()J");
172 
173   method_onConnectionStateChanged =
174       env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
175 
176   method_onAudioStateChanged =
177       env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
178 
179   method_onCodecConfigChanged = env->GetMethodID(
180       clazz, "onCodecConfigChanged",
181       "(Landroid/bluetooth/BluetoothCodecConfig;[Landroid/bluetooth/"
182       "BluetoothCodecConfig;[Landroid/bluetooth/BluetoothCodecConfig;)V");
183 
184   ALOGI("%s: succeeds", __func__);
185 }
186 
prepareCodecPreferences(JNIEnv * env,jobject object,jobjectArray codecConfigArray)187 static std::vector<btav_a2dp_codec_config_t> prepareCodecPreferences(
188     JNIEnv* env, jobject object, jobjectArray codecConfigArray) {
189   std::vector<btav_a2dp_codec_config_t> codec_preferences;
190 
191   int numConfigs = env->GetArrayLength(codecConfigArray);
192   for (int i = 0; i < numConfigs; i++) {
193     jobject jcodecConfig = env->GetObjectArrayElement(codecConfigArray, i);
194     if (jcodecConfig == nullptr) continue;
195     if (!env->IsInstanceOf(jcodecConfig,
196                            android_bluetooth_BluetoothCodecConfig.clazz)) {
197       ALOGE("Invalid BluetoothCodecConfig instance");
198       continue;
199     }
200     jint codecType = env->CallIntMethod(
201         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecType);
202     jint codecPriority = env->CallIntMethod(
203         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecPriority);
204     jint sampleRate = env->CallIntMethod(
205         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getSampleRate);
206     jint bitsPerSample = env->CallIntMethod(
207         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getBitsPerSample);
208     jint channelMode = env->CallIntMethod(
209         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getChannelMode);
210     jlong codecSpecific1 = env->CallLongMethod(
211         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific1);
212     jlong codecSpecific2 = env->CallLongMethod(
213         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific2);
214     jlong codecSpecific3 = env->CallLongMethod(
215         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific3);
216     jlong codecSpecific4 = env->CallLongMethod(
217         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific4);
218 
219     btav_a2dp_codec_config_t codec_config = {
220         .codec_type = static_cast<btav_a2dp_codec_index_t>(codecType),
221         .codec_priority =
222             static_cast<btav_a2dp_codec_priority_t>(codecPriority),
223         .sample_rate = static_cast<btav_a2dp_codec_sample_rate_t>(sampleRate),
224         .bits_per_sample =
225             static_cast<btav_a2dp_codec_bits_per_sample_t>(bitsPerSample),
226         .channel_mode =
227             static_cast<btav_a2dp_codec_channel_mode_t>(channelMode),
228         .codec_specific_1 = codecSpecific1,
229         .codec_specific_2 = codecSpecific2,
230         .codec_specific_3 = codecSpecific3,
231         .codec_specific_4 = codecSpecific4};
232 
233     codec_preferences.push_back(codec_config);
234   }
235   return codec_preferences;
236 }
237 
initNative(JNIEnv * env,jobject object,jobjectArray codecConfigArray)238 static void initNative(JNIEnv* env, jobject object,
239                        jobjectArray codecConfigArray) {
240   const bt_interface_t* btInf = getBluetoothInterface();
241   if (btInf == NULL) {
242     ALOGE("Bluetooth module is not loaded");
243     return;
244   }
245 
246   if (sBluetoothA2dpInterface != NULL) {
247     ALOGW("Cleaning up A2DP Interface before initializing...");
248     sBluetoothA2dpInterface->cleanup();
249     sBluetoothA2dpInterface = NULL;
250   }
251 
252   if (mCallbacksObj != NULL) {
253     ALOGW("Cleaning up A2DP callback object");
254     env->DeleteGlobalRef(mCallbacksObj);
255     mCallbacksObj = NULL;
256   }
257 
258   if ((mCallbacksObj = env->NewGlobalRef(object)) == NULL) {
259     ALOGE("Failed to allocate Global Ref for A2DP Callbacks");
260     return;
261   }
262 
263   android_bluetooth_BluetoothCodecConfig.clazz = (jclass)env->NewGlobalRef(
264       env->FindClass("android/bluetooth/BluetoothCodecConfig"));
265   if (android_bluetooth_BluetoothCodecConfig.clazz == nullptr) {
266     ALOGE("Failed to allocate Global Ref for BluetoothCodecConfig class");
267     return;
268   }
269 
270   sBluetoothA2dpInterface =
271       (btav_source_interface_t*)btInf->get_profile_interface(
272           BT_PROFILE_ADVANCED_AUDIO_ID);
273   if (sBluetoothA2dpInterface == NULL) {
274     ALOGE("Failed to get Bluetooth A2DP Interface");
275     return;
276   }
277 
278   std::vector<btav_a2dp_codec_config_t> codec_priorities =
279       prepareCodecPreferences(env, object, codecConfigArray);
280 
281   bt_status_t status =
282       sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks, codec_priorities);
283   if (status != BT_STATUS_SUCCESS) {
284     ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status);
285     sBluetoothA2dpInterface = NULL;
286     return;
287   }
288 }
289 
cleanupNative(JNIEnv * env,jobject object)290 static void cleanupNative(JNIEnv* env, jobject object) {
291   const bt_interface_t* btInf = getBluetoothInterface();
292   if (btInf == NULL) {
293     ALOGE("Bluetooth module is not loaded");
294     return;
295   }
296 
297   if (sBluetoothA2dpInterface != NULL) {
298     sBluetoothA2dpInterface->cleanup();
299     sBluetoothA2dpInterface = NULL;
300   }
301 
302   env->DeleteGlobalRef(android_bluetooth_BluetoothCodecConfig.clazz);
303   android_bluetooth_BluetoothCodecConfig.clazz = nullptr;
304 
305   if (mCallbacksObj != NULL) {
306     env->DeleteGlobalRef(mCallbacksObj);
307     mCallbacksObj = NULL;
308   }
309 }
310 
connectA2dpNative(JNIEnv * env,jobject object,jbyteArray address)311 static jboolean connectA2dpNative(JNIEnv* env, jobject object,
312                                   jbyteArray address) {
313   ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
314   if (!sBluetoothA2dpInterface) return JNI_FALSE;
315 
316   jbyte* addr = env->GetByteArrayElements(address, NULL);
317   if (!addr) {
318     jniThrowIOException(env, EINVAL);
319     return JNI_FALSE;
320   }
321 
322   bt_status_t status = sBluetoothA2dpInterface->connect((bt_bdaddr_t*)addr);
323   if (status != BT_STATUS_SUCCESS) {
324     ALOGE("Failed HF connection, status: %d", status);
325   }
326   env->ReleaseByteArrayElements(address, addr, 0);
327   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
328 }
329 
disconnectA2dpNative(JNIEnv * env,jobject object,jbyteArray address)330 static jboolean disconnectA2dpNative(JNIEnv* env, jobject object,
331                                      jbyteArray address) {
332   if (!sBluetoothA2dpInterface) return JNI_FALSE;
333 
334   jbyte* addr = env->GetByteArrayElements(address, NULL);
335   if (!addr) {
336     jniThrowIOException(env, EINVAL);
337     return JNI_FALSE;
338   }
339 
340   bt_status_t status = sBluetoothA2dpInterface->disconnect((bt_bdaddr_t*)addr);
341   if (status != BT_STATUS_SUCCESS) {
342     ALOGE("Failed HF disconnection, status: %d", status);
343   }
344   env->ReleaseByteArrayElements(address, addr, 0);
345   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
346 }
347 
setCodecConfigPreferenceNative(JNIEnv * env,jobject object,jobjectArray codecConfigArray)348 static jboolean setCodecConfigPreferenceNative(JNIEnv* env, jobject object,
349                                                jobjectArray codecConfigArray) {
350   if (!sBluetoothA2dpInterface) return JNI_FALSE;
351 
352   std::vector<btav_a2dp_codec_config_t> codec_preferences =
353       prepareCodecPreferences(env, object, codecConfigArray);
354 
355   bt_status_t status = sBluetoothA2dpInterface->config_codec(codec_preferences);
356   if (status != BT_STATUS_SUCCESS) {
357     ALOGE("Failed codec configuration, status: %d", status);
358   }
359   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
360 }
361 
362 static JNINativeMethod sMethods[] = {
363     {"classInitNative", "()V", (void*)classInitNative},
364     {"initNative", "([Landroid/bluetooth/BluetoothCodecConfig;)V",
365      (void*)initNative},
366     {"cleanupNative", "()V", (void*)cleanupNative},
367     {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
368     {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
369     {"setCodecConfigPreferenceNative",
370      "([Landroid/bluetooth/BluetoothCodecConfig;)Z",
371      (void*)setCodecConfigPreferenceNative},
372 };
373 
register_com_android_bluetooth_a2dp(JNIEnv * env)374 int register_com_android_bluetooth_a2dp(JNIEnv* env) {
375   return jniRegisterNativeMethods(env,
376                                   "com/android/bluetooth/a2dp/A2dpStateMachine",
377                                   sMethods, NELEM(sMethods));
378 }
379 }
380