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
28 namespace android {
29 static jmethodID method_onConnectionStateChanged;
30 static jmethodID method_onAudioStateChanged;
31
32 static const btav_interface_t *sBluetoothA2dpInterface = NULL;
33 static jobject mCallbacksObj = NULL;
34 static JNIEnv *sCallbackEnv = NULL;
35
checkCallbackThread()36 static 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
bta2dp_connection_state_callback(btav_connection_state_t state,bt_bdaddr_t * bd_addr)50 static 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
bta2dp_audio_state_callback(btav_audio_state_t state,bt_bdaddr_t * bd_addr)73 static 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
96 static btav_callbacks_t sBluetoothA2dpCallbacks = {
97 sizeof(sBluetoothA2dpCallbacks),
98 bta2dp_connection_state_callback,
99 bta2dp_audio_state_callback
100 };
101
classInitNative(JNIEnv * env,jclass clazz)102 static 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
initNative(JNIEnv * env,jobject object)137 static 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
cleanupNative(JNIEnv * env,jobject object)173 static 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
connectA2dpNative(JNIEnv * env,jobject object,jbyteArray address)193 static 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
disconnectA2dpNative(JNIEnv * env,jobject object,jbyteArray address)215 static 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
234 static 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
register_com_android_bluetooth_a2dp(JNIEnv * env)242 int 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