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