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 "com_android_bluetooth.h" 22#include "hardware/bt_av.h" 23#include "utils/Log.h" 24#include "android_runtime/AndroidRuntime.h" 25 26#include <string.h> 27 28namespace android { 29static jmethodID method_onConnectionStateChanged; 30static jmethodID method_onAudioStateChanged; 31 32static const btav_interface_t *sBluetoothA2dpInterface = NULL; 33static jobject mCallbacksObj = NULL; 34static JNIEnv *sCallbackEnv = NULL; 35 36static bool checkCallbackThread() { 37 // Always fetch the latest callbackEnv from AdapterService. 38 // Caching this could cause this sCallbackEnv to go out-of-sync 39 // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event 40 // is received 41 //if (sCallbackEnv == NULL) { 42 sCallbackEnv = getCallbackEnv(); 43 //} 44 45 JNIEnv* env = AndroidRuntime::getJNIEnv(); 46 if (sCallbackEnv != env || sCallbackEnv == NULL) return false; 47 return true; 48} 49 50static void bta2dp_connection_state_callback(btav_connection_state_t state, bt_bdaddr_t* bd_addr) { 51 jbyteArray addr; 52 53 ALOGI("%s", __FUNCTION__); 54 55 if (!checkCallbackThread()) { \ 56 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ 57 return; \ 58 } 59 addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); 60 if (!addr) { 61 ALOGE("Fail to new jbyteArray bd addr for connection state"); 62 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 63 return; 64 } 65 66 sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); 67 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint) state, 68 addr); 69 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 70 sCallbackEnv->DeleteLocalRef(addr); 71} 72 73static void bta2dp_audio_state_callback(btav_audio_state_t state, bt_bdaddr_t* bd_addr) { 74 jbyteArray addr; 75 76 ALOGI("%s", __FUNCTION__); 77 78 if (!checkCallbackThread()) { \ 79 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ 80 return; \ 81 } 82 addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); 83 if (!addr) { 84 ALOGE("Fail to new jbyteArray bd addr for connection state"); 85 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 86 return; 87 } 88 89 sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); 90 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint) state, 91 addr); 92 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 93 sCallbackEnv->DeleteLocalRef(addr); 94} 95 96static btav_callbacks_t sBluetoothA2dpCallbacks = { 97 sizeof(sBluetoothA2dpCallbacks), 98 bta2dp_connection_state_callback, 99 bta2dp_audio_state_callback, 100 NULL, /* audio_config_cb */ 101}; 102 103static void classInitNative(JNIEnv* env, jclass clazz) { 104 method_onConnectionStateChanged = 105 env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); 106 107 method_onAudioStateChanged = 108 env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V"); 109 110 ALOGI("%s: succeeds", __FUNCTION__); 111} 112 113static void initNative(JNIEnv *env, jobject object) { 114 const bt_interface_t* btInf; 115 bt_status_t status; 116 117 if ( (btInf = getBluetoothInterface()) == NULL) { 118 ALOGE("Bluetooth module is not loaded"); 119 return; 120 } 121 122 if (sBluetoothA2dpInterface !=NULL) { 123 ALOGW("Cleaning up A2DP Interface before initializing..."); 124 sBluetoothA2dpInterface->cleanup(); 125 sBluetoothA2dpInterface = NULL; 126 } 127 128 if (mCallbacksObj != NULL) { 129 ALOGW("Cleaning up A2DP callback object"); 130 env->DeleteGlobalRef(mCallbacksObj); 131 mCallbacksObj = NULL; 132 } 133 134 if ((mCallbacksObj = env->NewGlobalRef(object)) == NULL) { 135 ALOGE("Failed to allocate Global Ref for A2DP Callbacks"); 136 return; 137 } 138 139 if ( (sBluetoothA2dpInterface = (btav_interface_t *) 140 btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID)) == NULL) { 141 ALOGE("Failed to get Bluetooth A2DP Interface"); 142 return; 143 } 144 145 if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) { 146 ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status); 147 sBluetoothA2dpInterface = NULL; 148 return; 149 } 150} 151 152static void cleanupNative(JNIEnv *env, jobject object) { 153 const bt_interface_t* btInf; 154 155 if ( (btInf = getBluetoothInterface()) == NULL) { 156 ALOGE("Bluetooth module is not loaded"); 157 return; 158 } 159 160 if (sBluetoothA2dpInterface !=NULL) { 161 sBluetoothA2dpInterface->cleanup(); 162 sBluetoothA2dpInterface = NULL; 163 } 164 165 if (mCallbacksObj != NULL) { 166 env->DeleteGlobalRef(mCallbacksObj); 167 mCallbacksObj = NULL; 168 } 169} 170 171static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) { 172 jbyte *addr; 173 bt_bdaddr_t * btAddr; 174 bt_status_t status; 175 176 ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface); 177 if (!sBluetoothA2dpInterface) return JNI_FALSE; 178 179 addr = env->GetByteArrayElements(address, NULL); 180 btAddr = (bt_bdaddr_t *) addr; 181 if (!addr) { 182 jniThrowIOException(env, EINVAL); 183 return JNI_FALSE; 184 } 185 186 if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) { 187 ALOGE("Failed HF connection, status: %d", status); 188 } 189 env->ReleaseByteArrayElements(address, addr, 0); 190 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 191} 192 193static jboolean disconnectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) { 194 jbyte *addr; 195 bt_status_t status; 196 197 if (!sBluetoothA2dpInterface) return JNI_FALSE; 198 199 addr = env->GetByteArrayElements(address, NULL); 200 if (!addr) { 201 jniThrowIOException(env, EINVAL); 202 return JNI_FALSE; 203 } 204 205 if ( (status = sBluetoothA2dpInterface->disconnect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) { 206 ALOGE("Failed HF disconnection, status: %d", status); 207 } 208 env->ReleaseByteArrayElements(address, addr, 0); 209 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 210} 211 212static JNINativeMethod sMethods[] = { 213 {"classInitNative", "()V", (void *) classInitNative}, 214 {"initNative", "()V", (void *) initNative}, 215 {"cleanupNative", "()V", (void *) cleanupNative}, 216 {"connectA2dpNative", "([B)Z", (void *) connectA2dpNative}, 217 {"disconnectA2dpNative", "([B)Z", (void *) disconnectA2dpNative}, 218}; 219 220int register_com_android_bluetooth_a2dp(JNIEnv* env) 221{ 222 return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/A2dpStateMachine", 223 sMethods, NELEM(sMethods)); 224} 225 226} 227