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