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