Home | History | Annotate | Download | only in xmlparser
      1 /*
      2  * Copyright 2017, 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 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "MediaCodecsXmlParser"
     19 
     20 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
     21 
     22 #include <utils/Log.h>
     23 #include <media/stagefright/MediaErrors.h>
     24 #include <media/stagefright/omx/OMXUtils.h>
     25 #include <sys/stat.h>
     26 #include <expat.h>
     27 
     28 #include <cctype>
     29 #include <algorithm>
     30 
     31 namespace android {
     32 
     33 namespace {
     34 
     35 /**
     36  * Search for a file in a list of search directories.
     37  *
     38  * For each string `searchDir` in `searchDirs`, `searchDir/fileName` will be
     39  * tested whether it is a valid file name or not. If it is a valid file name,
     40  * the concatenated name (`searchDir/fileName`) will be stored in the output
     41  * variable `outPath`, and the function will return `true`. Otherwise, the
     42  * search continues until the `nullptr` element in `searchDirs` is reached, at
     43  * which point the function returns `false`.
     44  *
     45  * \param[in] searchDirs Null-terminated array of search paths.
     46  * \param[in] fileName Name of the file to search.
     47  * \param[out] outPath Full path of the file. `outPath` will hold a valid file
     48  * name if the return value of this function is `true`.
     49  * \return `true` if some element in `searchDirs` combined with `fileName` is a
     50  * valid file name; `false` otherwise.
     51  */
     52 bool findFileInDirs(
     53         const char* const* searchDirs,
     54         const char *fileName,
     55         std::string *outPath) {
     56     for (; *searchDirs != nullptr; ++searchDirs) {
     57         *outPath = std::string(*searchDirs) + "/" + fileName;
     58         struct stat fileStat;
     59         if (stat(outPath->c_str(), &fileStat) == 0 &&
     60                 S_ISREG(fileStat.st_mode)) {
     61             return true;
     62         }
     63     }
     64     return false;
     65 }
     66 
     67 bool strnEq(const char* s1, const char* s2, size_t count) {
     68     return strncmp(s1, s2, count) == 0;
     69 }
     70 
     71 bool strEq(const char* s1, const char* s2) {
     72     return strcmp(s1, s2) == 0;
     73 }
     74 
     75 bool striEq(const char* s1, const char* s2) {
     76     return strcasecmp(s1, s2) == 0;
     77 }
     78 
     79 bool strHasPrefix(const char* test, const char* prefix) {
     80     return strnEq(test, prefix, strlen(prefix));
     81 }
     82 
     83 bool parseBoolean(const char* s) {
     84     return striEq(s, "y") ||
     85             striEq(s, "yes") ||
     86             striEq(s, "t") ||
     87             striEq(s, "true") ||
     88             striEq(s, "1");
     89 }
     90 
     91 status_t limitFoundMissingAttr(const char* name, const char *attr, bool found = true) {
     92     ALOGE("limit '%s' with %s'%s' attribute", name,
     93             (found ? "" : "no "), attr);
     94     return -EINVAL;
     95 }
     96 
     97 status_t limitError(const char* name, const char *msg) {
     98     ALOGE("limit '%s' %s", name, msg);
     99     return -EINVAL;
    100 }
    101 
    102 status_t limitInvalidAttr(const char* name, const char* attr, const char* value) {
    103     ALOGE("limit '%s' with invalid '%s' attribute (%s)", name,
    104             attr, value);
    105     return -EINVAL;
    106 }
    107 
    108 }; // unnamed namespace
    109 
    110 constexpr char const* MediaCodecsXmlParser::defaultSearchDirs[];
    111 constexpr char const* MediaCodecsXmlParser::defaultMainXmlName;
    112 constexpr char const* MediaCodecsXmlParser::defaultPerformanceXmlName;
    113 constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
    114 
    115 MediaCodecsXmlParser::MediaCodecsXmlParser(
    116         const char* const* searchDirs,
    117         const char* mainXmlName,
    118         const char* performanceXmlName,
    119         const char* profilingResultsXmlPath) :
    120     mParsingStatus(NO_INIT),
    121     mUpdate(false),
    122     mCodecCounter(0) {
    123     std::string path;
    124     if (findFileInDirs(searchDirs, mainXmlName, &path)) {
    125         parseTopLevelXMLFile(path.c_str(), false);
    126     } else {
    127         ALOGE("Cannot find %s", mainXmlName);
    128         mParsingStatus = NAME_NOT_FOUND;
    129     }
    130     if (findFileInDirs(searchDirs, performanceXmlName, &path)) {
    131         parseTopLevelXMLFile(path.c_str(), true);
    132     }
    133     if (profilingResultsXmlPath != nullptr) {
    134         parseTopLevelXMLFile(profilingResultsXmlPath, true);
    135     }
    136 }
    137 
    138 bool MediaCodecsXmlParser::parseTopLevelXMLFile(
    139         const char *codecs_xml,
    140         bool ignore_errors) {
    141     // get href_base
    142     const char *href_base_end = strrchr(codecs_xml, '/');
    143     if (href_base_end != nullptr) {
    144         mHrefBase = std::string(codecs_xml, href_base_end - codecs_xml + 1);
    145     }
    146 
    147     mParsingStatus = OK; // keeping this here for safety
    148     mCurrentSection = SECTION_TOPLEVEL;
    149 
    150     parseXMLFile(codecs_xml);
    151 
    152     if (mParsingStatus != OK) {
    153         ALOGW("parseTopLevelXMLFile(%s) failed", codecs_xml);
    154         if (ignore_errors) {
    155             mParsingStatus = OK;
    156             return false;
    157         }
    158         mCodecMap.clear();
    159         return false;
    160     }
    161     return true;
    162 }
    163 
    164 MediaCodecsXmlParser::~MediaCodecsXmlParser() {
    165 }
    166 
    167 void MediaCodecsXmlParser::parseXMLFile(const char *path) {
    168     FILE *file = fopen(path, "r");
    169 
    170     if (file == nullptr) {
    171         ALOGW("unable to open media codecs configuration xml file: %s", path);
    172         mParsingStatus = NAME_NOT_FOUND;
    173         return;
    174     }
    175 
    176     XML_Parser parser = ::XML_ParserCreate(nullptr);
    177     LOG_FATAL_IF(parser == nullptr, "XML_MediaCodecsXmlParserCreate() failed.");
    178 
    179     ::XML_SetUserData(parser, this);
    180     ::XML_SetElementHandler(
    181             parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
    182 
    183     static constexpr int BUFF_SIZE = 512;
    184     while (mParsingStatus == OK) {
    185         void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
    186         if (buff == nullptr) {
    187             ALOGE("failed in call to XML_GetBuffer()");
    188             mParsingStatus = UNKNOWN_ERROR;
    189             break;
    190         }
    191 
    192         int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
    193         if (bytes_read < 0) {
    194             ALOGE("failed in call to read");
    195             mParsingStatus = ERROR_IO;
    196             break;
    197         }
    198 
    199         XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
    200         if (status != XML_STATUS_OK) {
    201             ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
    202             mParsingStatus = ERROR_MALFORMED;
    203             break;
    204         }
    205 
    206         if (bytes_read == 0) {
    207             break;
    208         }
    209     }
    210 
    211     ::XML_ParserFree(parser);
    212 
    213     fclose(file);
    214     file = nullptr;
    215 }
    216 
    217 // static
    218 void MediaCodecsXmlParser::StartElementHandlerWrapper(
    219         void *me, const char *name, const char **attrs) {
    220     static_cast<MediaCodecsXmlParser*>(me)->startElementHandler(name, attrs);
    221 }
    222 
    223 // static
    224 void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) {
    225     static_cast<MediaCodecsXmlParser*>(me)->endElementHandler(name);
    226 }
    227 
    228 status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) {
    229     const char *href = nullptr;
    230     size_t i = 0;
    231     while (attrs[i] != nullptr) {
    232         if (strEq(attrs[i], "href")) {
    233             if (attrs[++i] == nullptr) {
    234                 return -EINVAL;
    235             }
    236             href = attrs[i];
    237         } else {
    238             ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]);
    239             return -EINVAL;
    240         }
    241         ++i;
    242     }
    243 
    244     // For security reasons and for simplicity, file names can only contain
    245     // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
    246     for (i = 0; href[i] != '\0'; i++) {
    247         if (href[i] == '.' || href[i] == '_' ||
    248                 (href[i] >= '0' && href[i] <= '9') ||
    249                 (href[i] >= 'A' && href[i] <= 'Z') ||
    250                 (href[i] >= 'a' && href[i] <= 'z')) {
    251             continue;
    252         }
    253         ALOGE("invalid include file name: %s", href);
    254         return -EINVAL;
    255     }
    256 
    257     std::string filename = href;
    258     if (filename.compare(0, 13, "media_codecs_") != 0 ||
    259             filename.compare(filename.size() - 4, 4, ".xml") != 0) {
    260         ALOGE("invalid include file name: %s", href);
    261         return -EINVAL;
    262     }
    263     filename.insert(0, mHrefBase);
    264 
    265     parseXMLFile(filename.c_str());
    266     return mParsingStatus;
    267 }
    268 
    269 void MediaCodecsXmlParser::startElementHandler(
    270         const char *name, const char **attrs) {
    271     if (mParsingStatus != OK) {
    272         return;
    273     }
    274 
    275     bool inType = true;
    276 
    277     if (strEq(name, "Include")) {
    278         mParsingStatus = includeXMLFile(attrs);
    279         if (mParsingStatus == OK) {
    280             mSectionStack.push_back(mCurrentSection);
    281             mCurrentSection = SECTION_INCLUDE;
    282         }
    283         return;
    284     }
    285 
    286     switch (mCurrentSection) {
    287         case SECTION_TOPLEVEL:
    288         {
    289             if (strEq(name, "Decoders")) {
    290                 mCurrentSection = SECTION_DECODERS;
    291             } else if (strEq(name, "Encoders")) {
    292                 mCurrentSection = SECTION_ENCODERS;
    293             } else if (strEq(name, "Settings")) {
    294                 mCurrentSection = SECTION_SETTINGS;
    295             }
    296             break;
    297         }
    298 
    299         case SECTION_SETTINGS:
    300         {
    301             if (strEq(name, "Setting")) {
    302                 mParsingStatus = addSettingFromAttributes(attrs);
    303             }
    304             break;
    305         }
    306 
    307         case SECTION_DECODERS:
    308         {
    309             if (strEq(name, "MediaCodec")) {
    310                 mParsingStatus =
    311                     addMediaCodecFromAttributes(false /* encoder */, attrs);
    312 
    313                 mCurrentSection = SECTION_DECODER;
    314             }
    315             break;
    316         }
    317 
    318         case SECTION_ENCODERS:
    319         {
    320             if (strEq(name, "MediaCodec")) {
    321                 mParsingStatus =
    322                     addMediaCodecFromAttributes(true /* encoder */, attrs);
    323 
    324                 mCurrentSection = SECTION_ENCODER;
    325             }
    326             break;
    327         }
    328 
    329         case SECTION_DECODER:
    330         case SECTION_ENCODER:
    331         {
    332             if (strEq(name, "Quirk")) {
    333                 mParsingStatus = addQuirk(attrs);
    334             } else if (strEq(name, "Type")) {
    335                 mParsingStatus = addTypeFromAttributes(attrs,
    336                         (mCurrentSection == SECTION_ENCODER));
    337                 mCurrentSection =
    338                         (mCurrentSection == SECTION_DECODER ?
    339                         SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
    340             }
    341         }
    342         inType = false;
    343         // fall through
    344 
    345         case SECTION_DECODER_TYPE:
    346         case SECTION_ENCODER_TYPE:
    347         {
    348             // ignore limits and features specified outside of type
    349             bool outside = !inType &&
    350                     mCurrentType == mCurrentCodec->second.typeMap.end();
    351             if (outside &&
    352                     (strEq(name, "Limit") || strEq(name, "Feature"))) {
    353                 ALOGW("ignoring %s specified outside of a Type", name);
    354             } else if (strEq(name, "Limit")) {
    355                 mParsingStatus = addLimit(attrs);
    356             } else if (strEq(name, "Feature")) {
    357                 mParsingStatus = addFeature(attrs);
    358             }
    359             break;
    360         }
    361 
    362         default:
    363             break;
    364     }
    365 
    366 }
    367 
    368 void MediaCodecsXmlParser::endElementHandler(const char *name) {
    369     if (mParsingStatus != OK) {
    370         return;
    371     }
    372 
    373     switch (mCurrentSection) {
    374         case SECTION_SETTINGS:
    375         {
    376             if (strEq(name, "Settings")) {
    377                 mCurrentSection = SECTION_TOPLEVEL;
    378             }
    379             break;
    380         }
    381 
    382         case SECTION_DECODERS:
    383         {
    384             if (strEq(name, "Decoders")) {
    385                 mCurrentSection = SECTION_TOPLEVEL;
    386             }
    387             break;
    388         }
    389 
    390         case SECTION_ENCODERS:
    391         {
    392             if (strEq(name, "Encoders")) {
    393                 mCurrentSection = SECTION_TOPLEVEL;
    394             }
    395             break;
    396         }
    397 
    398         case SECTION_DECODER_TYPE:
    399         case SECTION_ENCODER_TYPE:
    400         {
    401             if (strEq(name, "Type")) {
    402                 mCurrentSection =
    403                         (mCurrentSection == SECTION_DECODER_TYPE ?
    404                         SECTION_DECODER : SECTION_ENCODER);
    405 
    406                 mCurrentType = mCurrentCodec->second.typeMap.end();
    407             }
    408             break;
    409         }
    410 
    411         case SECTION_DECODER:
    412         {
    413             if (strEq(name, "MediaCodec")) {
    414                 mCurrentSection = SECTION_DECODERS;
    415                 mCurrentName.clear();
    416             }
    417             break;
    418         }
    419 
    420         case SECTION_ENCODER:
    421         {
    422             if (strEq(name, "MediaCodec")) {
    423                 mCurrentSection = SECTION_ENCODERS;
    424                 mCurrentName.clear();
    425             }
    426             break;
    427         }
    428 
    429         case SECTION_INCLUDE:
    430         {
    431             if (strEq(name, "Include") && (mSectionStack.size() > 0)) {
    432                 mCurrentSection = mSectionStack.back();
    433                 mSectionStack.pop_back();
    434             }
    435             break;
    436         }
    437 
    438         default:
    439             break;
    440     }
    441 
    442 }
    443 
    444 status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) {
    445     const char *name = nullptr;
    446     const char *value = nullptr;
    447     const char *update = nullptr;
    448 
    449     size_t i = 0;
    450     while (attrs[i] != nullptr) {
    451         if (strEq(attrs[i], "name")) {
    452             if (attrs[++i] == nullptr) {
    453                 ALOGE("addSettingFromAttributes: name is null");
    454                 return -EINVAL;
    455             }
    456             name = attrs[i];
    457         } else if (strEq(attrs[i], "value")) {
    458             if (attrs[++i] == nullptr) {
    459                 ALOGE("addSettingFromAttributes: value is null");
    460                 return -EINVAL;
    461             }
    462             value = attrs[i];
    463         } else if (strEq(attrs[i], "update")) {
    464             if (attrs[++i] == nullptr) {
    465                 ALOGE("addSettingFromAttributes: update is null");
    466                 return -EINVAL;
    467             }
    468             update = attrs[i];
    469         } else {
    470             ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]);
    471             return -EINVAL;
    472         }
    473         ++i;
    474     }
    475 
    476     if (name == nullptr || value == nullptr) {
    477         ALOGE("addSettingFromAttributes: name or value unspecified");
    478         return -EINVAL;
    479     }
    480 
    481     // Boolean values are converted to "0" or "1".
    482     if (strHasPrefix(name, "supports-")) {
    483         value = parseBoolean(value) ? "1" : "0";
    484     }
    485 
    486     mUpdate = (update != nullptr) && parseBoolean(update);
    487     auto attribute = mServiceAttributeMap.find(name);
    488     if (attribute == mServiceAttributeMap.end()) { // New attribute name
    489         if (mUpdate) {
    490             ALOGE("addSettingFromAttributes: updating non-existing setting");
    491             return -EINVAL;
    492         }
    493         mServiceAttributeMap.insert(Attribute(name, value));
    494     } else { // Existing attribute name
    495         if (!mUpdate) {
    496             ALOGE("addSettingFromAttributes: adding existing setting");
    497         }
    498         attribute->second = value;
    499     }
    500 
    501     return OK;
    502 }
    503 
    504 status_t MediaCodecsXmlParser::addMediaCodecFromAttributes(
    505         bool encoder, const char **attrs) {
    506     const char *name = nullptr;
    507     const char *type = nullptr;
    508     const char *update = nullptr;
    509 
    510     size_t i = 0;
    511     while (attrs[i] != nullptr) {
    512         if (strEq(attrs[i], "name")) {
    513             if (attrs[++i] == nullptr) {
    514                 ALOGE("addMediaCodecFromAttributes: name is null");
    515                 return -EINVAL;
    516             }
    517             name = attrs[i];
    518         } else if (strEq(attrs[i], "type")) {
    519             if (attrs[++i] == nullptr) {
    520                 ALOGE("addMediaCodecFromAttributes: type is null");
    521                 return -EINVAL;
    522             }
    523             type = attrs[i];
    524         } else if (strEq(attrs[i], "update")) {
    525             if (attrs[++i] == nullptr) {
    526                 ALOGE("addMediaCodecFromAttributes: update is null");
    527                 return -EINVAL;
    528             }
    529             update = attrs[i];
    530         } else {
    531             ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
    532             return -EINVAL;
    533         }
    534         ++i;
    535     }
    536 
    537     if (name == nullptr) {
    538         ALOGE("addMediaCodecFromAttributes: name not found");
    539         return -EINVAL;
    540     }
    541 
    542     mUpdate = (update != nullptr) && parseBoolean(update);
    543     mCurrentCodec = mCodecMap.find(name);
    544     if (mCurrentCodec == mCodecMap.end()) { // New codec name
    545         if (mUpdate) {
    546             ALOGE("addMediaCodecFromAttributes: updating non-existing codec");
    547             return -EINVAL;
    548         }
    549         // Create a new codec in mCodecMap
    550         mCurrentCodec = mCodecMap.insert(
    551                 Codec(name, CodecProperties())).first;
    552         if (type != nullptr) {
    553             mCurrentType = mCurrentCodec->second.typeMap.insert(
    554                     Type(type, AttributeMap())).first;
    555         } else {
    556             mCurrentType = mCurrentCodec->second.typeMap.end();
    557         }
    558         mCurrentCodec->second.isEncoder = encoder;
    559         mCurrentCodec->second.order = mCodecCounter++;
    560     } else { // Existing codec name
    561         if (!mUpdate) {
    562             ALOGE("addMediaCodecFromAttributes: adding existing codec");
    563             return -EINVAL;
    564         }
    565         if (type != nullptr) {
    566             mCurrentType = mCurrentCodec->second.typeMap.find(type);
    567             if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
    568                 ALOGE("addMediaCodecFromAttributes: updating non-existing type");
    569                 return -EINVAL;
    570             }
    571         } else {
    572             // This should happen only when the codec has at most one type.
    573             mCurrentType = mCurrentCodec->second.typeMap.begin();
    574         }
    575     }
    576 
    577     return OK;
    578 }
    579 
    580 status_t MediaCodecsXmlParser::addQuirk(const char **attrs) {
    581     const char *name = nullptr;
    582 
    583     size_t i = 0;
    584     while (attrs[i] != nullptr) {
    585         if (strEq(attrs[i], "name")) {
    586             if (attrs[++i] == nullptr) {
    587                 ALOGE("addQuirk: name is null");
    588                 return -EINVAL;
    589             }
    590             name = attrs[i];
    591         } else {
    592             ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]);
    593             return -EINVAL;
    594         }
    595         ++i;
    596     }
    597 
    598     if (name == nullptr) {
    599         ALOGE("addQuirk: name not found");
    600         return -EINVAL;
    601     }
    602 
    603     mCurrentCodec->second.quirkSet.emplace(name);
    604     return OK;
    605 }
    606 
    607 status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) {
    608     const char *name = nullptr;
    609     const char *update = nullptr;
    610 
    611     size_t i = 0;
    612     while (attrs[i] != nullptr) {
    613         if (strEq(attrs[i], "name")) {
    614             if (attrs[++i] == nullptr) {
    615                 ALOGE("addTypeFromAttributes: name is null");
    616                 return -EINVAL;
    617             }
    618             name = attrs[i];
    619         } else if (strEq(attrs[i], "update")) {
    620             if (attrs[++i] == nullptr) {
    621                 ALOGE("addTypeFromAttributes: update is null");
    622                 return -EINVAL;
    623             }
    624             update = attrs[i];
    625         } else {
    626             ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]);
    627             return -EINVAL;
    628         }
    629         ++i;
    630     }
    631 
    632     if (name == nullptr) {
    633         return -EINVAL;
    634     }
    635 
    636     mCurrentCodec->second.isEncoder = encoder;
    637     mCurrentType = mCurrentCodec->second.typeMap.find(name);
    638     if (!mUpdate) {
    639         if (mCurrentType != mCurrentCodec->second.typeMap.end()) {
    640             ALOGE("addTypeFromAttributes: re-defining existing type without update");
    641             return -EINVAL;
    642         }
    643         mCurrentType = mCurrentCodec->second.typeMap.insert(
    644                 Type(name, AttributeMap())).first;
    645     } else if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
    646         ALOGE("addTypeFromAttributes: updating non-existing type");
    647     }
    648     return OK;
    649 }
    650 
    651 status_t MediaCodecsXmlParser::addLimit(const char **attrs) {
    652     const char* a_name = nullptr;
    653     const char* a_default = nullptr;
    654     const char* a_in = nullptr;
    655     const char* a_max = nullptr;
    656     const char* a_min = nullptr;
    657     const char* a_range = nullptr;
    658     const char* a_ranges = nullptr;
    659     const char* a_scale = nullptr;
    660     const char* a_value = nullptr;
    661 
    662     size_t i = 0;
    663     while (attrs[i] != nullptr) {
    664         if (strEq(attrs[i], "name")) {
    665             if (attrs[++i] == nullptr) {
    666                 ALOGE("addLimit: name is null");
    667                 return -EINVAL;
    668             }
    669             a_name = attrs[i];
    670         } else if (strEq(attrs[i], "default")) {
    671             if (attrs[++i] == nullptr) {
    672                 ALOGE("addLimit: default is null");
    673                 return -EINVAL;
    674             }
    675             a_default = attrs[i];
    676         } else if (strEq(attrs[i], "in")) {
    677             if (attrs[++i] == nullptr) {
    678                 ALOGE("addLimit: in is null");
    679                 return -EINVAL;
    680             }
    681             a_in = attrs[i];
    682         } else if (strEq(attrs[i], "max")) {
    683             if (attrs[++i] == nullptr) {
    684                 ALOGE("addLimit: max is null");
    685                 return -EINVAL;
    686             }
    687             a_max = attrs[i];
    688         } else if (strEq(attrs[i], "min")) {
    689             if (attrs[++i] == nullptr) {
    690                 ALOGE("addLimit: min is null");
    691                 return -EINVAL;
    692             }
    693             a_min = attrs[i];
    694         } else if (strEq(attrs[i], "range")) {
    695             if (attrs[++i] == nullptr) {
    696                 ALOGE("addLimit: range is null");
    697                 return -EINVAL;
    698             }
    699             a_range = attrs[i];
    700         } else if (strEq(attrs[i], "ranges")) {
    701             if (attrs[++i] == nullptr) {
    702                 ALOGE("addLimit: ranges is null");
    703                 return -EINVAL;
    704             }
    705             a_ranges = attrs[i];
    706         } else if (strEq(attrs[i], "scale")) {
    707             if (attrs[++i] == nullptr) {
    708                 ALOGE("addLimit: scale is null");
    709                 return -EINVAL;
    710             }
    711             a_scale = attrs[i];
    712         } else if (strEq(attrs[i], "value")) {
    713             if (attrs[++i] == nullptr) {
    714                 ALOGE("addLimit: value is null");
    715                 return -EINVAL;
    716             }
    717             a_value = attrs[i];
    718         } else {
    719             ALOGE("addLimit: unrecognized limit: %s", attrs[i]);
    720             return -EINVAL;
    721         }
    722         ++i;
    723     }
    724 
    725     if (a_name == nullptr) {
    726         ALOGE("limit with no 'name' attribute");
    727         return -EINVAL;
    728     }
    729 
    730     // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
    731     // measured-frame-rate, measured-blocks-per-second: range
    732     // quality: range + default + [scale]
    733     // complexity: range + default
    734     if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
    735         ALOGW("ignoring null type");
    736         return OK;
    737     }
    738 
    739     std::string range;
    740     if (strEq(a_name, "aspect-ratio") ||
    741             strEq(a_name, "bitrate") ||
    742             strEq(a_name, "block-count") ||
    743             strEq(a_name, "blocks-per-second") ||
    744             strEq(a_name, "complexity") ||
    745             strEq(a_name, "frame-rate") ||
    746             strEq(a_name, "quality") ||
    747             strEq(a_name, "size") ||
    748             strEq(a_name, "measured-blocks-per-second") ||
    749             strHasPrefix(a_name, "measured-frame-rate-")) {
    750         // "range" is specified in exactly one of the following forms:
    751         // 1) min-max
    752         // 2) value-value
    753         // 3) range
    754         if (a_min != nullptr && a_max != nullptr) {
    755             // min-max
    756             if (a_range != nullptr || a_value != nullptr) {
    757                 return limitError(a_name, "has 'min' and 'max' as well as 'range' or "
    758                         "'value' attributes");
    759             }
    760             range = a_min;
    761             range += '-';
    762             range += a_max;
    763         } else if (a_min != nullptr || a_max != nullptr) {
    764             return limitError(a_name, "has only 'min' or 'max' attribute");
    765         } else if (a_value != nullptr) {
    766             // value-value
    767             if (a_range != nullptr) {
    768                 return limitError(a_name, "has both 'range' and 'value' attributes");
    769             }
    770             range = a_value;
    771             range += '-';
    772             range += a_value;
    773         } else if (a_range == nullptr) {
    774             return limitError(a_name, "with no 'range', 'value' or 'min'/'max' attributes");
    775         } else {
    776             // range
    777             range = a_range;
    778         }
    779 
    780         // "aspect-ratio" requires some special treatment.
    781         if (strEq(a_name, "aspect-ratio")) {
    782             // "aspect-ratio" must have "in".
    783             if (a_in == nullptr) {
    784                 return limitFoundMissingAttr(a_name, "in", false);
    785             }
    786             // "in" must be either "pixels" or "blocks".
    787             if (!strEq(a_in, "pixels") && !strEq(a_in, "blocks")) {
    788                 return limitInvalidAttr(a_name, "in", a_in);
    789             }
    790             // name will be "pixel-aspect-ratio-range" or
    791             // "block-aspect-ratio-range".
    792             mCurrentType->second[
    793                     std::string(a_in).substr(0, strlen(a_in) - 1) +
    794                     "-aspect-ratio-range"] = range;
    795         } else {
    796             // For everything else (apart from "aspect-ratio"), simply append
    797             // "-range" to the name for the range-type property.
    798             mCurrentType->second[std::string(a_name) + "-range"] = range;
    799 
    800             // Only "quality" may have "scale".
    801             if (!strEq(a_name, "quality") && a_scale != nullptr) {
    802                 return limitFoundMissingAttr(a_name, "scale");
    803             } else if (strEq(a_name, "quality")) {
    804                 // The default value of "quality-scale" is "linear".
    805                 mCurrentType->second["quality-scale"] = a_scale == nullptr ?
    806                         "linear" : a_scale;
    807             }
    808 
    809             // "quality" and "complexity" must have "default".
    810             // Other limits must not have "default".
    811             if (strEq(a_name, "quality") || strEq(a_name, "complexity")) {
    812                 if (a_default == nullptr) {
    813                     return limitFoundMissingAttr(a_name, "default", false);
    814                 }
    815                 // name will be "quality-default" or "complexity-default".
    816                 mCurrentType->second[std::string(a_name) + "-default"] = a_default;
    817             } else if (a_default != nullptr) {
    818                 return limitFoundMissingAttr(a_name, "default", true);
    819             }
    820         }
    821     } else {
    822         if (a_default != nullptr) {
    823             return limitFoundMissingAttr(a_name, "default");
    824         }
    825         if (a_in != nullptr) {
    826             return limitFoundMissingAttr(a_name, "in");
    827         }
    828         if (a_scale != nullptr) {
    829             return limitFoundMissingAttr(a_name, "scale");
    830         }
    831         if (a_range != nullptr) {
    832             return limitFoundMissingAttr(a_name, "range");
    833         }
    834         if (a_min != nullptr) {
    835             return limitFoundMissingAttr(a_name, "min");
    836         }
    837 
    838         if (a_max != nullptr) {
    839             // "max" must exist if and only if name is "channel-count" or
    840             // "concurrent-instances".
    841             // "min" is not ncessary.
    842             if (strEq(a_name, "channel-count") ||
    843                     strEq(a_name, "concurrent-instances")) {
    844                 mCurrentType->second[std::string("max-") + a_name] = a_max;
    845             } else {
    846                 return limitFoundMissingAttr(a_name, "max", false);
    847             }
    848         } else if (strEq(a_name, "channel-count") ||
    849                 strEq(a_name, "concurrent-instances")) {
    850             return limitFoundMissingAttr(a_name, "max");
    851         }
    852 
    853         if (a_ranges != nullptr) {
    854             // "ranges" must exist if and only if name is "sample-rate".
    855             if (strEq(a_name, "sample-rate")) {
    856                 mCurrentType->second["sample-rate-ranges"] = a_ranges;
    857             } else {
    858                 return limitFoundMissingAttr(a_name, "ranges", false);
    859             }
    860         } else if (strEq(a_name, "sample-rate")) {
    861             return limitFoundMissingAttr(a_name, "ranges");
    862         }
    863 
    864         if (a_value != nullptr) {
    865             // "value" must exist if and only if name is "alignment" or
    866             // "block-size".
    867             if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
    868                 mCurrentType->second[a_name] = a_value;
    869             } else {
    870                 return limitFoundMissingAttr(a_name, "value", false);
    871             }
    872         } else if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
    873             return limitFoundMissingAttr(a_name, "value", false);
    874         }
    875 
    876     }
    877 
    878     return OK;
    879 }
    880 
    881 status_t MediaCodecsXmlParser::addFeature(const char **attrs) {
    882     size_t i = 0;
    883     const char *name = nullptr;
    884     int32_t optional = -1;
    885     int32_t required = -1;
    886     const char *value = nullptr;
    887 
    888     while (attrs[i] != nullptr) {
    889         if (strEq(attrs[i], "name")) {
    890             if (attrs[++i] == nullptr) {
    891                 ALOGE("addFeature: name is null");
    892                 return -EINVAL;
    893             }
    894             name = attrs[i];
    895         } else if (strEq(attrs[i], "optional")) {
    896             if (attrs[++i] == nullptr) {
    897                 ALOGE("addFeature: optional is null");
    898                 return -EINVAL;
    899             }
    900             optional = parseBoolean(attrs[i]) ? 1 : 0;
    901         } else if (strEq(attrs[i], "required")) {
    902             if (attrs[++i] == nullptr) {
    903                 ALOGE("addFeature: required is null");
    904                 return -EINVAL;
    905             }
    906             required = parseBoolean(attrs[i]) ? 1 : 0;
    907         } else if (strEq(attrs[i], "value")) {
    908             if (attrs[++i] == nullptr) {
    909                 ALOGE("addFeature: value is null");
    910                 return -EINVAL;
    911             }
    912             value = attrs[i];
    913         } else {
    914             ALOGE("addFeature: unrecognized attribute: %s", attrs[i]);
    915             return -EINVAL;
    916         }
    917         ++i;
    918     }
    919 
    920     // Every feature must have a name.
    921     if (name == nullptr) {
    922         ALOGE("feature with no 'name' attribute");
    923         return -EINVAL;
    924     }
    925 
    926     if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
    927         ALOGW("ignoring null type");
    928         return OK;
    929     }
    930 
    931     if ((optional != -1) || (required != -1)) {
    932         if (optional == required) {
    933             ALOGE("feature '%s' is both/neither optional and required", name);
    934             return -EINVAL;
    935         }
    936         if ((optional == 1) || (required == 1)) {
    937             if (value != nullptr) {
    938                 ALOGE("feature '%s' cannot have extra 'value'", name);
    939                 return -EINVAL;
    940             }
    941             mCurrentType->second[std::string("feature-") + name] =
    942                     optional == 1 ? "0" : "1";
    943             return OK;
    944         }
    945     }
    946     mCurrentType->second[std::string("feature-") + name] = value == nullptr ?
    947             "0" : value;
    948     return OK;
    949 }
    950 
    951 const MediaCodecsXmlParser::AttributeMap&
    952         MediaCodecsXmlParser::getServiceAttributeMap() const {
    953     return mServiceAttributeMap;
    954 }
    955 
    956 const MediaCodecsXmlParser::CodecMap&
    957         MediaCodecsXmlParser::getCodecMap() const {
    958     return mCodecMap;
    959 }
    960 
    961 const MediaCodecsXmlParser::RoleMap&
    962         MediaCodecsXmlParser::getRoleMap() const {
    963     if (mRoleMap.empty()) {
    964         generateRoleMap();
    965     }
    966     return mRoleMap;
    967 }
    968 
    969 const char* MediaCodecsXmlParser::getCommonPrefix() const {
    970     if (mCommonPrefix.empty()) {
    971         generateCommonPrefix();
    972     }
    973     return mCommonPrefix.data();
    974 }
    975 
    976 status_t MediaCodecsXmlParser::getParsingStatus() const {
    977     return mParsingStatus;
    978 }
    979 
    980 void MediaCodecsXmlParser::generateRoleMap() const {
    981     for (const auto& codec : mCodecMap) {
    982         const auto& codecName = codec.first;
    983         bool isEncoder = codec.second.isEncoder;
    984         size_t order = codec.second.order;
    985         const auto& typeMap = codec.second.typeMap;
    986         for (const auto& type : typeMap) {
    987             const auto& typeName = type.first;
    988             const char* roleName = GetComponentRole(isEncoder, typeName.data());
    989             if (roleName == nullptr) {
    990                 ALOGE("Cannot find the role for %s of type %s",
    991                         isEncoder ? "an encoder" : "a decoder",
    992                         typeName.data());
    993                 continue;
    994             }
    995             const auto& typeAttributeMap = type.second;
    996 
    997             auto roleIterator = mRoleMap.find(roleName);
    998             std::multimap<size_t, NodeInfo>* nodeList;
    999             if (roleIterator == mRoleMap.end()) {
   1000                 RoleProperties roleProperties;
   1001                 roleProperties.type = typeName;
   1002                 roleProperties.isEncoder = isEncoder;
   1003                 auto insertResult = mRoleMap.insert(
   1004                         std::make_pair(roleName, roleProperties));
   1005                 if (!insertResult.second) {
   1006                     ALOGE("Cannot add role %s", roleName);
   1007                     continue;
   1008                 }
   1009                 nodeList = &insertResult.first->second.nodeList;
   1010             } else {
   1011                 if (roleIterator->second.type != typeName) {
   1012                     ALOGE("Role %s has mismatching types: %s and %s",
   1013                             roleName,
   1014                             roleIterator->second.type.data(),
   1015                             typeName.data());
   1016                     continue;
   1017                 }
   1018                 if (roleIterator->second.isEncoder != isEncoder) {
   1019                     ALOGE("Role %s cannot be both an encoder and a decoder",
   1020                             roleName);
   1021                     continue;
   1022                 }
   1023                 nodeList = &roleIterator->second.nodeList;
   1024             }
   1025 
   1026             NodeInfo nodeInfo;
   1027             nodeInfo.name = codecName;
   1028             nodeInfo.attributeList.reserve(typeAttributeMap.size());
   1029             for (const auto& attribute : typeAttributeMap) {
   1030                 nodeInfo.attributeList.push_back(
   1031                         Attribute{attribute.first, attribute.second});
   1032             }
   1033             nodeList->insert(std::make_pair(
   1034                     std::move(order), std::move(nodeInfo)));
   1035         }
   1036     }
   1037 }
   1038 
   1039 void MediaCodecsXmlParser::generateCommonPrefix() const {
   1040     if (mCodecMap.empty()) {
   1041         return;
   1042     }
   1043     auto i = mCodecMap.cbegin();
   1044     auto first = i->first.cbegin();
   1045     auto last = i->first.cend();
   1046     for (++i; i != mCodecMap.cend(); ++i) {
   1047         last = std::mismatch(
   1048                 first, last, i->first.cbegin(), i->first.cend()).first;
   1049     }
   1050     mCommonPrefix.insert(mCommonPrefix.begin(), first, last);
   1051 }
   1052 
   1053 } // namespace android
   1054 
   1055