Remove setQuirks() from IOMXNode and IOmxNode.

Test: Media post-submit tests on Pixel phone.
Test: Manual use of Camera, Photos, Play Movies and YouTube apps.
Bug: 36952714
Change-Id: I230df51c2d658e29cffec369ba622e336c3402d2
diff --git a/media/vndk/xmlparser/1.0/Android.bp b/media/vndk/xmlparser/1.0/Android.bp
new file mode 100644
index 0000000..c48703c
--- /dev/null
+++ b/media/vndk/xmlparser/1.0/Android.bp
@@ -0,0 +1,37 @@
+cc_library_shared {
+
+    name: "libstagefright_xmlparser@1.0",
+
+    srcs: [
+        "MediaCodecsXmlParser.cpp",
+    ],
+
+    include_dirs: [
+        "frameworks/av/media/libstagefright",
+        "frameworks/av/include",
+    ],
+
+    shared_libs: [
+        "libexpat",
+        "libutils",
+        "liblog",
+        "libcutils",
+        "libstagefright_foundation",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    clang: true,
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+    },
+
+}
+
diff --git a/media/vndk/xmlparser/1.0/MediaCodecsXmlParser.cpp b/media/vndk/xmlparser/1.0/MediaCodecsXmlParser.cpp
new file mode 100644
index 0000000..84e5514
--- /dev/null
+++ b/media/vndk/xmlparser/1.0/MediaCodecsXmlParser.cpp
@@ -0,0 +1,862 @@
+/*
+ * Copyright 2017, 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_NDEBUG 0
+#define LOG_TAG "MediaCodecsXmlParser"
+#include <utils/Log.h>
+
+#include <media/vndk/xmlparser/1.0/MediaCodecsXmlParser.h>
+
+#include <media/MediaCodecInfo.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include <sys/stat.h>
+
+#include <expat.h>
+#include <string>
+
+#define MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH 256
+
+namespace android {
+
+namespace { // Local variables and functions
+
+const char *kProfilingResults =
+        "/data/misc/media/media_codecs_profiling_results.xml";
+
+// Treblized media codec list will be located in /odm/etc or /vendor/etc.
+const char *kConfigLocationList[] =
+        {"/odm/etc", "/vendor/etc", "/etc"};
+constexpr int kConfigLocationListSize =
+        (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
+
+bool findMediaCodecListFileFullPath(
+        const char *file_name, std::string *out_path) {
+    for (int i = 0; i < kConfigLocationListSize; i++) {
+        *out_path = std::string(kConfigLocationList[i]) + "/" + file_name;
+        struct stat file_stat;
+        if (stat(out_path->c_str(), &file_stat) == 0 &&
+                S_ISREG(file_stat.st_mode)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// Find TypeInfo by name.
+std::vector<TypeInfo>::iterator findTypeInfo(
+        CodecInfo &codecInfo, const AString &typeName) {
+    return std::find_if(
+            codecInfo.mTypes.begin(), codecInfo.mTypes.end(),
+            [typeName](const auto &typeInfo) {
+                return typeInfo.mName == typeName;
+            });
+}
+
+// Convert a string into a boolean value.
+bool ParseBoolean(const char *s) {
+    if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
+        return true;
+    }
+    char *end;
+    unsigned long res = strtoul(s, &end, 10);
+    return *s != '\0' && *end == '\0' && res > 0;
+}
+
+} // unnamed namespace
+
+MediaCodecsXmlParser::MediaCodecsXmlParser() :
+    mInitCheck(NO_INIT),
+    mUpdate(false) {
+    std::string config_file_path;
+    if (findMediaCodecListFileFullPath(
+            "media_codecs.xml", &config_file_path)) {
+        parseTopLevelXMLFile(config_file_path.c_str(), false);
+    } else {
+        mInitCheck = NAME_NOT_FOUND;
+    }
+    if (findMediaCodecListFileFullPath(
+            "media_codecs_performance.xml", &config_file_path)) {
+        parseTopLevelXMLFile(config_file_path.c_str(), true);
+    }
+    parseTopLevelXMLFile(kProfilingResults, true);
+}
+
+void MediaCodecsXmlParser::parseTopLevelXMLFile(
+        const char *codecs_xml, bool ignore_errors) {
+    // get href_base
+    const char *href_base_end = strrchr(codecs_xml, '/');
+    if (href_base_end != NULL) {
+        mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
+    }
+
+    mInitCheck = OK; // keeping this here for safety
+    mCurrentSection = SECTION_TOPLEVEL;
+    mDepth = 0;
+
+    parseXMLFile(codecs_xml);
+
+    if (mInitCheck != OK) {
+        if (ignore_errors) {
+            mInitCheck = OK;
+            return;
+        }
+        mCodecInfos.clear();
+        return;
+    }
+}
+
+MediaCodecsXmlParser::~MediaCodecsXmlParser() {
+}
+
+status_t MediaCodecsXmlParser::initCheck() const {
+    return mInitCheck;
+}
+
+void MediaCodecsXmlParser::parseXMLFile(const char *path) {
+    FILE *file = fopen(path, "r");
+
+    if (file == NULL) {
+        ALOGW("unable to open media codecs configuration xml file: %s", path);
+        mInitCheck = NAME_NOT_FOUND;
+        return;
+    }
+
+    ALOGV("Start parsing %s", path);
+    XML_Parser parser = ::XML_ParserCreate(NULL);
+    CHECK(parser != NULL);
+
+    ::XML_SetUserData(parser, this);
+    ::XML_SetElementHandler(
+            parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
+
+    const int BUFF_SIZE = 512;
+    while (mInitCheck == OK) {
+        void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
+        if (buff == NULL) {
+            ALOGE("failed in call to XML_GetBuffer()");
+            mInitCheck = UNKNOWN_ERROR;
+            break;
+        }
+
+        int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
+        if (bytes_read < 0) {
+            ALOGE("failed in call to read");
+            mInitCheck = ERROR_IO;
+            break;
+        }
+
+        XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
+        if (status != XML_STATUS_OK) {
+            ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
+            mInitCheck = ERROR_MALFORMED;
+            break;
+        }
+
+        if (bytes_read == 0) {
+            break;
+        }
+    }
+
+    ::XML_ParserFree(parser);
+
+    fclose(file);
+    file = NULL;
+}
+
+// static
+void MediaCodecsXmlParser::StartElementHandlerWrapper(
+        void *me, const char *name, const char **attrs) {
+    static_cast<MediaCodecsXmlParser *>(me)->startElementHandler(name, attrs);
+}
+
+// static
+void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) {
+    static_cast<MediaCodecsXmlParser *>(me)->endElementHandler(name);
+}
+
+status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) {
+    const char *href = NULL;
+    size_t i = 0;
+    while (attrs[i] != NULL) {
+        if (!strcmp(attrs[i], "href")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            href = attrs[i + 1];
+            ++i;
+        } else {
+            ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]);
+            return -EINVAL;
+        }
+        ++i;
+    }
+
+    // For security reasons and for simplicity, file names can only contain
+    // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
+    for (i = 0; href[i] != '\0'; i++) {
+        if (href[i] == '.' || href[i] == '_' ||
+                (href[i] >= '0' && href[i] <= '9') ||
+                (href[i] >= 'A' && href[i] <= 'Z') ||
+                (href[i] >= 'a' && href[i] <= 'z')) {
+            continue;
+        }
+        ALOGE("invalid include file name: %s", href);
+        return -EINVAL;
+    }
+
+    AString filename = href;
+    if (!filename.startsWith("media_codecs_") ||
+        !filename.endsWith(".xml")) {
+        ALOGE("invalid include file name: %s", href);
+        return -EINVAL;
+    }
+    filename.insert(mHrefBase, 0);
+
+    parseXMLFile(filename.c_str());
+    return mInitCheck;
+}
+
+void MediaCodecsXmlParser::startElementHandler(
+        const char *name, const char **attrs) {
+    if (mInitCheck != OK) {
+        return;
+    }
+
+    bool inType = true;
+
+    if (!strcmp(name, "Include")) {
+        mInitCheck = includeXMLFile(attrs);
+        if (mInitCheck == OK) {
+            mPastSections.push(mCurrentSection);
+            mCurrentSection = SECTION_INCLUDE;
+        }
+        ++mDepth;
+        return;
+    }
+
+    switch (mCurrentSection) {
+        case SECTION_TOPLEVEL:
+        {
+            if (!strcmp(name, "Decoders")) {
+                mCurrentSection = SECTION_DECODERS;
+            } else if (!strcmp(name, "Encoders")) {
+                mCurrentSection = SECTION_ENCODERS;
+            } else if (!strcmp(name, "Settings")) {
+                mCurrentSection = SECTION_SETTINGS;
+            }
+            break;
+        }
+
+        case SECTION_SETTINGS:
+        {
+            if (!strcmp(name, "Setting")) {
+                mInitCheck = addSettingFromAttributes(attrs);
+            }
+            break;
+        }
+
+        case SECTION_DECODERS:
+        {
+            if (!strcmp(name, "MediaCodec")) {
+                mInitCheck =
+                    addMediaCodecFromAttributes(false /* encoder */, attrs);
+
+                mCurrentSection = SECTION_DECODER;
+            }
+            break;
+        }
+
+        case SECTION_ENCODERS:
+        {
+            if (!strcmp(name, "MediaCodec")) {
+                mInitCheck =
+                    addMediaCodecFromAttributes(true /* encoder */, attrs);
+
+                mCurrentSection = SECTION_ENCODER;
+            }
+            break;
+        }
+
+        case SECTION_DECODER:
+        case SECTION_ENCODER:
+        {
+            if (!strcmp(name, "Quirk")) {
+                mInitCheck = addQuirk(attrs);
+            } else if (!strcmp(name, "Type")) {
+                mInitCheck = addTypeFromAttributes(attrs, (mCurrentSection == SECTION_ENCODER));
+                mCurrentSection =
+                    (mCurrentSection == SECTION_DECODER
+                            ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
+            }
+        }
+        inType = false;
+        // fall through
+
+        case SECTION_DECODER_TYPE:
+        case SECTION_ENCODER_TYPE:
+        {
+            // ignore limits and features specified outside of type
+            bool outside = !inType && mCurrentType == mCodecInfos[mCurrentName].mTypes.end();
+            if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
+                ALOGW("ignoring %s specified outside of a Type", name);
+            } else if (!strcmp(name, "Limit")) {
+                mInitCheck = addLimit(attrs);
+            } else if (!strcmp(name, "Feature")) {
+                mInitCheck = addFeature(attrs);
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    ++mDepth;
+}
+
+void MediaCodecsXmlParser::endElementHandler(const char *name) {
+    if (mInitCheck != OK) {
+        return;
+    }
+
+    switch (mCurrentSection) {
+        case SECTION_SETTINGS:
+        {
+            if (!strcmp(name, "Settings")) {
+                mCurrentSection = SECTION_TOPLEVEL;
+            }
+            break;
+        }
+
+        case SECTION_DECODERS:
+        {
+            if (!strcmp(name, "Decoders")) {
+                mCurrentSection = SECTION_TOPLEVEL;
+            }
+            break;
+        }
+
+        case SECTION_ENCODERS:
+        {
+            if (!strcmp(name, "Encoders")) {
+                mCurrentSection = SECTION_TOPLEVEL;
+            }
+            break;
+        }
+
+        case SECTION_DECODER_TYPE:
+        case SECTION_ENCODER_TYPE:
+        {
+            if (!strcmp(name, "Type")) {
+                mCurrentSection =
+                    (mCurrentSection == SECTION_DECODER_TYPE
+                            ? SECTION_DECODER : SECTION_ENCODER);
+
+                mCurrentType = mCodecInfos[mCurrentName].mTypes.end();
+            }
+            break;
+        }
+
+        case SECTION_DECODER:
+        {
+            if (!strcmp(name, "MediaCodec")) {
+                mCurrentSection = SECTION_DECODERS;
+                mCurrentName.clear();
+            }
+            break;
+        }
+
+        case SECTION_ENCODER:
+        {
+            if (!strcmp(name, "MediaCodec")) {
+                mCurrentSection = SECTION_ENCODERS;
+                mCurrentName.clear();
+            }
+            break;
+        }
+
+        case SECTION_INCLUDE:
+        {
+            if (!strcmp(name, "Include") && mPastSections.size() > 0) {
+                mCurrentSection = mPastSections.top();
+                mPastSections.pop();
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    --mDepth;
+}
+
+status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) {
+    const char *name = NULL;
+    const char *value = NULL;
+    const char *update = NULL;
+
+    size_t i = 0;
+    while (attrs[i] != NULL) {
+        if (!strcmp(attrs[i], "name")) {
+            if (attrs[i + 1] == NULL) {
+                ALOGE("addSettingFromAttributes: name is null");
+                return -EINVAL;
+            }
+            name = attrs[i + 1];
+            ++i;
+        } else if (!strcmp(attrs[i], "value")) {
+            if (attrs[i + 1] == NULL) {
+                ALOGE("addSettingFromAttributes: value is null");
+                return -EINVAL;
+            }
+            value = attrs[i + 1];
+            ++i;
+        } else if (!strcmp(attrs[i], "update")) {
+            if (attrs[i + 1] == NULL) {
+                ALOGE("addSettingFromAttributes: update is null");
+                return -EINVAL;
+            }
+            update = attrs[i + 1];
+            ++i;
+        } else {
+            ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]);
+            return -EINVAL;
+        }
+
+        ++i;
+    }
+
+    if (name == NULL || value == NULL) {
+        ALOGE("addSettingFromAttributes: name or value unspecified");
+        return -EINVAL;
+    }
+
+    mUpdate = (update != NULL) && ParseBoolean(update);
+    if (mUpdate != (mGlobalSettings.count(name) > 0)) {
+        ALOGE("addSettingFromAttributes: updating non-existing setting");
+        return -EINVAL;
+    }
+    mGlobalSettings[name] = value;
+
+    return OK;
+}
+
+status_t MediaCodecsXmlParser::addMediaCodecFromAttributes(
+        bool encoder, const char **attrs) {
+    const char *name = NULL;
+    const char *type = NULL;
+    const char *update = NULL;
+
+    size_t i = 0;
+    while (attrs[i] != NULL) {
+        if (!strcmp(attrs[i], "name")) {
+            if (attrs[i + 1] == NULL) {
+                ALOGE("addMediaCodecFromAttributes: name is null");
+                return -EINVAL;
+            }
+            name = attrs[i + 1];
+            ++i;
+        } else if (!strcmp(attrs[i], "type")) {
+            if (attrs[i + 1] == NULL) {
+                ALOGE("addMediaCodecFromAttributes: type is null");
+                return -EINVAL;
+            }
+            type = attrs[i + 1];
+            ++i;
+        } else if (!strcmp(attrs[i], "update")) {
+            if (attrs[i + 1] == NULL) {
+                ALOGE("addMediaCodecFromAttributes: update is null");
+                return -EINVAL;
+            }
+            update = attrs[i + 1];
+            ++i;
+        } else {
+            ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
+            return -EINVAL;
+        }
+
+        ++i;
+    }
+
+    if (name == NULL) {
+        ALOGE("addMediaCodecFromAttributes: name not found");
+        return -EINVAL;
+    }
+
+    mUpdate = (update != NULL) && ParseBoolean(update);
+    if (mUpdate != (mCodecInfos.count(name) > 0)) {
+        ALOGE("addMediaCodecFromAttributes: updating non-existing codec or vice versa");
+        return -EINVAL;
+    }
+
+    CodecInfo *info = &mCodecInfos[name];
+    if (mUpdate) {
+        // existing codec
+        mCurrentName = name;
+        mCurrentType = info->mTypes.begin();
+        if (type != NULL) {
+            // existing type
+            mCurrentType = findTypeInfo(*info, type);
+            if (mCurrentType == info->mTypes.end()) {
+                ALOGE("addMediaCodecFromAttributes: updating non-existing type");
+                return -EINVAL;
+            }
+        }
+    } else {
+        // new codec
+        mCurrentName = name;
+        mQuirks[name].clear();
+        info->mTypes.clear();
+        info->mTypes.emplace_back();
+        mCurrentType = --info->mTypes.end();
+        mCurrentType->mName = type;
+        info->mIsEncoder = encoder;
+    }
+
+    return OK;
+}
+
+status_t MediaCodecsXmlParser::addQuirk(const char **attrs) {
+    const char *name = NULL;
+
+    size_t i = 0;
+    while (attrs[i] != NULL) {
+        if (!strcmp(attrs[i], "name")) {
+            if (attrs[i + 1] == NULL) {
+                ALOGE("addQuirk: name is null");
+                return -EINVAL;
+            }
+            name = attrs[i + 1];
+            ++i;
+        } else {
+            ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]);
+            return -EINVAL;
+        }
+
+        ++i;
+    }
+
+    if (name == NULL) {
+        ALOGE("addQuirk: name not found");
+        return -EINVAL;
+    }
+
+    mQuirks[mCurrentName].emplace_back(name);
+    return OK;
+}
+
+status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) {
+    const char *name = NULL;
+    const char *update = NULL;
+
+    size_t i = 0;
+    while (attrs[i] != NULL) {
+        if (!strcmp(attrs[i], "name")) {
+            if (attrs[i + 1] == NULL) {
+                ALOGE("addTypeFromAttributes: name is null");
+                return -EINVAL;
+            }
+            name = attrs[i + 1];
+            ++i;
+        } else if (!strcmp(attrs[i], "update")) {
+            if (attrs[i + 1] == NULL) {
+                ALOGE("addTypeFromAttributes: update is null");
+                return -EINVAL;
+            }
+            update = attrs[i + 1];
+            ++i;
+        } else {
+            ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]);
+            return -EINVAL;
+        }
+
+        ++i;
+    }
+
+    if (name == NULL) {
+        return -EINVAL;
+    }
+
+    CodecInfo *info = &mCodecInfos[mCurrentName];
+    info->mIsEncoder = encoder;
+    mCurrentType = findTypeInfo(*info, name);
+    if (!mUpdate) {
+        if (mCurrentType != info->mTypes.end()) {
+            ALOGE("addTypeFromAttributes: re-defining existing type without update");
+            return -EINVAL;
+        }
+        info->mTypes.emplace_back();
+        mCurrentType = --info->mTypes.end();
+    } else if (mCurrentType == info->mTypes.end()) {
+        ALOGE("addTypeFromAttributes: updating non-existing type");
+        return -EINVAL;
+    }
+
+    return OK;
+}
+
+static status_t limitFoundMissingAttr(const AString &name, const char *attr, bool found = true) {
+    ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
+            (found ? "" : "no "), attr);
+    return -EINVAL;
+}
+
+static status_t limitError(const AString &name, const char *msg) {
+    ALOGE("limit '%s' %s", name.c_str(), msg);
+    return -EINVAL;
+}
+
+static status_t limitInvalidAttr(const AString &name, const char *attr, const AString &value) {
+    ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
+            attr, value.c_str());
+    return -EINVAL;
+}
+
+status_t MediaCodecsXmlParser::addLimit(const char **attrs) {
+    sp<AMessage> msg = new AMessage();
+
+    size_t i = 0;
+    while (attrs[i] != NULL) {
+        if (attrs[i + 1] == NULL) {
+            ALOGE("addLimit: limit is not given");
+            return -EINVAL;
+        }
+
+        // attributes with values
+        if (!strcmp(attrs[i], "name")
+                || !strcmp(attrs[i], "default")
+                || !strcmp(attrs[i], "in")
+                || !strcmp(attrs[i], "max")
+                || !strcmp(attrs[i], "min")
+                || !strcmp(attrs[i], "range")
+                || !strcmp(attrs[i], "ranges")
+                || !strcmp(attrs[i], "scale")
+                || !strcmp(attrs[i], "value")) {
+            msg->setString(attrs[i], attrs[i + 1]);
+            ++i;
+        } else {
+            ALOGE("addLimit: unrecognized limit: %s", attrs[i]);
+            return -EINVAL;
+        }
+        ++i;
+    }
+
+    AString name;
+    if (!msg->findString("name", &name)) {
+        ALOGE("limit with no 'name' attribute");
+        return -EINVAL;
+    }
+
+    // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
+    // measured-frame-rate, measured-blocks-per-second: range
+    // quality: range + default + [scale]
+    // complexity: range + default
+    bool found;
+    if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
+        ALOGW("ignoring null type");
+        return OK;
+    }
+
+    if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
+            || name == "blocks-per-second" || name == "complexity"
+            || name == "frame-rate" || name == "quality" || name == "size"
+            || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
+        AString min, max;
+        if (msg->findString("min", &min) && msg->findString("max", &max)) {
+            min.append("-");
+            min.append(max);
+            if (msg->contains("range") || msg->contains("value")) {
+                return limitError(name, "has 'min' and 'max' as well as 'range' or "
+                        "'value' attributes");
+            }
+            msg->setString("range", min);
+        } else if (msg->contains("min") || msg->contains("max")) {
+            return limitError(name, "has only 'min' or 'max' attribute");
+        } else if (msg->findString("value", &max)) {
+            min = max;
+            min.append("-");
+            min.append(max);
+            if (msg->contains("range")) {
+                return limitError(name, "has both 'range' and 'value' attributes");
+            }
+            msg->setString("range", min);
+        }
+
+        AString range, scale = "linear", def, in_;
+        if (!msg->findString("range", &range)) {
+            return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
+        }
+
+        if ((name == "quality" || name == "complexity") ^
+                (found = msg->findString("default", &def))) {
+            return limitFoundMissingAttr(name, "default", found);
+        }
+        if (name != "quality" && msg->findString("scale", &scale)) {
+            return limitFoundMissingAttr(name, "scale");
+        }
+        if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
+            return limitFoundMissingAttr(name, "in", found);
+        }
+
+        if (name == "aspect-ratio") {
+            if (!(in_ == "pixels") && !(in_ == "blocks")) {
+                return limitInvalidAttr(name, "in", in_);
+            }
+            in_.erase(5, 1); // (pixel|block)-aspect-ratio
+            in_.append("-");
+            in_.append(name);
+            name = in_;
+        }
+        if (name == "quality") {
+            mCurrentType->mDetails["quality-scale"] = scale;
+        }
+        if (name == "quality" || name == "complexity") {
+            AString tag = name;
+            tag.append("-default");
+            mCurrentType->mDetails[tag] = def;
+        }
+        AString tag = name;
+        tag.append("-range");
+        mCurrentType->mDetails[tag] = range;
+    } else {
+        AString max, value, ranges;
+        if (msg->contains("default")) {
+            return limitFoundMissingAttr(name, "default");
+        } else if (msg->contains("in")) {
+            return limitFoundMissingAttr(name, "in");
+        } else if ((name == "channel-count" || name == "concurrent-instances") ^
+                (found = msg->findString("max", &max))) {
+            return limitFoundMissingAttr(name, "max", found);
+        } else if (msg->contains("min")) {
+            return limitFoundMissingAttr(name, "min");
+        } else if (msg->contains("range")) {
+            return limitFoundMissingAttr(name, "range");
+        } else if ((name == "sample-rate") ^
+                (found = msg->findString("ranges", &ranges))) {
+            return limitFoundMissingAttr(name, "ranges", found);
+        } else if (msg->contains("scale")) {
+            return limitFoundMissingAttr(name, "scale");
+        } else if ((name == "alignment" || name == "block-size") ^
+                (found = msg->findString("value", &value))) {
+            return limitFoundMissingAttr(name, "value", found);
+        }
+
+        if (max.size()) {
+            AString tag = "max-";
+            tag.append(name);
+            mCurrentType->mDetails[tag] = max;
+        } else if (value.size()) {
+            mCurrentType->mDetails[name] = value;
+        } else if (ranges.size()) {
+            AString tag = name;
+            tag.append("-ranges");
+            mCurrentType->mDetails[tag] = ranges;
+        } else {
+            ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
+        }
+    }
+
+    return OK;
+}
+
+status_t MediaCodecsXmlParser::addFeature(const char **attrs) {
+    size_t i = 0;
+    const char *name = NULL;
+    int32_t optional = -1;
+    int32_t required = -1;
+    const char *value = NULL;
+
+    while (attrs[i] != NULL) {
+        if (attrs[i + 1] == NULL) {
+            ALOGE("addFeature: feature is not given");
+            return -EINVAL;
+        }
+
+        // attributes with values
+        if (!strcmp(attrs[i], "name")) {
+            name = attrs[i + 1];
+            ++i;
+        } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
+            int value = (int)ParseBoolean(attrs[i + 1]);
+            if (!strcmp(attrs[i], "optional")) {
+                optional = value;
+            } else {
+                required = value;
+            }
+            ++i;
+        } else if (!strcmp(attrs[i], "value")) {
+            value = attrs[i + 1];
+            ++i;
+        } else {
+            ALOGE("addFeature: unrecognized attribute: %s", attrs[i]);
+            return -EINVAL;
+        }
+        ++i;
+    }
+    if (name == NULL) {
+        ALOGE("feature with no 'name' attribute");
+        return -EINVAL;
+    }
+
+    if (optional == required && optional != -1) {
+        ALOGE("feature '%s' is both/neither optional and required", name);
+        return -EINVAL;
+    }
+
+    if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
+        ALOGW("ignoring null type");
+        return OK;
+    }
+    if (value != NULL) {
+        mCurrentType->mStringFeatures[name] = value;
+    } else {
+        mCurrentType->mBoolFeatures[name] = (required == 1) || (optional == 0);
+    }
+    return OK;
+}
+
+void MediaCodecsXmlParser::getGlobalSettings(
+        std::map<AString, AString> *settings) const {
+    settings->clear();
+    settings->insert(mGlobalSettings.begin(), mGlobalSettings.end());
+}
+
+status_t MediaCodecsXmlParser::getCodecInfo(const char *name, CodecInfo *info) const {
+    if (mCodecInfos.count(name) == 0) {
+        ALOGE("Codec not found with name '%s'", name);
+        return NAME_NOT_FOUND;
+    }
+    *info = mCodecInfos.at(name);
+    return OK;
+}
+
+status_t MediaCodecsXmlParser::getQuirks(const char *name, std::vector<AString> *quirks) const {
+    if (mQuirks.count(name) == 0) {
+        ALOGE("Codec not found with name '%s'", name);
+        return NAME_NOT_FOUND;
+    }
+    quirks->clear();
+    quirks->insert(quirks->end(), mQuirks.at(name).begin(), mQuirks.at(name).end());
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/vndk/xmlparser/Android.bp b/media/vndk/xmlparser/Android.bp
new file mode 100644
index 0000000..a233d6c
--- /dev/null
+++ b/media/vndk/xmlparser/Android.bp
@@ -0,0 +1,4 @@
+subdirs = [
+    "*",
+]
+