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