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