/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define STREAM_TO_UINT8(u8, p) \ { \ (u8) = (uint8_t)(*(p)); \ (p) += 1; \ } #define STREAM_TO_UINT16(u16, p) \ { \ (u16) = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); \ (p) += 2; \ } #define STREAM_TO_UINT32(u32, p) \ { \ (u32) = (((uint32_t)(*(p))) + ((((uint32_t)(*((p) + 1)))) << 8) + \ ((((uint32_t)(*((p) + 2)))) << 16) + \ ((((uint32_t)(*((p) + 3)))) << 24)); \ (p) += 4; \ } #define LOG_TAG "BTAudioAseConfigAidl" #include "BluetoothLeAudioAseConfigurationSettingProvider.h" #include #include #include #include #include #include #include #include #include #include #include "flatbuffers/idl.h" #include "flatbuffers/util.h" namespace aidl { namespace android { namespace hardware { namespace bluetooth { namespace audio { /* Internal structure definition */ std::map>, std::vector>, ConfigurationFlags>> configurations_; std::vector ase_configuration_settings_; constexpr uint8_t kIsoDataPathHci = 0x00; constexpr uint8_t kIsoDataPathPlatformDefault = 0x01; constexpr uint8_t kIsoDataPathDisabled = 0xFF; constexpr uint8_t kLeAudioDirectionSink = 0x01; constexpr uint8_t kLeAudioDirectionSource = 0x02; constexpr uint8_t kLeAudioDirectionBoth = kLeAudioDirectionSink | kLeAudioDirectionSource; /* Sampling Frequencies */ constexpr uint8_t kLeAudioSamplingFreq8000Hz = 0x01; constexpr uint8_t kLeAudioSamplingFreq11025Hz = 0x02; constexpr uint8_t kLeAudioSamplingFreq16000Hz = 0x03; constexpr uint8_t kLeAudioSamplingFreq22050Hz = 0x04; constexpr uint8_t kLeAudioSamplingFreq24000Hz = 0x05; constexpr uint8_t kLeAudioSamplingFreq32000Hz = 0x06; constexpr uint8_t kLeAudioSamplingFreq44100Hz = 0x07; constexpr uint8_t kLeAudioSamplingFreq48000Hz = 0x08; constexpr uint8_t kLeAudioSamplingFreq88200Hz = 0x09; constexpr uint8_t kLeAudioSamplingFreq96000Hz = 0x0A; constexpr uint8_t kLeAudioSamplingFreq176400Hz = 0x0B; constexpr uint8_t kLeAudioSamplingFreq192000Hz = 0x0C; constexpr uint8_t kLeAudioSamplingFreq384000Hz = 0x0D; /* Frame Durations */ constexpr uint8_t kLeAudioCodecFrameDur7500us = 0x00; constexpr uint8_t kLeAudioCodecFrameDur10000us = 0x01; /* Audio Allocations */ constexpr uint32_t kLeAudioLocationNotAllowed = 0x00000000; constexpr uint32_t kLeAudioLocationFrontLeft = 0x00000001; constexpr uint32_t kLeAudioLocationFrontRight = 0x00000002; constexpr uint32_t kLeAudioLocationFrontCenter = 0x00000004; constexpr uint32_t kLeAudioLocationLowFreqEffects1 = 0x00000008; constexpr uint32_t kLeAudioLocationBackLeft = 0x00000010; constexpr uint32_t kLeAudioLocationBackRight = 0x00000020; constexpr uint32_t kLeAudioLocationFrontLeftOfCenter = 0x00000040; constexpr uint32_t kLeAudioLocationFrontRightOfCenter = 0x00000080; constexpr uint32_t kLeAudioLocationBackCenter = 0x00000100; constexpr uint32_t kLeAudioLocationLowFreqEffects2 = 0x00000200; constexpr uint32_t kLeAudioLocationSideLeft = 0x00000400; constexpr uint32_t kLeAudioLocationSideRight = 0x00000800; constexpr uint32_t kLeAudioLocationTopFrontLeft = 0x00001000; constexpr uint32_t kLeAudioLocationTopFrontRight = 0x00002000; constexpr uint32_t kLeAudioLocationTopFrontCenter = 0x00004000; constexpr uint32_t kLeAudioLocationTopCenter = 0x00008000; constexpr uint32_t kLeAudioLocationTopBackLeft = 0x00010000; constexpr uint32_t kLeAudioLocationTopBackRight = 0x00020000; constexpr uint32_t kLeAudioLocationTopSideLeft = 0x00040000; constexpr uint32_t kLeAudioLocationTopSideRight = 0x00080000; constexpr uint32_t kLeAudioLocationTopBackCenter = 0x00100000; constexpr uint32_t kLeAudioLocationBottomFrontCenter = 0x00200000; constexpr uint32_t kLeAudioLocationBottomFrontLeft = 0x00400000; constexpr uint32_t kLeAudioLocationBottomFrontRight = 0x00800000; constexpr uint32_t kLeAudioLocationFrontLeftWide = 0x01000000; constexpr uint32_t kLeAudioLocationFrontRightWide = 0x02000000; constexpr uint32_t kLeAudioLocationLeftSurround = 0x04000000; constexpr uint32_t kLeAudioLocationRightSurround = 0x08000000; constexpr uint32_t kLeAudioLocationAnyLeft = kLeAudioLocationFrontLeft | kLeAudioLocationBackLeft | kLeAudioLocationFrontLeftOfCenter | kLeAudioLocationSideLeft | kLeAudioLocationTopFrontLeft | kLeAudioLocationTopBackLeft | kLeAudioLocationTopSideLeft | kLeAudioLocationBottomFrontLeft | kLeAudioLocationFrontLeftWide | kLeAudioLocationLeftSurround; constexpr uint32_t kLeAudioLocationAnyRight = kLeAudioLocationFrontRight | kLeAudioLocationBackRight | kLeAudioLocationFrontRightOfCenter | kLeAudioLocationSideRight | kLeAudioLocationTopFrontRight | kLeAudioLocationTopBackRight | kLeAudioLocationTopSideRight | kLeAudioLocationBottomFrontRight | kLeAudioLocationFrontRightWide | kLeAudioLocationRightSurround; constexpr uint32_t kLeAudioLocationStereo = kLeAudioLocationFrontLeft | kLeAudioLocationFrontRight; /* Octets Per Frame */ constexpr uint16_t kLeAudioCodecFrameLen30 = 30; constexpr uint16_t kLeAudioCodecFrameLen40 = 40; constexpr uint16_t kLeAudioCodecFrameLen60 = 60; constexpr uint16_t kLeAudioCodecFrameLen80 = 80; constexpr uint16_t kLeAudioCodecFrameLen100 = 100; constexpr uint16_t kLeAudioCodecFrameLen120 = 120; /* Helper map for matching various sampling frequency notations */ const std::map sampling_freq_map = { {kLeAudioSamplingFreq8000Hz, CodecSpecificConfigurationLtv::SamplingFrequency::HZ8000}, {kLeAudioSamplingFreq16000Hz, CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000}, {kLeAudioSamplingFreq24000Hz, CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000}, {kLeAudioSamplingFreq32000Hz, CodecSpecificConfigurationLtv::SamplingFrequency::HZ32000}, {kLeAudioSamplingFreq44100Hz, CodecSpecificConfigurationLtv::SamplingFrequency::HZ44100}, {kLeAudioSamplingFreq48000Hz, CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000}}; /* Helper map for matching various frame durations notations */ const std::map frame_duration_map = { {kLeAudioCodecFrameDur7500us, CodecSpecificConfigurationLtv::FrameDuration::US7500}, {kLeAudioCodecFrameDur10000us, CodecSpecificConfigurationLtv::FrameDuration::US10000}}; /* Helper map for matching various audio channel allocation notations */ std::map audio_channel_allocation_map = { {kLeAudioLocationNotAllowed, CodecSpecificConfigurationLtv::AudioChannelAllocation::NOT_ALLOWED}, {kLeAudioLocationFrontLeft, CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_LEFT}, {kLeAudioLocationFrontRight, CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_RIGHT}, {kLeAudioLocationFrontCenter, CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER}, {kLeAudioLocationLowFreqEffects1, CodecSpecificConfigurationLtv::AudioChannelAllocation:: LOW_FREQUENCY_EFFECTS_1}, {kLeAudioLocationBackLeft, CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_LEFT}, {kLeAudioLocationBackRight, CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_RIGHT}, {kLeAudioLocationFrontLeftOfCenter, CodecSpecificConfigurationLtv::AudioChannelAllocation:: FRONT_LEFT_OF_CENTER}, {kLeAudioLocationFrontRightOfCenter, CodecSpecificConfigurationLtv::AudioChannelAllocation:: FRONT_RIGHT_OF_CENTER}, {kLeAudioLocationBackCenter, CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_CENTER}, {kLeAudioLocationLowFreqEffects2, CodecSpecificConfigurationLtv::AudioChannelAllocation:: LOW_FREQUENCY_EFFECTS_2}, {kLeAudioLocationSideLeft, CodecSpecificConfigurationLtv::AudioChannelAllocation::SIDE_LEFT}, {kLeAudioLocationSideRight, CodecSpecificConfigurationLtv::AudioChannelAllocation::SIDE_RIGHT}, {kLeAudioLocationTopFrontLeft, CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_LEFT}, {kLeAudioLocationTopFrontRight, CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_RIGHT}, {kLeAudioLocationTopFrontCenter, CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_CENTER}, {kLeAudioLocationTopCenter, CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_CENTER}, {kLeAudioLocationTopBackLeft, CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_LEFT}, {kLeAudioLocationTopBackRight, CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_RIGHT}, {kLeAudioLocationTopSideLeft, CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_SIDE_LEFT}, {kLeAudioLocationTopSideRight, CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_SIDE_RIGHT}, {kLeAudioLocationTopBackCenter, CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_CENTER}, {kLeAudioLocationBottomFrontCenter, CodecSpecificConfigurationLtv::AudioChannelAllocation:: BOTTOM_FRONT_CENTER}, {kLeAudioLocationBottomFrontLeft, CodecSpecificConfigurationLtv::AudioChannelAllocation::BOTTOM_FRONT_LEFT}, {kLeAudioLocationBottomFrontRight, CodecSpecificConfigurationLtv::AudioChannelAllocation::BOTTOM_FRONT_RIGHT}, {kLeAudioLocationFrontLeftWide, CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_LEFT_WIDE}, {kLeAudioLocationFrontRightWide, CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_RIGHT_WIDE}, {kLeAudioLocationLeftSurround, CodecSpecificConfigurationLtv::AudioChannelAllocation::LEFT_SURROUND}, {kLeAudioLocationRightSurround, CodecSpecificConfigurationLtv::AudioChannelAllocation::RIGHT_SURROUND}, }; static const std::vector< std::pair> kLeAudioSetConfigs = {{"/vendor/etc/aidl/le_audio/" "aidl_audio_set_configurations.bfbs", "/vendor/etc/aidl/le_audio/" "aidl_audio_set_configurations.json"}}; static const std::vector< std::pair> kLeAudioSetScenarios = {{"/vendor/etc/aidl/le_audio/" "aidl_audio_set_scenarios.bfbs", "/vendor/etc/aidl/le_audio/" "aidl_audio_set_scenarios.json"}}; /* Implementation */ std::vector AudioSetConfigurationProviderJson::GetLeAudioAseConfigurationSettings() { AudioSetConfigurationProviderJson::LoadAudioSetConfigurationProviderJson(); return ase_configuration_settings_; } void AudioSetConfigurationProviderJson:: LoadAudioSetConfigurationProviderJson() { if (configurations_.empty() || ase_configuration_settings_.empty()) { ase_configuration_settings_.clear(); configurations_.clear(); auto loaded = LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios, CodecLocation::ADSP); if (!loaded) LOG(ERROR) << ": Unable to load le audio set configuration files."; } else LOG(INFO) << ": Reusing loaded le audio set configuration"; } const le_audio::CodecSpecificConfiguration* AudioSetConfigurationProviderJson::LookupCodecSpecificParam( const flatbuffers::Vector>* flat_codec_specific_params, le_audio::CodecSpecificLtvGenericTypes type) { auto it = std::find_if( flat_codec_specific_params->cbegin(), flat_codec_specific_params->cend(), [&type](const auto& csc) { return (csc->type() == type); }); return (it != flat_codec_specific_params->cend()) ? *it : nullptr; } void AudioSetConfigurationProviderJson::populateAudioChannelAllocation( CodecSpecificConfigurationLtv::AudioChannelAllocation& audio_channel_allocation, uint32_t audio_location) { audio_channel_allocation.bitmask = 0; for (auto [allocation, bitmask] : audio_channel_allocation_map) { if (audio_location & allocation) audio_channel_allocation.bitmask |= bitmask; } } void AudioSetConfigurationProviderJson::populateConfigurationData( LeAudioAseConfiguration& ase, const flatbuffers::Vector< flatbuffers::Offset>* flat_codec_specific_params) { uint8_t sampling_frequency = 0; uint8_t frame_duration = 0; uint32_t audio_channel_allocation = 0; uint16_t octets_per_codec_frame = 0; uint8_t codec_frames_blocks_per_sdu = 0; auto param = LookupCodecSpecificParam( flat_codec_specific_params, le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_SAMPLING_FREQUENCY); if (param) { auto ptr = param->compound_value()->value()->data(); STREAM_TO_UINT8(sampling_frequency, ptr); } param = LookupCodecSpecificParam( flat_codec_specific_params, le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_FRAME_DURATION); if (param) { auto ptr = param->compound_value()->value()->data(); STREAM_TO_UINT8(frame_duration, ptr); } param = LookupCodecSpecificParam( flat_codec_specific_params, le_audio:: CodecSpecificLtvGenericTypes_SUPPORTED_AUDIO_CHANNEL_ALLOCATION); if (param) { auto ptr = param->compound_value()->value()->data(); STREAM_TO_UINT32(audio_channel_allocation, ptr); } param = LookupCodecSpecificParam( flat_codec_specific_params, le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_OCTETS_PER_CODEC_FRAME); if (param) { auto ptr = param->compound_value()->value()->data(); STREAM_TO_UINT16(octets_per_codec_frame, ptr); } param = LookupCodecSpecificParam( flat_codec_specific_params, le_audio:: CodecSpecificLtvGenericTypes_SUPPORTED_CODEC_FRAME_BLOCKS_PER_SDU); if (param) { auto ptr = param->compound_value()->value()->data(); STREAM_TO_UINT8(codec_frames_blocks_per_sdu, ptr); } // Make the correct value ase.codecConfiguration = std::vector(); auto sampling_freq_it = sampling_freq_map.find(sampling_frequency); if (sampling_freq_it != sampling_freq_map.end()) ase.codecConfiguration.push_back(sampling_freq_it->second); auto frame_duration_it = frame_duration_map.find(frame_duration); if (frame_duration_it != frame_duration_map.end()) ase.codecConfiguration.push_back(frame_duration_it->second); CodecSpecificConfigurationLtv::AudioChannelAllocation channel_allocation; populateAudioChannelAllocation(channel_allocation, audio_channel_allocation); ase.codecConfiguration.push_back(channel_allocation); auto octet_structure = CodecSpecificConfigurationLtv::OctetsPerCodecFrame(); octet_structure.value = octets_per_codec_frame; ase.codecConfiguration.push_back(octet_structure); auto frame_sdu_structure = CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU(); frame_sdu_structure.value = codec_frames_blocks_per_sdu; ase.codecConfiguration.push_back(frame_sdu_structure); } void AudioSetConfigurationProviderJson::populateAseConfiguration( LeAudioAseConfiguration& ase, const le_audio::AudioSetSubConfiguration* flat_subconfig, const le_audio::QosConfiguration* qos_cfg) { // Target latency switch (qos_cfg->target_latency()) { case le_audio::AudioSetConfigurationTargetLatency:: AudioSetConfigurationTargetLatency_BALANCED_RELIABILITY: ase.targetLatency = LeAudioAseConfiguration::TargetLatency::BALANCED_LATENCY_RELIABILITY; break; case le_audio::AudioSetConfigurationTargetLatency:: AudioSetConfigurationTargetLatency_HIGH_RELIABILITY: ase.targetLatency = LeAudioAseConfiguration::TargetLatency::HIGHER_RELIABILITY; break; case le_audio::AudioSetConfigurationTargetLatency:: AudioSetConfigurationTargetLatency_LOW: ase.targetLatency = LeAudioAseConfiguration::TargetLatency::LOWER; break; default: ase.targetLatency = LeAudioAseConfiguration::TargetLatency::UNDEFINED; break; }; ase.targetPhy = Phy::TWO_M; // Making CodecId if (flat_subconfig->codec_id()->coding_format() == (uint8_t)CodecId::Core::LC3) { ase.codecId = CodecId::Core::LC3; } else { auto vendorC = CodecId::Vendor(); vendorC.codecId = flat_subconfig->codec_id()->vendor_codec_id(); vendorC.id = flat_subconfig->codec_id()->vendor_company_id(); ase.codecId = vendorC; } // Codec configuration data populateConfigurationData(ase, flat_subconfig->codec_configuration()); } void AudioSetConfigurationProviderJson::populateAseQosConfiguration( LeAudioAseQosConfiguration& qos, const le_audio::QosConfiguration* qos_cfg, LeAudioAseConfiguration& ase) { std::optional frameBlock = std::nullopt; std::optional frameDuration = std::nullopt; std::optional allocation = std::nullopt; std::optional octet = std::nullopt; for (auto& cfg_ltv : ase.codecConfiguration) { auto tag = cfg_ltv.getTag(); if (tag == CodecSpecificConfigurationLtv::codecFrameBlocksPerSDU) { frameBlock = cfg_ltv.get(); } else if (tag == CodecSpecificConfigurationLtv::frameDuration) { frameDuration = cfg_ltv.get(); } else if (tag == CodecSpecificConfigurationLtv::audioChannelAllocation) { allocation = cfg_ltv.get(); } else if (tag == CodecSpecificConfigurationLtv::octetsPerCodecFrame) { octet = cfg_ltv.get(); } } int frameBlockValue = 1; if (frameBlock.has_value()) frameBlockValue = frameBlock.value().value; // Populate maxSdu if (allocation.has_value() && octet.has_value()) { auto channel_count = std::bitset<32>(allocation.value().bitmask).count(); qos.maxSdu = channel_count * octet.value().value * frameBlockValue; } // Populate sduIntervalUs if (frameDuration.has_value()) { switch (frameDuration.value()) { case CodecSpecificConfigurationLtv::FrameDuration::US7500: qos.sduIntervalUs = 7500; break; case CodecSpecificConfigurationLtv::FrameDuration::US10000: qos.sduIntervalUs = 10000; break; } qos.sduIntervalUs *= frameBlockValue; } qos.maxTransportLatencyMs = qos_cfg->max_transport_latency(); qos.retransmissionNum = qos_cfg->retransmission_number(); } // Parse into AseDirectionConfiguration AseDirectionConfiguration AudioSetConfigurationProviderJson::SetConfigurationFromFlatSubconfig( const le_audio::AudioSetSubConfiguration* flat_subconfig, const le_audio::QosConfiguration* qos_cfg, CodecLocation location) { AseDirectionConfiguration direction_conf; LeAudioAseConfiguration ase; LeAudioAseQosConfiguration qos; LeAudioDataPathConfiguration path; // Translate into LeAudioAseConfiguration populateAseConfiguration(ase, flat_subconfig, qos_cfg); // Translate into LeAudioAseQosConfiguration populateAseQosConfiguration(qos, qos_cfg, ase); // Translate location to data path id switch (location) { case CodecLocation::ADSP: path.isoDataPathConfiguration.isTransparent = true; path.dataPathId = kIsoDataPathPlatformDefault; break; case CodecLocation::HOST: path.isoDataPathConfiguration.isTransparent = true; path.dataPathId = kIsoDataPathHci; break; case CodecLocation::CONTROLLER: path.isoDataPathConfiguration.isTransparent = false; path.dataPathId = kIsoDataPathPlatformDefault; break; } // Move codecId to iso data path path.isoDataPathConfiguration.codecId = ase.codecId.value(); direction_conf.aseConfiguration = ase; direction_conf.qosConfiguration = qos; direction_conf.dataPathConfiguration = path; return direction_conf; } // Parse into AseDirectionConfiguration and the ConfigurationFlags // and put them in the given list. void AudioSetConfigurationProviderJson::processSubconfig( const le_audio::AudioSetSubConfiguration* subconfig, const le_audio::QosConfiguration* qos_cfg, std::vector>& directionAseConfiguration, CodecLocation location) { directionAseConfiguration.push_back( SetConfigurationFromFlatSubconfig(subconfig, qos_cfg, location)); } void AudioSetConfigurationProviderJson::PopulateAseConfigurationFromFlat( const le_audio::AudioSetConfiguration* flat_cfg, std::vector* codec_cfgs, std::vector* qos_cfgs, CodecLocation location, std::vector>& sourceAseConfiguration, std::vector>& sinkAseConfiguration, ConfigurationFlags& /*configurationFlags*/) { if (flat_cfg == nullptr) { LOG(ERROR) << "flat_cfg cannot be null"; return; } std::string codec_config_key = flat_cfg->codec_config_name()->str(); auto* qos_config_key_array = flat_cfg->qos_config_name(); constexpr std::string_view default_qos = "QoS_Config_Balanced_Reliability"; std::string qos_sink_key(default_qos); std::string qos_source_key(default_qos); /* We expect maximum two QoS settings. First for Sink and second for Source */ if (qos_config_key_array->size() > 0) { qos_sink_key = qos_config_key_array->Get(0)->str(); if (qos_config_key_array->size() > 1) { qos_source_key = qos_config_key_array->Get(1)->str(); } else { qos_source_key = qos_sink_key; } } LOG(INFO) << "Audio set config " << flat_cfg->name()->c_str() << ": codec config " << codec_config_key.c_str() << ", qos_sink " << qos_sink_key.c_str() << ", qos_source " << qos_source_key.c_str(); // Find the first qos config that match the name const le_audio::QosConfiguration* qos_sink_cfg = nullptr; for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) { if ((*i)->name()->str() == qos_sink_key) { qos_sink_cfg = *i; break; } } const le_audio::QosConfiguration* qos_source_cfg = nullptr; for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) { if ((*i)->name()->str() == qos_source_key) { qos_source_cfg = *i; break; } } // First codec_cfg with the same name const le_audio::CodecConfiguration* codec_cfg = nullptr; for (auto i = codec_cfgs->begin(); i != codec_cfgs->end(); ++i) { if ((*i)->name()->str() == codec_config_key) { codec_cfg = *i; break; } } // Process each subconfig and put it into the correct list if (codec_cfg != nullptr && codec_cfg->subconfigurations()) { /* Load subconfigurations */ for (auto subconfig : *codec_cfg->subconfigurations()) { if (subconfig->direction() == kLeAudioDirectionSink) { processSubconfig(subconfig, qos_sink_cfg, sinkAseConfiguration, location); } else { processSubconfig(subconfig, qos_source_cfg, sourceAseConfiguration, location); } } } else { if (codec_cfg == nullptr) { LOG(ERROR) << "No codec config matching key " << codec_config_key.c_str() << " found"; } else { LOG(ERROR) << "Configuration '" << flat_cfg->name()->c_str() << "' has no valid subconfigurations."; } } // TODO: Populate information for ConfigurationFlags } bool AudioSetConfigurationProviderJson::LoadConfigurationsFromFiles( const char* schema_file, const char* content_file, CodecLocation location) { flatbuffers::Parser configurations_parser_; std::string configurations_schema_binary_content; bool ok = flatbuffers::LoadFile(schema_file, true, &configurations_schema_binary_content); LOG(INFO) << __func__ << ": Loading file " << schema_file; if (!ok) return ok; /* Load the binary schema */ ok = configurations_parser_.Deserialize( (uint8_t*)configurations_schema_binary_content.c_str(), configurations_schema_binary_content.length()); if (!ok) return ok; /* Load the content from JSON */ std::string configurations_json_content; LOG(INFO) << __func__ << ": Loading file " << content_file; ok = flatbuffers::LoadFile(content_file, false, &configurations_json_content); if (!ok) return ok; /* Parse */ LOG(INFO) << __func__ << ": Parse JSON content"; ok = configurations_parser_.Parse(configurations_json_content.c_str()); if (!ok) return ok; /* Import from flatbuffers */ LOG(INFO) << __func__ << ": Build flat buffer structure"; auto configurations_root = le_audio::GetAudioSetConfigurations( configurations_parser_.builder_.GetBufferPointer()); if (!configurations_root) return false; auto flat_qos_configs = configurations_root->qos_configurations(); if ((flat_qos_configs == nullptr) || (flat_qos_configs->size() == 0)) return false; LOG(DEBUG) << ": Updating " << flat_qos_configs->size() << " qos config entries."; std::vector qos_cfgs; for (auto const& flat_qos_cfg : *flat_qos_configs) { qos_cfgs.push_back(flat_qos_cfg); } auto flat_codec_configs = configurations_root->codec_configurations(); if ((flat_codec_configs == nullptr) || (flat_codec_configs->size() == 0)) return false; LOG(DEBUG) << ": Updating " << flat_codec_configs->size() << " codec config entries."; std::vector codec_cfgs; for (auto const& flat_codec_cfg : *flat_codec_configs) { codec_cfgs.push_back(flat_codec_cfg); } auto flat_configs = configurations_root->configurations(); if ((flat_configs == nullptr) || (flat_configs->size() == 0)) return false; LOG(DEBUG) << ": Updating " << flat_configs->size() << " config entries."; for (auto const& flat_cfg : *flat_configs) { // Create 3 vector to use std::vector> sourceAseConfiguration; std::vector> sinkAseConfiguration; ConfigurationFlags configurationFlags; PopulateAseConfigurationFromFlat(flat_cfg, &codec_cfgs, &qos_cfgs, location, sourceAseConfiguration, sinkAseConfiguration, configurationFlags); if (sourceAseConfiguration.empty() && sinkAseConfiguration.empty()) continue; configurations_[flat_cfg->name()->str()] = std::make_tuple( sourceAseConfiguration, sinkAseConfiguration, configurationFlags); } return true; } bool AudioSetConfigurationProviderJson::LoadScenariosFromFiles( const char* schema_file, const char* content_file) { flatbuffers::Parser scenarios_parser_; std::string scenarios_schema_binary_content; bool ok = flatbuffers::LoadFile(schema_file, true, &scenarios_schema_binary_content); LOG(INFO) << __func__ << ": Loading file " << schema_file; if (!ok) return ok; /* Load the binary schema */ ok = scenarios_parser_.Deserialize( (uint8_t*)scenarios_schema_binary_content.c_str(), scenarios_schema_binary_content.length()); if (!ok) return ok; /* Load the content from JSON */ LOG(INFO) << __func__ << ": Loading file " << content_file; std::string scenarios_json_content; ok = flatbuffers::LoadFile(content_file, false, &scenarios_json_content); if (!ok) return ok; /* Parse */ LOG(INFO) << __func__ << ": Parse json content"; ok = scenarios_parser_.Parse(scenarios_json_content.c_str()); if (!ok) return ok; /* Import from flatbuffers */ LOG(INFO) << __func__ << ": Build flat buffer structure"; auto scenarios_root = le_audio::GetAudioSetScenarios( scenarios_parser_.builder_.GetBufferPointer()); if (!scenarios_root) return false; auto flat_scenarios = scenarios_root->scenarios(); if ((flat_scenarios == nullptr) || (flat_scenarios->size() == 0)) return false; LOG(INFO) << __func__ << ": Turn flat buffer into structure"; AudioContext media_context = AudioContext(); media_context.bitmask = (AudioContext::ALERTS | AudioContext::INSTRUCTIONAL | AudioContext::NOTIFICATIONS | AudioContext::EMERGENCY_ALARM | AudioContext::UNSPECIFIED | AudioContext::MEDIA | AudioContext::SOUND_EFFECTS); AudioContext conversational_context = AudioContext(); conversational_context.bitmask = (AudioContext::RINGTONE_ALERTS | AudioContext::CONVERSATIONAL); AudioContext live_context = AudioContext(); live_context.bitmask = AudioContext::LIVE_AUDIO; AudioContext game_context = AudioContext(); game_context.bitmask = AudioContext::GAME; AudioContext voice_assistants_context = AudioContext(); voice_assistants_context.bitmask = AudioContext::VOICE_ASSISTANTS; LOG(DEBUG) << "Updating " << flat_scenarios->size() << " scenarios."; for (auto const& scenario : *flat_scenarios) { LOG(DEBUG) << "Scenario " << scenario->name()->c_str() << " configs: " << scenario->configurations()->size(); if (!scenario->configurations()) continue; std::string scenario_name = scenario->name()->c_str(); AudioContext context; if (scenario_name == "Media") context = AudioContext(media_context); else if (scenario_name == "Conversational") context = AudioContext(conversational_context); else if (scenario_name == "Live") context = AudioContext(live_context); else if (scenario_name == "Game") context = AudioContext(game_context); else if (scenario_name == "VoiceAssistants") context = AudioContext(voice_assistants_context); for (auto it = scenario->configurations()->begin(); it != scenario->configurations()->end(); ++it) { auto config_name = it->str(); auto configuration = configurations_.find(config_name); if (configuration == configurations_.end()) continue; LOG(DEBUG) << "Getting configuration with name: " << config_name; auto [source, sink, flags] = configuration->second; // Each configuration will create a LeAudioAseConfigurationSetting // with the same {context, packing} // and different data LeAudioAseConfigurationSetting setting; setting.audioContext = context; // TODO: Packing setting.sourceAseConfiguration = source; setting.sinkAseConfiguration = sink; setting.flags = flags; // Add to list of setting LOG(DEBUG) << "Pushing configuration to list: " << config_name; ase_configuration_settings_.push_back(setting); } } return true; } bool AudioSetConfigurationProviderJson::LoadContent( std::vector> config_files, std::vector> scenario_files, CodecLocation location) { for (auto [schema, content] : config_files) { if (!LoadConfigurationsFromFiles(schema, content, location)) return false; } for (auto [schema, content] : scenario_files) { if (!LoadScenariosFromFiles(schema, content)) return false; } return true; } } // namespace audio } // namespace bluetooth } // namespace hardware } // namespace android } // namespace aidl