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
25 #include <string.h>
26
27 namespace android {
28 static jmethodID method_onConnectionStateChanged;
29 static jmethodID method_onAudioStateChanged;
30 static jmethodID method_onAudioConfigChanged;
31
32 static const btav_sink_interface_t* sBluetoothA2dpInterface = NULL;
33 static jobject mCallbacksObj = NULL;
34
bta2dp_connection_state_callback(const RawAddress & bd_addr,btav_connection_state_t state)35 static void bta2dp_connection_state_callback(const RawAddress& bd_addr,
36 btav_connection_state_t state) {
37 ALOGI("%s", __func__);
38 CallbackEnv sCallbackEnv(__func__);
39 if (!sCallbackEnv.valid()) return;
40
41 ScopedLocalRef<jbyteArray> addr(
42 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
43 if (!addr.get()) {
44 ALOGE("Fail to new jbyteArray bd addr for connection state");
45 return;
46 }
47
48 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
49 (const jbyte*)bd_addr.address);
50 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
51 addr.get(), (jint)state);
52 }
53
bta2dp_audio_state_callback(const RawAddress & bd_addr,btav_audio_state_t state)54 static void bta2dp_audio_state_callback(const RawAddress& bd_addr,
55 btav_audio_state_t state) {
56 ALOGI("%s", __func__);
57 CallbackEnv sCallbackEnv(__func__);
58 if (!sCallbackEnv.valid()) return;
59
60 ScopedLocalRef<jbyteArray> addr(
61 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
62 if (!addr.get()) {
63 ALOGE("Fail to new jbyteArray bd addr for connection state");
64 return;
65 }
66
67 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
68 (const jbyte*)bd_addr.address);
69 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged,
70 addr.get(), (jint)state);
71 }
72
bta2dp_audio_config_callback(const RawAddress & bd_addr,uint32_t sample_rate,uint8_t channel_count)73 static void bta2dp_audio_config_callback(const RawAddress& bd_addr,
74 uint32_t sample_rate,
75 uint8_t channel_count) {
76 ALOGI("%s", __func__);
77 CallbackEnv sCallbackEnv(__func__);
78 if (!sCallbackEnv.valid()) return;
79
80 ScopedLocalRef<jbyteArray> addr(
81 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
82 if (!addr.get()) {
83 ALOGE("Fail to new jbyteArray bd addr for connection state");
84 return;
85 }
86
87 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
88 (const jbyte*)bd_addr.address);
89 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioConfigChanged,
90 addr.get(), (jint)sample_rate,
91 (jint)channel_count);
92 }
93
94 static btav_sink_callbacks_t sBluetoothA2dpCallbacks = {
95 sizeof(sBluetoothA2dpCallbacks), bta2dp_connection_state_callback,
96 bta2dp_audio_state_callback, bta2dp_audio_config_callback,
97 };
98
classInitNative(JNIEnv * env,jclass clazz)99 static void classInitNative(JNIEnv* env, jclass clazz) {
100 method_onConnectionStateChanged =
101 env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V");
102
103 method_onAudioStateChanged =
104 env->GetMethodID(clazz, "onAudioStateChanged", "([BI)V");
105
106 method_onAudioConfigChanged =
107 env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V");
108
109 ALOGI("%s: succeeds", __func__);
110 }
111
initNative(JNIEnv * env,jobject object,jint maxConnectedAudioDevices)112 static void initNative(JNIEnv* env, jobject object,
113 jint maxConnectedAudioDevices) {
114 const bt_interface_t* btInf = getBluetoothInterface();
115 if (btInf == NULL) {
116 ALOGE("Bluetooth module is not loaded");
117 return;
118 }
119
120 if (sBluetoothA2dpInterface != NULL) {
121 ALOGW("Cleaning up A2DP Interface before initializing...");
122 sBluetoothA2dpInterface->cleanup();
123 sBluetoothA2dpInterface = NULL;
124 }
125
126 if (mCallbacksObj != NULL) {
127 ALOGW("Cleaning up A2DP callback object");
128 env->DeleteGlobalRef(mCallbacksObj);
129 mCallbacksObj = NULL;
130 }
131
132 sBluetoothA2dpInterface =
133 (btav_sink_interface_t*)btInf->get_profile_interface(
134 BT_PROFILE_ADVANCED_AUDIO_SINK_ID);
135 if (sBluetoothA2dpInterface == NULL) {
136 ALOGE("Failed to get Bluetooth A2DP Sink Interface");
137 return;
138 }
139
140 bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks,
141 maxConnectedAudioDevices);
142 if (status != BT_STATUS_SUCCESS) {
143 ALOGE("Failed to initialize Bluetooth A2DP Sink, status: %d", status);
144 sBluetoothA2dpInterface = NULL;
145 return;
146 }
147
148 mCallbacksObj = env->NewGlobalRef(object);
149 }
150
cleanupNative(JNIEnv * env,jobject object)151 static void cleanupNative(JNIEnv* env, jobject object) {
152 const bt_interface_t* btInf = getBluetoothInterface();
153
154 if (btInf == NULL) {
155 ALOGE("Bluetooth module is not loaded");
156 return;
157 }
158
159 if (sBluetoothA2dpInterface != NULL) {
160 sBluetoothA2dpInterface->cleanup();
161 sBluetoothA2dpInterface = NULL;
162 }
163
164 if (mCallbacksObj != NULL) {
165 env->DeleteGlobalRef(mCallbacksObj);
166 mCallbacksObj = NULL;
167 }
168 }
169
connectA2dpNative(JNIEnv * env,jobject object,jbyteArray address)170 static jboolean connectA2dpNative(JNIEnv* env, jobject object,
171 jbyteArray address) {
172 ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
173 if (!sBluetoothA2dpInterface) return JNI_FALSE;
174
175 jbyte* addr = env->GetByteArrayElements(address, NULL);
176 if (!addr) {
177 jniThrowIOException(env, EINVAL);
178 return JNI_FALSE;
179 }
180
181 RawAddress bd_addr;
182 bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
183 bt_status_t status = sBluetoothA2dpInterface->connect(bd_addr);
184 if (status != BT_STATUS_SUCCESS) {
185 ALOGE("Failed HF connection, status: %d", status);
186 }
187 env->ReleaseByteArrayElements(address, addr, 0);
188 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
189 }
190
disconnectA2dpNative(JNIEnv * env,jobject object,jbyteArray address)191 static jboolean disconnectA2dpNative(JNIEnv* env, jobject object,
192 jbyteArray address) {
193 if (!sBluetoothA2dpInterface) return JNI_FALSE;
194
195 jbyte* addr = env->GetByteArrayElements(address, NULL);
196 if (!addr) {
197 jniThrowIOException(env, EINVAL);
198 return JNI_FALSE;
199 }
200
201 RawAddress bd_addr;
202 bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
203 bt_status_t status = sBluetoothA2dpInterface->disconnect(bd_addr);
204 if (status != BT_STATUS_SUCCESS) {
205 ALOGE("Failed HF disconnection, status: %d", status);
206 }
207 env->ReleaseByteArrayElements(address, addr, 0);
208 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
209 }
210
informAudioFocusStateNative(JNIEnv * env,jobject object,jint focus_state)211 static void informAudioFocusStateNative(JNIEnv* env, jobject object,
212 jint focus_state) {
213 if (!sBluetoothA2dpInterface) return;
214 sBluetoothA2dpInterface->set_audio_focus_state((uint8_t)focus_state);
215 }
216
informAudioTrackGainNative(JNIEnv * env,jobject object,jfloat gain)217 static void informAudioTrackGainNative(JNIEnv* env, jobject object,
218 jfloat gain) {
219 if (!sBluetoothA2dpInterface) return;
220 sBluetoothA2dpInterface->set_audio_track_gain((float)gain);
221 }
222
setActiveDeviceNative(JNIEnv * env,jobject object,jbyteArray address)223 static jboolean setActiveDeviceNative(JNIEnv* env, jobject object,
224 jbyteArray address) {
225 if (!sBluetoothA2dpInterface) return JNI_FALSE;
226
227 ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
228
229 jbyte* addr = env->GetByteArrayElements(address, NULL);
230 if (!addr) {
231 jniThrowIOException(env, EINVAL);
232 return JNI_FALSE;
233 }
234
235 RawAddress rawAddress;
236 rawAddress.FromOctets((uint8_t*)addr);
237 bt_status_t status = sBluetoothA2dpInterface->set_active_device(rawAddress);
238 if (status != BT_STATUS_SUCCESS) {
239 ALOGE("Failed sending passthru command, status: %d", status);
240 }
241 env->ReleaseByteArrayElements(address, addr, 0);
242
243 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
244 }
245
246 static JNINativeMethod sMethods[] = {
247 {"classInitNative", "()V", (void*)classInitNative},
248 {"initNative", "(I)V", (void*)initNative},
249 {"cleanupNative", "()V", (void*)cleanupNative},
250 {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
251 {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
252 {"informAudioFocusStateNative", "(I)V", (void*)informAudioFocusStateNative},
253 {"informAudioTrackGainNative", "(F)V", (void*)informAudioTrackGainNative},
254 {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative},
255 };
256
register_com_android_bluetooth_a2dp_sink(JNIEnv * env)257 int register_com_android_bluetooth_a2dp_sink(JNIEnv* env) {
258 return jniRegisterNativeMethods(
259 env, "com/android/bluetooth/a2dpsink/A2dpSinkService", sMethods,
260 NELEM(sMethods));
261 }
262 }
263