• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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