1 /*
2 * Copyright 2019 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
3 * www.ehima.com
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <bluetooth/log.h>
19 #include <hardware/bluetooth.h>
20 #include <hardware/bt_le_audio.h>
21
22 #include <vector>
23
24 #include "bta_le_audio_api.h"
25 #include "btif_common.h"
26 #include "btif_profile_storage.h"
27 #include "stack/include/main_thread.h"
28
29 using base::Bind;
30 using base::Unretained;
31 using bluetooth::le_audio::btle_audio_codec_config_t;
32 using bluetooth::le_audio::ConnectionState;
33 using bluetooth::le_audio::GroupNodeStatus;
34 using bluetooth::le_audio::GroupStatus;
35 using bluetooth::le_audio::GroupStreamStatus;
36 using bluetooth::le_audio::LeAudioClientCallbacks;
37 using bluetooth::le_audio::LeAudioClientInterface;
38 using bluetooth::le_audio::UnicastMonitorModeStatus;
39 using namespace bluetooth;
40
41 namespace {
42 class LeAudioClientInterfaceImpl;
43 std::unique_ptr<LeAudioClientInterface> leAudioInstance;
44 std::atomic_bool initialized = false;
45
46 class LeAudioClientInterfaceImpl : public LeAudioClientInterface,
47 public LeAudioClientCallbacks {
48 ~LeAudioClientInterfaceImpl() = default;
49
OnInitialized(void)50 void OnInitialized(void) {
51 do_in_jni_thread(
52 Bind(&LeAudioClientCallbacks::OnInitialized, Unretained(callbacks)));
53 }
54
OnConnectionState(ConnectionState state,const RawAddress & address)55 void OnConnectionState(ConnectionState state,
56 const RawAddress& address) override {
57 do_in_jni_thread(Bind(&LeAudioClientCallbacks::OnConnectionState,
58 Unretained(callbacks), state, address));
59 }
60
OnGroupStatus(int group_id,GroupStatus group_status)61 void OnGroupStatus(int group_id, GroupStatus group_status) override {
62 do_in_jni_thread(Bind(&LeAudioClientCallbacks::OnGroupStatus,
63 Unretained(callbacks), group_id, group_status));
64 }
65
OnGroupNodeStatus(const RawAddress & addr,int group_id,GroupNodeStatus node_status)66 void OnGroupNodeStatus(const RawAddress& addr, int group_id,
67 GroupNodeStatus node_status) override {
68 do_in_jni_thread(Bind(&LeAudioClientCallbacks::OnGroupNodeStatus,
69 Unretained(callbacks), addr, group_id, node_status));
70 }
71
OnAudioConf(uint8_t direction,int group_id,uint32_t snk_audio_location,uint32_t src_audio_location,uint16_t avail_cont)72 void OnAudioConf(uint8_t direction, int group_id, uint32_t snk_audio_location,
73 uint32_t src_audio_location, uint16_t avail_cont) override {
74 do_in_jni_thread(Bind(&LeAudioClientCallbacks::OnAudioConf,
75 Unretained(callbacks), direction, group_id,
76 snk_audio_location, src_audio_location, avail_cont));
77 }
78
OnSinkAudioLocationAvailable(const RawAddress & address,uint32_t snk_audio_location)79 void OnSinkAudioLocationAvailable(const RawAddress& address,
80 uint32_t snk_audio_location) override {
81 do_in_jni_thread(Bind(&LeAudioClientCallbacks::OnSinkAudioLocationAvailable,
82 Unretained(callbacks), address, snk_audio_location));
83 }
84
OnAudioLocalCodecCapabilities(std::vector<btle_audio_codec_config_t> local_input_capa_codec_conf,std::vector<btle_audio_codec_config_t> local_output_capa_codec_conf)85 void OnAudioLocalCodecCapabilities(
86 std::vector<btle_audio_codec_config_t> local_input_capa_codec_conf,
87 std::vector<btle_audio_codec_config_t> local_output_capa_codec_conf)
88 override {
89 do_in_jni_thread(
90 Bind(&LeAudioClientCallbacks::OnAudioLocalCodecCapabilities,
91 Unretained(callbacks), local_input_capa_codec_conf,
92 local_output_capa_codec_conf));
93 }
94
OnAudioGroupCurrentCodecConf(int group_id,btle_audio_codec_config_t input_codec_conf,btle_audio_codec_config_t output_codec_conf)95 void OnAudioGroupCurrentCodecConf(
96 int group_id, btle_audio_codec_config_t input_codec_conf,
97 btle_audio_codec_config_t output_codec_conf) override {
98 do_in_jni_thread(Bind(&LeAudioClientCallbacks::OnAudioGroupCurrentCodecConf,
99 Unretained(callbacks), group_id, input_codec_conf,
100 output_codec_conf));
101 }
102
OnAudioGroupSelectableCodecConf(int group_id,std::vector<btle_audio_codec_config_t> input_selectable_codec_conf,std::vector<btle_audio_codec_config_t> output_selectable_codec_conf)103 void OnAudioGroupSelectableCodecConf(
104 int group_id,
105 std::vector<btle_audio_codec_config_t> input_selectable_codec_conf,
106 std::vector<btle_audio_codec_config_t> output_selectable_codec_conf)
107 override {
108 do_in_jni_thread(
109 Bind(&LeAudioClientCallbacks::OnAudioGroupSelectableCodecConf,
110 Unretained(callbacks), group_id, input_selectable_codec_conf,
111 output_selectable_codec_conf));
112 }
113
OnHealthBasedRecommendationAction(const RawAddress & address,bluetooth::le_audio::LeAudioHealthBasedAction action)114 void OnHealthBasedRecommendationAction(
115 const RawAddress& address,
116 bluetooth::le_audio::LeAudioHealthBasedAction action) override {
117 do_in_jni_thread(
118 Bind(&LeAudioClientCallbacks::OnHealthBasedRecommendationAction,
119 Unretained(callbacks), address, action));
120 }
121
OnHealthBasedGroupRecommendationAction(int group_id,bluetooth::le_audio::LeAudioHealthBasedAction action)122 void OnHealthBasedGroupRecommendationAction(
123 int group_id,
124 bluetooth::le_audio::LeAudioHealthBasedAction action) override {
125 do_in_jni_thread(
126 Bind(&LeAudioClientCallbacks::OnHealthBasedGroupRecommendationAction,
127 Unretained(callbacks), group_id, action));
128 }
129
OnUnicastMonitorModeStatus(uint8_t direction,UnicastMonitorModeStatus status)130 void OnUnicastMonitorModeStatus(uint8_t direction,
131 UnicastMonitorModeStatus status) override {
132 do_in_jni_thread(Bind(&LeAudioClientCallbacks::OnUnicastMonitorModeStatus,
133 Unretained(callbacks), direction, status));
134 }
135
OnGroupStreamStatus(int group_id,GroupStreamStatus group_stream_status)136 void OnGroupStreamStatus(int group_id,
137 GroupStreamStatus group_stream_status) override {
138 do_in_jni_thread(Bind(&LeAudioClientCallbacks::OnGroupStreamStatus,
139 Unretained(callbacks), group_id,
140 group_stream_status));
141 }
142
Initialize(LeAudioClientCallbacks * callbacks,const std::vector<btle_audio_codec_config_t> & offloading_preference)143 void Initialize(LeAudioClientCallbacks* callbacks,
144 const std::vector<btle_audio_codec_config_t>&
145 offloading_preference) override {
146 this->callbacks = callbacks;
147
148 for (auto codec : offloading_preference) {
149 log::info("supported codec: {}", codec.ToString());
150 }
151
152 do_in_main_thread(
153 FROM_HERE,
154 Bind(&LeAudioClient::Initialize, this,
155 jni_thread_wrapper(Bind(&btif_storage_load_bonded_leaudio)),
156 base::Bind([]() -> bool {
157 return LeAudioHalVerifier::SupportsLeAudio();
158 }),
159 offloading_preference));
160
161 /* It might be not yet initialized, but setting this flag here is safe,
162 * because other calls will check this and the native instance
163 */
164 initialized = true;
165 }
166
Cleanup(void)167 void Cleanup(void) override {
168 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
169 log::verbose(
170 "call ignored, due to already started cleanup procedure or service "
171 "being not read");
172 return;
173 }
174
175 initialized = false;
176
177 do_in_main_thread(FROM_HERE, Bind(&LeAudioClient::Cleanup));
178 }
179
RemoveDevice(const RawAddress & address)180 void RemoveDevice(const RawAddress& address) override {
181 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
182 log::verbose(
183 "call ignored, due to already started cleanup procedure or service "
184 "being not read");
185
186 do_in_jni_thread(Bind(&btif_storage_remove_leaudio, address));
187 return;
188 }
189
190 do_in_main_thread(FROM_HERE,
191 Bind(&LeAudioClient::RemoveDevice,
192 Unretained(LeAudioClient::Get()), address));
193
194 do_in_jni_thread(Bind(&btif_storage_remove_leaudio, address));
195 }
196
Connect(const RawAddress & address)197 void Connect(const RawAddress& address) override {
198 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
199 log::verbose(
200 "call ignored, due to already started cleanup procedure or service "
201 "being not read");
202 return;
203 }
204
205 do_in_main_thread(FROM_HERE,
206 Bind(&LeAudioClient::Connect,
207 Unretained(LeAudioClient::Get()), address));
208 }
209
Disconnect(const RawAddress & address)210 void Disconnect(const RawAddress& address) override {
211 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
212 log::verbose(
213 "call ignored, due to already started cleanup procedure or service "
214 "being not read");
215 return;
216 }
217
218 do_in_main_thread(FROM_HERE,
219 Bind(&LeAudioClient::Disconnect,
220 Unretained(LeAudioClient::Get()), address));
221 }
222
SetEnableState(const RawAddress & address,bool enabled)223 void SetEnableState(const RawAddress& address, bool enabled) override {
224 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
225 log::verbose(
226 "call ignored, due to already started cleanup procedure or service "
227 "being not read");
228 return;
229 }
230
231 do_in_main_thread(FROM_HERE,
232 Bind(&LeAudioClient::SetEnableState,
233 Unretained(LeAudioClient::Get()), address, enabled));
234 }
235
GroupAddNode(const int group_id,const RawAddress & address)236 void GroupAddNode(const int group_id, const RawAddress& address) override {
237 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
238 log::verbose(
239 "call ignored, due to already started cleanup procedure or service "
240 "being not read");
241 return;
242 }
243
244 do_in_main_thread(
245 FROM_HERE, Bind(&LeAudioClient::GroupAddNode,
246 Unretained(LeAudioClient::Get()), group_id, address));
247 }
248
GroupRemoveNode(const int group_id,const RawAddress & address)249 void GroupRemoveNode(const int group_id, const RawAddress& address) override {
250 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
251 log::verbose(
252 "call ignored, due to already started cleanup procedure or service "
253 "being not read");
254 return;
255 }
256
257 do_in_main_thread(
258 FROM_HERE, Bind(&LeAudioClient::GroupRemoveNode,
259 Unretained(LeAudioClient::Get()), group_id, address));
260 }
261
GroupSetActive(const int group_id)262 void GroupSetActive(const int group_id) override {
263 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
264 log::verbose(
265 "call ignored, due to already started cleanup procedure or service "
266 "being not read");
267 return;
268 }
269
270 do_in_main_thread(FROM_HERE,
271 Bind(&LeAudioClient::GroupSetActive,
272 Unretained(LeAudioClient::Get()), group_id));
273 }
274
SetCodecConfigPreference(int group_id,btle_audio_codec_config_t input_codec_config,btle_audio_codec_config_t output_codec_config)275 void SetCodecConfigPreference(int group_id,
276 btle_audio_codec_config_t input_codec_config,
277 btle_audio_codec_config_t output_codec_config) {
278 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
279 log::verbose(
280 "call ignored, due to already started cleanup procedure or service "
281 "being not read");
282 return;
283 }
284 do_in_main_thread(FROM_HERE,
285 Bind(&LeAudioClient::SetCodecConfigPreference,
286 Unretained(LeAudioClient::Get()), group_id,
287 input_codec_config, output_codec_config));
288 }
289
SetCcidInformation(int ccid,int context_type)290 void SetCcidInformation(int ccid, int context_type) {
291 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
292 log::verbose(
293 "call ignored, due to already started cleanup procedure or service "
294 "being not read");
295 return;
296 }
297
298 do_in_main_thread(
299 FROM_HERE, Bind(&LeAudioClient::SetCcidInformation,
300 Unretained(LeAudioClient::Get()), ccid, context_type));
301 }
302
SetInCall(bool in_call)303 void SetInCall(bool in_call) {
304 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
305 log::verbose(
306 "call ignored, due to already started cleanup procedure or service "
307 "being not read");
308 return;
309 }
310
311 do_in_main_thread(FROM_HERE,
312 Bind(&LeAudioClient::SetInCall,
313 Unretained(LeAudioClient::Get()), in_call));
314 }
315
SetUnicastMonitorMode(uint8_t direction,bool enable)316 void SetUnicastMonitorMode(uint8_t direction, bool enable) {
317 log::verbose("enable: {}", enable);
318 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
319 log::verbose(
320 "Unicast monitoring mode set ignored, due to already"
321 " started cleanup procedure or service being not read");
322 return;
323 }
324
325 do_in_main_thread(
326 FROM_HERE, Bind(&LeAudioClient::SetUnicastMonitorMode,
327 Unretained(LeAudioClient::Get()), direction, enable));
328 }
329
SendAudioProfilePreferences(int group_id,bool is_output_preference_le_audio,bool is_duplex_preference_le_audio)330 void SendAudioProfilePreferences(int group_id,
331 bool is_output_preference_le_audio,
332 bool is_duplex_preference_le_audio) {
333 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
334 log::verbose(
335 "call ignored, due to already started cleanup procedure or service "
336 "being not read");
337 return;
338 }
339
340 do_in_main_thread(
341 FROM_HERE,
342 Bind(&LeAudioClient::SendAudioProfilePreferences,
343 Unretained(LeAudioClient::Get()), group_id,
344 is_output_preference_le_audio, is_duplex_preference_le_audio));
345 }
346
SetGroupAllowedContextMask(int group_id,int sink_context_types,int source_context_types)347 void SetGroupAllowedContextMask(int group_id, int sink_context_types,
348 int source_context_types) {
349 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
350 log::verbose(
351 "call ignored, due to already started cleanup procedure or service "
352 "being not read");
353 return;
354 }
355
356 log::info("group_id: {}, sink context types: {}, source context types: {}",
357 group_id, sink_context_types, source_context_types);
358
359 do_in_main_thread(FROM_HERE,
360 Bind(&LeAudioClient::SetGroupAllowedContextMask,
361 Unretained(LeAudioClient::Get()), group_id,
362 sink_context_types, source_context_types));
363 }
364
365 private:
366 LeAudioClientCallbacks* callbacks;
367 };
368
369 } /* namespace */
370
btif_le_audio_get_interface()371 LeAudioClientInterface* btif_le_audio_get_interface() {
372 if (!leAudioInstance) {
373 leAudioInstance.reset(new LeAudioClientInterfaceImpl());
374 }
375
376 return leAudioInstance.get();
377 }
378