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