blob: 14d27f245ec7158b40d448e8f7ce0cd4b8603ad5 [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_";
François Gaffie460bd6d2025-01-17 14:33:30 +010055static constexpr const char* gLegacyStrategyPrefix = "STRATEGY_";
François Gaffie57ccab72024-04-17 11:47:51 +020056static constexpr const char* gLegacyOutputDevicePrefix = "AUDIO_DEVICE_OUT_";
57static constexpr const char* gLegacyInputDevicePrefix = "AUDIO_DEVICE_IN_";
58static constexpr const char* gLegacyStreamPrefix = "AUDIO_STREAM_";
59static constexpr const char* gLegacySourcePrefix = "AUDIO_SOURCE_";
60
61std::optional<std::vector<std::optional<AudioHalCapDomain>>>&
62CapEngineConfigXmlConverter::getAidlCapEngineConfig() {
63 return mAidlCapDomains;
64}
65
66ConversionResult<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))) {
Mikhail Naganov802a1da2024-11-01 14:38:08 -070073 AudioHalCapCriterionV2::AvailableDevices value;
74 value.values.emplace_back(VALUE_OR_RETURN(
75 convertDeviceTypeToAidl(gLegacyInputDevicePrefix + criterionValue)));
76 rule.criterionAndValue = AudioHalCapCriterionV2::make<Tag::availableInputDevices>(value);
77
François Gaffie57ccab72024-04-17 11:47:51 +020078 } else if (iequals(criterionName, toString(Tag::availableOutputDevices))) {
Mikhail Naganov802a1da2024-11-01 14:38:08 -070079 AudioHalCapCriterionV2::AvailableDevices value;
80 value.values.emplace_back(VALUE_OR_RETURN(
81 convertDeviceTypeToAidl(gLegacyOutputDevicePrefix + criterionValue)));
82 rule.criterionAndValue = AudioHalCapCriterionV2::make<Tag::availableOutputDevices>(value);
François Gaffie57ccab72024-04-17 11:47:51 +020083 } else if (iequals(criterionName, toString(Tag::availableInputDevicesAddresses))) {
Mikhail Naganov802a1da2024-11-01 14:38:08 -070084 AudioHalCapCriterionV2::AvailableDevicesAddresses value;
85 value.values.emplace_back(criterionValue);
86 rule.criterionAndValue =
87 AudioHalCapCriterionV2::make<Tag::availableInputDevicesAddresses>(value);
François Gaffie57ccab72024-04-17 11:47:51 +020088 } else if (iequals(criterionName, toString(Tag::availableOutputDevicesAddresses))) {
Mikhail Naganov802a1da2024-11-01 14:38:08 -070089 AudioHalCapCriterionV2::AvailableDevicesAddresses value;
90 value.values.emplace_back(criterionValue);
91 rule.criterionAndValue =
92 AudioHalCapCriterionV2::make<Tag::availableOutputDevicesAddresses>(value);
François Gaffie57ccab72024-04-17 11:47:51 +020093 } else if (iequals(criterionName, toString(Tag::telephonyMode))) {
Mikhail Naganov802a1da2024-11-01 14:38:08 -070094 AudioHalCapCriterionV2::TelephonyMode value;
95 value.values.emplace_back(VALUE_OR_RETURN(convertTelephonyModeToAidl(criterionValue)));
96 rule.criterionAndValue = AudioHalCapCriterionV2::make<Tag::telephonyMode>(value);
François Gaffie57ccab72024-04-17 11:47:51 +020097 } else if (!fastcmp<strncmp>(criterionName.c_str(), kXsdcForceConfigForUse,
98 strlen(kXsdcForceConfigForUse))) {
Mikhail Naganov802a1da2024-11-01 14:38:08 -070099 AudioHalCapCriterionV2::ForceConfigForUse value;
Mikhail Naganov206ad4d2024-11-20 21:17:52 +0000100 value.values.emplace_back(
101 VALUE_OR_RETURN(convertForceUseToAidl(criterionName, criterionValue)));
Mikhail Naganov802a1da2024-11-01 14:38:08 -0700102 rule.criterionAndValue = AudioHalCapCriterionV2::make<Tag::forceConfigForUse>(value);
François Gaffie57ccab72024-04-17 11:47:51 +0200103 } 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
122ConversionResult<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
148ConversionResult<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 }
François Gaffie460bd6d2025-01-17 14:33:30 +0100163 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 }
François Gaffie57ccab72024-04-17 11:47:51 +0200174 }
175 return unexpected(BAD_VALUE);
176}
177
178ConversionResult<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
199ConversionResult<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
221ConversionResult<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
229ConversionResult<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
316ConversionResult<std::vector<AudioHalCapParameter>>
317CapEngineConfigXmlConverter::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
326ConversionResult<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
343ConversionResult<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
356ConversionResult<std::vector<AudioHalCapConfiguration>>
357CapEngineConfigXmlConverter::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
376ConversionResult<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
392void 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