blob: bc6d046f31a5ab5f513cf26e25043c1c98798f4c [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "APM::AudioPolicyEngine/PFWWrapperConfig"
#define LOG_NDEBUG 0
#include "ParameterManagerWrapperConfig.h"
#include <media/convert.h>
#include <utils/Log.h>
#include <libxml/parser.h>
#include <libxml/xinclude.h>
#include <string>
#include <vector>
#include <sstream>
#include <istream>
namespace android {
using utilities::convertTo;
namespace audio_policy {
namespace wrapper_config {
namespace detail {
std::string getXmlAttribute(const xmlNode *cur, const char *attribute)
{
xmlChar *xmlValue = xmlGetProp(cur, (const xmlChar *)attribute);
if (xmlValue == NULL) {
return "";
}
std::string value((const char *)xmlValue);
xmlFree(xmlValue);
return value;
}
template <class Trait>
static status_t deserializeCollection(_xmlDoc *doc, const _xmlNode *cur,
typename Trait::Collection &collection,
size_t &nbSkippedElement)
{
const xmlNode *root = cur->xmlChildrenNode;
while (root != NULL) {
if (xmlStrcmp(root->name, (const xmlChar *)Trait::collectionTag) &&
xmlStrcmp(root->name, (const xmlChar *)Trait::tag)) {
root = root->next;
continue;
}
const xmlNode *child = root;
if (!xmlStrcmp(child->name, (const xmlChar *)Trait::collectionTag)) {
child = child->xmlChildrenNode;
}
while (child != NULL) {
if (!xmlStrcmp(child->name, (const xmlChar *)Trait::tag)) {
status_t status = Trait::deserialize(doc, child, collection);
if (status == NO_ERROR) {
nbSkippedElement += 1;
}
}
child = child->next;
}
if (!xmlStrcmp(root->name, (const xmlChar *)Trait::tag)) {
return NO_ERROR;
}
root = root->next;
}
return NO_ERROR;
}
const char *const ValueTraits::tag = "value";
const char *const ValueTraits::collectionTag = "values";
const char ValueTraits::Attributes::literal[] = "literal";
const char ValueTraits::Attributes::numerical[] = "numerical";
status_t ValueTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child, Collection &values)
{
std::string literal = getXmlAttribute(child, Attributes::literal);
if (literal.empty()) {
ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal);
return BAD_VALUE;
}
uint32_t numerical = 0;
std::string numericalTag = getXmlAttribute(child, Attributes::numerical);
if (numericalTag.empty()) {
ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal);
return BAD_VALUE;
}
if (!convertTo(numericalTag, numerical)) {
ALOGE("%s: : Invalid value(%s)", __FUNCTION__, numericalTag.c_str());
return BAD_VALUE;
}
values.push_back({numerical, literal});
return NO_ERROR;
}
const char *const CriterionTypeTraits::tag = "criterion_type";
const char *const CriterionTypeTraits::collectionTag = "criterion_types";
const char CriterionTypeTraits::Attributes::name[] = "name";
const char CriterionTypeTraits::Attributes::type[] = "type";
status_t CriterionTypeTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
Collection &criterionTypes)
{
std::string name = getXmlAttribute(child, Attributes::name);
if (name.empty()) {
ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name);
return BAD_VALUE;
}
ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::name, name.c_str());
std::string type = getXmlAttribute(child, Attributes::type);
if (type.empty()) {
ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::type);
return BAD_VALUE;
}
ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::type, type.c_str());
bool isInclusive(type == "inclusive");
ValuePairs pairs;
size_t nbSkippedElements = 0;
detail::deserializeCollection<detail::ValueTraits>(doc, child, pairs, nbSkippedElements);
criterionTypes.push_back({name, isInclusive, pairs});
return NO_ERROR;
}
const char *const CriterionTraits::tag = "criterion";
const char *const CriterionTraits::collectionTag = "criteria";
const char CriterionTraits::Attributes::name[] = "name";
const char CriterionTraits::Attributes::type[] = "type";
const char CriterionTraits::Attributes::defaultVal[] = "default";
status_t CriterionTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child, Collection &criteria)
{
std::string name = getXmlAttribute(child, Attributes::name);
if (name.empty()) {
ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name);
return BAD_VALUE;
}
ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str());
std::string defaultValue = getXmlAttribute(child, Attributes::defaultVal);
if (defaultValue.empty()) {
// Not mandatory to provide a default value for a criterion, even it is recommanded...
ALOGV("%s: No attribute %s found", __FUNCTION__, Attributes::defaultVal);
}
ALOGV("%s: %s = %s", __FUNCTION__, Attributes::defaultVal, defaultValue.c_str());
std::string typeName = getXmlAttribute(child, Attributes::type);
if (typeName.empty()) {
ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name);
return BAD_VALUE;
}
ALOGV("%s: %s = %s", __FUNCTION__, Attributes::type, typeName.c_str());
criteria.push_back({name, typeName, defaultValue});
return NO_ERROR;
}
} // namespace detail
ParsingResult parse(const char* path) {
xmlDocPtr doc;
doc = xmlParseFile(path);
if (doc == NULL) {
ALOGE("%s: Could not parse document %s", __FUNCTION__, path);
return {nullptr, 0};
}
xmlNodePtr cur = xmlDocGetRootElement(doc);
if (cur == NULL) {
ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path);
xmlFreeDoc(doc);
return {nullptr, 0};
}
if (xmlXIncludeProcess(doc) < 0) {
ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path);
return {nullptr, 0};
}
size_t nbSkippedElements = 0;
auto config = std::make_unique<Config>();
detail::deserializeCollection<detail::CriterionTraits>(
doc, cur, config->criteria, nbSkippedElements);
detail::deserializeCollection<detail::CriterionTypeTraits>(
doc, cur, config->criterionTypes, nbSkippedElements);
return {std::move(config), nbSkippedElements};
}
} // namespace wrapper_config
} // namespace audio_policy
} // namespace android