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