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 <base/logging.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/btu.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::LeAudioClientCallbacks;
36 using bluetooth::le_audio::LeAudioClientInterface;
37
38 namespace {
39 class LeAudioClientInterfaceImpl;
40 std::unique_ptr<LeAudioClientInterface> leAudioInstance;
41 std::atomic_bool initialized = false;
42
43 class LeAudioClientInterfaceImpl : public LeAudioClientInterface,
44 public LeAudioClientCallbacks {
45 ~LeAudioClientInterfaceImpl() = default;
46
OnInitialized(void)47 void OnInitialized(void) {
48 do_in_jni_thread(FROM_HERE, Bind(&LeAudioClientCallbacks::OnInitialized,
49 Unretained(callbacks)));
50 }
51
OnConnectionState(ConnectionState state,const RawAddress & address)52 void OnConnectionState(ConnectionState state,
53 const RawAddress& address) override {
54 do_in_jni_thread(FROM_HERE, Bind(&LeAudioClientCallbacks::OnConnectionState,
55 Unretained(callbacks), state, address));
56 }
57
OnGroupStatus(int group_id,GroupStatus group_status)58 void OnGroupStatus(int group_id, GroupStatus group_status) override {
59 do_in_jni_thread(FROM_HERE,
60 Bind(&LeAudioClientCallbacks::OnGroupStatus,
61 Unretained(callbacks), group_id, group_status));
62 }
63
OnGroupNodeStatus(const RawAddress & addr,int group_id,GroupNodeStatus node_status)64 void OnGroupNodeStatus(const RawAddress& addr, int group_id,
65 GroupNodeStatus node_status) override {
66 do_in_jni_thread(FROM_HERE,
67 Bind(&LeAudioClientCallbacks::OnGroupNodeStatus,
68 Unretained(callbacks), addr, group_id, node_status));
69 }
70
OnAudioConf(uint8_t direction,int group_id,uint32_t snk_audio_location,uint32_t src_audio_location,uint16_t avail_cont)71 void OnAudioConf(uint8_t direction, int group_id, uint32_t snk_audio_location,
72 uint32_t src_audio_location, uint16_t avail_cont) override {
73 do_in_jni_thread(FROM_HERE,
74 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(FROM_HERE,
82 Bind(&LeAudioClientCallbacks::OnSinkAudioLocationAvailable,
83 Unretained(callbacks), address, snk_audio_location));
84 }
85
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)86 void OnAudioLocalCodecCapabilities(
87 std::vector<btle_audio_codec_config_t> local_input_capa_codec_conf,
88 std::vector<btle_audio_codec_config_t> local_output_capa_codec_conf)
89 override {
90 do_in_jni_thread(
91 FROM_HERE, Bind(&LeAudioClientCallbacks::OnAudioLocalCodecCapabilities,
92 Unretained(callbacks), local_input_capa_codec_conf,
93 local_output_capa_codec_conf));
94 }
95
OnAudioGroupCodecConf(int group_id,btle_audio_codec_config_t input_codec_conf,btle_audio_codec_config_t output_codec_conf,std::vector<btle_audio_codec_config_t> input_selectable_codec_conf,std::vector<btle_audio_codec_config_t> output_selectable_codec_conf)96 void OnAudioGroupCodecConf(
97 int group_id, btle_audio_codec_config_t input_codec_conf,
98 btle_audio_codec_config_t output_codec_conf,
99 std::vector<btle_audio_codec_config_t> input_selectable_codec_conf,
100 std::vector<btle_audio_codec_config_t> output_selectable_codec_conf)
101 override {
102 do_in_jni_thread(FROM_HERE,
103 Bind(&LeAudioClientCallbacks::OnAudioGroupCodecConf,
104 Unretained(callbacks), group_id, input_codec_conf,
105 output_codec_conf, input_selectable_codec_conf,
106 output_selectable_codec_conf));
107 }
108
Initialize(LeAudioClientCallbacks * callbacks,const std::vector<btle_audio_codec_config_t> & offloading_preference)109 void Initialize(LeAudioClientCallbacks* callbacks,
110 const std::vector<btle_audio_codec_config_t>&
111 offloading_preference) override {
112 this->callbacks = callbacks;
113
114 for (auto codec : offloading_preference) {
115 LOG_INFO("supported codec: %s", codec.ToString().c_str());
116 }
117
118 LeAudioClient::InitializeAudioSetConfigurationProvider();
119 do_in_main_thread(
120 FROM_HERE, Bind(&LeAudioClient::Initialize, this,
121 jni_thread_wrapper(
122 FROM_HERE, Bind(&btif_storage_load_bonded_leaudio)),
123 base::Bind([]() -> bool {
124 return LeAudioHalVerifier::SupportsLeAudio();
125 }),
126 offloading_preference));
127
128 /* It might be not yet initialized, but setting this flag here is safe,
129 * because other calls will check this and the native instance
130 */
131 initialized = true;
132 }
133
Cleanup(void)134 void Cleanup(void) override {
135 DVLOG(2) << __func__;
136 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
137 DVLOG(2) << __func__
138 << " call ignored, due to already started cleanup procedure or "
139 "service being not read";
140 return;
141 }
142
143 initialized = false;
144
145 do_in_main_thread(
146 FROM_HERE,
147 Bind(&LeAudioClient::Cleanup,
148 jni_thread_wrapper(
149 FROM_HERE,
150 Bind(&LeAudioClient::CleanupAudioSetConfigurationProvider))));
151 }
152
RemoveDevice(const RawAddress & address)153 void RemoveDevice(const RawAddress& address) override {
154 DVLOG(2) << __func__ << " address: " << ADDRESS_TO_LOGGABLE_STR(address);
155
156 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
157 DVLOG(2) << __func__
158 << " call ignored, due to already started cleanup procedure or "
159 "service being not read";
160
161 do_in_jni_thread(FROM_HERE, Bind(&btif_storage_remove_leaudio, address));
162 return;
163 }
164
165 do_in_main_thread(FROM_HERE,
166 Bind(&LeAudioClient::RemoveDevice,
167 Unretained(LeAudioClient::Get()), address));
168
169 do_in_jni_thread(FROM_HERE, Bind(&btif_storage_remove_leaudio, address));
170 }
171
Connect(const RawAddress & address)172 void Connect(const RawAddress& address) override {
173 DVLOG(2) << __func__ << " address: " << ADDRESS_TO_LOGGABLE_STR(address);
174
175 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
176 DVLOG(2) << __func__
177 << " call ignored, due to already started cleanup procedure or "
178 "service being not read";
179 return;
180 }
181
182 do_in_main_thread(FROM_HERE,
183 Bind(&LeAudioClient::Connect,
184 Unretained(LeAudioClient::Get()), address));
185 }
186
Disconnect(const RawAddress & address)187 void Disconnect(const RawAddress& address) override {
188 DVLOG(2) << __func__ << " address: " << ADDRESS_TO_LOGGABLE_STR(address);
189
190 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
191 DVLOG(2) << __func__
192 << " call ignored, due to already started cleanup procedure or "
193 "service being not read";
194 return;
195 }
196
197 do_in_main_thread(FROM_HERE,
198 Bind(&LeAudioClient::Disconnect,
199 Unretained(LeAudioClient::Get()), address));
200 }
201
SetEnableState(const RawAddress & address,bool enabled)202 void SetEnableState(const RawAddress& address, bool enabled) override {
203 DVLOG(2) << __func__ << " address: " << address << ", enabled: " << enabled;
204
205 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
206 DVLOG(2) << __func__
207 << " call ignored, due to already started cleanup procedure or "
208 "service being not read";
209 return;
210 }
211
212 do_in_main_thread(FROM_HERE,
213 Bind(&LeAudioClient::SetEnableState,
214 Unretained(LeAudioClient::Get()), address, enabled));
215 }
216
GroupAddNode(const int group_id,const RawAddress & address)217 void GroupAddNode(const int group_id, const RawAddress& address) override {
218 DVLOG(2) << __func__ << " group_id: " << group_id
219 << " address: " << ADDRESS_TO_LOGGABLE_STR(address);
220
221 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
222 DVLOG(2) << __func__
223 << " call ignored, due to already started cleanup procedure or "
224 "service being not read";
225 return;
226 }
227
228 do_in_main_thread(
229 FROM_HERE, Bind(&LeAudioClient::GroupAddNode,
230 Unretained(LeAudioClient::Get()), group_id, address));
231 }
232
GroupRemoveNode(const int group_id,const RawAddress & address)233 void GroupRemoveNode(const int group_id, const RawAddress& address) override {
234 DVLOG(2) << __func__ << " group_id: " << group_id
235 << " address: " << ADDRESS_TO_LOGGABLE_STR(address);
236 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
237 DVLOG(2) << __func__
238 << " call ignored, due to already started cleanup procedure or "
239 "service being not read";
240 return;
241 }
242
243 do_in_main_thread(
244 FROM_HERE, Bind(&LeAudioClient::GroupRemoveNode,
245 Unretained(LeAudioClient::Get()), group_id, address));
246 }
247
GroupSetActive(const int group_id)248 void GroupSetActive(const int group_id) override {
249 DVLOG(2) << __func__ << " group_id: " << group_id;
250 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
251 DVLOG(2) << __func__
252 << " call ignored, due to already started cleanup procedure or "
253 "service being not read";
254 return;
255 }
256
257 do_in_main_thread(FROM_HERE,
258 Bind(&LeAudioClient::GroupSetActive,
259 Unretained(LeAudioClient::Get()), group_id));
260 }
261
SetCodecConfigPreference(int group_id,btle_audio_codec_config_t input_codec_config,btle_audio_codec_config_t output_codec_config)262 void SetCodecConfigPreference(int group_id,
263 btle_audio_codec_config_t input_codec_config,
264 btle_audio_codec_config_t output_codec_config) {
265 DVLOG(2) << __func__ << " group_id: " << group_id;
266 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
267 DVLOG(2) << __func__
268 << " call ignored, due to already started cleanup procedure or "
269 "service being not read";
270 return;
271 }
272 do_in_main_thread(FROM_HERE,
273 Bind(&LeAudioClient::SetCodecConfigPreference,
274 Unretained(LeAudioClient::Get()), group_id,
275 input_codec_config, output_codec_config));
276 }
277
SetCcidInformation(int ccid,int context_type)278 void SetCcidInformation(int ccid, int context_type) {
279 DVLOG(2) << __func__ << " ccid: " << ccid << " context_type"
280 << context_type;
281 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
282 DVLOG(2) << __func__
283 << " call ignored, due to already started cleanup procedure or "
284 "service being not read";
285 return;
286 }
287
288 do_in_main_thread(
289 FROM_HERE, Bind(&LeAudioClient::SetCcidInformation,
290 Unretained(LeAudioClient::Get()), ccid, context_type));
291 }
292
SetInCall(bool in_call)293 void SetInCall(bool in_call) {
294 DVLOG(2) << __func__ << " in_call: " << in_call;
295 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
296 DVLOG(2) << __func__
297 << " call ignored, due to already started cleanup procedure or "
298 "service being not read";
299 return;
300 }
301
302 do_in_main_thread(FROM_HERE,
303 Bind(&LeAudioClient::SetInCall,
304 Unretained(LeAudioClient::Get()), in_call));
305 }
306
SendAudioProfilePreferences(int group_id,bool is_output_preference_le_audio,bool is_duplex_preference_le_audio)307 void SendAudioProfilePreferences(int group_id,
308 bool is_output_preference_le_audio,
309 bool is_duplex_preference_le_audio) {
310 DVLOG(2) << __func__ << " group_id: " << group_id
311 << ", is_output_preference_le_audio: "
312 << is_output_preference_le_audio
313 << ", is_duplex_preference_le_audio: "
314 << is_duplex_preference_le_audio;
315 if (!initialized || !LeAudioClient::IsLeAudioClientRunning()) {
316 DVLOG(2) << __func__
317 << " call ignored, due to already started cleanup procedure or "
318 "service being not read";
319 return;
320 }
321
322 do_in_main_thread(
323 FROM_HERE,
324 Bind(&LeAudioClient::SendAudioProfilePreferences,
325 Unretained(LeAudioClient::Get()), group_id,
326 is_output_preference_le_audio, is_duplex_preference_le_audio));
327 }
328
329 private:
330 LeAudioClientCallbacks* callbacks;
331 };
332
333 } /* namespace */
334
btif_le_audio_get_interface()335 LeAudioClientInterface* btif_le_audio_get_interface() {
336 if (!leAudioInstance) {
337 leAudioInstance.reset(new LeAudioClientInterfaceImpl());
338 }
339
340 return leAudioInstance.get();
341 }
342