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