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