1 /* Copyright 2019 HIMSA II K/S - www.himsa.com
2 * Represented by EHIMA - www.ehima.com
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 "BluetoothLeAudioServiceJni"
18
19 #define LOG_NDEBUG 0
20
21 #include <hardware/bluetooth.h>
22
23 #include <array>
24 #include <optional>
25 #include <shared_mutex>
26
27 #include "com_android_bluetooth.h"
28 #include "hardware/bt_le_audio.h"
29
30 using bluetooth::le_audio::ConnectionState;
31 using bluetooth::le_audio::GroupStatus;
32 using bluetooth::le_audio::LeAudioClientCallbacks;
33 using bluetooth::le_audio::LeAudioClientInterface;
34
35 namespace android {
36 static jmethodID method_onConnectionStateChanged;
37 static jmethodID method_onGroupStatus;
38 static jmethodID method_onAudioConf;
39 static jmethodID method_onSetMemberAvailable;
40
41 static LeAudioClientInterface* sLeAudioClientInterface = nullptr;
42 static std::shared_timed_mutex interface_mutex;
43
44 static jobject mCallbacksObj = nullptr;
45 static std::shared_timed_mutex callbacks_mutex;
46
47 class LeAudioClientCallbacksImpl : public LeAudioClientCallbacks {
48 public:
49 ~LeAudioClientCallbacksImpl() = default;
50
OnConnectionState(ConnectionState state,const RawAddress & bd_addr)51 void OnConnectionState(ConnectionState state,
52 const RawAddress& bd_addr) override {
53 LOG(INFO) << __func__ << ", state:" << int(state);
54
55 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
56 CallbackEnv sCallbackEnv(__func__);
57 if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
58
59 ScopedLocalRef<jbyteArray> addr(
60 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
61 if (!addr.get()) {
62 LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state";
63 return;
64 }
65
66 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
67 (jbyte*)&bd_addr);
68 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
69 (jint)state, addr.get());
70 }
71
OnGroupStatus(uint8_t group_id,GroupStatus group_status,uint8_t group_flags)72 void OnGroupStatus(uint8_t group_id, GroupStatus group_status,
73 uint8_t group_flags) override {
74 LOG(INFO) << __func__;
75
76 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
77 CallbackEnv sCallbackEnv(__func__);
78 if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
79
80 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGroupStatus,
81 (jint)group_id, (jint)group_status,
82 (jint)group_flags);
83 }
84
OnAudioConf(const RawAddress & bd_addr,uint8_t direction,uint8_t group_id,uint32_t sink_audio_location,uint32_t source_audio_location)85 void OnAudioConf(const RawAddress& bd_addr, uint8_t direction,
86 uint8_t group_id, uint32_t sink_audio_location,
87 uint32_t source_audio_location) override {
88 LOG(INFO) << __func__;
89
90 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
91 CallbackEnv sCallbackEnv(__func__);
92 if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
93
94 ScopedLocalRef<jbyteArray> addr(
95 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
96 if (!addr.get()) {
97 LOG(ERROR) << "Failed to new jbyteArray bd addr for group status";
98 return;
99 }
100
101 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
102 (jbyte*)&bd_addr);
103 sCallbackEnv->CallVoidMethod(
104 mCallbacksObj, method_onAudioConf, (jint)direction, (jint)group_id,
105 (jint)sink_audio_location, (jint)source_audio_location, addr.get());
106 }
107
OnSetMemberAvailable(const RawAddress & bd_addr,uint8_t group_id)108 void OnSetMemberAvailable(const RawAddress& bd_addr,
109 uint8_t group_id) override {
110 LOG(INFO) << __func__ << ", group id:" << int(group_id);
111
112 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
113 CallbackEnv sCallbackEnv(__func__);
114 if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
115
116 ScopedLocalRef<jbyteArray> addr(
117 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
118 if (!addr.get()) {
119 LOG(ERROR) << "Failed to new jbyteArray bd addr for connection state";
120 return;
121 }
122
123 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
124 (jbyte*)&bd_addr);
125 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetMemberAvailable,
126 addr.get(), (jint)group_id);
127 }
128 };
129
130 static LeAudioClientCallbacksImpl sLeAudioClientCallbacks;
131
classInitNative(JNIEnv * env,jclass clazz)132 static void classInitNative(JNIEnv* env, jclass clazz) {
133 method_onGroupStatus = env->GetMethodID(clazz, "onGroupStatus", "(III)V");
134 method_onAudioConf = env->GetMethodID(clazz, "onAudioConf", "(IIII[B)V");
135 method_onConnectionStateChanged =
136 env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
137 method_onSetMemberAvailable =
138 env->GetMethodID(clazz, "onSetMemberAvailable", "([BI)V");
139 }
140
initNative(JNIEnv * env,jobject object)141 static void initNative(JNIEnv* env, jobject object) {
142 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
143 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
144
145 const bt_interface_t* btInf = getBluetoothInterface();
146 if (btInf == nullptr) {
147 LOG(ERROR) << "Bluetooth module is not loaded";
148 return;
149 }
150
151 if (mCallbacksObj != nullptr) {
152 LOG(INFO) << "Cleaning up LeAudio callback object";
153 env->DeleteGlobalRef(mCallbacksObj);
154 mCallbacksObj = nullptr;
155 }
156
157 if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
158 LOG(ERROR) << "Failed to allocate Global Ref for LeAudio Callbacks";
159 return;
160 }
161
162 sLeAudioClientInterface =
163 (LeAudioClientInterface*)btInf->get_profile_interface(
164 BT_PROFILE_LE_AUDIO_ID);
165 if (sLeAudioClientInterface == nullptr) {
166 LOG(ERROR) << "Failed to get Bluetooth LeAudio Interface";
167 return;
168 }
169
170 sLeAudioClientInterface->Initialize(&sLeAudioClientCallbacks);
171 }
172
cleanupNative(JNIEnv * env,jobject object)173 static void cleanupNative(JNIEnv* env, jobject object) {
174 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
175 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
176
177 const bt_interface_t* btInf = getBluetoothInterface();
178 if (btInf == nullptr) {
179 LOG(ERROR) << "Bluetooth module is not loaded";
180 return;
181 }
182
183 if (sLeAudioClientInterface != nullptr) {
184 sLeAudioClientInterface->Cleanup();
185 sLeAudioClientInterface = nullptr;
186 }
187
188 if (mCallbacksObj != nullptr) {
189 env->DeleteGlobalRef(mCallbacksObj);
190 mCallbacksObj = nullptr;
191 }
192 }
193
connectLeAudioNative(JNIEnv * env,jobject object,jbyteArray address)194 static jboolean connectLeAudioNative(JNIEnv* env, jobject object,
195 jbyteArray address) {
196 LOG(INFO) << __func__;
197 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
198 if (!sLeAudioClientInterface) return JNI_FALSE;
199
200 jbyte* addr = env->GetByteArrayElements(address, nullptr);
201 if (!addr) {
202 jniThrowIOException(env, EINVAL);
203 return JNI_FALSE;
204 }
205
206 RawAddress* tmpraw = (RawAddress*)addr;
207 sLeAudioClientInterface->Connect(*tmpraw);
208 env->ReleaseByteArrayElements(address, addr, 0);
209 return JNI_TRUE;
210 }
211
disconnectLeAudioNative(JNIEnv * env,jobject object,jbyteArray address)212 static jboolean disconnectLeAudioNative(JNIEnv* env, jobject object,
213 jbyteArray address) {
214 LOG(INFO) << __func__;
215 std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
216 if (!sLeAudioClientInterface) return JNI_FALSE;
217
218 jbyte* addr = env->GetByteArrayElements(address, nullptr);
219 if (!addr) {
220 jniThrowIOException(env, EINVAL);
221 return JNI_FALSE;
222 }
223
224 RawAddress* tmpraw = (RawAddress*)addr;
225 sLeAudioClientInterface->Disconnect(*tmpraw);
226 env->ReleaseByteArrayElements(address, addr, 0);
227 return JNI_TRUE;
228 }
229
groupStreamNative(JNIEnv * env,jobject object,jint group_id,jint content_type)230 static void groupStreamNative(JNIEnv* env, jobject object, jint group_id,
231 jint content_type) {
232 LOG(INFO) << __func__;
233
234 if (!sLeAudioClientInterface) {
235 LOG(ERROR) << __func__ << ": Failed to get the Bluetooth LeAudio Interface";
236 return;
237 }
238
239 sLeAudioClientInterface->GroupStream(group_id, content_type);
240 }
241
groupSuspendNative(JNIEnv * env,jobject object,jint group_id)242 static void groupSuspendNative(JNIEnv* env, jobject object, jint group_id) {
243 LOG(INFO) << __func__;
244
245 if (!sLeAudioClientInterface) {
246 LOG(ERROR) << __func__ << ": Failed to get the Bluetooth LeAudio Interface";
247 return;
248 }
249
250 sLeAudioClientInterface->GroupSuspend(group_id);
251 }
252
groupStopNative(JNIEnv * env,jobject object,jint group_id)253 static void groupStopNative(JNIEnv* env, jobject object, jint group_id) {
254 LOG(INFO) << __func__;
255
256 if (!sLeAudioClientInterface) {
257 LOG(ERROR) << __func__ << ": Failed to get the Bluetooth LeAudio Interface";
258 return;
259 }
260
261 sLeAudioClientInterface->GroupStop(group_id);
262 }
263
264 static JNINativeMethod sMethods[] = {
265 {"classInitNative", "()V", (void*)classInitNative},
266 {"initNative", "()V", (void*)initNative},
267 {"cleanupNative", "()V", (void*)cleanupNative},
268 {"connectLeAudioNative", "([B)Z", (void*)connectLeAudioNative},
269 {"disconnectLeAudioNative", "([B)Z", (void*)disconnectLeAudioNative},
270 {"groupStreamNative", "(II)V", (void*)groupStreamNative},
271 {"groupSuspendNative", "(I)V", (void*)groupSuspendNative},
272 {"groupStopNative", "(I)V", (void*)groupStopNative},
273 };
274
register_com_android_bluetooth_le_audio(JNIEnv * env)275 int register_com_android_bluetooth_le_audio(JNIEnv* env) {
276 return jniRegisterNativeMethods(
277 env, "com/android/bluetooth/le_audio/LeAudioNativeInterface", sMethods,
278 NELEM(sMethods));
279 }
280 } // namespace android
281