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