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