• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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