xref: /aosp_15_r20/frameworks/av/services/audiopolicy/engine/config/src/EngineConfig.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2018 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 <cstdint>
18 #include <istream>
19 #include <map>
20 #include <sstream>
21 #include <stdarg.h>
22 #include <string>
23 #include <vector>
24 #include <unordered_map>
25 
26 #define LOG_TAG "APM::AudioPolicyEngine/Config"
27 //#define LOG_NDEBUG 0
28 
29 #include "EngineConfig.h"
30 #include <TypeConverter.h>
31 #include <Volume.h>
32 #include <cutils/properties.h>
33 #include <libxml/parser.h>
34 #include <libxml/xinclude.h>
35 #include <media/AidlConversion.h>
36 #include <media/AidlConversionUtil.h>
37 #include <media/TypeConverter.h>
38 #include <media/convert.h>
39 #include <system/audio_config.h>
40 #include <utils/Log.h>
41 
42 namespace android {
43 
44 using media::audio::common::AudioStreamType;
45 using utilities::convertTo;
46 
47 namespace engineConfig {
48 
49 static constexpr const char *gVersionAttribute = "version";
50 static const char *const gReferenceElementName = "reference";
51 static const char *const gReferenceAttributeName = "name";
52 
53 namespace {
54 
55 static bool gIsConfigurableEngine = false;
56 
aidl2legacy_AudioHalProductStrategy_ProductStrategyType(int id)57 ConversionResult<std::string> aidl2legacy_AudioHalProductStrategy_ProductStrategyType(int id) {
58     using AudioProductStrategyType = media::audio::common::AudioProductStrategyType;
59 
60 #define STRATEGY_ENTRY(name) {static_cast<int>(AudioProductStrategyType::name), "STRATEGY_" #name}
61     static const std::unordered_map<int, std::string> productStrategyMap = {STRATEGY_ENTRY(MEDIA),
62                             STRATEGY_ENTRY(PHONE),
63                             STRATEGY_ENTRY(SONIFICATION),
64                             STRATEGY_ENTRY(SONIFICATION_RESPECTFUL),
65                             STRATEGY_ENTRY(DTMF),
66                             STRATEGY_ENTRY(ENFORCED_AUDIBLE),
67                             STRATEGY_ENTRY(TRANSMITTED_THROUGH_SPEAKER),
68                             STRATEGY_ENTRY(ACCESSIBILITY)};
69 #undef STRATEGY_ENTRY
70 
71     if (id >= media::audio::common::AudioHalProductStrategy::VENDOR_STRATEGY_ID_START) {
72         return std::to_string(id);
73     }
74     auto it = productStrategyMap.find(id);
75     if (it == productStrategyMap.end()) {
76         return base::unexpected(BAD_VALUE);
77     }
78     return it->second;
79 }
80 
aidl2legacy_AudioHalAttributeGroup_AttributesGroup(const media::audio::common::AudioHalAttributesGroup & aidl)81 ConversionResult<AttributesGroup> aidl2legacy_AudioHalAttributeGroup_AttributesGroup(
82         const media::audio::common::AudioHalAttributesGroup& aidl) {
83     AttributesGroup legacy;
84     // StreamType may only be set to AudioStreamType.INVALID when using the
85     // Configurable Audio Policy (CAP) engine. An AudioHalAttributesGroup with
86     // AudioStreamType.INVALID is used when the volume group and attributes are
87     // not associated to any AudioStreamType.
88     legacy.stream = ((aidl.streamType == AudioStreamType::INVALID) ? AUDIO_STREAM_DEFAULT :
89             VALUE_OR_RETURN(aidl2legacy_AudioStreamType_audio_stream_type_t(aidl.streamType)));
90     legacy.volumeGroup = aidl.volumeGroupName;
91     legacy.attributesVect = VALUE_OR_RETURN(convertContainer<AttributesVector>(
92                     aidl.attributes, aidl2legacy_AudioAttributes_audio_attributes_t));
93     return legacy;
94 }
95 
aidl2legacy_AudioHalProductStrategy_ProductStrategy(const media::audio::common::AudioHalProductStrategy & aidl)96 ConversionResult<ProductStrategy> aidl2legacy_AudioHalProductStrategy_ProductStrategy(
97         const media::audio::common::AudioHalProductStrategy& aidl) {
98     ProductStrategy legacy;
99     legacy.name = aidl.name.value_or(VALUE_OR_RETURN(
100                     aidl2legacy_AudioHalProductStrategy_ProductStrategyType(aidl.id)));
101     legacy.id = aidl.id;
102     legacy.attributesGroups = VALUE_OR_RETURN(convertContainer<AttributesGroups>(
103                     aidl.attributesGroups,
104                     aidl2legacy_AudioHalAttributeGroup_AttributesGroup));
105     return legacy;
106 }
107 
legacy_device_category_to_string(device_category legacy)108 ConversionResult<std::string> legacy_device_category_to_string(device_category legacy) {
109     std::string s;
110     if (DeviceCategoryConverter::toString(legacy, s)) {
111         return s;
112     }
113     return base::unexpected(BAD_VALUE);
114 }
115 
aidl2legacy_DeviceCategory(const media::audio::common::AudioHalVolumeCurve::DeviceCategory aidl)116 ConversionResult<std::string> aidl2legacy_DeviceCategory(
117         const media::audio::common::AudioHalVolumeCurve::DeviceCategory aidl) {
118     using DeviceCategory = media::audio::common::AudioHalVolumeCurve::DeviceCategory;
119     switch (aidl) {
120         case DeviceCategory::HEADSET:
121             return legacy_device_category_to_string(DEVICE_CATEGORY_HEADSET);
122         case DeviceCategory::SPEAKER:
123             return legacy_device_category_to_string(DEVICE_CATEGORY_SPEAKER);
124         case DeviceCategory::EARPIECE:
125             return legacy_device_category_to_string(DEVICE_CATEGORY_EARPIECE);
126         case DeviceCategory::EXT_MEDIA:
127             return legacy_device_category_to_string(DEVICE_CATEGORY_EXT_MEDIA);
128         case DeviceCategory::HEARING_AID:
129             return legacy_device_category_to_string(DEVICE_CATEGORY_HEARING_AID);
130     }
131     return base::unexpected(BAD_VALUE);
132 }
133 
aidl2legacy_AudioHalCurvePoint_CurvePoint(const media::audio::common::AudioHalVolumeCurve::CurvePoint & aidl)134 ConversionResult<CurvePoint> aidl2legacy_AudioHalCurvePoint_CurvePoint(
135         const media::audio::common::AudioHalVolumeCurve::CurvePoint& aidl) {
136     CurvePoint legacy;
137     legacy.index = VALUE_OR_RETURN(convertIntegral<int>(aidl.index));
138     legacy.attenuationInMb = aidl.attenuationMb;
139     return legacy;
140 }
141 
aidl2legacy_AudioHalVolumeCurve_VolumeCurve(const media::audio::common::AudioHalVolumeCurve & aidl)142 ConversionResult<VolumeCurve> aidl2legacy_AudioHalVolumeCurve_VolumeCurve(
143         const media::audio::common::AudioHalVolumeCurve& aidl) {
144     VolumeCurve legacy;
145     legacy.deviceCategory = VALUE_OR_RETURN(aidl2legacy_DeviceCategory(aidl.deviceCategory));
146     legacy.curvePoints = VALUE_OR_RETURN(convertContainer<CurvePoints>(
147                     aidl.curvePoints, aidl2legacy_AudioHalCurvePoint_CurvePoint));
148     return legacy;
149 }
150 
aidl2legacy_AudioHalVolumeGroup_VolumeGroup(const media::audio::common::AudioHalVolumeGroup & aidl)151 ConversionResult<VolumeGroup> aidl2legacy_AudioHalVolumeGroup_VolumeGroup(
152         const media::audio::common::AudioHalVolumeGroup& aidl) {
153     VolumeGroup legacy;
154     legacy.name = aidl.name;
155     legacy.indexMin = aidl.minIndex;
156     legacy.indexMax = aidl.maxIndex;
157     legacy.volumeCurves = VALUE_OR_RETURN(convertContainer<VolumeCurves>(
158                     aidl.volumeCurves, aidl2legacy_AudioHalVolumeCurve_VolumeCurve));
159     return legacy;
160 }
161 }  // namespace
162 
163 template<typename E, typename C>
164 struct BaseSerializerTraits {
165     typedef E Element;
166     typedef C Collection;
167     typedef void* PtrSerializingCtx;
168 };
169 
170 struct AttributesGroupTraits : public BaseSerializerTraits<AttributesGroup, AttributesGroups> {
171     static constexpr const char *tag = "AttributesGroup";
172     static constexpr const char *collectionTag = "AttributesGroups";
173 
174     struct Attributes {
175         static constexpr const char *streamType = "streamType";
176         static constexpr const char *volumeGroup = "volumeGroup";
177     };
178     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &ps);
179 };
180 
181 struct ProductStrategyTraits : public BaseSerializerTraits<ProductStrategy, ProductStrategies> {
182     static constexpr const char *tag = "ProductStrategy";
183     static constexpr const char *collectionTag = "ProductStrategies";
184 
185     struct Attributes {
186         static constexpr const char *name = "name";
187         static constexpr const char *id = "id";
188     };
189     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &ps);
190 };
191 struct ValueTraits : public BaseSerializerTraits<ValuePair, ValuePairs> {
192     static constexpr const char *tag = "value";
193     static constexpr const char *collectionTag = "values";
194 
195     struct Attributes {
196         static constexpr const char *literal = "literal";
197         static constexpr const char *numerical = "numerical";
198         static constexpr const char *androidType = "android_type";
199     };
200 
201     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
202                                          Collection &collection);
203 };
204 struct CriterionTypeTraits : public BaseSerializerTraits<CriterionType, CriterionTypes> {
205     static constexpr const char *tag = "criterion_type";
206     static constexpr const char *collectionTag = "criterion_types";
207 
208     struct Attributes {
209         static constexpr const char *name = "name";
210         static constexpr const char *type = "type";
211     };
212 
213     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
214                                          Collection &collection);
215 };
216 struct CriterionTraits : public BaseSerializerTraits<Criterion, Criteria> {
217     static constexpr const char *tag = "criterion";
218     static constexpr const char *collectionTag = "criteria";
219 
220     struct Attributes {
221         static constexpr const char *name = "name";
222         static constexpr const char *type = "type";
223         static constexpr const char *defaultVal = "default";
224     };
225 
226     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
227                                          Collection &collection);
228 };
229 struct VolumeTraits : public BaseSerializerTraits<VolumeCurve, VolumeCurves> {
230     static constexpr const char *tag = "volume";
231     static constexpr const char *collectionTag = "volumes";
232     static constexpr const char *volumePointTag = "point";
233 
234     struct Attributes {
235         static constexpr const char *deviceCategory = "deviceCategory";
236         static constexpr const char *stream = "stream"; // For legacy volume curves
237         static constexpr const char *reference = "ref"; /**< For volume curves factorization. */
238     };
239 
240     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
241                                          Collection &collection);
242 };
243 struct VolumeGroupTraits : public BaseSerializerTraits<VolumeGroup, VolumeGroups> {
244     static constexpr const char *tag = "volumeGroup";
245     static constexpr const char *collectionTag = "volumeGroups";
246 
247     struct Attributes {
248         static constexpr const char *name = "name";
249         static constexpr const char *stream = "stream"; // For legacy volume curves
250         static constexpr const char *indexMin = "indexMin";
251         static constexpr const char *indexMax = "indexMax";
252     };
253 
254     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
255                                          Collection &collection);
256 };
257 
258 template <class T>
259 constexpr void (*xmlDeleter)(T* t);
260 template <>
261 constexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc;
262 template <>
__anoneb8adccd0202(xmlChar *s) 263 constexpr auto xmlDeleter<xmlChar> = [](xmlChar *s) { xmlFree(s); };
264 
265 /** @return a unique_ptr with the correct deleter for the libxml2 object. */
266 template <class T>
make_xmlUnique(T * t)267 constexpr auto make_xmlUnique(T *t) {
268     // Wrap deleter in lambda to enable empty base optimization
269     auto deleter = [](T *t) { xmlDeleter<T>(t); };
270     return std::unique_ptr<T, decltype(deleter)>{t, deleter};
271 }
272 
getXmlAttribute(const xmlNode * cur,const char * attribute)273 std::string getXmlAttribute(const xmlNode *cur, const char *attribute)
274 {
275     auto charPtr = make_xmlUnique(xmlGetProp(cur, reinterpret_cast<const xmlChar *>(attribute)));
276     if (charPtr == NULL) {
277         return "";
278     }
279     std::string value(reinterpret_cast<const char*>(charPtr.get()));
280     return value;
281 }
282 
getReference(const _xmlNode * root,const _xmlNode * & refNode,const std::string & refName,const char * collectionTag)283 static void getReference(const _xmlNode *root, const _xmlNode *&refNode, const std::string &refName,
284                          const char *collectionTag)
285 {
286     for (root = root->xmlChildrenNode; root != NULL; root = root->next) {
287         if (!xmlStrcmp(root->name, (const xmlChar *)collectionTag)) {
288             for (xmlNode *cur = root->xmlChildrenNode; cur != NULL; cur = cur->next) {
289                 if ((!xmlStrcmp(cur->name, (const xmlChar *)gReferenceElementName))) {
290                     std::string name = getXmlAttribute(cur, gReferenceAttributeName);
291                     if (refName == name) {
292                         refNode = cur;
293                         return;
294                     }
295                 }
296             }
297         }
298     }
299     return;
300 }
301 
302 template <class Trait>
deserializeCollection(_xmlDoc * doc,const _xmlNode * cur,typename Trait::Collection & collection,size_t & nbSkippedElement)303 static status_t deserializeCollection(_xmlDoc *doc, const _xmlNode *cur,
304                                       typename Trait::Collection &collection,
305                                       size_t &nbSkippedElement)
306 {
307     for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
308         if (xmlStrcmp(cur->name, (const xmlChar *)Trait::collectionTag) &&
309             xmlStrcmp(cur->name, (const xmlChar *)Trait::tag)) {
310             continue;
311         }
312         const xmlNode *child = cur;
313         if (!xmlStrcmp(child->name, (const xmlChar *)Trait::collectionTag)) {
314             child = child->xmlChildrenNode;
315         }
316         for (; child != NULL; child = child->next) {
317             if (!xmlStrcmp(child->name, (const xmlChar *)Trait::tag)) {
318                 status_t status = Trait::deserialize(doc, child, collection);
319                 if (status != NO_ERROR) {
320                     nbSkippedElement += 1;
321                 }
322             }
323         }
324         if (!xmlStrcmp(cur->name, (const xmlChar *)Trait::tag)) {
325             return NO_ERROR;
326         }
327     }
328     return NO_ERROR;
329 }
330 
331 static constexpr const char *attributesAttributeRef = "attributesRef"; /**< for factorization. */
332 
parseAttributes(const _xmlNode * cur,audio_attributes_t & attributes)333 static status_t parseAttributes(const _xmlNode *cur, audio_attributes_t &attributes)
334 {
335     for (; cur != NULL; cur = cur->next) {
336         if (!xmlStrcmp(cur->name, (const xmlChar *)("ContentType"))) {
337             std::string contentTypeXml = getXmlAttribute(cur, "value");
338             audio_content_type_t contentType;
339             if (not AudioContentTypeConverter::fromString(contentTypeXml.c_str(), contentType)) {
340                 ALOGE("Invalid content type %s", contentTypeXml.c_str());
341                 return BAD_VALUE;
342             }
343             attributes.content_type = contentType;
344             ALOGV("%s content type %s",  __FUNCTION__, contentTypeXml.c_str());
345         }
346         if (!xmlStrcmp(cur->name, (const xmlChar *)("Usage"))) {
347             std::string usageXml = getXmlAttribute(cur, "value");
348             audio_usage_t usage;
349             if (not UsageTypeConverter::fromString(usageXml.c_str(), usage)) {
350                 ALOGE("Invalid usage %s", usageXml.c_str());
351                 return BAD_VALUE;
352             }
353             attributes.usage = usage;
354             ALOGV("%s usage %s",  __FUNCTION__, usageXml.c_str());
355         }
356         if (!xmlStrcmp(cur->name, (const xmlChar *)("Flags"))) {
357             std::string flags = getXmlAttribute(cur, "value");
358 
359             ALOGV("%s flags %s",  __FUNCTION__, flags.c_str());
360             attributes.flags = static_cast<audio_flags_mask_t>(
361                     AudioFlagConverter::maskFromString(flags, " "));
362         }
363         if (!xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) {
364             std::string bundleKey = getXmlAttribute(cur, "key");
365             std::string bundleValue = getXmlAttribute(cur, "value");
366 
367             ALOGV("%s Bundle %s %s",  __FUNCTION__, bundleKey.c_str(), bundleValue.c_str());
368 
369             std::string tags(bundleKey + "=" + bundleValue);
370             std::strncpy(attributes.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
371         }
372     }
373     return NO_ERROR;
374 }
375 
deserializeAttributes(_xmlDoc * doc,const _xmlNode * cur,audio_attributes_t & attributes)376 static status_t deserializeAttributes(_xmlDoc *doc, const _xmlNode *cur,
377                                       audio_attributes_t &attributes) {
378     // Retrieve content type, usage, flags, and bundle from xml
379     for (; cur != NULL; cur = cur->next) {
380         if (not xmlStrcmp(cur->name, (const xmlChar *)("Attributes"))) {
381             const xmlNode *attrNode = cur;
382             std::string attrRef = getXmlAttribute(cur, attributesAttributeRef);
383             if (!attrRef.empty()) {
384                 getReference(xmlDocGetRootElement(doc), attrNode, attrRef, attributesAttributeRef);
385                 if (attrNode == NULL) {
386                     ALOGE("%s: No reference found for %s", __FUNCTION__, attrRef.c_str());
387                     return BAD_VALUE;
388                 }
389                 return deserializeAttributes(doc, attrNode->xmlChildrenNode, attributes);
390             }
391             return parseAttributes(attrNode->xmlChildrenNode, attributes);
392         }
393         if (not xmlStrcmp(cur->name, (const xmlChar *)("ContentType")) ||
394                 not xmlStrcmp(cur->name, (const xmlChar *)("Usage")) ||
395                 not xmlStrcmp(cur->name, (const xmlChar *)("Flags")) ||
396                 not xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) {
397             return parseAttributes(cur, attributes);
398         }
399     }
400     return BAD_VALUE;
401 }
402 
deserializeAttributesCollection(_xmlDoc * doc,const _xmlNode * cur,AttributesVector & collection)403 static status_t deserializeAttributesCollection(_xmlDoc *doc, const _xmlNode *cur,
404                                                 AttributesVector &collection)
405 {
406     status_t ret = BAD_VALUE;
407     // Either we do provide only one attributes or a collection of supported attributes
408     for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
409         if (not xmlStrcmp(cur->name, (const xmlChar *)("Attributes")) ||
410                 not xmlStrcmp(cur->name, (const xmlChar *)("ContentType")) ||
411                 not xmlStrcmp(cur->name, (const xmlChar *)("Usage")) ||
412                 not xmlStrcmp(cur->name, (const xmlChar *)("Flags")) ||
413                 not xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) {
414             audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
415             ret = deserializeAttributes(doc, cur, attributes);
416             if (ret == NO_ERROR) {
417                 collection.push_back(attributes);
418                 // We are done if the "Attributes" balise is omitted, only one Attributes is allowed
419                 if (xmlStrcmp(cur->name, (const xmlChar *)("Attributes"))) {
420                     return ret;
421                 }
422             }
423         }
424     }
425     return ret;
426 }
427 
deserialize(_xmlDoc * doc,const _xmlNode * child,Collection & attributesGroup)428 status_t AttributesGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
429                                             Collection &attributesGroup)
430 {
431     std::string volumeGroup = getXmlAttribute(child, Attributes::volumeGroup);
432     if (volumeGroup.empty()) {
433         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::volumeGroup);
434     }
435     ALOGV("%s: %s = %s", __FUNCTION__, Attributes::volumeGroup, volumeGroup.c_str());
436 
437     audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT;
438     std::string streamTypeXml = getXmlAttribute(child, Attributes::streamType);
439     if (streamTypeXml.empty()) {
440         ALOGV("%s: No attribute %s found", __FUNCTION__, Attributes::streamType);
441     } else {
442         ALOGV("%s: %s = %s", __FUNCTION__, Attributes::streamType, streamTypeXml.c_str());
443         if (not StreamTypeConverter::fromString(streamTypeXml.c_str(), streamType)) {
444             ALOGE("Invalid stream type %s", streamTypeXml.c_str());
445             return BAD_VALUE;
446         }
447     }
448     AttributesVector attributesVect;
449     deserializeAttributesCollection(doc, child, attributesVect);
450 
451     attributesGroup.push_back({streamType, volumeGroup, attributesVect});
452     return NO_ERROR;
453 }
454 
deserialize(_xmlDoc *,const _xmlNode * child,Collection & values)455 status_t ValueTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child, Collection &values)
456 {
457     std::string literal = getXmlAttribute(child, Attributes::literal);
458     if (literal.empty()) {
459         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal);
460         return BAD_VALUE;
461     }
462     uint32_t androidType = 0;
463     std::string androidTypeliteral = getXmlAttribute(child, Attributes::androidType);
464     if (!androidTypeliteral.empty()) {
465         ALOGV("%s: androidType %s", __FUNCTION__, androidTypeliteral.c_str());
466         if (!convertTo(androidTypeliteral, androidType)) {
467             ALOGE("%s: : Invalid typeset value(%s)", __FUNCTION__, androidTypeliteral.c_str());
468             return BAD_VALUE;
469         }
470     }
471     uint64_t numerical = 0;
472     std::string numericalTag = getXmlAttribute(child, Attributes::numerical);
473     if (numericalTag.empty()) {
474         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal);
475         return BAD_VALUE;
476     }
477     if (!convertTo(numericalTag, numerical)) {
478         ALOGE("%s: : Invalid value(%s)", __FUNCTION__, numericalTag.c_str());
479         return BAD_VALUE;
480     }
481     values.push_back({numerical,  androidType, literal});
482     return NO_ERROR;
483 }
484 
deserialize(_xmlDoc * doc,const _xmlNode * child,Collection & criterionTypes)485 status_t CriterionTypeTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
486                                           Collection &criterionTypes)
487 {
488     std::string name = getXmlAttribute(child, Attributes::name);
489     if (name.empty()) {
490         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name);
491         return BAD_VALUE;
492     }
493     ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::name, name.c_str());
494 
495     std::string type = getXmlAttribute(child, Attributes::type);
496     if (type.empty()) {
497         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::type);
498         return BAD_VALUE;
499     }
500     ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::type, type.c_str());
501     bool isInclusive(type == "inclusive");
502 
503     ValuePairs pairs;
504     size_t nbSkippedElements = 0;
505     deserializeCollection<ValueTraits>(doc, child, pairs, nbSkippedElements);
506     criterionTypes.push_back({name, isInclusive, pairs});
507     return NO_ERROR;
508 }
509 
deserialize(_xmlDoc *,const _xmlNode * child,Collection & criteria)510 status_t CriterionTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child,
511                                       Collection &criteria)
512 {
513     std::string name = getXmlAttribute(child, Attributes::name);
514     if (name.empty()) {
515         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name);
516         return BAD_VALUE;
517     }
518     ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str());
519 
520     std::string defaultValue = getXmlAttribute(child, Attributes::defaultVal);
521     if (defaultValue.empty()) {
522         // Not mandatory to provide a default value for a criterion, even it is recommanded...
523         ALOGV("%s: No attribute %s found (but recommanded)", __FUNCTION__, Attributes::defaultVal);
524     }
525     ALOGV("%s: %s = %s", __FUNCTION__, Attributes::defaultVal, defaultValue.c_str());
526 
527     std::string typeName = getXmlAttribute(child, Attributes::type);
528     if (typeName.empty()) {
529         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name);
530         return BAD_VALUE;
531     }
532     ALOGV("%s: %s = %s", __FUNCTION__, Attributes::type, typeName.c_str());
533 
534     criteria.push_back({name, typeName, defaultValue});
535     return NO_ERROR;
536 }
537 
deserialize(_xmlDoc * doc,const _xmlNode * child,Collection & strategies)538 status_t ProductStrategyTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
539                                             Collection &strategies)
540 {
541     std::string name = getXmlAttribute(child, Attributes::name);
542     if (name.empty()) {
543         ALOGE("ProductStrategyTraits No attribute %s found", Attributes::name);
544         return BAD_VALUE;
545     }
546     int id = PRODUCT_STRATEGY_NONE;
547     std::string idLiteral = getXmlAttribute(child, Attributes::id);
548     if (!idLiteral.empty()) {
549         if (!convertTo(idLiteral, id)) {
550             return BAD_VALUE;
551         }
552     } else {
553         legacy_strategy legacyId;
554         if (legacy_strategy_from_string(name.c_str(), &legacyId)) {
555             id = legacyId;
556         } else if (!gIsConfigurableEngine) {
557             return BAD_VALUE;
558         }
559         // With a configurable engine it can be a vendor-provided strategy name.
560     }
561     ALOGV("%s: %s, %s = %d", __FUNCTION__, name.c_str(), Attributes::id, id);
562 
563     size_t skipped = 0;
564     AttributesGroups attrGroups;
565     deserializeCollection<AttributesGroupTraits>(doc, child, attrGroups, skipped);
566 
567     strategies.push_back({name, id, attrGroups});
568     return NO_ERROR;
569 }
570 
deserialize(_xmlDoc * doc,const _xmlNode * root,Collection & volumes)571 status_t VolumeTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes)
572 {
573     std::string deviceCategory = getXmlAttribute(root, Attributes::deviceCategory);
574     if (deviceCategory.empty()) {
575         ALOGW("%s: No %s found", __FUNCTION__, Attributes::deviceCategory);
576     }
577     std::string referenceName = getXmlAttribute(root, Attributes::reference);
578     const _xmlNode *ref = NULL;
579     if (!referenceName.empty()) {
580         getReference(xmlDocGetRootElement(doc), ref, referenceName, collectionTag);
581         if (ref == NULL) {
582             ALOGE("%s: No reference Ptr found for %s", __FUNCTION__, referenceName.c_str());
583             return BAD_VALUE;
584         }
585     }
586     // Retrieve curve point from reference element if found or directly from current curve
587     CurvePoints curvePoints;
588     for (const xmlNode *child = referenceName.empty() ?
589          root->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) {
590         if (!xmlStrcmp(child->name, (const xmlChar *)volumePointTag)) {
591             auto pointXml = make_xmlUnique(xmlNodeListGetString(doc, child->xmlChildrenNode, 1));
592             if (pointXml == NULL) {
593                 return BAD_VALUE;
594             }
595             ALOGV("%s: %s=%s", __func__, tag, reinterpret_cast<const char*>(pointXml.get()));
596             std::vector<int> point;
597             collectionFromString<DefaultTraits<int>>(
598                         reinterpret_cast<const char*>(pointXml.get()), point, ",");
599             if (point.size() != 2) {
600                 ALOGE("%s: Invalid %s: %s", __func__, volumePointTag,
601                       reinterpret_cast<const char*>(pointXml.get()));
602                 return BAD_VALUE;
603             }
604             curvePoints.push_back({point[0], point[1]});
605         }
606     }
607     volumes.push_back({ deviceCategory, curvePoints });
608     return NO_ERROR;
609 }
610 
deserialize(_xmlDoc * doc,const _xmlNode * root,Collection & volumes)611 status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes)
612 {
613     std::string name;
614     int indexMin = 0;
615     int indexMax = 0;
616     StreamVector streams = {};
617     AttributesVector attributesVect = {};
618 
619     for (const xmlNode *child = root->xmlChildrenNode; child != NULL; child = child->next) {
620         if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::name)) {
621             auto nameXml = make_xmlUnique(xmlNodeListGetString(doc, child->xmlChildrenNode, 1));
622             if (nameXml == nullptr) {
623                 return BAD_VALUE;
624             }
625             name = reinterpret_cast<const char*>(nameXml.get());
626         }
627         if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMin)) {
628             auto indexMinXml = make_xmlUnique(xmlNodeListGetString(doc, child->xmlChildrenNode, 1));
629             if (indexMinXml == nullptr) {
630                 return BAD_VALUE;
631             }
632             std::string indexMinLiteral(reinterpret_cast<const char*>(indexMinXml.get()));
633             if (!convertTo(indexMinLiteral, indexMin)) {
634                 return BAD_VALUE;
635             }
636         }
637         if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMax)) {
638             auto indexMaxXml = make_xmlUnique(xmlNodeListGetString(doc, child->xmlChildrenNode, 1));
639             if (indexMaxXml == nullptr) {
640                 return BAD_VALUE;
641             }
642             std::string indexMaxLiteral(reinterpret_cast<const char*>(indexMaxXml.get()));
643             if (!convertTo(indexMaxLiteral, indexMax)) {
644                 return BAD_VALUE;
645             }
646         }
647     }
648     deserializeAttributesCollection(doc, root, attributesVect);
649 
650     std::string streamNames;
651     for (const auto &stream : streams) {
652         streamNames += android::toString(stream) + " ";
653     }
654     std::string attrmNames;
655     for (const auto &attr : attributesVect) {
656         attrmNames += android::toString(attr) + "\n";
657     }
658     ALOGV("%s: group=%s indexMin=%d, indexMax=%d streams=%s attributes=%s",
659           __func__, name.c_str(), indexMin, indexMax, streamNames.c_str(), attrmNames.c_str( ));
660 
661     VolumeCurves groupVolumeCurves;
662     size_t skipped = 0;
663     deserializeCollection<VolumeTraits>(doc, root, groupVolumeCurves, skipped);
664     volumes.push_back({ name, indexMin, indexMax, groupVolumeCurves });
665     return NO_ERROR;
666 }
667 
668 static constexpr const char *legacyVolumecollectionTag = "volumes";
669 static constexpr const char *legacyVolumeTag = "volume";
670 
deserializeLegacyVolume(_xmlDoc * doc,const _xmlNode * cur,std::map<std::string,VolumeCurves> & legacyVolumes)671 status_t deserializeLegacyVolume(_xmlDoc *doc, const _xmlNode *cur,
672                                  std::map<std::string, VolumeCurves> &legacyVolumes)
673 {
674     std::string streamTypeLiteral = getXmlAttribute(cur, "stream");
675     if (streamTypeLiteral.empty()) {
676         ALOGE("%s: No attribute stream found", __func__);
677         return BAD_VALUE;
678     }
679     std::string deviceCategoryLiteral = getXmlAttribute(cur, "deviceCategory");
680     if (deviceCategoryLiteral.empty()) {
681         ALOGE("%s: No attribute deviceCategory found", __func__);
682         return BAD_VALUE;
683     }
684     std::string referenceName = getXmlAttribute(cur, "ref");
685     const xmlNode *ref = NULL;
686     if (!referenceName.empty()) {
687         getReference(xmlDocGetRootElement(doc), ref, referenceName, legacyVolumecollectionTag);
688         if (ref == NULL) {
689             ALOGE("%s: No reference Ptr found for %s", __func__, referenceName.c_str());
690             return BAD_VALUE;
691         }
692         ALOGV("%s: reference found for %s", __func__, referenceName.c_str());
693     }
694     CurvePoints curvePoints;
695     for (const xmlNode *child = referenceName.empty() ?
696          cur->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) {
697         if (!xmlStrcmp(child->name, (const xmlChar *)VolumeTraits::volumePointTag)) {
698             auto pointXml = make_xmlUnique(xmlNodeListGetString(doc, child->xmlChildrenNode, 1));
699             if (pointXml == NULL) {
700                 return BAD_VALUE;
701             }
702             ALOGV("%s: %s=%s", __func__, legacyVolumeTag,
703                   reinterpret_cast<const char*>(pointXml.get()));
704             std::vector<int> point;
705             collectionFromString<DefaultTraits<int>>(
706                         reinterpret_cast<const char*>(pointXml.get()), point, ",");
707             if (point.size() != 2) {
708                 ALOGE("%s: Invalid %s: %s", __func__, VolumeTraits::volumePointTag,
709                       reinterpret_cast<const char*>(pointXml.get()));
710                 return BAD_VALUE;
711             }
712             curvePoints.push_back({point[0], point[1]});
713         }
714     }
715     legacyVolumes[streamTypeLiteral].push_back({ deviceCategoryLiteral, curvePoints });
716     return NO_ERROR;
717 }
718 
deserializeLegacyVolumeCollection(_xmlDoc * doc,const _xmlNode * cur,VolumeGroups & volumeGroups,size_t & nbSkippedElement)719 static status_t deserializeLegacyVolumeCollection(_xmlDoc *doc, const _xmlNode *cur,
720                                                   VolumeGroups &volumeGroups,
721                                                   size_t &nbSkippedElement)
722 {
723     std::map<std::string, VolumeCurves> legacyVolumeMap;
724     for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
725         if (xmlStrcmp(cur->name, (const xmlChar *)legacyVolumecollectionTag)) {
726             continue;
727         }
728         const xmlNode *child = cur->xmlChildrenNode;
729         for (; child != NULL; child = child->next) {
730             if (!xmlStrcmp(child->name, (const xmlChar *)legacyVolumeTag)) {
731 
732                 status_t status = deserializeLegacyVolume(doc, child, legacyVolumeMap);
733                 if (status != NO_ERROR) {
734                     nbSkippedElement += 1;
735                 }
736             }
737         }
738     }
739     VolumeGroups tempVolumeGroups = volumeGroups;
740     for (const auto &volumeMapIter : legacyVolumeMap) {
741         // In order to let AudioService setting the min and max (compatibility), set Min and Max
742         // to -1 except for private streams
743         audio_stream_type_t streamType;
744         if (!StreamTypeConverter::fromString(volumeMapIter.first, streamType)) {
745             ALOGE("%s: Invalid stream %s", __func__, volumeMapIter.first.c_str());
746             return BAD_VALUE;
747         }
748         int indexMin = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 0 : -1;
749         int indexMax = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 100 : -1;
750         tempVolumeGroups.push_back(
751                 { volumeMapIter.first, indexMin, indexMax, volumeMapIter.second });
752     }
753     std::swap(tempVolumeGroups, volumeGroups);
754     return NO_ERROR;
755 }
756 
757 namespace {
758 
759 class XmlErrorHandler {
760 public:
XmlErrorHandler()761     XmlErrorHandler() {
762         xmlSetGenericErrorFunc(this, &xmlErrorHandler);
763     }
764     XmlErrorHandler(const XmlErrorHandler&) = delete;
765     XmlErrorHandler(XmlErrorHandler&&) = delete;
766     XmlErrorHandler& operator=(const XmlErrorHandler&) = delete;
767     XmlErrorHandler& operator=(XmlErrorHandler&&) = delete;
~XmlErrorHandler()768     ~XmlErrorHandler() {
769         xmlSetGenericErrorFunc(NULL, NULL);
770         if (!mErrorMessage.empty()) {
771             ALOG(LOG_ERROR, "libxml2", "%s", mErrorMessage.c_str());
772         }
773     }
xmlErrorHandler(void * ctx,const char * msg,...)774     static void xmlErrorHandler(void* ctx, const char* msg, ...) {
775         char buffer[256];
776         va_list args;
777         va_start(args, msg);
778         vsnprintf(buffer, sizeof(buffer), msg, args);
779         va_end(args);
780         static_cast<XmlErrorHandler*>(ctx)->mErrorMessage += buffer;
781     }
782 private:
783     std::string mErrorMessage;
784 };
785 
786 }  // namespace
787 
parse(const char * path,bool isConfigurable)788 ParsingResult parse(const char* path, bool isConfigurable) {
789     XmlErrorHandler errorHandler;
790     auto doc = make_xmlUnique(xmlParseFile(path));
791     if (doc == NULL) {
792         // It is OK not to find an engine config file at the default location
793         // as the caller will default to hardcoded default config
794         if (strncmp(path, DEFAULT_PATH, strlen(DEFAULT_PATH))) {
795             ALOGW("%s: Could not parse document %s", __FUNCTION__, path);
796         }
797         return {nullptr, 0};
798     }
799     xmlNodePtr cur = xmlDocGetRootElement(doc.get());
800     if (cur == NULL) {
801         ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path);
802         return {nullptr, 0};
803     }
804     if (xmlXIncludeProcess(doc.get()) < 0) {
805         ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path);
806         return {nullptr, 0};
807     }
808     std::string version = getXmlAttribute(cur, gVersionAttribute);
809     if (version.empty()) {
810         ALOGE("%s: No version found", __func__);
811         return {nullptr, 0};
812     }
813     gIsConfigurableEngine = isConfigurable;
814     size_t nbSkippedElements = 0;
815     auto config = std::make_unique<Config>();
816     config->version = std::stof(version);
817     deserializeCollection<ProductStrategyTraits>(
818                 doc.get(), cur, config->productStrategies, nbSkippedElements);
819     deserializeCollection<CriterionTraits>(
820                 doc.get(), cur, config->criteria, nbSkippedElements);
821     deserializeCollection<CriterionTypeTraits>(
822                 doc.get(), cur, config->criterionTypes, nbSkippedElements);
823     deserializeCollection<VolumeGroupTraits>(
824                 doc.get(), cur, config->volumeGroups, nbSkippedElements);
825 
826     return {std::move(config), nbSkippedElements};
827 }
828 
parseLegacyVolumeFile(const char * path,VolumeGroups & volumeGroups)829 android::status_t parseLegacyVolumeFile(const char* path, VolumeGroups &volumeGroups) {
830     XmlErrorHandler errorHandler;
831     auto doc = make_xmlUnique(xmlParseFile(path));
832     if (doc == NULL) {
833         ALOGE("%s: Could not parse document %s", __FUNCTION__, path);
834         return BAD_VALUE;
835     }
836     xmlNodePtr cur = xmlDocGetRootElement(doc.get());
837     if (cur == NULL) {
838         ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path);
839         return BAD_VALUE;
840     }
841     if (xmlXIncludeProcess(doc.get()) < 0) {
842         ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path);
843         return BAD_VALUE;
844     }
845     size_t nbSkippedElements = 0;
846     return deserializeLegacyVolumeCollection(doc.get(), cur, volumeGroups, nbSkippedElements);
847 }
848 
parseLegacyVolumes(VolumeGroups & volumeGroups)849 android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups) {
850     if (std::string audioPolicyXmlConfigFile = audio_get_audio_policy_config_file();
851             !audioPolicyXmlConfigFile.empty()) {
852         return parseLegacyVolumeFile(audioPolicyXmlConfigFile.c_str(), volumeGroups);
853     } else {
854         ALOGE("No readable audio policy config file found");
855         return BAD_VALUE;
856     }
857 }
858 
convert(const::android::media::audio::common::AudioHalEngineConfig & aidlConfig)859 ParsingResult convert(const ::android::media::audio::common::AudioHalEngineConfig& aidlConfig) {
860     auto config = std::make_unique<engineConfig::Config>();
861     config->version = 1.0f;
862     if (auto conv = convertContainer<engineConfig::ProductStrategies>(
863                     aidlConfig.productStrategies,
864                     aidl2legacy_AudioHalProductStrategy_ProductStrategy); conv.ok()) {
865         config->productStrategies = std::move(conv.value());
866     } else {
867         return ParsingResult{};
868     }
869     if (auto conv = convertContainer<engineConfig::VolumeGroups>(
870                     aidlConfig.volumeGroups,
871                     aidl2legacy_AudioHalVolumeGroup_VolumeGroup); conv.ok()) {
872         config->volumeGroups = std::move(conv.value());
873     } else {
874         return ParsingResult{};
875     }
876     return {.parsedConfig=std::move(config), .nbSkippedElement=0};
877  }
878 
879 } // namespace engineConfig
880 } // namespace android
881