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};
101
102static void classInitNative(JNIEnv* env, jclass clazz) {
103    int err;
104    const bt_interface_t* btInf;
105    bt_status_t status;
106
107    method_onConnectionStateChanged =
108        env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
109
110    method_onAudioStateChanged =
111        env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
112    /*
113    if ( (btInf = getBluetoothInterface()) == NULL) {
114        ALOGE("Bluetooth module is not loaded");
115        return;
116    }
117
118    if ( (sBluetoothA2dpInterface = (btav_interface_t *)
119          btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID)) == NULL) {
120        ALOGE("Failed to get Bluetooth A2DP Interface");
121        return;
122    }
123    */
124
125    // TODO(BT) do this only once or
126    //          Do we need to do this every time the BT reenables?
127    /*
128    if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {
129        ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status);
130        sBluetoothA2dpInterface = NULL;
131        return;
132    }*/
133
134    ALOGI("%s: succeeds", __FUNCTION__);
135}
136
137static void initNative(JNIEnv *env, jobject object) {
138    const bt_interface_t* btInf;
139    bt_status_t status;
140
141    if ( (btInf = getBluetoothInterface()) == NULL) {
142        ALOGE("Bluetooth module is not loaded");
143        return;
144    }
145
146    if (sBluetoothA2dpInterface !=NULL) {
147         ALOGW("Cleaning up A2DP Interface before initializing...");
148         sBluetoothA2dpInterface->cleanup();
149         sBluetoothA2dpInterface = NULL;
150    }
151
152    if (mCallbacksObj != NULL) {
153         ALOGW("Cleaning up A2DP callback object");
154         env->DeleteGlobalRef(mCallbacksObj);
155         mCallbacksObj = NULL;
156    }
157
158    if ( (sBluetoothA2dpInterface = (btav_interface_t *)
159          btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID)) == NULL) {
160        ALOGE("Failed to get Bluetooth A2DP Interface");
161        return;
162    }
163
164    if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {
165        ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status);
166        sBluetoothA2dpInterface = NULL;
167        return;
168    }
169
170    mCallbacksObj = env->NewGlobalRef(object);
171}
172
173static void cleanupNative(JNIEnv *env, jobject object) {
174    const bt_interface_t* btInf;
175    bt_status_t status;
176
177    if ( (btInf = getBluetoothInterface()) == NULL) {
178        ALOGE("Bluetooth module is not loaded");
179        return;
180    }
181
182    if (sBluetoothA2dpInterface !=NULL) {
183        sBluetoothA2dpInterface->cleanup();
184        sBluetoothA2dpInterface = NULL;
185    }
186
187    if (mCallbacksObj != NULL) {
188        env->DeleteGlobalRef(mCallbacksObj);
189        mCallbacksObj = NULL;
190    }
191}
192
193static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
194    jbyte *addr;
195    bt_bdaddr_t * btAddr;
196    bt_status_t status;
197
198    ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface);
199    if (!sBluetoothA2dpInterface) return JNI_FALSE;
200
201    addr = env->GetByteArrayElements(address, NULL);
202    btAddr = (bt_bdaddr_t *) addr;
203    if (!addr) {
204        jniThrowIOException(env, EINVAL);
205        return JNI_FALSE;
206    }
207
208    if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
209        ALOGE("Failed HF connection, status: %d", status);
210    }
211    env->ReleaseByteArrayElements(address, addr, 0);
212    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
213}
214
215static jboolean disconnectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
216    jbyte *addr;
217    bt_status_t status;
218
219    if (!sBluetoothA2dpInterface) return JNI_FALSE;
220
221    addr = env->GetByteArrayElements(address, NULL);
222    if (!addr) {
223        jniThrowIOException(env, EINVAL);
224        return JNI_FALSE;
225    }
226
227    if ( (status = sBluetoothA2dpInterface->disconnect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
228        ALOGE("Failed HF disconnection, status: %d", status);
229    }
230    env->ReleaseByteArrayElements(address, addr, 0);
231    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
232}
233
234static JNINativeMethod sMethods[] = {
235    {"classInitNative", "()V", (void *) classInitNative},
236    {"initNative", "()V", (void *) initNative},
237    {"cleanupNative", "()V", (void *) cleanupNative},
238    {"connectA2dpNative", "([B)Z", (void *) connectA2dpNative},
239    {"disconnectA2dpNative", "([B)Z", (void *) disconnectA2dpNative},
240};
241
242int register_com_android_bluetooth_a2dp(JNIEnv* env)
243{
244    return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/A2dpStateMachine",
245                                    sMethods, NELEM(sMethods));
246}
247
248}
249