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