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