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