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