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