/* * Copyright (C) 2024 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 LOG_TAG "AHAL_Config" #include #include #include #include #include #include #include "core-impl/CapEngineConfigXmlConverter.h" #include "core-impl/XsdcConversion.h" using aidl::android::hardware::audio::common::iequals; using aidl::android::media::audio::common::AudioDeviceAddress; using aidl::android::media::audio::common::AudioDeviceDescription; using aidl::android::media::audio::common::AudioHalCapConfiguration; using aidl::android::media::audio::common::AudioHalCapCriterionV2; using aidl::android::media::audio::common::AudioHalCapDomain; using aidl::android::media::audio::common::AudioHalCapParameter; using aidl::android::media::audio::common::AudioHalCapRule; using aidl::android::media::audio::common::AudioPolicyForceUse; using aidl::android::media::audio::common::AudioSource; using aidl::android::media::audio::common::AudioStreamType; using ::android::BAD_VALUE; using ::android::base::unexpected; using ::android::utilities::convertTo; namespace eng_xsd = android::audio::policy::capengine::configuration; namespace aidl::android::hardware::audio::core::internal { static constexpr const char* gStrategiesParameter = "product_strategies"; static constexpr const char* gInputSourcesParameter = "input_sources"; static constexpr const char* gStreamsParameter = "streams"; static constexpr const char* gOutputDevicesParameter = "selected_output_devices"; static constexpr const char* gOutputDeviceAddressParameter = "device_address"; static constexpr const char* gStrategyPrefix = "vx_"; static constexpr const char* gLegacyOutputDevicePrefix = "AUDIO_DEVICE_OUT_"; static constexpr const char* gLegacyInputDevicePrefix = "AUDIO_DEVICE_IN_"; static constexpr const char* gLegacyStreamPrefix = "AUDIO_STREAM_"; static constexpr const char* gLegacySourcePrefix = "AUDIO_SOURCE_"; std::optional>>& CapEngineConfigXmlConverter::getAidlCapEngineConfig() { return mAidlCapDomains; } ConversionResult convertCriterionRuleToAidl( const eng_xsd::SelectionCriterionRuleType& xsdcRule) { using Tag = AudioHalCapCriterionV2::Tag; AudioHalCapRule::CriterionRule rule{}; std::string criterionName = xsdcRule.getSelectionCriterion(); std::string criterionValue = xsdcRule.getValue(); if (iequals(criterionName, toString(Tag::availableInputDevices))) { AudioHalCapCriterionV2::AvailableDevices value; value.values.emplace_back(VALUE_OR_RETURN( convertDeviceTypeToAidl(gLegacyInputDevicePrefix + criterionValue))); rule.criterionAndValue = AudioHalCapCriterionV2::make(value); } else if (iequals(criterionName, toString(Tag::availableOutputDevices))) { AudioHalCapCriterionV2::AvailableDevices value; value.values.emplace_back(VALUE_OR_RETURN( convertDeviceTypeToAidl(gLegacyOutputDevicePrefix + criterionValue))); rule.criterionAndValue = AudioHalCapCriterionV2::make(value); } else if (iequals(criterionName, toString(Tag::availableInputDevicesAddresses))) { AudioHalCapCriterionV2::AvailableDevicesAddresses value; value.values.emplace_back(criterionValue); rule.criterionAndValue = AudioHalCapCriterionV2::make(value); } else if (iequals(criterionName, toString(Tag::availableOutputDevicesAddresses))) { AudioHalCapCriterionV2::AvailableDevicesAddresses value; value.values.emplace_back(criterionValue); rule.criterionAndValue = AudioHalCapCriterionV2::make(value); } else if (iequals(criterionName, toString(Tag::telephonyMode))) { AudioHalCapCriterionV2::TelephonyMode value; value.values.emplace_back(VALUE_OR_RETURN(convertTelephonyModeToAidl(criterionValue))); rule.criterionAndValue = AudioHalCapCriterionV2::make(value); } else if (!fastcmp(criterionName.c_str(), kXsdcForceConfigForUse, strlen(kXsdcForceConfigForUse))) { AudioHalCapCriterionV2::ForceConfigForUse value; value.values.emplace_back( VALUE_OR_RETURN(convertForceUseToAidl(criterionName, criterionValue))); rule.criterionAndValue = AudioHalCapCriterionV2::make(value); } else { LOG(ERROR) << __func__ << " unrecognized criterion " << criterionName; return unexpected(BAD_VALUE); } if (xsdcRule.getMatchesWhen() == eng_xsd::MatchesWhenEnum::Excludes) { rule.matchingRule = AudioHalCapRule::MatchingRule::EXCLUDES; } else if (xsdcRule.getMatchesWhen() == eng_xsd::MatchesWhenEnum::Includes) { rule.matchingRule = AudioHalCapRule::MatchingRule::INCLUDES; } else if (xsdcRule.getMatchesWhen() == eng_xsd::MatchesWhenEnum::Is) { rule.matchingRule = AudioHalCapRule::MatchingRule::IS; } else if (xsdcRule.getMatchesWhen() == eng_xsd::MatchesWhenEnum::IsNot) { rule.matchingRule = AudioHalCapRule::MatchingRule::IS_NOT; } else { LOG(ERROR) << "Unsupported match when rule."; return unexpected(BAD_VALUE); } return rule; } ConversionResult convertRule(const eng_xsd::CompoundRuleType& xsdcCompoundRule) { AudioHalCapRule rule{}; bool isPreviousCompoundRule = true; if (xsdcCompoundRule.getType() == eng_xsd::TypeEnum::Any) { rule.compoundRule = AudioHalCapRule::CompoundRule::ANY; } else if (xsdcCompoundRule.getType() == eng_xsd::TypeEnum::All) { rule.compoundRule = AudioHalCapRule::CompoundRule::ALL; } else { LOG(ERROR) << "Unsupported compound rule type."; return unexpected(BAD_VALUE); } for (const auto& childXsdcCoumpoundRule : xsdcCompoundRule.getCompoundRule_optional()) { if (childXsdcCoumpoundRule.hasCompoundRule_optional()) { rule.nestedRules.push_back(VALUE_OR_FATAL(convertRule(childXsdcCoumpoundRule))); } else if (childXsdcCoumpoundRule.hasSelectionCriterionRule_optional()) { rule.nestedRules.push_back(VALUE_OR_FATAL(convertRule(childXsdcCoumpoundRule))); } } if (xsdcCompoundRule.hasSelectionCriterionRule_optional()) { for (const auto& xsdcRule : xsdcCompoundRule.getSelectionCriterionRule_optional()) { rule.criterionRules.push_back(VALUE_OR_FATAL(convertCriterionRuleToAidl(xsdcRule))); } } return rule; } ConversionResult getAudioProductStrategyId(const std::string& path) { std::vector strings; std::istringstream pathStream(path); std::string stringToken; while (getline(pathStream, stringToken, '/')) { std::size_t pos = stringToken.find(gStrategyPrefix); if (pos != std::string::npos) { std::string strategyIdLiteral = stringToken.substr(pos + std::strlen(gStrategyPrefix)); int strategyId; if (!convertTo(strategyIdLiteral, strategyId)) { LOG(ERROR) << "Invalid strategy " << stringToken << " from path " << path; return unexpected(BAD_VALUE); } return strategyId; } } return unexpected(BAD_VALUE); } ConversionResult getAudioSource(const std::string& path) { std::vector strings; std::istringstream pathStream(path); std::string stringToken; while (getline(pathStream, stringToken, '/')) { if (stringToken.find(gInputSourcesParameter) != std::string::npos) { getline(pathStream, stringToken, '/'); std::transform(stringToken.begin(), stringToken.end(), stringToken.begin(), [](char c) { return std::toupper(c); }); std::string legacySourceLiteral = "AUDIO_SOURCE_" + stringToken; audio_source_t legacySource; if (!::android::SourceTypeConverter::fromString(legacySourceLiteral, legacySource)) { LOG(ERROR) << "Invalid source " << stringToken << " from path " << path; return unexpected(BAD_VALUE); } return legacy2aidl_audio_source_t_AudioSource(legacySource); } } return unexpected(BAD_VALUE); } ConversionResult getAudioStreamType(const std::string& path) { std::vector strings; std::istringstream pathStream(path); std::string stringToken; while (getline(pathStream, stringToken, '/')) { if (stringToken.find(gStreamsParameter) != std::string::npos) { getline(pathStream, stringToken, '/'); std::transform(stringToken.begin(), stringToken.end(), stringToken.begin(), [](char c) { return std::toupper(c); }); std::string legacyStreamLiteral = std::string(gLegacyStreamPrefix) + stringToken; audio_stream_type_t legacyStream; if (!::android::StreamTypeConverter::fromString(legacyStreamLiteral, legacyStream)) { LOG(ERROR) << "Invalid stream " << stringToken << " from path " << path; return unexpected(BAD_VALUE); } return legacy2aidl_audio_stream_type_t_AudioStreamType(legacyStream); } } return unexpected(BAD_VALUE); } ConversionResult toUpperAndAppendPrefix(const std::string& capName, const std::string& legacyPrefix) { std::string legacyName = capName; std::transform(legacyName.begin(), legacyName.end(), legacyName.begin(), [](char c) { return std::toupper(c); }); return legacyPrefix + legacyName; } ConversionResult CapEngineConfigXmlConverter::convertParamToAidl( const eng_xsd::ConfigurableElementSettingsType& element) { const auto& path = element.getPath(); AudioHalCapParameter parameterSetting; if (path.find(gStrategiesParameter) != std::string::npos) { int strategyId = VALUE_OR_FATAL(getAudioProductStrategyId(path)); if (path.find(gOutputDevicesParameter) != std::string::npos) { // Value is 1 or 0 if (!element.hasBitParameter_optional()) { LOG(ERROR) << "Invalid strategy value type"; return unexpected(BAD_VALUE); } // Convert name to output device type const auto* xsdcParam = element.getFirstBitParameter_optional(); std::string outputDevice = VALUE_OR_FATAL(toUpperAndAppendPrefix( eng_xsd::toString(xsdcParam->getName()), gLegacyOutputDevicePrefix)); audio_devices_t legacyType; if (!::android::OutputDeviceConverter::fromString(outputDevice, legacyType)) { LOG(ERROR) << "Invalid strategy device type " << outputDevice; return unexpected(BAD_VALUE); } AudioDeviceDescription aidlDevice = VALUE_OR_FATAL(legacy2aidl_audio_devices_t_AudioDeviceDescription(legacyType)); bool isSelected; if (!convertTo(xsdcParam->getValue(), isSelected)) { LOG(ERROR) << "Invalid strategy device selection value " << xsdcParam->getValue(); return unexpected(BAD_VALUE); } parameterSetting = AudioHalCapParameter::StrategyDevice(aidlDevice, strategyId, isSelected); } else if (path.find(gOutputDeviceAddressParameter) != std::string::npos) { // Value is the address if (!element.hasStringParameter_optional()) { return unexpected(BAD_VALUE); } std::string address = element.getFirstStringParameter_optional()->getValue(); parameterSetting = AudioHalCapParameter::StrategyDeviceAddress( AudioDeviceAddress(address), strategyId); } } else if (path.find(gInputSourcesParameter) != std::string::npos) { // Value is 1 or 0 if (!element.hasBitParameter_optional()) { LOG(ERROR) << "Invalid source value type"; return unexpected(BAD_VALUE); } AudioSource audioSourceAidl = VALUE_OR_FATAL(getAudioSource(path)); const auto* xsdcParam = element.getFirstBitParameter_optional(); std::string inputDeviceLiteral = VALUE_OR_FATAL(toUpperAndAppendPrefix( eng_xsd::toString(xsdcParam->getName()), gLegacyInputDevicePrefix)); audio_devices_t inputDeviceType; if (!::android::InputDeviceConverter::fromString(inputDeviceLiteral, inputDeviceType)) { LOG(ERROR) << "Invalid source device type " << inputDeviceLiteral; return unexpected(BAD_VALUE); } AudioDeviceDescription aidlDevice = VALUE_OR_FATAL(legacy2aidl_audio_devices_t_AudioDeviceDescription(inputDeviceType)); bool isSelected; if (!convertTo(xsdcParam->getValue(), isSelected)) { LOG(ERROR) << "Invalid source value type " << xsdcParam->getValue(); return unexpected(BAD_VALUE); } parameterSetting = AudioHalCapParameter::InputSourceDevice(aidlDevice, audioSourceAidl, isSelected); } else if (path.find(gStreamsParameter) != std::string::npos) { AudioStreamType audioStreamAidl = VALUE_OR_FATAL(getAudioStreamType(path)); if (!element.hasEnumParameter_optional()) { LOG(ERROR) << "Invalid stream value type"; return unexpected(BAD_VALUE); } const auto* xsdcParam = element.getFirstEnumParameter_optional(); std::string profileLiteral = VALUE_OR_FATAL(toUpperAndAppendPrefix(xsdcParam->getValue(), gLegacyStreamPrefix)); audio_stream_type_t profileLegacyStream; if (!::android::StreamTypeConverter::fromString(profileLiteral, profileLegacyStream)) { LOG(ERROR) << "Invalid stream value " << profileLiteral; return unexpected(BAD_VALUE); } AudioStreamType profileStreamAidl = VALUE_OR_FATAL( legacy2aidl_audio_stream_type_t_AudioStreamType(profileLegacyStream)); parameterSetting = AudioHalCapParameter::StreamVolumeProfile(audioStreamAidl, profileStreamAidl); } return parameterSetting; } ConversionResult> CapEngineConfigXmlConverter::convertSettingToAidl( const eng_xsd::SettingsType::Configuration& xsdcSetting) { std::vector aidlCapParameterSettings; for (const auto& element : xsdcSetting.getConfigurableElement()) { aidlCapParameterSettings.push_back(VALUE_OR_FATAL(convertParamToAidl(element))); } return aidlCapParameterSettings; } ConversionResult CapEngineConfigXmlConverter::convertConfigurationToAidl( const eng_xsd::ConfigurationsType::Configuration& xsdcConfiguration, const eng_xsd::SettingsType::Configuration& xsdcSettingConfiguration) { AudioHalCapConfiguration aidlCapConfiguration; aidlCapConfiguration.name = xsdcConfiguration.getName(); if (xsdcConfiguration.hasCompoundRule()) { if (xsdcConfiguration.getCompoundRule().size() != 1) { return unexpected(BAD_VALUE); } aidlCapConfiguration.rule = VALUE_OR_FATAL(convertRule(xsdcConfiguration.getCompoundRule()[0])); aidlCapConfiguration.parameterSettings = VALUE_OR_FATAL(convertSettingToAidl(xsdcSettingConfiguration)); } return aidlCapConfiguration; } ConversionResult getConfigurationByName( const std::string& name, const std::vector& xsdcSettingsVec) { for (const auto& xsdcSettings : xsdcSettingsVec) { for (const auto& xsdcConfiguration : xsdcSettings.getConfiguration()) { if (xsdcConfiguration.getName() == name) { return xsdcConfiguration; } } } LOG(ERROR) << __func__ << " failed to find configuration " << name; return unexpected(BAD_VALUE); } ConversionResult> CapEngineConfigXmlConverter::convertConfigurationsToAidl( const std::vector& xsdcConfigurationsVec, const std::vector& xsdcSettingsVec) { if (xsdcConfigurationsVec.empty() || xsdcSettingsVec.empty()) { LOG(ERROR) << __func__ << " empty configurations/settings"; return unexpected(BAD_VALUE); } std::vector aidlConfigurations; for (const auto& xsdcConfigurations : xsdcConfigurationsVec) { for (const auto& xsdcConfiguration : xsdcConfigurations.getConfiguration()) { auto xsdcSettingConfiguration = VALUE_OR_FATAL( getConfigurationByName(xsdcConfiguration.getName(), xsdcSettingsVec)); aidlConfigurations.push_back(VALUE_OR_FATAL( convertConfigurationToAidl(xsdcConfiguration, xsdcSettingConfiguration))); } } return aidlConfigurations; } ConversionResult CapEngineConfigXmlConverter::convertConfigurableDomainToAidl( const eng_xsd::ConfigurableDomainType& xsdcConfigurableDomain) { AudioHalCapDomain aidlConfigurableDomain; aidlConfigurableDomain.name = xsdcConfigurableDomain.getName(); if (xsdcConfigurableDomain.hasSequenceAware() && xsdcConfigurableDomain.getSequenceAware()) { LOG(ERROR) << "sequence aware not supported."; return unexpected(BAD_VALUE); } if (xsdcConfigurableDomain.hasConfigurations() && xsdcConfigurableDomain.hasSettings()) { aidlConfigurableDomain.configurations = VALUE_OR_FATAL(convertConfigurationsToAidl( xsdcConfigurableDomain.getConfigurations(), xsdcConfigurableDomain.getSettings())); } return aidlConfigurableDomain; } void CapEngineConfigXmlConverter::init() { if (getXsdcConfig()->hasConfigurableDomain()) { mAidlCapDomains = std::make_optional<>(VALUE_OR_FATAL( (convertCollectionToAidlOptionalValues( getXsdcConfig()->getConfigurableDomain(), std::bind(&CapEngineConfigXmlConverter::convertConfigurableDomainToAidl, this, std::placeholders::_1))))); } else { mAidlCapDomains = std::nullopt; } } } // namespace aidl::android::hardware::audio::core::internal