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