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 #define LOG_TAG "APM::AudioPolicyEngine/Config"
18 //#define LOG_NDEBUG 0
19 
20 #include "EngineConfig.h"
21 #include <cutils/properties.h>
22 #include <media/TypeConverter.h>
23 #include <media/convert.h>
24 #include <system/audio_config.h>
25 #include <utils/Log.h>
26 #include <libxml/parser.h>
27 #include <libxml/xinclude.h>
28 #include <string>
29 #include <vector>
30 #include <map>
31 #include <sstream>
32 #include <istream>
33 
34 #include <cstdint>
35 #include <stdarg.h>
36 #include <string>
37 
38 namespace android {
39 
40 using utilities::convertTo;
41 
42 namespace engineConfig {
43 
44 static constexpr const char *gVersionAttribute = "version";
45 static const char *const gReferenceElementName = "reference";
46 static const char *const gReferenceAttributeName = "name";
47 
48 template<typename E, typename C>
49 struct BaseSerializerTraits {
50     typedef E Element;
51     typedef C Collection;
52     typedef void* PtrSerializingCtx;
53 };
54 
55 struct AttributesGroupTraits : public BaseSerializerTraits<AttributesGroup, AttributesGroups> {
56     static constexpr const char *tag = "AttributesGroup";
57     static constexpr const char *collectionTag = "AttributesGroups";
58 
59     struct Attributes {
60         static constexpr const char *name = "name";
61         static constexpr const char *streamType = "streamType";
62         static constexpr const char *volumeGroup = "volumeGroup";
63     };
64     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &ps);
65 };
66 
67 struct ProductStrategyTraits : public BaseSerializerTraits<ProductStrategy, ProductStrategies> {
68     static constexpr const char *tag = "ProductStrategy";
69     static constexpr const char *collectionTag = "ProductStrategies";
70 
71     struct Attributes {
72         static constexpr const char *name = "name";
73     };
74     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &ps);
75 };
76 struct ValueTraits : public BaseSerializerTraits<ValuePair, ValuePairs> {
77     static constexpr const char *tag = "value";
78     static constexpr const char *collectionTag = "values";
79 
80     struct Attributes {
81         static constexpr const char *literal = "literal";
82         static constexpr const char *numerical = "numerical";
83     };
84 
85     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
86                                          Collection &collection);
87 };
88 struct CriterionTypeTraits : public BaseSerializerTraits<CriterionType, CriterionTypes> {
89     static constexpr const char *tag = "criterion_type";
90     static constexpr const char *collectionTag = "criterion_types";
91 
92     struct Attributes {
93         static constexpr const char *name = "name";
94         static constexpr const char *type = "type";
95     };
96 
97     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
98                                          Collection &collection);
99 };
100 struct CriterionTraits : public BaseSerializerTraits<Criterion, Criteria> {
101     static constexpr const char *tag = "criterion";
102     static constexpr const char *collectionTag = "criteria";
103 
104     struct Attributes {
105         static constexpr const char *name = "name";
106         static constexpr const char *type = "type";
107         static constexpr const char *defaultVal = "default";
108     };
109 
110     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
111                                          Collection &collection);
112 };
113 struct VolumeTraits : public BaseSerializerTraits<VolumeCurve, VolumeCurves> {
114     static constexpr const char *tag = "volume";
115     static constexpr const char *collectionTag = "volumes";
116     static constexpr const char *volumePointTag = "point";
117 
118     struct Attributes {
119         static constexpr const char *deviceCategory = "deviceCategory";
120         static constexpr const char *stream = "stream"; // For legacy volume curves
121         static constexpr const char *reference = "ref"; /**< For volume curves factorization. */
122     };
123 
124     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
125                                          Collection &collection);
126 };
127 struct VolumeGroupTraits : public BaseSerializerTraits<VolumeGroup, VolumeGroups> {
128     static constexpr const char *tag = "volumeGroup";
129     static constexpr const char *collectionTag = "volumeGroups";
130 
131     struct Attributes {
132         static constexpr const char *name = "name";
133         static constexpr const char *stream = "stream"; // For legacy volume curves
134         static constexpr const char *indexMin = "indexMin";
135         static constexpr const char *indexMax = "indexMax";
136     };
137 
138     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
139                                          Collection &collection);
140 };
141 
142 using xmlCharUnique = std::unique_ptr<xmlChar, decltype(xmlFree)>;
143 
getXmlAttribute(const xmlNode * cur,const char * attribute)144 std::string getXmlAttribute(const xmlNode *cur, const char *attribute)
145 {
146     xmlCharUnique charPtr(xmlGetProp(cur, reinterpret_cast<const xmlChar *>(attribute)), xmlFree);
147     if (charPtr == NULL) {
148         return "";
149     }
150     std::string value(reinterpret_cast<const char*>(charPtr.get()));
151     return value;
152 }
153 
getReference(const _xmlNode * root,const _xmlNode * & refNode,const std::string & refName,const char * collectionTag)154 static void getReference(const _xmlNode *root, const _xmlNode *&refNode, const std::string &refName,
155                          const char *collectionTag)
156 {
157     for (root = root->xmlChildrenNode; root != NULL; root = root->next) {
158         if (!xmlStrcmp(root->name, (const xmlChar *)collectionTag)) {
159             for (xmlNode *cur = root->xmlChildrenNode; cur != NULL; cur = cur->next) {
160                 if ((!xmlStrcmp(cur->name, (const xmlChar *)gReferenceElementName))) {
161                     std::string name = getXmlAttribute(cur, gReferenceAttributeName);
162                     if (refName == name) {
163                         refNode = cur;
164                         return;
165                     }
166                 }
167             }
168         }
169     }
170     return;
171 }
172 
173 template <class Trait>
deserializeCollection(_xmlDoc * doc,const _xmlNode * cur,typename Trait::Collection & collection,size_t & nbSkippedElement)174 static status_t deserializeCollection(_xmlDoc *doc, const _xmlNode *cur,
175                                       typename Trait::Collection &collection,
176                                       size_t &nbSkippedElement)
177 {
178     for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
179         if (xmlStrcmp(cur->name, (const xmlChar *)Trait::collectionTag) &&
180             xmlStrcmp(cur->name, (const xmlChar *)Trait::tag)) {
181             continue;
182         }
183         const xmlNode *child = cur;
184         if (!xmlStrcmp(child->name, (const xmlChar *)Trait::collectionTag)) {
185             child = child->xmlChildrenNode;
186         }
187         for (; child != NULL; child = child->next) {
188             if (!xmlStrcmp(child->name, (const xmlChar *)Trait::tag)) {
189                 status_t status = Trait::deserialize(doc, child, collection);
190                 if (status != NO_ERROR) {
191                     nbSkippedElement += 1;
192                 }
193             }
194         }
195         if (!xmlStrcmp(cur->name, (const xmlChar *)Trait::tag)) {
196             return NO_ERROR;
197         }
198     }
199     return NO_ERROR;
200 }
201 
202 static constexpr const char *attributesAttributeRef = "attributesRef"; /**< for factorization. */
203 
parseAttributes(const _xmlNode * cur,audio_attributes_t & attributes)204 static status_t parseAttributes(const _xmlNode *cur, audio_attributes_t &attributes)
205 {
206     for (; cur != NULL; cur = cur->next) {
207         if (!xmlStrcmp(cur->name, (const xmlChar *)("ContentType"))) {
208             std::string contentTypeXml = getXmlAttribute(cur, "value");
209             audio_content_type_t contentType;
210             if (not AudioContentTypeConverter::fromString(contentTypeXml.c_str(), contentType)) {
211                 ALOGE("Invalid content type %s", contentTypeXml.c_str());
212                 return BAD_VALUE;
213             }
214             attributes.content_type = contentType;
215             ALOGV("%s content type %s",  __FUNCTION__, contentTypeXml.c_str());
216         }
217         if (!xmlStrcmp(cur->name, (const xmlChar *)("Usage"))) {
218             std::string usageXml = getXmlAttribute(cur, "value");
219             audio_usage_t usage;
220             if (not UsageTypeConverter::fromString(usageXml.c_str(), usage)) {
221                 ALOGE("Invalid usage %s", usageXml.c_str());
222                 return BAD_VALUE;
223             }
224             attributes.usage = usage;
225             ALOGV("%s usage %s",  __FUNCTION__, usageXml.c_str());
226         }
227         if (!xmlStrcmp(cur->name, (const xmlChar *)("Flags"))) {
228             std::string flags = getXmlAttribute(cur, "value");
229 
230             ALOGV("%s flags %s",  __FUNCTION__, flags.c_str());
231             attributes.flags = AudioFlagConverter::maskFromString(flags, " ");
232         }
233         if (!xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) {
234             std::string bundleKey = getXmlAttribute(cur, "key");
235             std::string bundleValue = getXmlAttribute(cur, "value");
236 
237             ALOGV("%s Bundle %s %s",  __FUNCTION__, bundleKey.c_str(), bundleValue.c_str());
238 
239             std::string tags(bundleKey + "=" + bundleValue);
240             std::strncpy(attributes.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
241         }
242     }
243     return NO_ERROR;
244 }
245 
deserializeAttributes(_xmlDoc * doc,const _xmlNode * cur,audio_attributes_t & attributes)246 static status_t deserializeAttributes(_xmlDoc *doc, const _xmlNode *cur,
247                                       audio_attributes_t &attributes) {
248     // Retrieve content type, usage, flags, and bundle from xml
249     for (; cur != NULL; cur = cur->next) {
250         if (not xmlStrcmp(cur->name, (const xmlChar *)("Attributes"))) {
251             const xmlNode *attrNode = cur;
252             std::string attrRef = getXmlAttribute(cur, attributesAttributeRef);
253             if (!attrRef.empty()) {
254                 getReference(xmlDocGetRootElement(doc), attrNode, attrRef, attributesAttributeRef);
255                 if (attrNode == NULL) {
256                     ALOGE("%s: No reference found for %s", __FUNCTION__, attrRef.c_str());
257                     return BAD_VALUE;
258                 }
259                 return deserializeAttributes(doc, attrNode->xmlChildrenNode, attributes);
260             }
261             return parseAttributes(attrNode->xmlChildrenNode, attributes);
262         }
263         if (not xmlStrcmp(cur->name, (const xmlChar *)("ContentType")) ||
264                 not xmlStrcmp(cur->name, (const xmlChar *)("Usage")) ||
265                 not xmlStrcmp(cur->name, (const xmlChar *)("Flags")) ||
266                 not xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) {
267             return parseAttributes(cur, attributes);
268         }
269     }
270     return BAD_VALUE;
271 }
272 
deserializeAttributesCollection(_xmlDoc * doc,const _xmlNode * cur,AttributesVector & collection)273 static status_t deserializeAttributesCollection(_xmlDoc *doc, const _xmlNode *cur,
274                                                 AttributesVector &collection)
275 {
276     status_t ret = BAD_VALUE;
277     // Either we do provide only one attributes or a collection of supported attributes
278     for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
279         if (not xmlStrcmp(cur->name, (const xmlChar *)("Attributes")) ||
280                 not xmlStrcmp(cur->name, (const xmlChar *)("ContentType")) ||
281                 not xmlStrcmp(cur->name, (const xmlChar *)("Usage")) ||
282                 not xmlStrcmp(cur->name, (const xmlChar *)("Flags")) ||
283                 not xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) {
284             audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
285             ret = deserializeAttributes(doc, cur, attributes);
286             if (ret == NO_ERROR) {
287                 collection.push_back(attributes);
288                 // We are done if the "Attributes" balise is omitted, only one Attributes is allowed
289                 if (xmlStrcmp(cur->name, (const xmlChar *)("Attributes"))) {
290                     return ret;
291                 }
292             }
293         }
294     }
295     return ret;
296 }
297 
deserialize(_xmlDoc * doc,const _xmlNode * child,Collection & attributesGroup)298 status_t AttributesGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
299                                             Collection &attributesGroup)
300 {
301     std::string name = getXmlAttribute(child, Attributes::name);
302     if (name.empty()) {
303         ALOGV("AttributesGroupTraits No attribute %s found", Attributes::name);
304     }
305     ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str());
306 
307     std::string volumeGroup = getXmlAttribute(child, Attributes::volumeGroup);
308     if (volumeGroup.empty()) {
309         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::volumeGroup);
310     }
311     ALOGV("%s: %s = %s", __FUNCTION__, Attributes::volumeGroup, volumeGroup.c_str());
312 
313     audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT;
314     std::string streamTypeXml = getXmlAttribute(child, Attributes::streamType);
315     if (streamTypeXml.empty()) {
316         ALOGV("%s: No attribute %s found", __FUNCTION__, Attributes::streamType);
317     } else {
318         ALOGV("%s: %s = %s", __FUNCTION__, Attributes::streamType, streamTypeXml.c_str());
319         if (not StreamTypeConverter::fromString(streamTypeXml.c_str(), streamType)) {
320             ALOGE("Invalid stream type %s", streamTypeXml.c_str());
321             return BAD_VALUE;
322         }
323     }
324     AttributesVector attributesVect;
325     deserializeAttributesCollection(doc, child, attributesVect);
326 
327     attributesGroup.push_back({name, streamType, volumeGroup, attributesVect});
328     return NO_ERROR;
329 }
330 
deserialize(_xmlDoc *,const _xmlNode * child,Collection & values)331 status_t ValueTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child, Collection &values)
332 {
333     std::string literal = getXmlAttribute(child, Attributes::literal);
334     if (literal.empty()) {
335         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal);
336         return BAD_VALUE;
337     }
338     uint32_t numerical = 0;
339     std::string numericalTag = getXmlAttribute(child, Attributes::numerical);
340     if (numericalTag.empty()) {
341         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal);
342         return BAD_VALUE;
343     }
344     if (!convertTo(numericalTag, numerical)) {
345         ALOGE("%s: : Invalid value(%s)", __FUNCTION__, numericalTag.c_str());
346         return BAD_VALUE;
347     }
348     values.push_back({numerical, literal});
349     return NO_ERROR;
350 }
351 
deserialize(_xmlDoc * doc,const _xmlNode * child,Collection & criterionTypes)352 status_t CriterionTypeTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
353                                           Collection &criterionTypes)
354 {
355     std::string name = getXmlAttribute(child, Attributes::name);
356     if (name.empty()) {
357         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name);
358         return BAD_VALUE;
359     }
360     ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::name, name.c_str());
361 
362     std::string type = getXmlAttribute(child, Attributes::type);
363     if (type.empty()) {
364         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::type);
365         return BAD_VALUE;
366     }
367     ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::type, type.c_str());
368     bool isInclusive(type == "inclusive");
369 
370     ValuePairs pairs;
371     size_t nbSkippedElements = 0;
372     deserializeCollection<ValueTraits>(doc, child, pairs, nbSkippedElements);
373     criterionTypes.push_back({name, isInclusive, pairs});
374     return NO_ERROR;
375 }
376 
deserialize(_xmlDoc *,const _xmlNode * child,Collection & criteria)377 status_t CriterionTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child,
378                                       Collection &criteria)
379 {
380     std::string name = getXmlAttribute(child, Attributes::name);
381     if (name.empty()) {
382         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name);
383         return BAD_VALUE;
384     }
385     ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str());
386 
387     std::string defaultValue = getXmlAttribute(child, Attributes::defaultVal);
388     if (defaultValue.empty()) {
389         // Not mandatory to provide a default value for a criterion, even it is recommanded...
390         ALOGV("%s: No attribute %s found (but recommanded)", __FUNCTION__, Attributes::defaultVal);
391     }
392     ALOGV("%s: %s = %s", __FUNCTION__, Attributes::defaultVal, defaultValue.c_str());
393 
394     std::string typeName = getXmlAttribute(child, Attributes::type);
395     if (typeName.empty()) {
396         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name);
397         return BAD_VALUE;
398     }
399     ALOGV("%s: %s = %s", __FUNCTION__, Attributes::type, typeName.c_str());
400 
401     criteria.push_back({name, typeName, defaultValue});
402     return NO_ERROR;
403 }
404 
deserialize(_xmlDoc * doc,const _xmlNode * child,Collection & strategies)405 status_t ProductStrategyTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
406                                             Collection &strategies)
407 {
408     std::string name = getXmlAttribute(child, Attributes::name);
409     if (name.empty()) {
410         ALOGE("ProductStrategyTraits No attribute %s found", Attributes::name);
411         return BAD_VALUE;
412     }
413     ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str());
414 
415     size_t skipped = 0;
416     AttributesGroups attrGroups;
417     deserializeCollection<AttributesGroupTraits>(doc, child, attrGroups, skipped);
418 
419     strategies.push_back({name, attrGroups});
420     return NO_ERROR;
421 }
422 
deserialize(_xmlDoc * doc,const _xmlNode * root,Collection & volumes)423 status_t VolumeTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes)
424 {
425     std::string deviceCategory = getXmlAttribute(root, Attributes::deviceCategory);
426     if (deviceCategory.empty()) {
427         ALOGW("%s: No %s found", __FUNCTION__, Attributes::deviceCategory);
428     }
429     std::string referenceName = getXmlAttribute(root, Attributes::reference);
430     const _xmlNode *ref = NULL;
431     if (!referenceName.empty()) {
432         getReference(xmlDocGetRootElement(doc), ref, referenceName, collectionTag);
433         if (ref == NULL) {
434             ALOGE("%s: No reference Ptr found for %s", __FUNCTION__, referenceName.c_str());
435             return BAD_VALUE;
436         }
437     }
438     // Retrieve curve point from reference element if found or directly from current curve
439     CurvePoints curvePoints;
440     for (const xmlNode *child = referenceName.empty() ?
441          root->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) {
442         if (!xmlStrcmp(child->name, (const xmlChar *)volumePointTag)) {
443             xmlCharUnique pointXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
444             if (pointXml == NULL) {
445                 return BAD_VALUE;
446             }
447             ALOGV("%s: %s=%s", __func__, tag, reinterpret_cast<const char*>(pointXml.get()));
448             std::vector<int> point;
449             collectionFromString<DefaultTraits<int>>(
450                         reinterpret_cast<const char*>(pointXml.get()), point, ",");
451             if (point.size() != 2) {
452                 ALOGE("%s: Invalid %s: %s", __func__, volumePointTag,
453                       reinterpret_cast<const char*>(pointXml.get()));
454                 return BAD_VALUE;
455             }
456             curvePoints.push_back({point[0], point[1]});
457         }
458     }
459     volumes.push_back({ deviceCategory, curvePoints });
460     return NO_ERROR;
461 }
462 
deserialize(_xmlDoc * doc,const _xmlNode * root,Collection & volumes)463 status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes)
464 {
465     std::string name;
466     int indexMin = 0;
467     int indexMax = 0;
468     StreamVector streams = {};
469     AttributesVector attributesVect = {};
470 
471     for (const xmlNode *child = root->xmlChildrenNode; child != NULL; child = child->next) {
472         if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::name)) {
473             xmlCharUnique nameXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
474             if (nameXml == nullptr) {
475                 return BAD_VALUE;
476             }
477             name = reinterpret_cast<const char*>(nameXml.get());
478         }
479         if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMin)) {
480             xmlCharUnique indexMinXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
481             if (indexMinXml == nullptr) {
482                 return BAD_VALUE;
483             }
484             std::string indexMinLiteral(reinterpret_cast<const char*>(indexMinXml.get()));
485             if (!convertTo(indexMinLiteral, indexMin)) {
486                 return BAD_VALUE;
487             }
488         }
489         if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMax)) {
490             xmlCharUnique indexMaxXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
491             if (indexMaxXml == nullptr) {
492                 return BAD_VALUE;
493             }
494             std::string indexMaxLiteral(reinterpret_cast<const char*>(indexMaxXml.get()));
495             if (!convertTo(indexMaxLiteral, indexMax)) {
496                 return BAD_VALUE;
497             }
498         }
499     }
500     deserializeAttributesCollection(doc, root, attributesVect);
501 
502     std::string streamNames;
503     for (const auto &stream : streams) {
504         streamNames += android::toString(stream) + " ";
505     }
506     std::string attrmNames;
507     for (const auto &attr : attributesVect) {
508         attrmNames += android::toString(attr) + "\n";
509     }
510     ALOGV("%s: group=%s indexMin=%d, indexMax=%d streams=%s attributes=%s",
511           __func__, name.c_str(), indexMin, indexMax, streamNames.c_str(), attrmNames.c_str( ));
512 
513     VolumeCurves groupVolumeCurves;
514     size_t skipped = 0;
515     deserializeCollection<VolumeTraits>(doc, root, groupVolumeCurves, skipped);
516     volumes.push_back({ name, indexMin, indexMax, groupVolumeCurves });
517     return NO_ERROR;
518 }
519 
520 static constexpr const char *legacyVolumecollectionTag = "volumes";
521 static constexpr const char *legacyVolumeTag = "volume";
522 
deserializeLegacyVolume(_xmlDoc * doc,const _xmlNode * cur,std::map<std::string,VolumeCurves> & legacyVolumes)523 status_t deserializeLegacyVolume(_xmlDoc *doc, const _xmlNode *cur,
524                                  std::map<std::string, VolumeCurves> &legacyVolumes)
525 {
526     std::string streamTypeLiteral = getXmlAttribute(cur, "stream");
527     if (streamTypeLiteral.empty()) {
528         ALOGE("%s: No attribute stream found", __func__);
529         return BAD_VALUE;
530     }
531     std::string deviceCategoryLiteral = getXmlAttribute(cur, "deviceCategory");
532     if (deviceCategoryLiteral.empty()) {
533         ALOGE("%s: No attribute deviceCategory found", __func__);
534         return BAD_VALUE;
535     }
536     std::string referenceName = getXmlAttribute(cur, "ref");
537     const xmlNode *ref = NULL;
538     if (!referenceName.empty()) {
539         getReference(xmlDocGetRootElement(doc), ref, referenceName, legacyVolumecollectionTag);
540         if (ref == NULL) {
541             ALOGE("%s: No reference Ptr found for %s", __func__, referenceName.c_str());
542             return BAD_VALUE;
543         }
544         ALOGV("%s: reference found for %s", __func__, referenceName.c_str());
545     }
546     CurvePoints curvePoints;
547     for (const xmlNode *child = referenceName.empty() ?
548          cur->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) {
549         if (!xmlStrcmp(child->name, (const xmlChar *)VolumeTraits::volumePointTag)) {
550             xmlCharUnique pointXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
551             if (pointXml == NULL) {
552                 return BAD_VALUE;
553             }
554             ALOGV("%s: %s=%s", __func__, legacyVolumeTag,
555                   reinterpret_cast<const char*>(pointXml.get()));
556             std::vector<int> point;
557             collectionFromString<DefaultTraits<int>>(
558                         reinterpret_cast<const char*>(pointXml.get()), point, ",");
559             if (point.size() != 2) {
560                 ALOGE("%s: Invalid %s: %s", __func__, VolumeTraits::volumePointTag,
561                       reinterpret_cast<const char*>(pointXml.get()));
562                 return BAD_VALUE;
563             }
564             curvePoints.push_back({point[0], point[1]});
565         }
566     }
567     legacyVolumes[streamTypeLiteral].push_back({ deviceCategoryLiteral, curvePoints });
568     return NO_ERROR;
569 }
570 
deserializeLegacyVolumeCollection(_xmlDoc * doc,const _xmlNode * cur,VolumeGroups & volumeGroups,size_t & nbSkippedElement)571 static status_t deserializeLegacyVolumeCollection(_xmlDoc *doc, const _xmlNode *cur,
572                                                   VolumeGroups &volumeGroups,
573                                                   size_t &nbSkippedElement)
574 {
575     std::map<std::string, VolumeCurves> legacyVolumeMap;
576     for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
577         if (xmlStrcmp(cur->name, (const xmlChar *)legacyVolumecollectionTag)) {
578             continue;
579         }
580         const xmlNode *child = cur->xmlChildrenNode;
581         for (; child != NULL; child = child->next) {
582             if (!xmlStrcmp(child->name, (const xmlChar *)legacyVolumeTag)) {
583 
584                 status_t status = deserializeLegacyVolume(doc, child, legacyVolumeMap);
585                 if (status != NO_ERROR) {
586                     nbSkippedElement += 1;
587                 }
588             }
589         }
590     }
591     for (const auto &volumeMapIter : legacyVolumeMap) {
592         // In order to let AudioService setting the min and max (compatibility), set Min and Max
593         // to -1 except for private streams
594         audio_stream_type_t streamType;
595         if (!StreamTypeConverter::fromString(volumeMapIter.first, streamType)) {
596             ALOGE("%s: Invalid stream %s", __func__, volumeMapIter.first.c_str());
597             return BAD_VALUE;
598         }
599         int indexMin = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 0 : -1;
600         int indexMax = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 100 : -1;
601         volumeGroups.push_back({ volumeMapIter.first, indexMin, indexMax, volumeMapIter.second });
602     }
603     return NO_ERROR;
604 }
605 
606 namespace {
607 
608 class XmlErrorHandler {
609 public:
XmlErrorHandler()610     XmlErrorHandler() {
611         xmlSetGenericErrorFunc(this, &xmlErrorHandler);
612     }
613     XmlErrorHandler(const XmlErrorHandler&) = delete;
614     XmlErrorHandler(XmlErrorHandler&&) = delete;
615     XmlErrorHandler& operator=(const XmlErrorHandler&) = delete;
616     XmlErrorHandler& operator=(XmlErrorHandler&&) = delete;
~XmlErrorHandler()617     ~XmlErrorHandler() {
618         xmlSetGenericErrorFunc(NULL, NULL);
619         if (!mErrorMessage.empty()) {
620             ALOG(LOG_ERROR, "libxml2", "%s", mErrorMessage.c_str());
621         }
622     }
xmlErrorHandler(void * ctx,const char * msg,...)623     static void xmlErrorHandler(void* ctx, const char* msg, ...) {
624         char buffer[256];
625         va_list args;
626         va_start(args, msg);
627         vsnprintf(buffer, sizeof(buffer), msg, args);
628         va_end(args);
629         static_cast<XmlErrorHandler*>(ctx)->mErrorMessage += buffer;
630     }
631 private:
632     std::string mErrorMessage;
633 };
634 
635 }  // namespace
636 
parse(const char * path)637 ParsingResult parse(const char* path) {
638     XmlErrorHandler errorHandler;
639     xmlDocPtr doc;
640     doc = xmlParseFile(path);
641     if (doc == NULL) {
642         ALOGE("%s: Could not parse document %s", __FUNCTION__, path);
643         return {nullptr, 0};
644     }
645     xmlNodePtr cur = xmlDocGetRootElement(doc);
646     if (cur == NULL) {
647         ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path);
648         xmlFreeDoc(doc);
649         return {nullptr, 0};
650     }
651     if (xmlXIncludeProcess(doc) < 0) {
652         ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path);
653         return {nullptr, 0};
654     }
655     std::string version = getXmlAttribute(cur, gVersionAttribute);
656     if (version.empty()) {
657         ALOGE("%s: No version found", __func__);
658         return {nullptr, 0};
659     }
660     size_t nbSkippedElements = 0;
661     auto config = std::make_unique<Config>();
662     config->version = std::stof(version);
663     deserializeCollection<ProductStrategyTraits>(
664                 doc, cur, config->productStrategies, nbSkippedElements);
665     deserializeCollection<CriterionTraits>(
666                 doc, cur, config->criteria, nbSkippedElements);
667     deserializeCollection<CriterionTypeTraits>(
668                 doc, cur, config->criterionTypes, nbSkippedElements);
669     deserializeCollection<VolumeGroupTraits>(
670                 doc, cur, config->volumeGroups, nbSkippedElements);
671 
672     return {std::move(config), nbSkippedElements};
673 }
674 
parseLegacyVolumeFile(const char * path,VolumeGroups & volumeGroups)675 android::status_t parseLegacyVolumeFile(const char* path, VolumeGroups &volumeGroups) {
676     XmlErrorHandler errorHandler;
677     xmlDocPtr doc;
678     doc = xmlParseFile(path);
679     if (doc == NULL) {
680         ALOGE("%s: Could not parse document %s", __FUNCTION__, path);
681         return BAD_VALUE;
682     }
683     xmlNodePtr cur = xmlDocGetRootElement(doc);
684     if (cur == NULL) {
685         ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path);
686         xmlFreeDoc(doc);
687         return BAD_VALUE;
688     }
689     if (xmlXIncludeProcess(doc) < 0) {
690         ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path);
691         return BAD_VALUE;
692     }
693     size_t nbSkippedElements = 0;
694     return deserializeLegacyVolumeCollection(doc, cur, volumeGroups, nbSkippedElements);
695 }
696 
697 static const int gApmXmlConfigFilePathMaxLength = 128;
698 
699 static constexpr const char *apmXmlConfigFileName = "audio_policy_configuration.xml";
700 static constexpr const char *apmA2dpOffloadDisabledXmlConfigFileName =
701         "audio_policy_configuration_a2dp_offload_disabled.xml";
702 
parseLegacyVolumes(VolumeGroups & volumeGroups)703 android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups) {
704     char audioPolicyXmlConfigFile[gApmXmlConfigFilePathMaxLength];
705     std::vector<const char *> fileNames;
706     status_t ret;
707 
708     if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false) &&
709             property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
710         // A2DP offload supported but disabled: try to use special XML file
711         fileNames.push_back(apmA2dpOffloadDisabledXmlConfigFileName);
712     }
713     fileNames.push_back(apmXmlConfigFileName);
714 
715     for (const char* fileName : fileNames) {
716         for (const auto& path : audio_get_configuration_paths()) {
717             snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),
718                      "%s/%s", path.c_str(), fileName);
719             ret = parseLegacyVolumeFile(audioPolicyXmlConfigFile, volumeGroups);
720             if (ret == NO_ERROR) {
721                 return ret;
722             }
723         }
724     }
725     return BAD_VALUE;
726 }
727 
728 } // namespace engineConfig
729 } // namespace android
730