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