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