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 50 static void bta2dp_connection_state_callback(btav_connection_state_t state, 51 RawAddress* 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(RawAddress))); 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(RawAddress), 64 (jbyte*)bd_addr); 65 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, 66 (jint)state, addr.get()); 67 } 68 69 static void bta2dp_audio_state_callback(btav_audio_state_t state, 70 RawAddress* 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(RawAddress))); 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(RawAddress), 83 (jbyte*)bd_addr); 84 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, 85 (jint)state, addr.get()); 86 } 87 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 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 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 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 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 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((RawAddress*)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 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((RawAddress*)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 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 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