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