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 "BluetoothA2dpSinkServiceJni" 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_onAudioConfigChanged; 32 33 static const btav_sink_interface_t* sBluetoothA2dpInterface = NULL; 34 static jobject mCallbacksObj = NULL; 35 36 static void bta2dp_connection_state_callback(btav_connection_state_t state, 37 RawAddress* bd_addr) { 38 ALOGI("%s", __func__); 39 CallbackEnv sCallbackEnv(__func__); 40 if (!sCallbackEnv.valid()) return; 41 42 ScopedLocalRef<jbyteArray> addr( 43 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); 44 if (!addr.get()) { 45 ALOGE("Fail to new jbyteArray bd addr for connection state"); 46 return; 47 } 48 49 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), 50 (jbyte*)bd_addr); 51 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, 52 (jint)state, addr.get()); 53 } 54 55 static void bta2dp_audio_state_callback(btav_audio_state_t state, 56 RawAddress* bd_addr) { 57 ALOGI("%s", __func__); 58 CallbackEnv sCallbackEnv(__func__); 59 if (!sCallbackEnv.valid()) return; 60 61 ScopedLocalRef<jbyteArray> addr( 62 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); 63 if (!addr.get()) { 64 ALOGE("Fail to new jbyteArray bd addr for connection state"); 65 return; 66 } 67 68 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), 69 (jbyte*)bd_addr); 70 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, 71 (jint)state, addr.get()); 72 } 73 74 static void bta2dp_audio_config_callback(RawAddress* bd_addr, 75 uint32_t sample_rate, 76 uint8_t channel_count) { 77 ALOGI("%s", __func__); 78 CallbackEnv sCallbackEnv(__func__); 79 if (!sCallbackEnv.valid()) return; 80 81 ScopedLocalRef<jbyteArray> addr( 82 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); 83 if (!addr.get()) { 84 ALOGE("Fail to new jbyteArray bd addr for connection state"); 85 return; 86 } 87 88 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), 89 (jbyte*)bd_addr); 90 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioConfigChanged, 91 addr.get(), (jint)sample_rate, 92 (jint)channel_count); 93 } 94 95 static btav_sink_callbacks_t sBluetoothA2dpCallbacks = { 96 sizeof(sBluetoothA2dpCallbacks), bta2dp_connection_state_callback, 97 bta2dp_audio_state_callback, bta2dp_audio_config_callback, 98 }; 99 100 static void classInitNative(JNIEnv* env, jclass clazz) { 101 method_onConnectionStateChanged = 102 env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); 103 104 method_onAudioStateChanged = 105 env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V"); 106 107 method_onAudioConfigChanged = 108 env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V"); 109 110 ALOGI("%s: succeeds", __func__); 111 } 112 113 static void initNative(JNIEnv* env, jobject object) { 114 const bt_interface_t* btInf = getBluetoothInterface(); 115 if (btInf == NULL) { 116 ALOGE("Bluetooth module is not loaded"); 117 return; 118 } 119 120 if (sBluetoothA2dpInterface != NULL) { 121 ALOGW("Cleaning up A2DP Interface before initializing..."); 122 sBluetoothA2dpInterface->cleanup(); 123 sBluetoothA2dpInterface = NULL; 124 } 125 126 if (mCallbacksObj != NULL) { 127 ALOGW("Cleaning up A2DP callback object"); 128 env->DeleteGlobalRef(mCallbacksObj); 129 mCallbacksObj = NULL; 130 } 131 132 sBluetoothA2dpInterface = 133 (btav_sink_interface_t*)btInf->get_profile_interface( 134 BT_PROFILE_ADVANCED_AUDIO_SINK_ID); 135 if (sBluetoothA2dpInterface == NULL) { 136 ALOGE("Failed to get Bluetooth A2DP Sink Interface"); 137 return; 138 } 139 140 bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks); 141 if (status != BT_STATUS_SUCCESS) { 142 ALOGE("Failed to initialize Bluetooth A2DP Sink, status: %d", status); 143 sBluetoothA2dpInterface = NULL; 144 return; 145 } 146 147 mCallbacksObj = env->NewGlobalRef(object); 148 } 149 150 static void cleanupNative(JNIEnv* env, jobject object) { 151 const bt_interface_t* btInf = getBluetoothInterface(); 152 153 if (btInf == NULL) { 154 ALOGE("Bluetooth module is not loaded"); 155 return; 156 } 157 158 if (sBluetoothA2dpInterface != NULL) { 159 sBluetoothA2dpInterface->cleanup(); 160 sBluetoothA2dpInterface = NULL; 161 } 162 163 if (mCallbacksObj != NULL) { 164 env->DeleteGlobalRef(mCallbacksObj); 165 mCallbacksObj = NULL; 166 } 167 } 168 169 static jboolean connectA2dpNative(JNIEnv* env, jobject object, 170 jbyteArray address) { 171 ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface); 172 if (!sBluetoothA2dpInterface) return JNI_FALSE; 173 174 jbyte* addr = env->GetByteArrayElements(address, NULL); 175 if (!addr) { 176 jniThrowIOException(env, EINVAL); 177 return JNI_FALSE; 178 } 179 180 bt_status_t status = sBluetoothA2dpInterface->connect((RawAddress*)addr); 181 if (status != BT_STATUS_SUCCESS) { 182 ALOGE("Failed HF connection, status: %d", status); 183 } 184 env->ReleaseByteArrayElements(address, addr, 0); 185 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 186 } 187 188 static jboolean disconnectA2dpNative(JNIEnv* env, jobject object, 189 jbyteArray address) { 190 if (!sBluetoothA2dpInterface) return JNI_FALSE; 191 192 jbyte* addr = env->GetByteArrayElements(address, NULL); 193 if (!addr) { 194 jniThrowIOException(env, EINVAL); 195 return JNI_FALSE; 196 } 197 198 bt_status_t status = sBluetoothA2dpInterface->disconnect((RawAddress*)addr); 199 if (status != BT_STATUS_SUCCESS) { 200 ALOGE("Failed HF disconnection, status: %d", status); 201 } 202 env->ReleaseByteArrayElements(address, addr, 0); 203 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 204 } 205 206 static void informAudioFocusStateNative(JNIEnv* env, jobject object, 207 jint focus_state) { 208 if (!sBluetoothA2dpInterface) return; 209 sBluetoothA2dpInterface->set_audio_focus_state((uint8_t)focus_state); 210 } 211 212 static void informAudioTrackGainNative(JNIEnv* env, jobject object, 213 jfloat gain) { 214 if (!sBluetoothA2dpInterface) return; 215 sBluetoothA2dpInterface->set_audio_track_gain((float)gain); 216 } 217 218 static JNINativeMethod sMethods[] = { 219 {"classInitNative", "()V", (void*)classInitNative}, 220 {"initNative", "()V", (void*)initNative}, 221 {"cleanupNative", "()V", (void*)cleanupNative}, 222 {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative}, 223 {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative}, 224 {"informAudioFocusStateNative", "(I)V", (void*)informAudioFocusStateNative}, 225 {"informAudioTrackGainNative", "(F)V", (void*)informAudioTrackGainNative}, 226 }; 227 228 int register_com_android_bluetooth_a2dp_sink(JNIEnv* env) { 229 return jniRegisterNativeMethods( 230 env, "com/android/bluetooth/a2dpsink/A2dpSinkStateMachine", sMethods, 231 NELEM(sMethods)); 232 } 233 } 234