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 <string>
19 #include <string_view>
20
21 #include "audio_set_configurations_generated.h"
22 #include "audio_set_scenarios_generated.h"
23 #include "codec_manager.h"
24 #include "flatbuffers/idl.h"
25 #include "flatbuffers/util.h"
26 #include "le_audio_set_configuration_provider.h"
27 #include "osi/include/log.h"
28 #include "osi/include/osi.h"
29
30 using le_audio::set_configurations::AudioSetConfiguration;
31 using le_audio::set_configurations::AudioSetConfigurations;
32 using le_audio::set_configurations::CodecCapabilitySetting;
33 using le_audio::set_configurations::LeAudioCodecIdLc3;
34 using le_audio::set_configurations::QosConfigSetting;
35 using le_audio::set_configurations::SetConfiguration;
36 using le_audio::types::LeAudioContextType;
37
38 namespace le_audio {
39 using ::le_audio::CodecManager;
40
41 #ifdef OS_ANDROID
42 static const std::vector<
43 std::pair<const char* /*schema*/, const char* /*content*/>>
44 kLeAudioSetConfigs = {
45 {"/apex/com.android.btservices/etc/bluetooth/le_audio/"
46 "audio_set_configurations.bfbs",
47 "/apex/com.android.btservices/etc/bluetooth/le_audio/"
48 "audio_set_configurations.json"}};
49 static const std::vector<
50 std::pair<const char* /*schema*/, const char* /*content*/>>
51 kLeAudioSetScenarios = {{"/apex/com.android.btservices/etc/bluetooth/"
52 "le_audio/audio_set_scenarios.bfbs",
53 "/apex/com.android.btservices/etc/bluetooth/"
54 "le_audio/audio_set_scenarios.json"}};
55 #else
56 static const std::vector<
57 std::pair<const char* /*schema*/, const char* /*content*/>>
58 kLeAudioSetConfigs = {
59 {"audio_set_configurations.bfbs", "audio_set_configurations.json"}};
60 static const std::vector<
61 std::pair<const char* /*schema*/, const char* /*content*/>>
62 kLeAudioSetScenarios = {
63 {"audio_set_scenarios.bfbs", "audio_set_scenarios.json"}};
64 #endif
65
66 /** Provides a set configurations for the given context type */
67 struct AudioSetConfigurationProviderJson {
68 static constexpr auto kDefaultScenario = "Media";
69
AudioSetConfigurationProviderJsonle_audio::AudioSetConfigurationProviderJson70 AudioSetConfigurationProviderJson() {
71 ASSERT_LOG(LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios),
72 ": Unable to load le audio set configuration files.");
73 }
74
75 /* Use the same scenario configurations for different contexts to avoid
76 * internal reconfiguration and handover that produces time gap. When using
77 * the same scenario for different contexts, quality and configuration remains
78 * the same while changing to same scenario based context type.
79 */
ScenarioToContextTypesle_audio::AudioSetConfigurationProviderJson80 static auto ScenarioToContextTypes(const std::string& scenario) {
81 static const std::multimap<std::string,
82 ::le_audio::types::LeAudioContextType>
83 scenarios = {
84 {"Media", types::LeAudioContextType::ALERTS},
85 {"Media", types::LeAudioContextType::INSTRUCTIONAL},
86 {"Media", types::LeAudioContextType::NOTIFICATIONS},
87 {"Media", types::LeAudioContextType::EMERGENCYALARM},
88 {"Media", types::LeAudioContextType::UNSPECIFIED},
89 {"Media", types::LeAudioContextType::MEDIA},
90 {"Conversational", types::LeAudioContextType::RINGTONE},
91 {"Conversational", types::LeAudioContextType::CONVERSATIONAL},
92 {"Live", types::LeAudioContextType::LIVE},
93 {"Game", types::LeAudioContextType::GAME},
94 {"VoiceAssistants", types::LeAudioContextType::VOICEASSISTANTS},
95 };
96 return scenarios.equal_range(scenario);
97 }
98
ContextTypeToScenariole_audio::AudioSetConfigurationProviderJson99 static std::string ContextTypeToScenario(
100 ::le_audio::types::LeAudioContextType context_type) {
101 switch (context_type) {
102 case types::LeAudioContextType::ALERTS:
103 FALLTHROUGH_INTENDED;
104 case types::LeAudioContextType::INSTRUCTIONAL:
105 FALLTHROUGH_INTENDED;
106 case types::LeAudioContextType::NOTIFICATIONS:
107 FALLTHROUGH_INTENDED;
108 case types::LeAudioContextType::EMERGENCYALARM:
109 FALLTHROUGH_INTENDED;
110 case types::LeAudioContextType::UNSPECIFIED:
111 FALLTHROUGH_INTENDED;
112 case types::LeAudioContextType::SOUNDEFFECTS:
113 FALLTHROUGH_INTENDED;
114 case types::LeAudioContextType::MEDIA:
115 return "Media";
116 case types::LeAudioContextType::RINGTONE:
117 FALLTHROUGH_INTENDED;
118 case types::LeAudioContextType::CONVERSATIONAL:
119 return "Conversational";
120 case types::LeAudioContextType::LIVE:
121 return "Live";
122 case types::LeAudioContextType::GAME:
123 return "Game";
124 case types::LeAudioContextType::VOICEASSISTANTS:
125 return "VoiceAssistants";
126 default:
127 return kDefaultScenario;
128 }
129 }
130
GetConfigurationsByContextTypele_audio::AudioSetConfigurationProviderJson131 const AudioSetConfigurations* GetConfigurationsByContextType(
132 LeAudioContextType context_type) const {
133 if (context_configurations_.count(context_type))
134 return &context_configurations_.at(context_type);
135
136 LOG_WARN(": No predefined scenario for the context %d was found.",
137 (int)context_type);
138
139 auto [it_begin, it_end] = ScenarioToContextTypes(kDefaultScenario);
140 if (it_begin != it_end) {
141 LOG_WARN(": Using '%s' scenario by default.", kDefaultScenario);
142 return &context_configurations_.at(it_begin->second);
143 }
144
145 LOG_ERROR(
146 ": No valid configuration for the default '%s' scenario, or no audio "
147 "set configurations loaded at all.",
148 kDefaultScenario);
149 return nullptr;
150 };
151
152 private:
153 /* Codec configurations */
154 std::map<std::string, const AudioSetConfiguration> configurations_;
155
156 /* Maps of context types to a set of configuration structs */
157 std::map<::le_audio::types::LeAudioContextType, AudioSetConfigurations>
158 context_configurations_;
159
160 static const bluetooth::le_audio::CodecSpecificConfiguration*
LookupCodecSpecificParamle_audio::AudioSetConfigurationProviderJson161 LookupCodecSpecificParam(
162 const flatbuffers::Vector<
163 flatbuffers::Offset<bluetooth::le_audio::CodecSpecificConfiguration>>*
164 flat_codec_specific_params,
165 bluetooth::le_audio::CodecSpecificLtvGenericTypes type) {
166 auto it = std::find_if(
167 flat_codec_specific_params->cbegin(),
168 flat_codec_specific_params->cend(),
169 [&type](const auto& csc) { return (csc->type() == type); });
170 return (it != flat_codec_specific_params->cend()) ? *it : nullptr;
171 }
172
CodecCapabilitySettingFromFlatle_audio::AudioSetConfigurationProviderJson173 static CodecCapabilitySetting CodecCapabilitySettingFromFlat(
174 const bluetooth::le_audio::CodecId* flat_codec_id,
175 const flatbuffers::Vector<
176 flatbuffers::Offset<bluetooth::le_audio::CodecSpecificConfiguration>>*
177 flat_codec_specific_params) {
178 CodecCapabilitySetting codec;
179
180 /* Cache the le_audio::types::CodecId type value */
181 codec.id = types::LeAudioCodecId({
182 .coding_format = flat_codec_id->coding_format(),
183 .vendor_company_id = flat_codec_id->vendor_company_id(),
184 .vendor_codec_id = flat_codec_id->vendor_codec_id(),
185 });
186
187 /* Cache the types::LeAudioLc3Config type value */
188 uint8_t sampling_frequency = 0;
189 uint8_t frame_duration = 0;
190 uint32_t audio_channel_allocation = 0;
191 uint16_t octets_per_codec_frame = 0;
192 uint8_t codec_frames_blocks_per_sdu = 0;
193
194 auto param = LookupCodecSpecificParam(
195 flat_codec_specific_params,
196 bluetooth::le_audio::
197 CodecSpecificLtvGenericTypes_SUPPORTED_SAMPLING_FREQUENCY);
198 if (param) {
199 ASSERT_LOG((param->compound_value()->value()->size() == 1),
200 " Invalid compound value length: %d",
201 param->compound_value()->value()->size());
202 auto ptr = param->compound_value()->value()->data();
203 STREAM_TO_UINT8(sampling_frequency, ptr);
204 }
205
206 param = LookupCodecSpecificParam(
207 flat_codec_specific_params,
208 bluetooth::le_audio::
209 CodecSpecificLtvGenericTypes_SUPPORTED_FRAME_DURATION);
210 if (param) {
211 LOG_ASSERT(param->compound_value()->value()->size() == 1)
212 << " Invalid compound value length: "
213 << param->compound_value()->value()->size();
214 auto ptr = param->compound_value()->value()->data();
215 STREAM_TO_UINT8(frame_duration, ptr);
216 }
217
218 param = LookupCodecSpecificParam(
219 flat_codec_specific_params,
220 bluetooth::le_audio::
221 CodecSpecificLtvGenericTypes_SUPPORTED_AUDIO_CHANNEL_ALLOCATION);
222 if (param) {
223 ASSERT_LOG((param->compound_value()->value()->size() == 4),
224 " Invalid compound value length %d",
225 param->compound_value()->value()->size());
226 auto ptr = param->compound_value()->value()->data();
227 STREAM_TO_UINT32(audio_channel_allocation, ptr);
228 }
229
230 param = LookupCodecSpecificParam(
231 flat_codec_specific_params,
232 bluetooth::le_audio::
233 CodecSpecificLtvGenericTypes_SUPPORTED_OCTETS_PER_CODEC_FRAME);
234 if (param) {
235 ASSERT_LOG((param->compound_value()->value()->size() == 2),
236 " Invalid compound value length %d",
237 param->compound_value()->value()->size());
238 auto ptr = param->compound_value()->value()->data();
239 STREAM_TO_UINT16(octets_per_codec_frame, ptr);
240 }
241
242 param = LookupCodecSpecificParam(
243 flat_codec_specific_params,
244 bluetooth::le_audio::
245 CodecSpecificLtvGenericTypes_SUPPORTED_CODEC_FRAME_BLOCKS_PER_SDU);
246 if (param) {
247 ASSERT_LOG((param->compound_value()->value()->size() == 1),
248 " Invalid compound value length %d",
249 param->compound_value()->value()->size());
250 auto ptr = param->compound_value()->value()->data();
251 STREAM_TO_UINT8(codec_frames_blocks_per_sdu, ptr);
252 }
253
254 codec.config = types::LeAudioLc3Config({
255 .sampling_frequency = sampling_frequency,
256 .frame_duration = frame_duration,
257 .octets_per_codec_frame = octets_per_codec_frame,
258 .codec_frames_blocks_per_sdu = codec_frames_blocks_per_sdu,
259 .channel_count =
260 (uint8_t)std::bitset<32>(audio_channel_allocation).count(),
261 .audio_channel_allocation = audio_channel_allocation,
262 });
263 return codec;
264 }
265
SetConfigurationFromFlatSubconfigle_audio::AudioSetConfigurationProviderJson266 SetConfiguration SetConfigurationFromFlatSubconfig(
267 const bluetooth::le_audio::AudioSetSubConfiguration* flat_subconfig,
268 QosConfigSetting qos) {
269 auto strategy_int =
270 static_cast<int>(flat_subconfig->configuration_strategy());
271
272 bool valid_strategy =
273 (strategy_int >=
274 (int)types::LeAudioConfigurationStrategy::MONO_ONE_CIS_PER_DEVICE) &&
275 strategy_int < (int)types::LeAudioConfigurationStrategy::RFU;
276
277 types::LeAudioConfigurationStrategy strategy =
278 valid_strategy
279 ? static_cast<types::LeAudioConfigurationStrategy>(strategy_int)
280 : types::LeAudioConfigurationStrategy::RFU;
281
282 auto target_latency_int =
283 static_cast<int>(flat_subconfig->target_latency());
284
285 bool valid_target_latency =
286 (target_latency_int >= (int)types::kTargetLatencyLower &&
287 target_latency_int <= (int)types::kTargetLatencyHigherReliability);
288
289 uint8_t target_latency =
290 valid_target_latency ? static_cast<uint8_t>(target_latency_int)
291 : types::kTargetLatencyBalancedLatencyReliability;
292 return SetConfiguration(
293 flat_subconfig->direction(), flat_subconfig->device_cnt(),
294 flat_subconfig->ase_cnt(), target_latency,
295 CodecCapabilitySettingFromFlat(flat_subconfig->codec_id(),
296 flat_subconfig->codec_configuration()),
297 qos, strategy);
298 }
299
AudioSetConfigurationFromFlatle_audio::AudioSetConfigurationProviderJson300 AudioSetConfiguration AudioSetConfigurationFromFlat(
301 const bluetooth::le_audio::AudioSetConfiguration* flat_cfg,
302 std::vector<const bluetooth::le_audio::CodecConfiguration*>* codec_cfgs,
303 std::vector<const bluetooth::le_audio::QosConfiguration*>* qos_cfgs) {
304 ASSERT_LOG(flat_cfg != nullptr, "flat_cfg cannot be null");
305 std::string codec_config_key = flat_cfg->codec_config_name()->str();
306 auto* qos_config_key_array = flat_cfg->qos_config_name();
307
308 constexpr std::string_view default_qos = "QoS_Config_Server_Preferred";
309
310 std::string qos_sink_key(default_qos);
311 std::string qos_source_key(default_qos);
312
313 /* We expect maximum two QoS settings. First for Sink and second for Source
314 */
315 if (qos_config_key_array->size() > 0) {
316 qos_sink_key = qos_config_key_array->Get(0)->str();
317 if (qos_config_key_array->size() > 1) {
318 qos_source_key = qos_config_key_array->Get(1)->str();
319 } else {
320 qos_source_key = qos_sink_key;
321 }
322 }
323
324 LOG_INFO("Config name %s, qos_sink %s, qos_source %s",
325 codec_config_key.c_str(), qos_sink_key.c_str(),
326 qos_source_key.c_str());
327
328 const bluetooth::le_audio::QosConfiguration* qos_sink_cfg = nullptr;
329 for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) {
330 if ((*i)->name()->str() == qos_sink_key) {
331 qos_sink_cfg = *i;
332 break;
333 }
334 }
335
336 const bluetooth::le_audio::QosConfiguration* qos_source_cfg = nullptr;
337 for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) {
338 if ((*i)->name()->str() == qos_source_key) {
339 qos_source_cfg = *i;
340 break;
341 }
342 }
343
344 QosConfigSetting qos_sink;
345 if (qos_sink_cfg != nullptr) {
346 qos_sink.retransmission_number = qos_sink_cfg->retransmission_number();
347 qos_sink.max_transport_latency = qos_sink_cfg->max_transport_latency();
348 } else {
349 LOG_ERROR("No qos config matching key %s found", qos_sink_key.c_str());
350 }
351
352 QosConfigSetting qos_source;
353 if (qos_source_cfg != nullptr) {
354 qos_source.retransmission_number =
355 qos_source_cfg->retransmission_number();
356 qos_source.max_transport_latency =
357 qos_source_cfg->max_transport_latency();
358 } else {
359 LOG_ERROR("No qos config matching key %s found", qos_source_key.c_str());
360 }
361
362 const bluetooth::le_audio::CodecConfiguration* codec_cfg = nullptr;
363 for (auto i = codec_cfgs->begin(); i != codec_cfgs->end(); ++i) {
364 if ((*i)->name()->str() == codec_config_key) {
365 codec_cfg = *i;
366 break;
367 }
368 }
369
370 std::vector<SetConfiguration> subconfigs;
371 if (codec_cfg != nullptr && codec_cfg->subconfigurations()) {
372 /* Load subconfigurations */
373 for (auto subconfig : *codec_cfg->subconfigurations()) {
374 if (subconfig->direction() == le_audio::types::kLeAudioDirectionSink) {
375 subconfigs.push_back(
376 SetConfigurationFromFlatSubconfig(subconfig, qos_sink));
377 } else {
378 subconfigs.push_back(
379 SetConfigurationFromFlatSubconfig(subconfig, qos_source));
380 }
381 }
382 } else {
383 if (codec_cfg == nullptr) {
384 LOG_ERROR("No codec config matching key %s found",
385 codec_config_key.c_str());
386 } else {
387 LOG_ERROR("Configuration '%s' has no valid subconfigurations.",
388 flat_cfg->name()->c_str());
389 }
390 }
391
392 return AudioSetConfiguration({flat_cfg->name()->c_str(), subconfigs});
393 }
394
LoadConfigurationsFromFilesle_audio::AudioSetConfigurationProviderJson395 bool LoadConfigurationsFromFiles(const char* schema_file,
396 const char* content_file) {
397 flatbuffers::Parser configurations_parser_;
398 std::string configurations_schema_binary_content;
399 bool ok = flatbuffers::LoadFile(schema_file, true,
400 &configurations_schema_binary_content);
401 if (!ok) return ok;
402
403 /* Load the binary schema */
404 ok = configurations_parser_.Deserialize(
405 (uint8_t*)configurations_schema_binary_content.c_str(),
406 configurations_schema_binary_content.length());
407 if (!ok) return ok;
408
409 /* Load the content from JSON */
410 std::string configurations_json_content;
411 ok = flatbuffers::LoadFile(content_file, false,
412 &configurations_json_content);
413 if (!ok) return ok;
414
415 /* Parse */
416 ok = configurations_parser_.Parse(configurations_json_content.c_str());
417 if (!ok) return ok;
418
419 /* Import from flatbuffers */
420 auto configurations_root = bluetooth::le_audio::GetAudioSetConfigurations(
421 configurations_parser_.builder_.GetBufferPointer());
422 if (!configurations_root) return false;
423
424 auto flat_qos_configs = configurations_root->qos_configurations();
425 if ((flat_qos_configs == nullptr) || (flat_qos_configs->size() == 0))
426 return false;
427
428 LOG_DEBUG(": Updating %d qos config entries.", flat_qos_configs->size());
429 std::vector<const bluetooth::le_audio::QosConfiguration*> qos_cfgs;
430 for (auto const& flat_qos_cfg : *flat_qos_configs) {
431 qos_cfgs.push_back(flat_qos_cfg);
432 }
433
434 auto flat_codec_configs = configurations_root->codec_configurations();
435 if ((flat_codec_configs == nullptr) || (flat_codec_configs->size() == 0))
436 return false;
437
438 LOG_DEBUG(": Updating %d codec config entries.",
439 flat_codec_configs->size());
440 std::vector<const bluetooth::le_audio::CodecConfiguration*> codec_cfgs;
441 for (auto const& flat_codec_cfg : *flat_codec_configs) {
442 codec_cfgs.push_back(flat_codec_cfg);
443 }
444
445 auto flat_configs = configurations_root->configurations();
446 if ((flat_configs == nullptr) || (flat_configs->size() == 0)) return false;
447
448 LOG_DEBUG(": Updating %d config entries.", flat_configs->size());
449 for (auto const& flat_cfg : *flat_configs) {
450 configurations_.insert(
451 {flat_cfg->name()->str(),
452 AudioSetConfigurationFromFlat(flat_cfg, &codec_cfgs, &qos_cfgs)});
453 }
454
455 return true;
456 }
457
AudioSetConfigurationsFromFlatScenariole_audio::AudioSetConfigurationProviderJson458 AudioSetConfigurations AudioSetConfigurationsFromFlatScenario(
459 const bluetooth::le_audio::AudioSetScenario* const flat_scenario) {
460 AudioSetConfigurations items;
461 if (!flat_scenario->configurations()) return items;
462
463 for (auto config_name : *flat_scenario->configurations()) {
464 if (configurations_.count(config_name->str()) == 0) continue;
465
466 auto& cfg = configurations_.at(config_name->str());
467 items.push_back(&cfg);
468 }
469
470 return items;
471 }
472
LoadScenariosFromFilesle_audio::AudioSetConfigurationProviderJson473 bool LoadScenariosFromFiles(const char* schema_file,
474 const char* content_file) {
475 flatbuffers::Parser scenarios_parser_;
476 std::string scenarios_schema_binary_content;
477 bool ok = flatbuffers::LoadFile(schema_file, true,
478 &scenarios_schema_binary_content);
479 if (!ok) return ok;
480
481 /* Load the binary schema */
482 ok = scenarios_parser_.Deserialize(
483 (uint8_t*)scenarios_schema_binary_content.c_str(),
484 scenarios_schema_binary_content.length());
485 if (!ok) return ok;
486
487 /* Load the content from JSON */
488 std::string scenarios_json_content;
489 ok = flatbuffers::LoadFile(content_file, false, &scenarios_json_content);
490 if (!ok) return ok;
491
492 /* Parse */
493 ok = scenarios_parser_.Parse(scenarios_json_content.c_str());
494 if (!ok) return ok;
495
496 /* Import from flatbuffers */
497 auto scenarios_root = bluetooth::le_audio::GetAudioSetScenarios(
498 scenarios_parser_.builder_.GetBufferPointer());
499 if (!scenarios_root) return false;
500
501 auto flat_scenarios = scenarios_root->scenarios();
502 if ((flat_scenarios == nullptr) || (flat_scenarios->size() == 0))
503 return false;
504
505 LOG_DEBUG(": Updating %d scenarios.", flat_scenarios->size());
506 for (auto const& scenario : *flat_scenarios) {
507 auto [it_begin, it_end] =
508 ScenarioToContextTypes(scenario->name()->c_str());
509 for (auto it = it_begin; it != it_end; ++it) {
510 context_configurations_.insert_or_assign(
511 it->second, AudioSetConfigurationsFromFlatScenario(scenario));
512 }
513 }
514
515 return true;
516 }
517
LoadContentle_audio::AudioSetConfigurationProviderJson518 bool LoadContent(
519 std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
520 config_files,
521 std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
522 scenario_files) {
523 for (auto [schema, content] : config_files) {
524 if (!LoadConfigurationsFromFiles(schema, content)) return false;
525 }
526
527 for (auto [schema, content] : scenario_files) {
528 if (!LoadScenariosFromFiles(schema, content)) return false;
529 }
530 return true;
531 }
532 };
533
534 struct AudioSetConfigurationProvider::impl {
implle_audio::AudioSetConfigurationProvider::impl535 impl(const AudioSetConfigurationProvider& config_provider)
536 : config_provider_(config_provider) {}
537
Initializele_audio::AudioSetConfigurationProvider::impl538 void Initialize() {
539 ASSERT_LOG(!config_provider_impl_, " Config provider not available.");
540 config_provider_impl_ =
541 std::make_unique<AudioSetConfigurationProviderJson>();
542 }
543
Cleanuple_audio::AudioSetConfigurationProvider::impl544 void Cleanup() {
545 ASSERT_LOG(config_provider_impl_, " Config provider not available.");
546 config_provider_impl_.reset();
547 }
548
IsRunningle_audio::AudioSetConfigurationProvider::impl549 bool IsRunning() { return config_provider_impl_ ? true : false; }
550
Dumple_audio::AudioSetConfigurationProvider::impl551 void Dump(int fd) {
552 std::stringstream stream;
553
554 for (LeAudioContextType context : types::kLeAudioContextAllTypesArray) {
555 auto confs = Get()->GetConfigurations(context);
556 stream << "\n === Configurations for context type: " << (int)context
557 << ", num: " << (confs == nullptr ? 0 : confs->size()) << " \n";
558 if (confs && confs->size() > 0) {
559 for (const auto& conf : *confs) {
560 stream << " name: " << conf->name << " \n";
561 for (const auto& ent : conf->confs) {
562 stream << " direction: "
563 << (ent.direction == types::kLeAudioDirectionSink
564 ? "Sink (speaker)\n"
565 : "Source (mic)\n")
566 << " number of devices: " << +ent.device_cnt << " \n"
567 << " number of ASEs: " << +ent.ase_cnt << " \n"
568 << " target latency: " << +ent.target_latency << " \n"
569 << " strategy: " << (int)(ent.strategy) << " \n"
570 << " qos->retransmission_number: "
571 << +ent.qos.retransmission_number << " \n"
572 << " qos->max_transport_latency: "
573 << +ent.qos.max_transport_latency << " \n"
574 << " channel count: "
575 << +ent.codec.GetConfigChannelCount() << "\n";
576 }
577 }
578 }
579 }
580 dprintf(fd, "%s", stream.str().c_str());
581 }
582
583 const AudioSetConfigurationProvider& config_provider_;
584 std::unique_ptr<AudioSetConfigurationProviderJson> config_provider_impl_;
585 };
586
587 static std::unique_ptr<AudioSetConfigurationProvider> config_provider;
588
AudioSetConfigurationProvider()589 AudioSetConfigurationProvider::AudioSetConfigurationProvider()
590 : pimpl_(std::make_unique<AudioSetConfigurationProvider::impl>(*this)) {}
591
Initialize()592 void AudioSetConfigurationProvider::Initialize() {
593 if (!config_provider)
594 config_provider = std::make_unique<AudioSetConfigurationProvider>();
595
596 if (!config_provider->pimpl_->IsRunning())
597 config_provider->pimpl_->Initialize();
598 }
599
DebugDump(int fd)600 void AudioSetConfigurationProvider::DebugDump(int fd) {
601 if (!config_provider || !config_provider->pimpl_->IsRunning()) {
602 dprintf(
603 fd,
604 "\n AudioSetConfigurationProvider not initialized: config provider: "
605 "%d, pimpl: %d \n",
606 config_provider != nullptr,
607 (config_provider == nullptr ? 0
608 : config_provider->pimpl_->IsRunning()));
609 return;
610 }
611 dprintf(fd, "\n AudioSetConfigurationProvider: \n");
612 config_provider->pimpl_->Dump(fd);
613 }
614
Cleanup()615 void AudioSetConfigurationProvider::Cleanup() {
616 if (!config_provider) return;
617 if (config_provider->pimpl_->IsRunning()) config_provider->pimpl_->Cleanup();
618 config_provider.reset();
619 }
620
Get()621 AudioSetConfigurationProvider* AudioSetConfigurationProvider::Get() {
622 return config_provider.get();
623 }
624
625 const set_configurations::AudioSetConfigurations*
GetConfigurations(::le_audio::types::LeAudioContextType content_type) const626 AudioSetConfigurationProvider::GetConfigurations(
627 ::le_audio::types::LeAudioContextType content_type) const {
628 if (CodecManager::GetInstance()->GetCodecLocation() ==
629 types::CodecLocation::ADSP) {
630 LOG_DEBUG("Get offload config for the context type: %d", (int)content_type);
631 const AudioSetConfigurations* offload_confs =
632 CodecManager::GetInstance()->GetOffloadCodecConfig(content_type);
633
634 if (offload_confs != nullptr && !(*offload_confs).empty()) {
635 return offload_confs;
636 }
637
638 // TODO: Need to have a mechanism to switch to software session if offload
639 // doesn't support.
640 }
641
642 LOG_DEBUG("Get software config for the context type: %d", (int)content_type);
643
644 if (pimpl_->IsRunning())
645 return pimpl_->config_provider_impl_->GetConfigurationsByContextType(
646 content_type);
647
648 return nullptr;
649 }
650
651 } // namespace le_audio
652