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