xref: /aosp_15_r20/hardware/interfaces/audio/aidl/default/CapEngineConfigXmlConverter.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
1 /*
2  * Copyright (C) 2024 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 #define LOG_TAG "AHAL_Config"
18 
19 #include <aidl/android/media/audio/common/AudioProductStrategyType.h>
20 #include <android-base/logging.h>
21 #include <media/AidlConversionCppNdk.h>
22 #include <media/TypeConverter.h>
23 #include <media/convert.h>
24 #include <utils/FastStrcmp.h>
25 
26 #include "core-impl/CapEngineConfigXmlConverter.h"
27 #include "core-impl/XsdcConversion.h"
28 
29 using aidl::android::hardware::audio::common::iequals;
30 using aidl::android::media::audio::common::AudioDeviceAddress;
31 using aidl::android::media::audio::common::AudioDeviceDescription;
32 using aidl::android::media::audio::common::AudioHalCapConfiguration;
33 using aidl::android::media::audio::common::AudioHalCapCriterionV2;
34 using aidl::android::media::audio::common::AudioHalCapDomain;
35 using aidl::android::media::audio::common::AudioHalCapParameter;
36 using aidl::android::media::audio::common::AudioHalCapRule;
37 using aidl::android::media::audio::common::AudioPolicyForceUse;
38 using aidl::android::media::audio::common::AudioSource;
39 using aidl::android::media::audio::common::AudioStreamType;
40 
41 using ::android::BAD_VALUE;
42 using ::android::base::unexpected;
43 using ::android::utilities::convertTo;
44 
45 namespace eng_xsd = android::audio::policy::capengine::configuration;
46 
47 namespace aidl::android::hardware::audio::core::internal {
48 
49 static constexpr const char* gStrategiesParameter = "product_strategies";
50 static constexpr const char* gInputSourcesParameter = "input_sources";
51 static constexpr const char* gStreamsParameter = "streams";
52 static constexpr const char* gOutputDevicesParameter = "selected_output_devices";
53 static constexpr const char* gOutputDeviceAddressParameter = "device_address";
54 static constexpr const char* gStrategyPrefix = "vx_";
55 static constexpr const char* gLegacyOutputDevicePrefix = "AUDIO_DEVICE_OUT_";
56 static constexpr const char* gLegacyInputDevicePrefix = "AUDIO_DEVICE_IN_";
57 static constexpr const char* gLegacyStreamPrefix = "AUDIO_STREAM_";
58 static constexpr const char* gLegacySourcePrefix = "AUDIO_SOURCE_";
59 
60 std::optional<std::vector<std::optional<AudioHalCapDomain>>>&
getAidlCapEngineConfig()61 CapEngineConfigXmlConverter::getAidlCapEngineConfig() {
62     return mAidlCapDomains;
63 }
64 
convertCriterionRuleToAidl(const eng_xsd::SelectionCriterionRuleType & xsdcRule)65 ConversionResult<AudioHalCapRule::CriterionRule> convertCriterionRuleToAidl(
66         const eng_xsd::SelectionCriterionRuleType& xsdcRule) {
67     using Tag = AudioHalCapCriterionV2::Tag;
68     AudioHalCapRule::CriterionRule rule{};
69     std::string criterionName = xsdcRule.getSelectionCriterion();
70     std::string criterionValue = xsdcRule.getValue();
71     if (iequals(criterionName, toString(Tag::availableInputDevices))) {
72         AudioHalCapCriterionV2::AvailableDevices value;
73         value.values.emplace_back(VALUE_OR_RETURN(
74                 convertDeviceTypeToAidl(gLegacyInputDevicePrefix + criterionValue)));
75         rule.criterionAndValue = AudioHalCapCriterionV2::make<Tag::availableInputDevices>(value);
76 
77     } else if (iequals(criterionName, toString(Tag::availableOutputDevices))) {
78         AudioHalCapCriterionV2::AvailableDevices value;
79         value.values.emplace_back(VALUE_OR_RETURN(
80                 convertDeviceTypeToAidl(gLegacyOutputDevicePrefix + criterionValue)));
81         rule.criterionAndValue = AudioHalCapCriterionV2::make<Tag::availableOutputDevices>(value);
82     } else if (iequals(criterionName, toString(Tag::availableInputDevicesAddresses))) {
83         AudioHalCapCriterionV2::AvailableDevicesAddresses value;
84         value.values.emplace_back(criterionValue);
85         rule.criterionAndValue =
86                 AudioHalCapCriterionV2::make<Tag::availableInputDevicesAddresses>(value);
87     } else if (iequals(criterionName, toString(Tag::availableOutputDevicesAddresses))) {
88         AudioHalCapCriterionV2::AvailableDevicesAddresses value;
89         value.values.emplace_back(criterionValue);
90         rule.criterionAndValue =
91                 AudioHalCapCriterionV2::make<Tag::availableOutputDevicesAddresses>(value);
92     } else if (iequals(criterionName, toString(Tag::telephonyMode))) {
93         AudioHalCapCriterionV2::TelephonyMode value;
94         value.values.emplace_back(VALUE_OR_RETURN(convertTelephonyModeToAidl(criterionValue)));
95         rule.criterionAndValue = AudioHalCapCriterionV2::make<Tag::telephonyMode>(value);
96     } else if (!fastcmp<strncmp>(criterionName.c_str(), kXsdcForceConfigForUse,
97             strlen(kXsdcForceConfigForUse))) {
98         AudioHalCapCriterionV2::ForceConfigForUse value;
99         value.values.emplace_back(
100                 VALUE_OR_RETURN(convertForceUseToAidl(criterionName, criterionValue)));
101         rule.criterionAndValue = AudioHalCapCriterionV2::make<Tag::forceConfigForUse>(value);
102     } else {
103         LOG(ERROR) << __func__ << " unrecognized criterion " << criterionName;
104         return unexpected(BAD_VALUE);
105     }
106     if (xsdcRule.getMatchesWhen() == eng_xsd::MatchesWhenEnum::Excludes) {
107         rule.matchingRule = AudioHalCapRule::MatchingRule::EXCLUDES;
108     } else if (xsdcRule.getMatchesWhen() == eng_xsd::MatchesWhenEnum::Includes) {
109         rule.matchingRule = AudioHalCapRule::MatchingRule::INCLUDES;
110     } else if (xsdcRule.getMatchesWhen() == eng_xsd::MatchesWhenEnum::Is) {
111         rule.matchingRule = AudioHalCapRule::MatchingRule::IS;
112     } else if (xsdcRule.getMatchesWhen() == eng_xsd::MatchesWhenEnum::IsNot) {
113         rule.matchingRule = AudioHalCapRule::MatchingRule::IS_NOT;
114     } else {
115         LOG(ERROR) << "Unsupported match when rule.";
116         return unexpected(BAD_VALUE);
117     }
118     return rule;
119 }
120 
convertRule(const eng_xsd::CompoundRuleType & xsdcCompoundRule)121 ConversionResult<AudioHalCapRule> convertRule(const eng_xsd::CompoundRuleType& xsdcCompoundRule) {
122     AudioHalCapRule rule{};
123     bool isPreviousCompoundRule = true;
124     if (xsdcCompoundRule.getType() == eng_xsd::TypeEnum::Any) {
125         rule.compoundRule = AudioHalCapRule::CompoundRule::ANY;
126     } else if (xsdcCompoundRule.getType() == eng_xsd::TypeEnum::All) {
127         rule.compoundRule = AudioHalCapRule::CompoundRule::ALL;
128     } else {
129         LOG(ERROR) << "Unsupported compound rule type.";
130         return unexpected(BAD_VALUE);
131     }
132     for (const auto& childXsdcCoumpoundRule : xsdcCompoundRule.getCompoundRule_optional()) {
133         if (childXsdcCoumpoundRule.hasCompoundRule_optional()) {
134             rule.nestedRules.push_back(VALUE_OR_FATAL(convertRule(childXsdcCoumpoundRule)));
135         } else if (childXsdcCoumpoundRule.hasSelectionCriterionRule_optional()) {
136             rule.nestedRules.push_back(VALUE_OR_FATAL(convertRule(childXsdcCoumpoundRule)));
137         }
138     }
139     if (xsdcCompoundRule.hasSelectionCriterionRule_optional()) {
140         for (const auto& xsdcRule : xsdcCompoundRule.getSelectionCriterionRule_optional()) {
141             rule.criterionRules.push_back(VALUE_OR_FATAL(convertCriterionRuleToAidl(xsdcRule)));
142         }
143     }
144     return rule;
145 }
146 
getAudioProductStrategyId(const std::string & path)147 ConversionResult<int> getAudioProductStrategyId(const std::string& path) {
148     std::vector<std::string> strings;
149     std::istringstream pathStream(path);
150     std::string stringToken;
151     while (getline(pathStream, stringToken, '/')) {
152         std::size_t pos = stringToken.find(gStrategyPrefix);
153         if (pos != std::string::npos) {
154             std::string strategyIdLiteral = stringToken.substr(pos + std::strlen(gStrategyPrefix));
155             int strategyId;
156             if (!convertTo(strategyIdLiteral, strategyId)) {
157                 LOG(ERROR) << "Invalid strategy " << stringToken << " from path " << path;
158                 return unexpected(BAD_VALUE);
159             }
160             return strategyId;
161         }
162     }
163     return unexpected(BAD_VALUE);
164 }
165 
getAudioSource(const std::string & path)166 ConversionResult<AudioSource> getAudioSource(const std::string& path) {
167     std::vector<std::string> strings;
168     std::istringstream pathStream(path);
169     std::string stringToken;
170     while (getline(pathStream, stringToken, '/')) {
171         if (stringToken.find(gInputSourcesParameter) != std::string::npos) {
172             getline(pathStream, stringToken, '/');
173             std::transform(stringToken.begin(), stringToken.end(), stringToken.begin(),
174                            [](char c) { return std::toupper(c); });
175             std::string legacySourceLiteral = "AUDIO_SOURCE_" + stringToken;
176             audio_source_t legacySource;
177             if (!::android::SourceTypeConverter::fromString(legacySourceLiteral, legacySource)) {
178                 LOG(ERROR) << "Invalid source " << stringToken << " from path " << path;
179                 return unexpected(BAD_VALUE);
180             }
181             return legacy2aidl_audio_source_t_AudioSource(legacySource);
182         }
183     }
184     return unexpected(BAD_VALUE);
185 }
186 
getAudioStreamType(const std::string & path)187 ConversionResult<AudioStreamType> getAudioStreamType(const std::string& path) {
188     std::vector<std::string> strings;
189     std::istringstream pathStream(path);
190     std::string stringToken;
191 
192     while (getline(pathStream, stringToken, '/')) {
193         if (stringToken.find(gStreamsParameter) != std::string::npos) {
194             getline(pathStream, stringToken, '/');
195             std::transform(stringToken.begin(), stringToken.end(), stringToken.begin(),
196                            [](char c) { return std::toupper(c); });
197             std::string legacyStreamLiteral = std::string(gLegacyStreamPrefix) + stringToken;
198             audio_stream_type_t legacyStream;
199             if (!::android::StreamTypeConverter::fromString(legacyStreamLiteral, legacyStream)) {
200                 LOG(ERROR) << "Invalid stream " << stringToken << " from path " << path;
201                 return unexpected(BAD_VALUE);
202             }
203             return legacy2aidl_audio_stream_type_t_AudioStreamType(legacyStream);
204         }
205     }
206     return unexpected(BAD_VALUE);
207 }
208 
toUpperAndAppendPrefix(const std::string & capName,const std::string & legacyPrefix)209 ConversionResult<std::string> toUpperAndAppendPrefix(const std::string& capName,
210                                                      const std::string& legacyPrefix) {
211     std::string legacyName = capName;
212     std::transform(legacyName.begin(), legacyName.end(), legacyName.begin(),
213                    [](char c) { return std::toupper(c); });
214     return legacyPrefix + legacyName;
215 }
216 
convertParamToAidl(const eng_xsd::ConfigurableElementSettingsType & element)217 ConversionResult<AudioHalCapParameter> CapEngineConfigXmlConverter::convertParamToAidl(
218         const eng_xsd::ConfigurableElementSettingsType& element) {
219     const auto& path = element.getPath();
220 
221     AudioHalCapParameter parameterSetting;
222     if (path.find(gStrategiesParameter) != std::string::npos) {
223         int strategyId = VALUE_OR_FATAL(getAudioProductStrategyId(path));
224         if (path.find(gOutputDevicesParameter) != std::string::npos) {
225             // Value is 1 or 0
226             if (!element.hasBitParameter_optional()) {
227                 LOG(ERROR) << "Invalid strategy value type";
228                 return unexpected(BAD_VALUE);
229             }
230             // Convert name to output device type
231             const auto* xsdcParam = element.getFirstBitParameter_optional();
232             std::string outputDevice = VALUE_OR_FATAL(toUpperAndAppendPrefix(
233                     eng_xsd::toString(xsdcParam->getName()), gLegacyOutputDevicePrefix));
234             audio_devices_t legacyType;
235             if (!::android::OutputDeviceConverter::fromString(outputDevice, legacyType)) {
236                 LOG(ERROR) << "Invalid strategy device type " << outputDevice;
237                 return unexpected(BAD_VALUE);
238             }
239             AudioDeviceDescription aidlDevice =
240                     VALUE_OR_FATAL(legacy2aidl_audio_devices_t_AudioDeviceDescription(legacyType));
241             bool isSelected;
242             if (!convertTo(xsdcParam->getValue(), isSelected)) {
243                 LOG(ERROR) << "Invalid strategy device selection value " << xsdcParam->getValue();
244                 return unexpected(BAD_VALUE);
245             }
246             parameterSetting =
247                     AudioHalCapParameter::StrategyDevice(aidlDevice, strategyId, isSelected);
248         } else if (path.find(gOutputDeviceAddressParameter) != std::string::npos) {
249             // Value is the address
250             if (!element.hasStringParameter_optional()) {
251                 return unexpected(BAD_VALUE);
252             }
253             std::string address = element.getFirstStringParameter_optional()->getValue();
254             parameterSetting = AudioHalCapParameter::StrategyDeviceAddress(
255                     AudioDeviceAddress(address), strategyId);
256         }
257     } else if (path.find(gInputSourcesParameter) != std::string::npos) {
258         // Value is 1 or 0
259         if (!element.hasBitParameter_optional()) {
260             LOG(ERROR) << "Invalid source value type";
261             return unexpected(BAD_VALUE);
262         }
263         AudioSource audioSourceAidl = VALUE_OR_FATAL(getAudioSource(path));
264         const auto* xsdcParam = element.getFirstBitParameter_optional();
265         std::string inputDeviceLiteral = VALUE_OR_FATAL(toUpperAndAppendPrefix(
266                 eng_xsd::toString(xsdcParam->getName()), gLegacyInputDevicePrefix));
267         audio_devices_t inputDeviceType;
268         if (!::android::InputDeviceConverter::fromString(inputDeviceLiteral, inputDeviceType)) {
269             LOG(ERROR) << "Invalid source device type " << inputDeviceLiteral;
270             return unexpected(BAD_VALUE);
271         }
272         AudioDeviceDescription aidlDevice =
273                 VALUE_OR_FATAL(legacy2aidl_audio_devices_t_AudioDeviceDescription(inputDeviceType));
274 
275         bool isSelected;
276         if (!convertTo(xsdcParam->getValue(), isSelected)) {
277             LOG(ERROR) << "Invalid source value type " << xsdcParam->getValue();
278             return unexpected(BAD_VALUE);
279         }
280         parameterSetting =
281                 AudioHalCapParameter::InputSourceDevice(aidlDevice, audioSourceAidl, isSelected);
282     } else if (path.find(gStreamsParameter) != std::string::npos) {
283         AudioStreamType audioStreamAidl = VALUE_OR_FATAL(getAudioStreamType(path));
284         if (!element.hasEnumParameter_optional()) {
285             LOG(ERROR) << "Invalid stream value type";
286             return unexpected(BAD_VALUE);
287         }
288         const auto* xsdcParam = element.getFirstEnumParameter_optional();
289         std::string profileLiteral =
290                 VALUE_OR_FATAL(toUpperAndAppendPrefix(xsdcParam->getValue(), gLegacyStreamPrefix));
291         audio_stream_type_t profileLegacyStream;
292         if (!::android::StreamTypeConverter::fromString(profileLiteral, profileLegacyStream)) {
293             LOG(ERROR) << "Invalid stream value " << profileLiteral;
294             return unexpected(BAD_VALUE);
295         }
296         AudioStreamType profileStreamAidl = VALUE_OR_FATAL(
297                 legacy2aidl_audio_stream_type_t_AudioStreamType(profileLegacyStream));
298         parameterSetting =
299                 AudioHalCapParameter::StreamVolumeProfile(audioStreamAidl, profileStreamAidl);
300     }
301     return parameterSetting;
302 }
303 
304 ConversionResult<std::vector<AudioHalCapParameter>>
convertSettingToAidl(const eng_xsd::SettingsType::Configuration & xsdcSetting)305 CapEngineConfigXmlConverter::convertSettingToAidl(
306         const eng_xsd::SettingsType::Configuration& xsdcSetting) {
307     std::vector<AudioHalCapParameter> aidlCapParameterSettings;
308     for (const auto& element : xsdcSetting.getConfigurableElement()) {
309         aidlCapParameterSettings.push_back(VALUE_OR_FATAL(convertParamToAidl(element)));
310     }
311     return aidlCapParameterSettings;
312 }
313 
convertConfigurationToAidl(const eng_xsd::ConfigurationsType::Configuration & xsdcConfiguration,const eng_xsd::SettingsType::Configuration & xsdcSettingConfiguration)314 ConversionResult<AudioHalCapConfiguration> CapEngineConfigXmlConverter::convertConfigurationToAidl(
315         const eng_xsd::ConfigurationsType::Configuration& xsdcConfiguration,
316         const eng_xsd::SettingsType::Configuration& xsdcSettingConfiguration) {
317     AudioHalCapConfiguration aidlCapConfiguration;
318     aidlCapConfiguration.name = xsdcConfiguration.getName();
319     if (xsdcConfiguration.hasCompoundRule()) {
320         if (xsdcConfiguration.getCompoundRule().size() != 1) {
321             return unexpected(BAD_VALUE);
322         }
323         aidlCapConfiguration.rule =
324                 VALUE_OR_FATAL(convertRule(xsdcConfiguration.getCompoundRule()[0]));
325         aidlCapConfiguration.parameterSettings =
326                 VALUE_OR_FATAL(convertSettingToAidl(xsdcSettingConfiguration));
327     }
328     return aidlCapConfiguration;
329 }
330 
getConfigurationByName(const std::string & name,const std::vector<eng_xsd::SettingsType> & xsdcSettingsVec)331 ConversionResult<eng_xsd::SettingsType::Configuration> getConfigurationByName(
332         const std::string& name, const std::vector<eng_xsd::SettingsType>& xsdcSettingsVec) {
333     for (const auto& xsdcSettings : xsdcSettingsVec) {
334         for (const auto& xsdcConfiguration : xsdcSettings.getConfiguration()) {
335             if (xsdcConfiguration.getName() == name) {
336                 return xsdcConfiguration;
337             }
338         }
339     }
340     LOG(ERROR) << __func__ << " failed to find configuration " << name;
341     return unexpected(BAD_VALUE);
342 }
343 
344 ConversionResult<std::vector<AudioHalCapConfiguration>>
convertConfigurationsToAidl(const std::vector<eng_xsd::ConfigurationsType> & xsdcConfigurationsVec,const std::vector<eng_xsd::SettingsType> & xsdcSettingsVec)345 CapEngineConfigXmlConverter::convertConfigurationsToAidl(
346         const std::vector<eng_xsd::ConfigurationsType>& xsdcConfigurationsVec,
347         const std::vector<eng_xsd::SettingsType>& xsdcSettingsVec) {
348     if (xsdcConfigurationsVec.empty() || xsdcSettingsVec.empty()) {
349         LOG(ERROR) << __func__ << " empty configurations/settings";
350         return unexpected(BAD_VALUE);
351     }
352     std::vector<AudioHalCapConfiguration> aidlConfigurations;
353     for (const auto& xsdcConfigurations : xsdcConfigurationsVec) {
354         for (const auto& xsdcConfiguration : xsdcConfigurations.getConfiguration()) {
355             auto xsdcSettingConfiguration = VALUE_OR_FATAL(
356                     getConfigurationByName(xsdcConfiguration.getName(), xsdcSettingsVec));
357             aidlConfigurations.push_back(VALUE_OR_FATAL(
358                     convertConfigurationToAidl(xsdcConfiguration, xsdcSettingConfiguration)));
359         }
360     }
361     return aidlConfigurations;
362 }
363 
convertConfigurableDomainToAidl(const eng_xsd::ConfigurableDomainType & xsdcConfigurableDomain)364 ConversionResult<AudioHalCapDomain> CapEngineConfigXmlConverter::convertConfigurableDomainToAidl(
365         const eng_xsd::ConfigurableDomainType& xsdcConfigurableDomain) {
366     AudioHalCapDomain aidlConfigurableDomain;
367 
368     aidlConfigurableDomain.name = xsdcConfigurableDomain.getName();
369     if (xsdcConfigurableDomain.hasSequenceAware() && xsdcConfigurableDomain.getSequenceAware()) {
370         LOG(ERROR) << "sequence aware not supported.";
371         return unexpected(BAD_VALUE);
372     }
373     if (xsdcConfigurableDomain.hasConfigurations() && xsdcConfigurableDomain.hasSettings()) {
374         aidlConfigurableDomain.configurations = VALUE_OR_FATAL(convertConfigurationsToAidl(
375                 xsdcConfigurableDomain.getConfigurations(), xsdcConfigurableDomain.getSettings()));
376     }
377     return aidlConfigurableDomain;
378 }
379 
init()380 void CapEngineConfigXmlConverter::init() {
381     if (getXsdcConfig()->hasConfigurableDomain()) {
382         mAidlCapDomains = std::make_optional<>(VALUE_OR_FATAL(
383                 (convertCollectionToAidlOptionalValues<eng_xsd::ConfigurableDomainType,
384                                                        AudioHalCapDomain>(
385                         getXsdcConfig()->getConfigurableDomain(),
386                         std::bind(&CapEngineConfigXmlConverter::convertConfigurableDomainToAidl,
387                                   this, std::placeholders::_1)))));
388     } else {
389         mAidlCapDomains = std::nullopt;
390     }
391 }
392 
393 }  // namespace aidl::android::hardware::audio::core::internal
394