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