1 /*
2  * Copyright (C) 2022 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 #include <fcntl.h>
18 #include <inttypes.h>
19 #include <unistd.h>
20 #include <functional>
21 #include <unordered_map>
22 
23 #include <aidl/android/media/audio/common/AudioFlag.h>
24 #include <aidl/android/media/audio/common/AudioHalEngineConfig.h>
25 #include <aidl/android/media/audio/common/AudioProductStrategyType.h>
26 
27 #include "core-impl/EngineConfigXmlConverter.h"
28 
29 using aidl::android::media::audio::common::AudioAttributes;
30 using aidl::android::media::audio::common::AudioContentType;
31 using aidl::android::media::audio::common::AudioFlag;
32 using aidl::android::media::audio::common::AudioHalAttributesGroup;
33 using aidl::android::media::audio::common::AudioHalCapCriterion;
34 using aidl::android::media::audio::common::AudioHalCapCriterionType;
35 using aidl::android::media::audio::common::AudioHalEngineConfig;
36 using aidl::android::media::audio::common::AudioHalProductStrategy;
37 using aidl::android::media::audio::common::AudioHalVolumeCurve;
38 using aidl::android::media::audio::common::AudioHalVolumeGroup;
39 using aidl::android::media::audio::common::AudioProductStrategyType;
40 using aidl::android::media::audio::common::AudioSource;
41 using aidl::android::media::audio::common::AudioStreamType;
42 using aidl::android::media::audio::common::AudioUsage;
43 
44 namespace xsd = android::audio::policy::engine::configuration;
45 
46 namespace aidl::android::hardware::audio::core::internal {
47 
48 /**
49  * Valid curve points take the form "<index>,<attenuationMb>", where the index
50  * must be in the range [0,100]. kInvalidCurvePointIndex is used to indicate
51  * that a point was formatted incorrectly (e.g. if a vendor accidentally typed a
52  * '.' instead of a ',' in their XML)-- using such a curve point will result in
53  * failed VTS tests.
54  */
55 static const int8_t kInvalidCurvePointIndex = -1;
56 
initProductStrategyMap()57 void EngineConfigXmlConverter::initProductStrategyMap() {
58 #define STRATEGY_ENTRY(name) {"STRATEGY_" #name, static_cast<int>(AudioProductStrategyType::name)}
59 
60     mProductStrategyMap = {STRATEGY_ENTRY(MEDIA),
61                            STRATEGY_ENTRY(PHONE),
62                            STRATEGY_ENTRY(SONIFICATION),
63                            STRATEGY_ENTRY(SONIFICATION_RESPECTFUL),
64                            STRATEGY_ENTRY(DTMF),
65                            STRATEGY_ENTRY(ENFORCED_AUDIBLE),
66                            STRATEGY_ENTRY(TRANSMITTED_THROUGH_SPEAKER),
67                            STRATEGY_ENTRY(ACCESSIBILITY)};
68 #undef STRATEGY_ENTRY
69 }
70 
convertProductStrategyNameToAidl(const std::string & xsdcProductStrategyName)71 int EngineConfigXmlConverter::convertProductStrategyNameToAidl(
72         const std::string& xsdcProductStrategyName) {
73     const auto [it, success] = mProductStrategyMap.insert(
74             std::make_pair(xsdcProductStrategyName, mNextVendorStrategy));
75     if (success) {
76         mNextVendorStrategy++;
77     }
78     return it->second;
79 }
80 
isDefaultAudioAttributes(const AudioAttributes & attributes)81 bool isDefaultAudioAttributes(const AudioAttributes& attributes) {
82     return ((attributes.contentType == AudioContentType::UNKNOWN) &&
83             (attributes.usage == AudioUsage::UNKNOWN) &&
84             (attributes.source == AudioSource::DEFAULT) && (attributes.flags == 0) &&
85             (attributes.tags.empty()));
86 }
87 
convertAudioAttributesToAidl(const xsd::AttributesType & xsdcAudioAttributes)88 AudioAttributes EngineConfigXmlConverter::convertAudioAttributesToAidl(
89         const xsd::AttributesType& xsdcAudioAttributes) {
90     if (xsdcAudioAttributes.hasAttributesRef()) {
91         if (mAttributesReferenceMap.empty()) {
92             mAttributesReferenceMap =
93                     generateReferenceMap<xsd::AttributesRef, xsd::AttributesRefType>(
94                             getXsdcConfig()->getAttributesRef());
95         }
96         return convertAudioAttributesToAidl(
97                 *(mAttributesReferenceMap.at(xsdcAudioAttributes.getAttributesRef())
98                           .getFirstAttributes()));
99     }
100     AudioAttributes aidlAudioAttributes;
101     if (xsdcAudioAttributes.hasContentType()) {
102         aidlAudioAttributes.contentType = static_cast<AudioContentType>(
103                 xsdcAudioAttributes.getFirstContentType()->getValue());
104     }
105     if (xsdcAudioAttributes.hasUsage()) {
106         aidlAudioAttributes.usage =
107                 static_cast<AudioUsage>(xsdcAudioAttributes.getFirstUsage()->getValue());
108     }
109     if (xsdcAudioAttributes.hasSource()) {
110         aidlAudioAttributes.source =
111                 static_cast<AudioSource>(xsdcAudioAttributes.getFirstSource()->getValue());
112     }
113     if (xsdcAudioAttributes.hasFlags()) {
114         std::vector<xsd::FlagType> xsdcFlagTypeVec =
115                 xsdcAudioAttributes.getFirstFlags()->getValue();
116         for (const xsd::FlagType& xsdcFlagType : xsdcFlagTypeVec) {
117             if (xsdcFlagType != xsd::FlagType::AUDIO_FLAG_NONE) {
118                 aidlAudioAttributes.flags |= 1 << (static_cast<int>(xsdcFlagType) - 1);
119             }
120         }
121     }
122     if (xsdcAudioAttributes.hasBundle()) {
123         const xsd::BundleType* xsdcBundle = xsdcAudioAttributes.getFirstBundle();
124         aidlAudioAttributes.tags[0] = xsdcBundle->getKey() + "=" + xsdcBundle->getValue();
125     }
126     if (isDefaultAudioAttributes(aidlAudioAttributes)) {
127         mDefaultProductStrategyId = std::optional<int>{-1};
128     }
129     return aidlAudioAttributes;
130 }
131 
convertAttributesGroupToAidl(const xsd::AttributesGroup & xsdcAttributesGroup)132 AudioHalAttributesGroup EngineConfigXmlConverter::convertAttributesGroupToAidl(
133         const xsd::AttributesGroup& xsdcAttributesGroup) {
134     AudioHalAttributesGroup aidlAttributesGroup;
135     static const int kStreamTypeEnumOffset =
136             static_cast<int>(xsd::Stream::AUDIO_STREAM_VOICE_CALL) -
137             static_cast<int>(AudioStreamType::VOICE_CALL);
138     aidlAttributesGroup.streamType = static_cast<AudioStreamType>(
139             static_cast<int>(xsdcAttributesGroup.getStreamType()) - kStreamTypeEnumOffset);
140     aidlAttributesGroup.volumeGroupName = xsdcAttributesGroup.getVolumeGroup();
141     if (xsdcAttributesGroup.hasAttributes_optional()) {
142         aidlAttributesGroup.attributes =
143                 convertCollectionToAidlUnchecked<xsd::AttributesType, AudioAttributes>(
144                         xsdcAttributesGroup.getAttributes_optional(),
145                         std::bind(&EngineConfigXmlConverter::convertAudioAttributesToAidl, this,
146                                   std::placeholders::_1));
147     } else if (xsdcAttributesGroup.hasContentType_optional() ||
148                xsdcAttributesGroup.hasUsage_optional() ||
149                xsdcAttributesGroup.hasSource_optional() ||
150                xsdcAttributesGroup.hasFlags_optional() ||
151                xsdcAttributesGroup.hasBundle_optional()) {
152         aidlAttributesGroup.attributes.push_back(convertAudioAttributesToAidl(xsd::AttributesType(
153                 xsdcAttributesGroup.getContentType_optional(),
154                 xsdcAttributesGroup.getUsage_optional(), xsdcAttributesGroup.getSource_optional(),
155                 xsdcAttributesGroup.getFlags_optional(), xsdcAttributesGroup.getBundle_optional(),
156                 std::nullopt)));
157 
158     } else {
159         // do nothing;
160         // TODO: check if this is valid or if we should treat as an error.
161         // Currently, attributes are not mandatory in schema, but an AttributesGroup
162         // without attributes does not make much sense.
163     }
164     return aidlAttributesGroup;
165 }
166 
convertProductStrategyToAidl(const xsd::ProductStrategies::ProductStrategy & xsdcProductStrategy)167 AudioHalProductStrategy EngineConfigXmlConverter::convertProductStrategyToAidl(
168         const xsd::ProductStrategies::ProductStrategy& xsdcProductStrategy) {
169     AudioHalProductStrategy aidlProductStrategy;
170 
171     aidlProductStrategy.id = convertProductStrategyNameToAidl(xsdcProductStrategy.getName());
172 
173     if (xsdcProductStrategy.hasAttributesGroup()) {
174         aidlProductStrategy.attributesGroups =
175                 convertCollectionToAidlUnchecked<xsd::AttributesGroup, AudioHalAttributesGroup>(
176                         xsdcProductStrategy.getAttributesGroup(),
177                         std::bind(&EngineConfigXmlConverter::convertAttributesGroupToAidl, this,
178                                   std::placeholders::_1));
179     }
180     if ((mDefaultProductStrategyId != std::nullopt) && (mDefaultProductStrategyId.value() == -1)) {
181         mDefaultProductStrategyId = aidlProductStrategy.id;
182     }
183     return aidlProductStrategy;
184 }
185 
convertCurvePointToAidl(const std::string & xsdcCurvePoint)186 AudioHalVolumeCurve::CurvePoint EngineConfigXmlConverter::convertCurvePointToAidl(
187         const std::string& xsdcCurvePoint) {
188     AudioHalVolumeCurve::CurvePoint aidlCurvePoint{};
189     if (sscanf(xsdcCurvePoint.c_str(), "%" SCNd8 ",%d", &aidlCurvePoint.index,
190                &aidlCurvePoint.attenuationMb) != 2) {
191         aidlCurvePoint.index = kInvalidCurvePointIndex;
192     }
193     return aidlCurvePoint;
194 }
195 
convertVolumeCurveToAidl(const xsd::Volume & xsdcVolumeCurve)196 AudioHalVolumeCurve EngineConfigXmlConverter::convertVolumeCurveToAidl(
197         const xsd::Volume& xsdcVolumeCurve) {
198     AudioHalVolumeCurve aidlVolumeCurve;
199     aidlVolumeCurve.deviceCategory =
200             static_cast<AudioHalVolumeCurve::DeviceCategory>(xsdcVolumeCurve.getDeviceCategory());
201     if (xsdcVolumeCurve.hasRef()) {
202         if (mVolumesReferenceMap.empty()) {
203             mVolumesReferenceMap = generateReferenceMap<xsd::VolumesType, xsd::VolumeRef>(
204                     getXsdcConfig()->getVolumes());
205         }
206         aidlVolumeCurve.curvePoints =
207                 convertCollectionToAidlUnchecked<std::string, AudioHalVolumeCurve::CurvePoint>(
208                         mVolumesReferenceMap.at(xsdcVolumeCurve.getRef()).getPoint(),
209                         std::bind(&EngineConfigXmlConverter::convertCurvePointToAidl, this,
210                                   std::placeholders::_1));
211     } else {
212         aidlVolumeCurve.curvePoints =
213                 convertCollectionToAidlUnchecked<std::string, AudioHalVolumeCurve::CurvePoint>(
214                         xsdcVolumeCurve.getPoint(),
215                         std::bind(&EngineConfigXmlConverter::convertCurvePointToAidl, this,
216                                   std::placeholders::_1));
217     }
218     return aidlVolumeCurve;
219 }
220 
convertVolumeGroupToAidl(const xsd::VolumeGroupsType::VolumeGroup & xsdcVolumeGroup)221 AudioHalVolumeGroup EngineConfigXmlConverter::convertVolumeGroupToAidl(
222         const xsd::VolumeGroupsType::VolumeGroup& xsdcVolumeGroup) {
223     AudioHalVolumeGroup aidlVolumeGroup;
224     aidlVolumeGroup.name = xsdcVolumeGroup.getName();
225     aidlVolumeGroup.minIndex = xsdcVolumeGroup.getIndexMin();
226     aidlVolumeGroup.maxIndex = xsdcVolumeGroup.getIndexMax();
227     aidlVolumeGroup.volumeCurves =
228             convertCollectionToAidlUnchecked<xsd::Volume, AudioHalVolumeCurve>(
229                     xsdcVolumeGroup.getVolume(),
230                     std::bind(&EngineConfigXmlConverter::convertVolumeCurveToAidl, this,
231                               std::placeholders::_1));
232     return aidlVolumeGroup;
233 }
234 
convertCapCriterionToAidl(const xsd::CriterionType & xsdcCriterion)235 AudioHalCapCriterion EngineConfigXmlConverter::convertCapCriterionToAidl(
236         const xsd::CriterionType& xsdcCriterion) {
237     AudioHalCapCriterion aidlCapCriterion;
238     aidlCapCriterion.name = xsdcCriterion.getName();
239     aidlCapCriterion.criterionTypeName = xsdcCriterion.getType();
240     aidlCapCriterion.defaultLiteralValue = xsdcCriterion.get_default();
241     return aidlCapCriterion;
242 }
243 
convertCriterionTypeValueToAidl(const xsd::ValueType & xsdcCriterionTypeValue)244 std::string EngineConfigXmlConverter::convertCriterionTypeValueToAidl(
245         const xsd::ValueType& xsdcCriterionTypeValue) {
246     return xsdcCriterionTypeValue.getLiteral();
247 }
248 
convertCapCriterionTypeToAidl(const xsd::CriterionTypeType & xsdcCriterionType)249 AudioHalCapCriterionType EngineConfigXmlConverter::convertCapCriterionTypeToAidl(
250         const xsd::CriterionTypeType& xsdcCriterionType) {
251     AudioHalCapCriterionType aidlCapCriterionType;
252     aidlCapCriterionType.name = xsdcCriterionType.getName();
253     aidlCapCriterionType.isInclusive = !(static_cast<bool>(xsdcCriterionType.getType()));
254     aidlCapCriterionType.values =
255             convertWrappedCollectionToAidlUnchecked<xsd::ValuesType, xsd::ValueType, std::string>(
256                     xsdcCriterionType.getValues(), &xsd::ValuesType::getValue,
257                     std::bind(&EngineConfigXmlConverter::convertCriterionTypeValueToAidl, this,
258                               std::placeholders::_1));
259     return aidlCapCriterionType;
260 }
261 
getAidlEngineConfig()262 AudioHalEngineConfig& EngineConfigXmlConverter::getAidlEngineConfig() {
263     return mAidlEngineConfig;
264 }
265 
init()266 void EngineConfigXmlConverter::init() {
267     initProductStrategyMap();
268     if (getXsdcConfig()->hasProductStrategies()) {
269         mAidlEngineConfig.productStrategies =
270                 convertWrappedCollectionToAidlUnchecked<xsd::ProductStrategies,
271                                                         xsd::ProductStrategies::ProductStrategy,
272                                                         AudioHalProductStrategy>(
273                         getXsdcConfig()->getProductStrategies(),
274                         &xsd::ProductStrategies::getProductStrategy,
275                         std::bind(&EngineConfigXmlConverter::convertProductStrategyToAidl, this,
276                                   std::placeholders::_1));
277         if (mDefaultProductStrategyId) {
278             mAidlEngineConfig.defaultProductStrategyId = mDefaultProductStrategyId.value();
279         }
280     }
281     if (getXsdcConfig()->hasVolumeGroups()) {
282         mAidlEngineConfig.volumeGroups = convertWrappedCollectionToAidlUnchecked<
283                 xsd::VolumeGroupsType, xsd::VolumeGroupsType::VolumeGroup, AudioHalVolumeGroup>(
284                 getXsdcConfig()->getVolumeGroups(), &xsd::VolumeGroupsType::getVolumeGroup,
285                 std::bind(&EngineConfigXmlConverter::convertVolumeGroupToAidl, this,
286                           std::placeholders::_1));
287     }
288     if (getXsdcConfig()->hasCriteria() && getXsdcConfig()->hasCriterion_types()) {
289         AudioHalEngineConfig::CapSpecificConfig capSpecificConfig;
290         capSpecificConfig.criteria =
291                 convertWrappedCollectionToAidlUnchecked<xsd::CriteriaType, xsd::CriterionType,
292                                                         AudioHalCapCriterion>(
293                         getXsdcConfig()->getCriteria(), &xsd::CriteriaType::getCriterion,
294                         std::bind(&EngineConfigXmlConverter::convertCapCriterionToAidl, this,
295                                   std::placeholders::_1));
296         capSpecificConfig.criterionTypes = convertWrappedCollectionToAidlUnchecked<
297                 xsd::CriterionTypesType, xsd::CriterionTypeType, AudioHalCapCriterionType>(
298                 getXsdcConfig()->getCriterion_types(), &xsd::CriterionTypesType::getCriterion_type,
299                 std::bind(&EngineConfigXmlConverter::convertCapCriterionTypeToAidl, this,
300                           std::placeholders::_1));
301         mAidlEngineConfig.capSpecificConfig = capSpecificConfig;
302     }
303 }
304 }  // namespace aidl::android::hardware::audio::core::internal
305