• 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     method_onConnectionStateChanged =
128         env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
129 
130     method_onAudioStateChanged =
131         env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
132 
133     method_onAudioConfigChanged =
134         env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V");
135 
136     ALOGI("%s: succeeds", __FUNCTION__);
137 }
138 
initNative(JNIEnv * env,jobject object)139 static void initNative(JNIEnv *env, jobject object) {
140     const bt_interface_t* btInf;
141     bt_status_t status;
142 
143     if ( (btInf = getBluetoothInterface()) == NULL) {
144         ALOGE("Bluetooth module is not loaded");
145         return;
146     }
147 
148     if (sBluetoothA2dpInterface !=NULL) {
149          ALOGW("Cleaning up A2DP Interface before initializing...");
150          sBluetoothA2dpInterface->cleanup();
151          sBluetoothA2dpInterface = NULL;
152     }
153 
154     if (mCallbacksObj != NULL) {
155          ALOGW("Cleaning up A2DP callback object");
156          env->DeleteGlobalRef(mCallbacksObj);
157          mCallbacksObj = NULL;
158     }
159 
160     if ( (sBluetoothA2dpInterface = (btav_interface_t *)
161           btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_SINK_ID)) == NULL) {
162         ALOGE("Failed to get Bluetooth A2DP Sink Interface");
163         return;
164     }
165 
166     if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {
167         ALOGE("Failed to initialize Bluetooth A2DP Sink, status: %d", status);
168         sBluetoothA2dpInterface = NULL;
169         return;
170     }
171 
172     mCallbacksObj = env->NewGlobalRef(object);
173 }
174 
cleanupNative(JNIEnv * env,jobject object)175 static void cleanupNative(JNIEnv *env, jobject object) {
176     const bt_interface_t* btInf;
177 
178     if ( (btInf = getBluetoothInterface()) == NULL) {
179         ALOGE("Bluetooth module is not loaded");
180         return;
181     }
182 
183     if (sBluetoothA2dpInterface !=NULL) {
184         sBluetoothA2dpInterface->cleanup();
185         sBluetoothA2dpInterface = NULL;
186     }
187 
188     if (mCallbacksObj != NULL) {
189         env->DeleteGlobalRef(mCallbacksObj);
190         mCallbacksObj = NULL;
191     }
192 }
193 
connectA2dpNative(JNIEnv * env,jobject object,jbyteArray address)194 static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
195     jbyte *addr;
196     bt_bdaddr_t * btAddr;
197     bt_status_t status;
198 
199     ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface);
200     if (!sBluetoothA2dpInterface) return JNI_FALSE;
201 
202     addr = env->GetByteArrayElements(address, NULL);
203     btAddr = (bt_bdaddr_t *) addr;
204     if (!addr) {
205         jniThrowIOException(env, EINVAL);
206         return JNI_FALSE;
207     }
208 
209     if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
210         ALOGE("Failed HF connection, status: %d", status);
211     }
212     env->ReleaseByteArrayElements(address, addr, 0);
213     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
214 }
215 
disconnectA2dpNative(JNIEnv * env,jobject object,jbyteArray address)216 static jboolean disconnectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
217     jbyte *addr;
218     bt_status_t status;
219 
220     if (!sBluetoothA2dpInterface) return JNI_FALSE;
221 
222     addr = env->GetByteArrayElements(address, NULL);
223     if (!addr) {
224         jniThrowIOException(env, EINVAL);
225         return JNI_FALSE;
226     }
227 
228     if ( (status = sBluetoothA2dpInterface->disconnect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
229         ALOGE("Failed HF disconnection, status: %d", status);
230     }
231     env->ReleaseByteArrayElements(address, addr, 0);
232     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
233 }
234 
informAudioFocusStateNative(JNIEnv * env,jobject object,jint focus_state)235 static void informAudioFocusStateNative(JNIEnv *env, jobject object, jint focus_state) {
236     if (!sBluetoothA2dpInterface) return;
237 
238     sBluetoothA2dpInterface->set_audio_focus_state((uint8_t)focus_state);
239 }
240 
informAudioTrackGainNative(JNIEnv * env,jobject object,jfloat gain)241 static void informAudioTrackGainNative(JNIEnv *env, jobject object, jfloat gain) {
242     if (!sBluetoothA2dpInterface) return;
243 
244     sBluetoothA2dpInterface->set_audio_track_gain((float) gain);
245 
246 }
247 
248 static JNINativeMethod sMethods[] = {
249     {"classInitNative", "()V", (void *) classInitNative},
250     {"initNative", "()V", (void *) initNative},
251     {"cleanupNative", "()V", (void *) cleanupNative},
252     {"connectA2dpNative", "([B)Z", (void *) connectA2dpNative},
253     {"disconnectA2dpNative", "([B)Z", (void *) disconnectA2dpNative},
254     {"informAudioFocusStateNative", "(I)V", (void *) informAudioFocusStateNative},
255     {"informAudioTrackGainNative", "(F)V", (void *) informAudioTrackGainNative},
256 };
257 
register_com_android_bluetooth_a2dp_sink(JNIEnv * env)258 int register_com_android_bluetooth_a2dp_sink(JNIEnv* env)
259 {
260     return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dpsink/A2dpSinkStateMachine",
261                                     sMethods, NELEM(sMethods));
262 }
263 
264 }
265