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