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