1 /*
2 * Copyright (c) 2022 The Android Open Source Project
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
18 #include <bluetooth/log.h>
19 #include <stdio.h>
20
21 #include <algorithm>
22 #include <cstdint>
23 #include <map>
24 #include <memory>
25 #include <mutex>
26 #include <sstream>
27 #include <string>
28 #include <string_view>
29 #include <utility>
30 #include <vector>
31
32 #include "audio_hal_client/audio_hal_client.h"
33 #include "audio_set_configurations_generated.h"
34 #include "audio_set_scenarios_generated.h"
35 #include "btm_iso_api_types.h"
36 #include "flatbuffers/buffer.h"
37 #include "flatbuffers/idl.h"
38 #include "flatbuffers/util.h"
39 #include "flatbuffers/vector.h"
40 #include "le_audio/le_audio_types.h"
41 #include "le_audio_set_configuration_provider.h"
42
43 using bluetooth::le_audio::set_configurations::AseConfiguration;
44 using bluetooth::le_audio::set_configurations::AudioSetConfiguration;
45 using bluetooth::le_audio::set_configurations::AudioSetConfigurations;
46 using bluetooth::le_audio::set_configurations::CodecConfigSetting;
47 using bluetooth::le_audio::set_configurations::QosConfigSetting;
48 using bluetooth::le_audio::types::LeAudioContextType;
49
50 namespace bluetooth::le_audio {
51
52 #ifdef __ANDROID__
53 static const std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
54 kLeAudioSetConfigs = {{"/apex/com.android.btservices/etc/bluetooth/le_audio/"
55 "audio_set_configurations.bfbs",
56 "/apex/com.android.btservices/etc/bluetooth/le_audio/"
57 "audio_set_configurations.json"}};
58 static const std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
59 kLeAudioSetScenarios = {{"/apex/com.android.btservices/etc/bluetooth/"
60 "le_audio/audio_set_scenarios.bfbs",
61 "/apex/com.android.btservices/etc/bluetooth/"
62 "le_audio/audio_set_scenarios.json"}};
63 #elif defined(TARGET_FLOSS)
64 static const std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
65 kLeAudioSetConfigs = {{"/etc/bluetooth/le_audio/audio_set_configurations.bfbs",
66 "/etc/bluetooth/le_audio/audio_set_configurations.json"}};
67 static const std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
68 kLeAudioSetScenarios = {{"/etc/bluetooth/le_audio/audio_set_scenarios.bfbs",
69 "/etc/bluetooth/le_audio/audio_set_scenarios.json"}};
70 #else
71 static const std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
72 kLeAudioSetConfigs = {{"audio_set_configurations.bfbs", "audio_set_configurations.json"}};
73 static const std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
74 kLeAudioSetScenarios = {{"audio_set_scenarios.bfbs", "audio_set_scenarios.json"}};
75 #endif
76
77 /** Provides a set configurations for the given context type */
78 struct AudioSetConfigurationProviderJson {
79 static constexpr auto kDefaultScenario = "Media";
80
AudioSetConfigurationProviderJsonbluetooth::le_audio::AudioSetConfigurationProviderJson81 AudioSetConfigurationProviderJson(types::CodecLocation location) {
82 log::assert_that(LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios, location),
83 ": Unable to load le audio set configuration files.");
84 }
85
86 /* Use the same scenario configurations for different contexts to avoid
87 * internal reconfiguration and handover that produces time gap. When using
88 * the same scenario for different contexts, quality and configuration remains
89 * the same while changing to same scenario based context type.
90 */
ScenarioToContextTypesbluetooth::le_audio::AudioSetConfigurationProviderJson91 static auto ScenarioToContextTypes(const std::string& scenario) {
92 static const std::multimap<std::string, ::bluetooth::le_audio::types::LeAudioContextType>
93 scenarios = {
94 {"Media", types::LeAudioContextType::ALERTS},
95 {"Media", types::LeAudioContextType::INSTRUCTIONAL},
96 {"Media", types::LeAudioContextType::NOTIFICATIONS},
97 {"Media", types::LeAudioContextType::EMERGENCYALARM},
98 {"Media", types::LeAudioContextType::UNSPECIFIED},
99 {"Media", types::LeAudioContextType::MEDIA},
100 {"Conversational", types::LeAudioContextType::RINGTONE},
101 {"Conversational", types::LeAudioContextType::CONVERSATIONAL},
102 {"Live", types::LeAudioContextType::LIVE},
103 {"Game", types::LeAudioContextType::GAME},
104 {"VoiceAssistants", types::LeAudioContextType::VOICEASSISTANTS},
105 };
106 return scenarios.equal_range(scenario);
107 }
108
ContextTypeToScenariobluetooth::le_audio::AudioSetConfigurationProviderJson109 static std::string ContextTypeToScenario(
110 ::bluetooth::le_audio::types::LeAudioContextType context_type) {
111 switch (context_type) {
112 case types::LeAudioContextType::ALERTS:
113 case types::LeAudioContextType::INSTRUCTIONAL:
114 case types::LeAudioContextType::NOTIFICATIONS:
115 case types::LeAudioContextType::EMERGENCYALARM:
116 case types::LeAudioContextType::UNSPECIFIED:
117 case types::LeAudioContextType::SOUNDEFFECTS:
118 case types::LeAudioContextType::MEDIA:
119 return "Media";
120 case types::LeAudioContextType::RINGTONE:
121 case types::LeAudioContextType::CONVERSATIONAL:
122 return "Conversational";
123 case types::LeAudioContextType::LIVE:
124 return "Live";
125 case types::LeAudioContextType::GAME:
126 return "Game";
127 case types::LeAudioContextType::VOICEASSISTANTS:
128 return "VoiceAssistants";
129 default:
130 return kDefaultScenario;
131 }
132 }
133
GetConfigurationsByContextTypebluetooth::le_audio::AudioSetConfigurationProviderJson134 const AudioSetConfigurations* GetConfigurationsByContextType(
135 LeAudioContextType context_type) const {
136 if (context_configurations_.count(context_type)) {
137 return &context_configurations_.at(context_type);
138 }
139
140 log::warn(": No predefined scenario for the context {} was found.", (int)context_type);
141
142 auto [it_begin, it_end] = ScenarioToContextTypes(kDefaultScenario);
143 if (it_begin != it_end) {
144 log::warn(": Using '{}' scenario by default.", kDefaultScenario);
145 return &context_configurations_.at(it_begin->second);
146 }
147
148 log::error(
149 ": No valid configuration for the default '{}' scenario, or no audio "
150 "set configurations loaded at all.",
151 kDefaultScenario);
152 return nullptr;
153 }
154
155 private:
156 /* Codec configurations */
157 std::map<std::string, const AudioSetConfiguration> configurations_;
158
159 /* Maps of context types to a set of configuration structs */
160 std::map<::bluetooth::le_audio::types::LeAudioContextType, AudioSetConfigurations>
161 context_configurations_;
162
CodecConfigSettingFromFlatbluetooth::le_audio::AudioSetConfigurationProviderJson163 static CodecConfigSetting CodecConfigSettingFromFlat(
164 const fbs::le_audio::CodecId* flat_codec_id,
165 const flatbuffers::Vector<flatbuffers::Offset<fbs::le_audio::CodecSpecificConfiguration>>*
166 flat_codec_specific_params) {
167 CodecConfigSetting codec;
168
169 /* Cache the bluetooth::le_audio::types::CodecId type value */
170 codec.id = types::LeAudioCodecId({
171 .coding_format = flat_codec_id->coding_format(),
172 .vendor_company_id = flat_codec_id->vendor_company_id(),
173 .vendor_codec_id = flat_codec_id->vendor_codec_id(),
174 });
175
176 /* Cache all the codec specific parameters */
177 for (auto const& param : *flat_codec_specific_params) {
178 auto const value = param->compound_value()->value();
179 codec.params.Add(param->type(),
180 std::vector<uint8_t>(value->data(), value->data() + value->size()));
181 }
182 return codec;
183 }
184
SetConfigurationFromFlatSubconfigbluetooth::le_audio::AudioSetConfigurationProviderJson185 void SetConfigurationFromFlatSubconfig(
186 const fbs::le_audio::AudioSetSubConfiguration* flat_subconfig, QosConfigSetting qos,
187 std::vector<AseConfiguration>& subconfigs, types::CodecLocation location) {
188 auto codec_config = CodecConfigSettingFromFlat(flat_subconfig->codec_id(),
189 flat_subconfig->codec_configuration());
190
191 // Fill in the remaining params
192 codec_config.channel_count_per_iso_stream = flat_subconfig->ase_channel_cnt();
193
194 auto config = AseConfiguration(codec_config, qos);
195
196 // Note that these parameters are set here since for now, we are using the
197 // common configuration source for all the codec locations.
198 switch (location) {
199 case types::CodecLocation::ADSP:
200 config.data_path_configuration.dataPathId =
201 bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault;
202 config.data_path_configuration.dataPathConfig = {};
203 config.data_path_configuration.isoDataPathConfig.isTransparent = true;
204 config.data_path_configuration.isoDataPathConfig.controllerDelayUs = 0;
205 config.data_path_configuration.isoDataPathConfig.codecId = {
206 .coding_format = bluetooth::hci::kIsoCodingFormatTransparent,
207 .vendor_company_id = 0,
208 .vendor_codec_id = 0};
209 config.data_path_configuration.isoDataPathConfig.configuration = {};
210 break;
211 case types::CodecLocation::HOST:
212 config.data_path_configuration.dataPathId = bluetooth::hci::iso_manager::kIsoDataPathHci;
213 config.data_path_configuration.dataPathConfig = {};
214 config.data_path_configuration.isoDataPathConfig.isTransparent = true;
215 config.data_path_configuration.isoDataPathConfig.codecId = {
216 .coding_format = bluetooth::hci::kIsoCodingFormatTransparent,
217 .vendor_company_id = 0,
218 .vendor_codec_id = 0};
219 config.data_path_configuration.isoDataPathConfig.controllerDelayUs = 0;
220 config.data_path_configuration.isoDataPathConfig.configuration = {};
221 break;
222 case types::CodecLocation::CONTROLLER:
223 config.data_path_configuration.dataPathId =
224 bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault;
225 config.data_path_configuration.dataPathConfig = {};
226 config.data_path_configuration.isoDataPathConfig.isTransparent = false;
227 // Note: The data path codecId matches the used codec, but for now there
228 // is no support for the custom path configuration data buffer.
229 config.data_path_configuration.isoDataPathConfig.codecId = codec_config.id;
230 config.data_path_configuration.isoDataPathConfig.controllerDelayUs = 0;
231 config.data_path_configuration.isoDataPathConfig.configuration = {};
232 break;
233 }
234
235 // Store each ASE configuration
236 for (auto i = flat_subconfig->ase_cnt(); i; --i) {
237 subconfigs.push_back(std::move(config));
238 }
239 }
240
ValidateTargetLatencybluetooth::le_audio::AudioSetConfigurationProviderJson241 static uint8_t ValidateTargetLatency(int flat_target_latency) {
242 auto target_latency_int = static_cast<int>(flat_target_latency);
243
244 bool valid_target_latency = (target_latency_int >= (int)types::kTargetLatencyLower &&
245 target_latency_int <= (int)types::kTargetLatencyHigherReliability);
246
247 return valid_target_latency ? static_cast<uint8_t>(target_latency_int)
248 : types::kTargetLatencyBalancedLatencyReliability;
249 }
250
AudioSetConfigurationFromFlatbluetooth::le_audio::AudioSetConfigurationProviderJson251 AudioSetConfiguration AudioSetConfigurationFromFlat(
252 const fbs::le_audio::AudioSetConfiguration* flat_cfg,
253 std::vector<const fbs::le_audio::CodecConfiguration*>* codec_cfgs,
254 std::vector<const fbs::le_audio::QosConfiguration*>* qos_cfgs,
255 types::CodecLocation location) {
256 log::assert_that(flat_cfg != nullptr, "flat_cfg cannot be null");
257 std::string codec_config_key = flat_cfg->codec_config_name()->str();
258 auto* qos_config_key_array = flat_cfg->qos_config_name();
259
260 constexpr std::string_view default_qos = "QoS_Config_Balanced_Reliability";
261
262 std::string qos_sink_key(default_qos);
263 std::string qos_source_key(default_qos);
264
265 /* We expect maximum two QoS settings. First for Sink and second for Source
266 */
267 if (qos_config_key_array->size() > 0) {
268 qos_sink_key = qos_config_key_array->Get(0)->str();
269 if (qos_config_key_array->size() > 1) {
270 qos_source_key = qos_config_key_array->Get(1)->str();
271 } else {
272 qos_source_key = qos_sink_key;
273 }
274 }
275
276 log::info("Audio set config {}: codec config {}, qos_sink {}, qos_source {}",
277 flat_cfg->name()->c_str(), codec_config_key, qos_sink_key, qos_source_key);
278
279 const fbs::le_audio::QosConfiguration* qos_sink_cfg = nullptr;
280 for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) {
281 if ((*i)->name()->str() == qos_sink_key) {
282 qos_sink_cfg = *i;
283 break;
284 }
285 }
286
287 const fbs::le_audio::QosConfiguration* qos_source_cfg = nullptr;
288 for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) {
289 if ((*i)->name()->str() == qos_source_key) {
290 qos_source_cfg = *i;
291 break;
292 }
293 }
294
295 types::BidirectionalPair<QosConfigSetting> qos;
296
297 if (qos_sink_cfg != nullptr) {
298 qos.sink.target_latency = ValidateTargetLatency(qos_sink_cfg->target_latency());
299 qos.sink.retransmission_number = qos_sink_cfg->retransmission_number();
300 qos.sink.max_transport_latency = qos_sink_cfg->max_transport_latency();
301 } else {
302 log::error("No qos config matching key {} found", qos_sink_key);
303 }
304
305 if (qos_source_cfg != nullptr) {
306 qos.source.target_latency = ValidateTargetLatency(qos_source_cfg->target_latency());
307 qos.source.retransmission_number = qos_source_cfg->retransmission_number();
308 qos.source.max_transport_latency = qos_source_cfg->max_transport_latency();
309 } else {
310 log::error("No qos config matching key {} found", qos_source_key);
311 }
312
313 const fbs::le_audio::CodecConfiguration* codec_cfg = nullptr;
314 for (auto i = codec_cfgs->begin(); i != codec_cfgs->end(); ++i) {
315 if ((*i)->name()->str() == codec_config_key) {
316 codec_cfg = *i;
317 break;
318 }
319 }
320
321 types::BidirectionalPair<std::vector<AseConfiguration>> subconfigs;
322 if (codec_cfg != nullptr && codec_cfg->subconfigurations()) {
323 /* Load subconfigurations */
324 for (auto subconfig : *codec_cfg->subconfigurations()) {
325 auto direction = subconfig->direction();
326 processSubconfig(*subconfig, qos.get(direction), subconfigs.get(direction), location);
327 }
328 } else {
329 if (codec_cfg == nullptr) {
330 log::error("No codec config matching key {} found", codec_config_key);
331 } else {
332 log::error("Configuration '{}' has no valid subconfigurations.", flat_cfg->name()->c_str());
333 }
334 }
335
336 return {
337 .name = flat_cfg->name()->c_str(),
338 .packing = bluetooth::hci::kIsoCigPackingSequential,
339 .confs = std::move(subconfigs),
340 };
341 }
342
processSubconfigbluetooth::le_audio::AudioSetConfigurationProviderJson343 void processSubconfig(const fbs::le_audio::AudioSetSubConfiguration& subconfig,
344 const QosConfigSetting& qos_setting,
345 std::vector<AseConfiguration>& subconfigs, types::CodecLocation location) {
346 SetConfigurationFromFlatSubconfig(&subconfig, qos_setting, subconfigs, location);
347
348 // Recalculate some qos params based on the Core Codec Configuration
349 for (auto& subconfig : subconfigs) {
350 const auto& core_config = subconfig.codec.params.GetAsCoreCodecConfig();
351 subconfig.qos.maxSdu = subconfig.codec.GetChannelCountPerIsoStream() *
352 core_config.octets_per_codec_frame.value_or(0) *
353 core_config.codec_frames_blocks_per_sdu.value_or(1);
354 subconfig.qos.sduIntervalUs = core_config.GetFrameDurationUs() *
355 core_config.codec_frames_blocks_per_sdu.value_or(1);
356 }
357 }
358
LoadConfigurationsFromFilesbluetooth::le_audio::AudioSetConfigurationProviderJson359 bool LoadConfigurationsFromFiles(const char* schema_file, const char* content_file,
360 types::CodecLocation location) {
361 flatbuffers::Parser configurations_parser_;
362 std::string configurations_schema_binary_content;
363 bool ok = flatbuffers::LoadFile(schema_file, true, &configurations_schema_binary_content);
364 if (!ok) {
365 return ok;
366 }
367
368 /* Load the binary schema */
369 ok = configurations_parser_.Deserialize((uint8_t*)configurations_schema_binary_content.c_str(),
370 configurations_schema_binary_content.length());
371 if (!ok) {
372 return ok;
373 }
374
375 /* Load the content from JSON */
376 std::string configurations_json_content;
377 ok = flatbuffers::LoadFile(content_file, false, &configurations_json_content);
378 if (!ok) {
379 return ok;
380 }
381
382 /* Parse */
383 ok = configurations_parser_.Parse(configurations_json_content.c_str());
384 if (!ok) {
385 return ok;
386 }
387
388 /* Import from flatbuffers */
389 auto configurations_root = fbs::le_audio::GetAudioSetConfigurations(
390 configurations_parser_.builder_.GetBufferPointer());
391 if (!configurations_root) {
392 return false;
393 }
394
395 auto flat_qos_configs = configurations_root->qos_configurations();
396 if ((flat_qos_configs == nullptr) || (flat_qos_configs->size() == 0)) {
397 return false;
398 }
399
400 log::debug(": Updating {} qos config entries.", flat_qos_configs->size());
401 std::vector<const fbs::le_audio::QosConfiguration*> qos_cfgs;
402 for (auto const& flat_qos_cfg : *flat_qos_configs) {
403 qos_cfgs.push_back(flat_qos_cfg);
404 }
405
406 auto flat_codec_configs = configurations_root->codec_configurations();
407 if ((flat_codec_configs == nullptr) || (flat_codec_configs->size() == 0)) {
408 return false;
409 }
410
411 log::debug(": Updating {} codec config entries.", flat_codec_configs->size());
412 std::vector<const fbs::le_audio::CodecConfiguration*> codec_cfgs;
413 for (auto const& flat_codec_cfg : *flat_codec_configs) {
414 codec_cfgs.push_back(flat_codec_cfg);
415 }
416
417 auto flat_configs = configurations_root->configurations();
418 if ((flat_configs == nullptr) || (flat_configs->size() == 0)) {
419 return false;
420 }
421
422 log::debug(": Updating {} config entries.", flat_configs->size());
423 for (auto const& flat_cfg : *flat_configs) {
424 auto configuration =
425 AudioSetConfigurationFromFlat(flat_cfg, &codec_cfgs, &qos_cfgs, location);
426 if (!configuration.confs.sink.empty() || !configuration.confs.source.empty()) {
427 configurations_.insert({flat_cfg->name()->str(), configuration});
428 }
429 }
430
431 return true;
432 }
433
AudioSetConfigurationsFromFlatScenariobluetooth::le_audio::AudioSetConfigurationProviderJson434 AudioSetConfigurations AudioSetConfigurationsFromFlatScenario(
435 const fbs::le_audio::AudioSetScenario* const flat_scenario) {
436 AudioSetConfigurations items;
437 if (!flat_scenario->configurations()) {
438 return items;
439 }
440
441 for (auto config_name : *flat_scenario->configurations()) {
442 if (configurations_.count(config_name->str()) == 0) {
443 continue;
444 }
445
446 auto& cfg = configurations_.at(config_name->str());
447 items.push_back(&cfg);
448 }
449
450 return items;
451 }
452
LoadScenariosFromFilesbluetooth::le_audio::AudioSetConfigurationProviderJson453 bool LoadScenariosFromFiles(const char* schema_file, const char* content_file) {
454 flatbuffers::Parser scenarios_parser_;
455 std::string scenarios_schema_binary_content;
456 bool ok = flatbuffers::LoadFile(schema_file, true, &scenarios_schema_binary_content);
457 if (!ok) {
458 return ok;
459 }
460
461 /* Load the binary schema */
462 ok = scenarios_parser_.Deserialize((uint8_t*)scenarios_schema_binary_content.c_str(),
463 scenarios_schema_binary_content.length());
464 if (!ok) {
465 return ok;
466 }
467
468 /* Load the content from JSON */
469 std::string scenarios_json_content;
470 ok = flatbuffers::LoadFile(content_file, false, &scenarios_json_content);
471 if (!ok) {
472 return ok;
473 }
474
475 /* Parse */
476 ok = scenarios_parser_.Parse(scenarios_json_content.c_str());
477 if (!ok) {
478 return ok;
479 }
480
481 /* Import from flatbuffers */
482 auto scenarios_root =
483 fbs::le_audio::GetAudioSetScenarios(scenarios_parser_.builder_.GetBufferPointer());
484 if (!scenarios_root) {
485 return false;
486 }
487
488 auto flat_scenarios = scenarios_root->scenarios();
489 if ((flat_scenarios == nullptr) || (flat_scenarios->size() == 0)) {
490 return false;
491 }
492
493 log::debug(": Updating {} scenarios.", flat_scenarios->size());
494 for (auto const& scenario : *flat_scenarios) {
495 log::debug("Scenario {} configs:", scenario->name()->c_str());
496 auto configs = AudioSetConfigurationsFromFlatScenario(scenario);
497 for (auto& config : configs) {
498 log::debug("\t\t Audio set config: {}", config->name);
499 }
500
501 auto [it_begin, it_end] = ScenarioToContextTypes(scenario->name()->c_str());
502 for (auto it = it_begin; it != it_end; ++it) {
503 context_configurations_.insert_or_assign(it->second,
504 AudioSetConfigurationsFromFlatScenario(scenario));
505 }
506 }
507
508 return true;
509 }
510
LoadContentbluetooth::le_audio::AudioSetConfigurationProviderJson511 bool LoadContent(
512 std::vector<std::pair<const char* /*schema*/, const char* /*content*/>> config_files,
513 std::vector<std::pair<const char* /*schema*/, const char* /*content*/>> scenario_files,
514 types::CodecLocation location) {
515 for (auto [schema, content] : config_files) {
516 if (!LoadConfigurationsFromFiles(schema, content, location)) {
517 return false;
518 }
519 }
520
521 for (auto [schema, content] : scenario_files) {
522 if (!LoadScenariosFromFiles(schema, content)) {
523 return false;
524 }
525 }
526 return true;
527 }
528 };
529
530 struct AudioSetConfigurationProvider::impl {
implbluetooth::le_audio::AudioSetConfigurationProvider::impl531 impl(const AudioSetConfigurationProvider& config_provider) : config_provider_(config_provider) {}
532
Initializebluetooth::le_audio::AudioSetConfigurationProvider::impl533 void Initialize(types::CodecLocation location) {
534 log::assert_that(!config_provider_impl_, "Config provider not available.");
535 config_provider_impl_ = std::make_unique<AudioSetConfigurationProviderJson>(location);
536 }
537
Cleanupbluetooth::le_audio::AudioSetConfigurationProvider::impl538 void Cleanup() {
539 log::assert_that(config_provider_impl_ != nullptr, "Config provider not available.");
540 config_provider_impl_.reset();
541 }
542
IsRunningbluetooth::le_audio::AudioSetConfigurationProvider::impl543 bool IsRunning() { return config_provider_impl_ ? true : false; }
544
Dumpbluetooth::le_audio::AudioSetConfigurationProvider::impl545 void Dump(int fd) {
546 std::stringstream stream;
547
548 for (LeAudioContextType context : types::kLeAudioContextAllTypesArray) {
549 auto confs = Get()->GetConfigurations(context);
550 stream << "\n === Configurations for context type: " << (int)context
551 << ", num: " << (confs == nullptr ? 0 : confs->size()) << " \n";
552 if (confs && confs->size() > 0) {
553 for (const auto& conf : *confs) {
554 stream << " name: " << conf->name << " \n";
555 for (const auto direction :
556 {types::kLeAudioDirectionSink, types::kLeAudioDirectionSource}) {
557 stream << " ASE configs for direction: "
558 << (direction == types::kLeAudioDirectionSink ? "Sink (speaker)\n"
559 : "Source (microphone)\n");
560 for (const auto& ent : conf->confs.get(direction)) {
561 stream << " ASE config: " << " qos->target latency: "
562 << +ent.qos.target_latency << " \n"
563 << " qos->retransmission_number: " << +ent.qos.retransmission_number
564 << " \n"
565 << " qos->max_transport_latency: " << +ent.qos.max_transport_latency
566 << " \n"
567 << " channel count per ISO stream: "
568 << +ent.codec.GetChannelCountPerIsoStream() << "\n";
569 }
570 }
571 }
572 }
573 }
574 dprintf(fd, "%s", stream.str().c_str());
575 }
576
577 const AudioSetConfigurationProvider& config_provider_;
578 std::unique_ptr<AudioSetConfigurationProviderJson> config_provider_impl_;
579 };
580
581 static std::unique_ptr<AudioSetConfigurationProvider> config_provider;
582 std::mutex instance_mutex;
583
AudioSetConfigurationProvider()584 AudioSetConfigurationProvider::AudioSetConfigurationProvider()
585 : pimpl_(std::make_unique<AudioSetConfigurationProvider::impl>(*this)) {}
586
Initialize(types::CodecLocation location)587 void AudioSetConfigurationProvider::Initialize(types::CodecLocation location) {
588 std::scoped_lock<std::mutex> lock(instance_mutex);
589 if (!config_provider) {
590 config_provider = std::make_unique<AudioSetConfigurationProvider>();
591 }
592
593 if (!config_provider->pimpl_->IsRunning()) {
594 config_provider->pimpl_->Initialize(location);
595 }
596 }
597
DebugDump(int fd)598 void AudioSetConfigurationProvider::DebugDump(int fd) {
599 std::scoped_lock<std::mutex> lock(instance_mutex);
600 if (!config_provider || !config_provider->pimpl_->IsRunning()) {
601 dprintf(fd,
602 "\n AudioSetConfigurationProvider not initialized: config provider: "
603 "%d, pimpl: %d \n",
604 config_provider != nullptr,
605 (config_provider == nullptr ? 0 : config_provider->pimpl_->IsRunning()));
606 return;
607 }
608 dprintf(fd, "\n AudioSetConfigurationProvider: \n");
609 config_provider->pimpl_->Dump(fd);
610 }
611
Cleanup()612 void AudioSetConfigurationProvider::Cleanup() {
613 std::scoped_lock<std::mutex> lock(instance_mutex);
614 if (!config_provider) {
615 return;
616 }
617 if (config_provider->pimpl_->IsRunning()) {
618 config_provider->pimpl_->Cleanup();
619 }
620 config_provider.reset();
621 }
622
Get()623 AudioSetConfigurationProvider* AudioSetConfigurationProvider::Get() {
624 return config_provider.get();
625 }
626
GetConfigurations(::bluetooth::le_audio::types::LeAudioContextType content_type) const627 const set_configurations::AudioSetConfigurations* AudioSetConfigurationProvider::GetConfigurations(
628 ::bluetooth::le_audio::types::LeAudioContextType content_type) const {
629 if (pimpl_->IsRunning()) {
630 return pimpl_->config_provider_impl_->GetConfigurationsByContextType(content_type);
631 }
632
633 return nullptr;
634 }
635
CheckConfigurationIsBiDirSwb(const set_configurations::AudioSetConfiguration & set_configuration) const636 bool AudioSetConfigurationProvider::CheckConfigurationIsBiDirSwb(
637 const set_configurations::AudioSetConfiguration& set_configuration) const {
638 uint8_t dir = 0;
639
640 for (auto direction :
641 {le_audio::types::kLeAudioDirectionSink, le_audio::types::kLeAudioDirectionSource}) {
642 for (const auto& conf : set_configuration.confs.get(direction)) {
643 if (conf.codec.GetSamplingFrequencyHz() >=
644 bluetooth::le_audio::LeAudioCodecConfiguration::kSampleRate32000) {
645 dir |= direction;
646 }
647 }
648 }
649 return dir == bluetooth::le_audio::types::kLeAudioDirectionBoth;
650 }
651
CheckConfigurationIsDualBiDirSwb(const set_configurations::AudioSetConfiguration & set_configuration) const652 bool AudioSetConfigurationProvider::CheckConfigurationIsDualBiDirSwb(
653 const set_configurations::AudioSetConfiguration& set_configuration) const {
654 types::BidirectionalPair<uint8_t> swb_direction_counter = {0, 0};
655
656 for (auto direction :
657 {le_audio::types::kLeAudioDirectionSink, le_audio::types::kLeAudioDirectionSource}) {
658 auto const& confs = set_configuration.confs.get(direction);
659 swb_direction_counter.get(direction) +=
660 std::count_if(confs.begin(), confs.end(), [](auto const& cfg) {
661 return cfg.codec.GetSamplingFrequencyHz() >=
662 bluetooth::le_audio::LeAudioCodecConfiguration::kSampleRate32000;
663 });
664 }
665
666 return (swb_direction_counter.sink > 1) && (swb_direction_counter.source > 1);
667 }
668
669 } // namespace bluetooth::le_audio
670