• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <android-base/logging.h>
23 #include <android-base/macros.h>
24 #include <android-base/properties.h>
25 #include <utils/Log.h>
26 
27 #include <media/stagefright/MediaErrors.h>
28 #include <media/stagefright/foundation/ADebug.h>
29 #include <media/stagefright/omx/OMXUtils.h>
30 
31 #include <expat.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 
36 #include <algorithm>
37 #include <cctype>
38 #include <string>
39 
40 namespace android {
41 
42 namespace {
43 
fileExists(const std::string & path)44 bool fileExists(const std::string &path) {
45     struct stat fileStat;
46     return stat(path.c_str(), &fileStat) == 0 && S_ISREG(fileStat.st_mode);
47 }
48 
49 /**
50  * Search for a file in a list of search directories.
51  *
52  * For each string `searchDir` in `searchDirs`, `searchDir/fileName` will be
53  * tested whether it is a valid file name or not. If it is a valid file name,
54  * the concatenated name (`searchDir/fileName`) will be stored in the output
55  * variable `outPath`, and the function will return `true`. Otherwise, the
56  * search continues until the `nullptr` element in `searchDirs` is reached, at
57  * which point the function returns `false`.
58  *
59  * \param[in] searchDirs array of search paths.
60  * \param[in] fileName Name of the file to search.
61  * \param[out] outPath Full path of the file. `outPath` will hold a valid file
62  * name if the return value of this function is `true`.
63  * \return `true` if some element in `searchDirs` combined with `fileName` is a
64  * valid file name; `false` otherwise.
65  */
findFileInDirs(const std::vector<std::string> & searchDirs,const std::string & fileName,std::string * outPath)66 bool findFileInDirs(
67         const std::vector<std::string> &searchDirs,
68         const std::string &fileName,
69         std::string *outPath) {
70     for (const std::string &searchDir : searchDirs) {
71         std::string path = searchDir + "/" + fileName;
72         if (fileExists(path)) {
73             *outPath = path;
74             return true;
75         }
76     }
77     return false;
78 }
79 
strnEq(const char * s1,const char * s2,size_t count)80 bool strnEq(const char* s1, const char* s2, size_t count) {
81     return strncmp(s1, s2, count) == 0;
82 }
83 
strEq(const char * s1,const char * s2)84 bool strEq(const char* s1, const char* s2) {
85     return strcmp(s1, s2) == 0;
86 }
87 
striEq(const char * s1,const char * s2)88 bool striEq(const char* s1, const char* s2) {
89     return strcasecmp(s1, s2) == 0;
90 }
91 
strHasPrefix(const char * test,const char * prefix)92 bool strHasPrefix(const char* test, const char* prefix) {
93     return strnEq(test, prefix, strlen(prefix));
94 }
95 
parseBoolean(const char * s)96 bool parseBoolean(const char* s) {
97     return striEq(s, "y") ||
98             striEq(s, "yes") ||
99             striEq(s, "enabled") ||
100             striEq(s, "t") ||
101             striEq(s, "true") ||
102             striEq(s, "1");
103 }
104 
105 
combineStatus(status_t a,status_t b)106 status_t combineStatus(status_t a, status_t b) {
107     if (a == NO_INIT) {
108         return b;
109     } else if ((a == OK && (b == NAME_NOT_FOUND || b == ALREADY_EXISTS || b == NO_INIT))
110             || (b == OK && (a == NAME_NOT_FOUND || a == ALREADY_EXISTS))) {
111         // ignore NAME_NOT_FOUND and ALREADY_EXIST errors as long as the other error is OK
112         // also handle OK + NO_INIT here
113         return OK;
114     } else {
115         // prefer the first error result
116         return a ? : b;
117     }
118 }
119 
parseCommaSeparatedStringSet(const char * s)120 MediaCodecsXmlParser::StringSet parseCommaSeparatedStringSet(const char *s) {
121     MediaCodecsXmlParser::StringSet result;
122     for (const char *ptr = s ? : ""; *ptr; ) {
123         const char *end = strchrnul(ptr, ',');
124         if (ptr != end) { // skip empty values
125             result.emplace(ptr, end - ptr);
126         }
127         ptr = end + ('\0' != *end);
128     }
129     return result;
130 }
131 
132 #define PLOGD(msg, ...) \
133         ALOGD(msg " at line %zu of %s", ##__VA_ARGS__, \
134                 (size_t)::XML_GetCurrentLineNumber(mParser.get()), mPath.c_str());
135 
136 }  // unnamed namespace
137 
getDefaultXmlNames()138 std::vector<std::string> MediaCodecsXmlParser::getDefaultXmlNames() {
139     static constexpr char const* prefixes[] = {
140             "media_codecs",
141             "media_codecs_performance"
142         };
143     static std::vector<std::string> variants = {
144             android::base::GetProperty("ro.media.xml_variant.codecs", ""),
145             android::base::GetProperty("ro.media.xml_variant.codecs_performance", "")
146         };
147     static std::vector<std::string> names = {
148             prefixes[0] + variants[0] + ".xml",
149             prefixes[1] + variants[1] + ".xml",
150 
151             // shaping information is not currently variant specific.
152             "media_codecs_shaping.xml"
153         };
154     return names;
155 }
156 
157 
158 struct MediaCodecsXmlParser::Impl {
159     // status + error message
160     struct Result {
161     private:
162         status_t mStatus;
163         std::string mError;
164 
165     public:
Resultandroid::MediaCodecsXmlParser::Impl::Result166         Result(status_t s, std::string error = "")
167             : mStatus(s),
168               mError(error) {
169             if (error.empty() && s) {
170                 mError = "Failed (" + std::string(asString(s)) + ")";
171             }
172         }
operator status_tandroid::MediaCodecsXmlParser::Impl::Result173         operator status_t() const { return mStatus; }
errorandroid::MediaCodecsXmlParser::Impl::Result174         std::string error() const { return mError; }
175     };
176 
177 
178     // Parsed data
179     struct Data {
180         // Service attributes
181         AttributeMap mServiceAttributeMap;
182         CodecMap mCodecMap;
183         Result addGlobal(std::string key, std::string value, bool updating);
184     };
185 
186     enum Section {
187         SECTION_TOPLEVEL,
188         SECTION_SETTINGS,
189         SECTION_DECODERS,
190         SECTION_DECODER,
191         SECTION_DECODER_TYPE,
192         SECTION_ENCODERS,
193         SECTION_ENCODER,
194         SECTION_ENCODER_TYPE,
195         SECTION_INCLUDE,
196         SECTION_VARIANT,
197         SECTION_UNKNOWN,
198     };
199 
200     // XML parsing state
201     struct State {
202     private:
203         Data *mData;
204 
205         // current codec and/or type, plus whether we are updating
206         struct CodecAndType {
207             std::string mName;
208             CodecMap::iterator mCodec;
209             TypeMap::iterator mType;
210             bool mUpdating;
211         };
212 
213         // using vectors as we need to reset their sizes
214         std::vector<std::string> mIncludeStack;
215         std::vector<Section> mSectionStack;
216         std::vector<StringSet> mVariantsStack;
217         std::vector<CodecAndType> mCurrent;
218 
219     public:
220         State(Data *data);
221 
dataandroid::MediaCodecsXmlParser::Impl::State222         Data &data() { return *mData; }
223 
224         // used to restore parsing state at XML include boundaries, in case parsing the included
225         // file fails.
226         struct RestorePoint {
227             size_t numIncludes;
228             size_t numSections;
229             size_t numVariantSets;
230             size_t numCodecAndTypes;
231         };
232 
233         // method manipulating restore points (all state stacks)
createRestorePointandroid::MediaCodecsXmlParser::Impl::State234         RestorePoint createRestorePoint() const {
235             return {
236                 mIncludeStack.size(), mSectionStack.size(), mVariantsStack.size(), mCurrent.size()
237             };
238         }
239 
restoreandroid::MediaCodecsXmlParser::Impl::State240         void restore(RestorePoint rp) {
241             CHECK_GE(mIncludeStack.size(), rp.numIncludes);
242             CHECK_GE(mSectionStack.size(), rp.numSections);
243             CHECK_GE(mVariantsStack.size(), rp.numVariantSets);
244             CHECK_GE(mCurrent.size(), rp.numCodecAndTypes);
245 
246             mIncludeStack.resize(rp.numIncludes);
247             mSectionStack.resize(rp.numSections);
248             mVariantsStack.resize(rp.numVariantSets);
249             mCurrent.resize(rp.numCodecAndTypes);
250         }
251 
252         // methods manipulating the include stack
253         Result enterInclude(const std::string &path);
exitIncludeandroid::MediaCodecsXmlParser::Impl::State254         void exitInclude() {
255             mIncludeStack.pop_back();
256         }
257 
258         // methods manipulating the codec/type stack/state
inCodecandroid::MediaCodecsXmlParser::Impl::State259         bool inCodec() const {
260             return !mCurrent.empty() && mCurrent.back().mCodec != mData->mCodecMap.end();
261         }
262 
inTypeandroid::MediaCodecsXmlParser::Impl::State263         bool inType() const {
264             return inCodec()
265                     && mCurrent.back().mType != mCurrent.back().mCodec->second.typeMap.end();
266         }
267 
268         Result enterMediaCodec(bool encoder, const char *name, const char *type, bool update);
269         Result enterType(const char *name, bool update);
exitCodecOrTypeandroid::MediaCodecsXmlParser::Impl::State270         void exitCodecOrType() {
271             mCurrent.pop_back();
272         }
273 
274         // can only be called when inCodec()
codecandroid::MediaCodecsXmlParser::Impl::State275         MediaCodecsXmlParser::CodecProperties &codec() {
276             return mCurrent.back().mCodec->second;
277         }
278         // can only be called when inCodec()
codecNameandroid::MediaCodecsXmlParser::Impl::State279         std::string codecName() const {
280             return mCurrent.back().mName;
281         }
282         // can only be called when inCodec()
updatingandroid::MediaCodecsXmlParser::Impl::State283         bool updating() const {
284             return mCurrent.back().mUpdating;
285         }
286         // can only be called when inType()
typeandroid::MediaCodecsXmlParser::Impl::State287         MediaCodecsXmlParser::AttributeMap &type() {
288             return mCurrent.back().mType->second;
289         }
290 
291         // methods manipulating the section stack
sectionandroid::MediaCodecsXmlParser::Impl::State292         Section section() const {
293             return mSectionStack.back();
294         }
295         Section lastNonIncludeSection() const;
enterSectionandroid::MediaCodecsXmlParser::Impl::State296         void enterSection(Section s) {
297             mSectionStack.push_back(s);
298         }
exitSectionandroid::MediaCodecsXmlParser::Impl::State299         void exitSection() {
300             mSectionStack.pop_back();
301             CHECK(!mSectionStack.empty());
302         }
303 
304         // methods manipulating the variants stack
variantsandroid::MediaCodecsXmlParser::Impl::State305         StringSet variants() const {
306             return mVariantsStack.back();
307         }
enterVariantsandroid::MediaCodecsXmlParser::Impl::State308         void enterVariants(StringSet variants) {
309             mVariantsStack.push_back(variants);
310         }
exitVariantsandroid::MediaCodecsXmlParser::Impl::State311         void exitVariants() {
312             mVariantsStack.pop_back();
313         }
314 
315         // utility methods
316 
317         // updates rank, domains, variants and enabledness on the current codec/type
318         Result updateCodec(
319                 const char *rank, StringSet domains, StringSet variants, const char *enabled);
320         // adds a key-value attribute detail to the current type of the current codec
321         void addDetail(const std::string &key, const std::string &value);
322     };
323 
324     /** XML Parser (state) */
325     struct Parser {
326         State *mState;
327 
328         Parser(State *state, std::string path);
329 
330         // keep track of the parser state
331         std::shared_ptr<XML_ParserStruct> mParser;
332         std::string mPath;
333         std::string mHrefBase;
334         status_t mStatus;
335 
336         void parseXmlFile();
337 
338         // XML parser callbacks
339         static void StartElementHandlerWrapper(void *me, const char *name, const char **attrs);
340         static void EndElementHandlerWrapper(void *me, const char *name);
341 
342         void startElementHandler(const char *name, const char **attrs);
343         void endElementHandler(const char *name);
344 
345         void updateStatus(status_t status);
346         void logAnyErrors(const Result &status) const;
getStatusandroid::MediaCodecsXmlParser::Impl::Parser347         status_t getStatus() const { return mStatus; }
348 
349         status_t addAlias(const char **attrs);
350         status_t addFeature(const char **attrs);
351         status_t addLimit(const char **attrs);
352         status_t addMapping(const char **attrs);
353         status_t addTuning(const char **attrs);
354         status_t addQuirk(const char **attrs, const char *prefix = nullptr);
355         status_t addSetting(const char **attrs, const char *prefix = nullptr);
356         status_t enterMediaCodec(const char **attrs, bool encoder);
357         status_t enterType(const char **attrs);
358         status_t includeXmlFile(const char **attrs);
359         status_t limitVariants(const char **attrs);
360 
361         status_t updateMediaCodec(
362                 const char *rank, const StringSet &domain, const StringSet &variants,
363                 const char *enabled);
364     };
365 
366     status_t parseXmlFilesInSearchDirs(
367         const std::vector<std::string> &fileNames,
368         const std::vector<std::string> &searchDirs);
369 
370     status_t parseXmlPath(const std::string &path);
371 
372     // Computed longest common prefix
373     Data mData;
374     State mState;
375 
376     // Role map
377     mutable std::string mCommonPrefix;
378     mutable RoleMap mRoleMap;
379     mutable std::mutex mLock;
380 
381     status_t mParsingStatus;
382 
Implandroid::MediaCodecsXmlParser::Impl383     Impl()
384         : mState(&mData),
385           mParsingStatus(NO_INIT) {
386     }
387 
388     void generateRoleMap() const;
389     void generateCommonPrefix() const;
390 
getServiceAttributeMapandroid::MediaCodecsXmlParser::Impl391     const AttributeMap& getServiceAttributeMap() const {
392         std::lock_guard<std::mutex> guard(mLock);
393         return mData.mServiceAttributeMap;
394     }
395 
getCodecMapandroid::MediaCodecsXmlParser::Impl396     const CodecMap& getCodecMap() const {
397         std::lock_guard<std::mutex> guard(mLock);
398         return mData.mCodecMap;
399     }
400 
401     const RoleMap& getRoleMap() const;
402     const char* getCommonPrefix() const;
403 
getParsingStatusandroid::MediaCodecsXmlParser::Impl404     status_t getParsingStatus() const {
405         std::lock_guard<std::mutex> guard(mLock);
406         return mParsingStatus;
407     }
408 };
409 
410 constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
411 
MediaCodecsXmlParser()412 MediaCodecsXmlParser::MediaCodecsXmlParser()
413     : mImpl(new Impl()) {
414 }
415 
parseXmlFilesInSearchDirs(const std::vector<std::string> & fileNames,const std::vector<std::string> & searchDirs)416 status_t MediaCodecsXmlParser::parseXmlFilesInSearchDirs(
417         const std::vector<std::string> &fileNames,
418         const std::vector<std::string> &searchDirs) {
419     return mImpl->parseXmlFilesInSearchDirs(fileNames, searchDirs);
420 }
421 
parseXmlPath(const std::string & path)422 status_t MediaCodecsXmlParser::parseXmlPath(const std::string &path) {
423     return mImpl->parseXmlPath(path);
424 }
425 
parseXmlFilesInSearchDirs(const std::vector<std::string> & fileNames,const std::vector<std::string> & searchDirs)426 status_t MediaCodecsXmlParser::Impl::parseXmlFilesInSearchDirs(
427         const std::vector<std::string> &fileNames,
428         const std::vector<std::string> &searchDirs) {
429     status_t res = NO_INIT;
430     for (const std::string fileName : fileNames) {
431         status_t err = NO_INIT;
432         std::string path;
433         if (findFileInDirs(searchDirs, fileName, &path)) {
434             err = parseXmlPath(path);
435         } else {
436             ALOGI("Did not find %s in search path", fileName.c_str());
437         }
438         res = combineStatus(res, err);
439     }
440     return res;
441 }
442 
parseXmlPath(const std::string & path)443 status_t MediaCodecsXmlParser::Impl::parseXmlPath(const std::string &path) {
444     std::lock_guard<std::mutex> guard(mLock);
445     if (!fileExists(path)) {
446         ALOGV("Cannot find %s", path.c_str());
447         mParsingStatus = combineStatus(mParsingStatus, NAME_NOT_FOUND);
448         return NAME_NOT_FOUND;
449     }
450 
451     // save state (even though we should always be at toplevel here)
452     State::RestorePoint rp = mState.createRestorePoint();
453     Parser parser(&mState, path);
454     parser.parseXmlFile();
455     mState.restore(rp);
456 
457     if (parser.getStatus() != OK) {
458         ALOGD("parseXmlPath(%s) failed with %s", path.c_str(), asString(parser.getStatus()));
459     }
460     mParsingStatus = combineStatus(mParsingStatus, parser.getStatus());
461     return parser.getStatus();
462 }
463 
~MediaCodecsXmlParser()464 MediaCodecsXmlParser::~MediaCodecsXmlParser() {
465 }
466 
State(MediaCodecsXmlParser::Impl::Data * data)467 MediaCodecsXmlParser::Impl::State::State(MediaCodecsXmlParser::Impl::Data *data)
468     : mData(data) {
469     mSectionStack.emplace_back(SECTION_TOPLEVEL);
470 }
471 
472 MediaCodecsXmlParser::Impl::Section
lastNonIncludeSection() const473 MediaCodecsXmlParser::Impl::State::lastNonIncludeSection() const {
474     for (auto it = mSectionStack.end(); it != mSectionStack.begin(); --it) {
475         if (it[-1] != SECTION_INCLUDE) {
476             return it[-1];
477         }
478     }
479     TRESPASS("must have non-include section");
480 }
481 
updateStatus(status_t status)482 void MediaCodecsXmlParser::Impl::Parser::updateStatus(status_t status) {
483     mStatus = combineStatus(mStatus, status);
484 }
485 
logAnyErrors(const Result & status) const486 void MediaCodecsXmlParser::Impl::Parser::logAnyErrors(const Result &status) const {
487     if (status) {
488         if (status.error().empty()) {
489             PLOGD("error %s", asString((status_t)status));
490         } else {
491             PLOGD("%s", status.error().c_str());
492         }
493     }
494 }
495 
Parser(State * state,std::string path)496 MediaCodecsXmlParser::Impl::Parser::Parser(State *state, std::string path)
497     : mState(state),
498       mPath(path),
499       mStatus(NO_INIT) {
500     // determine href_base
501     std::string::size_type end = path.rfind('/');
502     if (end != std::string::npos) {
503         mHrefBase = path.substr(0, end + 1);
504     }
505 }
506 
parseXmlFile()507 void MediaCodecsXmlParser::Impl::Parser::parseXmlFile() {
508     const char *path = mPath.c_str();
509     ALOGD("parsing %s...", path);
510     FILE *file = fopen(path, "r");
511 
512     if (file == nullptr) {
513         ALOGD("unable to open media codecs configuration xml file: %s", path);
514         mStatus = NAME_NOT_FOUND;
515         return;
516     }
517 
518     mParser = std::shared_ptr<XML_ParserStruct>(
519         ::XML_ParserCreate(nullptr),
520         [](XML_ParserStruct *parser) { ::XML_ParserFree(parser); });
521     LOG_FATAL_IF(!mParser, "XML_MediaCodecsXmlParserCreate() failed.");
522 
523     ::XML_SetUserData(mParser.get(), this);
524     ::XML_SetElementHandler(mParser.get(), StartElementHandlerWrapper, EndElementHandlerWrapper);
525 
526     static constexpr int BUFF_SIZE = 512;
527     // updateStatus(OK);
528     if (mStatus == NO_INIT) {
529         mStatus = OK;
530     }
531     while (mStatus == OK) {
532         void *buff = ::XML_GetBuffer(mParser.get(), BUFF_SIZE);
533         if (buff == nullptr) {
534             ALOGD("failed in call to XML_GetBuffer()");
535             mStatus = UNKNOWN_ERROR;
536             break;
537         }
538 
539         int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
540         if (bytes_read < 0) {
541             ALOGD("failed in call to read");
542             mStatus = ERROR_IO;
543             break;
544         }
545 
546         XML_Status status = ::XML_ParseBuffer(mParser.get(), bytes_read, bytes_read == 0);
547         if (status != XML_STATUS_OK) {
548             PLOGD("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(mParser.get())));
549             mStatus = ERROR_MALFORMED;
550             break;
551         }
552 
553         if (bytes_read == 0) {
554             break;
555         }
556     }
557 
558     mParser.reset();
559 
560     fclose(file);
561     file = nullptr;
562 }
563 
564 // static
StartElementHandlerWrapper(void * me,const char * name,const char ** attrs)565 void MediaCodecsXmlParser::Impl::Parser::StartElementHandlerWrapper(
566         void *me, const char *name, const char **attrs) {
567     static_cast<MediaCodecsXmlParser::Impl::Parser*>(me)->startElementHandler(name, attrs);
568 }
569 
570 // static
EndElementHandlerWrapper(void * me,const char * name)571 void MediaCodecsXmlParser::Impl::Parser::EndElementHandlerWrapper(void *me, const char *name) {
572     static_cast<MediaCodecsXmlParser::Impl::Parser*>(me)->endElementHandler(name);
573 }
574 
includeXmlFile(const char ** attrs)575 status_t MediaCodecsXmlParser::Impl::Parser::includeXmlFile(const char **attrs) {
576     const char *href = nullptr;
577     size_t i = 0;
578     while (attrs[i] != nullptr) {
579         CHECK((i & 1) == 0);
580         if (attrs[i + 1] == nullptr) {
581             PLOGD("Include: attribute '%s' is null", attrs[i]);
582             return BAD_VALUE;
583         }
584 
585         if (strEq(attrs[i], "href")) {
586             href = attrs[++i];
587         } else {
588             PLOGD("Include: ignoring unrecognized attribute '%s'", attrs[i]);
589             ++i;
590         }
591         ++i;
592     }
593 
594     if (href == nullptr) {
595         PLOGD("Include with no 'href' attribute");
596         return BAD_VALUE;
597     }
598 
599     // For security reasons and for simplicity, file names can only contain
600     // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
601     for (i = 0; href[i] != '\0'; i++) {
602         if (href[i] == '.' || href[i] == '_' ||
603                 (href[i] >= '0' && href[i] <= '9') ||
604                 (href[i] >= 'A' && href[i] <= 'Z') ||
605                 (href[i] >= 'a' && href[i] <= 'z')) {
606             continue;
607         }
608         PLOGD("invalid include file name: %s", href);
609         return BAD_VALUE;
610     }
611 
612     std::string filename = href;
613     if (filename.compare(0, 13, "media_codecs_") != 0 ||
614             filename.compare(filename.size() - 4, 4, ".xml") != 0) {
615         PLOGD("invalid include file name: %s", href);
616         return BAD_VALUE;
617     }
618     filename.insert(0, mHrefBase);
619 
620     Result res = mState->enterInclude(filename);
621     if (res) {
622         logAnyErrors(res);
623         return res;
624     }
625 
626     // save state so that we can resume even if XML parsing of the included file failed midway
627     State::RestorePoint rp = mState->createRestorePoint();
628     Parser parser(mState, filename);
629     parser.parseXmlFile();
630     mState->restore(rp);
631     mState->exitInclude();
632     return parser.getStatus();
633 }
634 
635 MediaCodecsXmlParser::Impl::Result
enterInclude(const std::string & fileName)636 MediaCodecsXmlParser::Impl::State::enterInclude(const std::string &fileName) {
637     if (std::find(mIncludeStack.begin(), mIncludeStack.end(), fileName)
638             != mIncludeStack.end()) {
639         return { BAD_VALUE, "recursive include chain" };
640     }
641     mIncludeStack.emplace_back(fileName);
642     return OK;
643 }
644 
startElementHandler(const char * name,const char ** attrs)645 void MediaCodecsXmlParser::Impl::Parser::startElementHandler(
646         const char *name, const char **attrs) {
647     bool inType = true;
648     Result err = NO_INIT;
649 
650     Section section = mState->section();
651 
652     // handle include at any level
653     if (strEq(name, "Include")) {
654         mState->enterSection(SECTION_INCLUDE);
655         updateStatus(includeXmlFile(attrs));
656         return;
657     }
658 
659     // handle include section (top level)
660     if (section == SECTION_INCLUDE) {
661         if (strEq(name, "Included")) {
662             return;
663         }
664         // imitate prior level
665         section = mState->lastNonIncludeSection();
666     }
667 
668     switch (section) {
669         case SECTION_TOPLEVEL:
670         {
671             Section nextSection;
672             if (strEq(name, "Decoders")) {
673                 nextSection = SECTION_DECODERS;
674             } else if (strEq(name, "Encoders")) {
675                 nextSection = SECTION_ENCODERS;
676             } else if (strEq(name, "Settings")) {
677                 nextSection = SECTION_SETTINGS;
678             } else if (strEq(name, "MediaCodecs") || strEq(name, "Included")) {
679                 return;
680             } else {
681                 break;
682             }
683             mState->enterSection(nextSection);
684             return;
685         }
686 
687         case SECTION_SETTINGS:
688         {
689             if (strEq(name, "Setting")) {
690                 err = addSetting(attrs);
691             } else if (strEq(name, "Variant")) {
692                 err = addSetting(attrs, "variant-");
693             } else if (strEq(name, "Domain")) {
694                 err = addSetting(attrs, "domain-");
695             } else {
696                 break;
697             }
698             updateStatus(err);
699             return;
700         }
701 
702         case SECTION_DECODERS:
703         case SECTION_ENCODERS:
704         {
705             if (strEq(name, "MediaCodec")) {
706                 err = enterMediaCodec(attrs, section == SECTION_ENCODERS);
707                 updateStatus(err);
708                 if (err != OK) { // skip this element on error
709                     mState->enterSection(SECTION_UNKNOWN);
710                 } else {
711                     mState->enterVariants(mState->codec().variantSet);
712                     mState->enterSection(
713                             section == SECTION_DECODERS ? SECTION_DECODER : SECTION_ENCODER);
714                 }
715                 return;
716             }
717             break;
718         }
719 
720         case SECTION_DECODER:
721         case SECTION_ENCODER:
722         {
723             if (strEq(name, "Quirk")) {
724                 err = addQuirk(attrs, "quirk::");
725             } else if (strEq(name, "Attribute")) {
726                 err = addQuirk(attrs, "attribute::");
727             } else if (strEq(name, "Alias")) {
728                 err = addAlias(attrs);
729             } else if (strEq(name, "Type")) {
730                 err = enterType(attrs);
731                 if (err != OK) { // skip this element on error
732                     mState->enterSection(SECTION_UNKNOWN);
733                 } else {
734                     mState->enterSection(
735                             section == SECTION_DECODER
736                                     ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
737                 }
738             }
739         }
740         inType = false;
741         FALLTHROUGH_INTENDED;
742 
743         case SECTION_DECODER_TYPE:
744         case SECTION_ENCODER_TYPE:
745         case SECTION_VARIANT:
746         {
747             // ignore limits and features specified outside of type
748             if (!mState->inType()
749                     && (strEq(name, "Limit") || strEq(name, "Feature")
750                         || strEq(name, "Variant") || strEq(name, "Mapping")
751                         || strEq(name, "Tuning"))) {
752                 PLOGD("ignoring %s specified outside of a Type", name);
753                 return;
754             } else if (strEq(name, "Limit")) {
755                 err = addLimit(attrs);
756             } else if (strEq(name, "Feature")) {
757                 err = addFeature(attrs);
758             } else if (strEq(name, "Mapping")) {
759                 err = addMapping(attrs);
760             } else if (strEq(name, "Tuning")) {
761                 err = addTuning(attrs);
762             } else if (strEq(name, "Variant") && section != SECTION_VARIANT) {
763                 err = limitVariants(attrs);
764                 mState->enterSection(err == OK ? SECTION_VARIANT : SECTION_UNKNOWN);
765             } else if (inType
766                     && (strEq(name, "Alias") || strEq(name, "Attribute") || strEq(name, "Quirk"))) {
767                 PLOGD("ignoring %s specified not directly in a MediaCodec", name);
768                 return;
769             } else if (err == NO_INIT) {
770                 break;
771             }
772             updateStatus(err);
773             return;
774         }
775 
776         default:
777             break;
778     }
779 
780     if (section != SECTION_UNKNOWN) {
781         PLOGD("Ignoring unrecognized tag <%s>", name);
782     }
783     mState->enterSection(SECTION_UNKNOWN);
784 }
785 
endElementHandler(const char * name)786 void MediaCodecsXmlParser::Impl::Parser::endElementHandler(const char *name) {
787     // XMLParser handles tag matching, so we really just need to handle the section state here
788     Section section = mState->section();
789     switch (section) {
790         case SECTION_INCLUDE:
791         {
792             // this could also be any of: Included, MediaCodecs
793             if (strEq(name, "Include")) {
794                 mState->exitSection();
795                 return;
796             }
797             break;
798         }
799 
800         case SECTION_SETTINGS:
801         {
802             // this could also be any of: Domain, Variant, Setting
803             if (strEq(name, "Settings")) {
804                 mState->exitSection();
805             }
806             break;
807         }
808 
809         case SECTION_DECODERS:
810         case SECTION_ENCODERS:
811         case SECTION_UNKNOWN:
812         {
813             mState->exitSection();
814             break;
815         }
816 
817         case SECTION_DECODER_TYPE:
818         case SECTION_ENCODER_TYPE:
819         {
820             // this could also be any of: Alias, Limit, Feature
821             if (strEq(name, "Type")) {
822                 mState->exitSection();
823                 mState->exitCodecOrType();
824             }
825             break;
826         }
827 
828         case SECTION_DECODER:
829         case SECTION_ENCODER:
830         {
831             // this could also be any of: Alias, Limit, Quirk, Variant
832             if (strEq(name, "MediaCodec")) {
833                 mState->exitSection();
834                 mState->exitCodecOrType();
835                 mState->exitVariants();
836             }
837             break;
838         }
839 
840         case SECTION_VARIANT:
841         {
842             // this could also be any of: Alias, Limit, Quirk
843             if (strEq(name, "Variant")) {
844                 mState->exitSection();
845                 mState->exitVariants();
846                 return;
847             }
848             break;
849         }
850 
851         default:
852             break;
853     }
854 }
855 
addSetting(const char ** attrs,const char * prefix)856 status_t MediaCodecsXmlParser::Impl::Parser::addSetting(const char **attrs, const char *prefix) {
857     const char *a_name = nullptr;
858     const char *a_value = nullptr;
859     const char *a_update = nullptr;
860     bool isBoolean = false;
861 
862     size_t i = 0;
863     while (attrs[i] != nullptr) {
864         CHECK((i & 1) == 0);
865         if (attrs[i + 1] == nullptr) {
866             PLOGD("Setting: attribute '%s' is null", attrs[i]);
867             return BAD_VALUE;
868         }
869 
870         if (strEq(attrs[i], "name")) {
871             a_name = attrs[++i];
872         } else if (strEq(attrs[i], "value") || strEq(attrs[i], "enabled")) {
873             if (a_value) {
874                 PLOGD("Setting: redundant attribute '%s'", attrs[i]);
875                 return BAD_VALUE;
876             }
877             isBoolean = strEq(attrs[i], "enabled");
878             a_value = attrs[++i];
879         } else if (strEq(attrs[i], "update")) {
880             a_update = attrs[++i];
881         } else {
882             PLOGD("Setting: ignoring unrecognized attribute '%s'", attrs[i]);
883             ++i;
884         }
885         ++i;
886     }
887 
888     if (a_name == nullptr || a_value == nullptr) {
889         PLOGD("Setting with no 'name' or 'value' attribute");
890         return BAD_VALUE;
891     }
892 
893     // Boolean values are converted to "0" or "1".
894     if (strHasPrefix(a_name, "supports-") || isBoolean) {
895         a_value = parseBoolean(a_value) ? "1" : "0";
896     }
897 
898     bool update = (a_update != nullptr) && parseBoolean(a_update);
899     Result res = mState->data().addGlobal(std::string(prefix ? : "") + a_name, a_value, update);
900     if (res != OK) {
901         PLOGD("Setting: %s", res.error().c_str());
902     }
903     return res;
904 }
905 
addGlobal(std::string key,std::string value,bool updating)906 MediaCodecsXmlParser::Impl::Result MediaCodecsXmlParser::Impl::Data::addGlobal(
907         std::string key, std::string value, bool updating) {
908     auto attribute = mServiceAttributeMap.find(key);
909     if (attribute == mServiceAttributeMap.end()) { // New attribute name
910         if (updating) {
911             return { NAME_NOT_FOUND, "cannot update non-existing setting" };
912         }
913         mServiceAttributeMap.insert(Attribute(key, value));
914     } else { // Existing attribute name
915         attribute->second = value;
916         if (!updating) {
917             return { ALREADY_EXISTS, "updating existing setting" };
918         }
919     }
920 
921     return OK;
922 }
923 
enterMediaCodec(const char ** attrs,bool encoder)924 status_t MediaCodecsXmlParser::Impl::Parser::enterMediaCodec(
925         const char **attrs, bool encoder) {
926     const char *a_name = nullptr;
927     const char *a_type = nullptr;
928     const char *a_update = nullptr;
929     const char *a_rank = nullptr;
930     const char *a_domain = nullptr;
931     const char *a_variant = nullptr;
932     const char *a_enabled = nullptr;
933 
934     size_t i = 0;
935     while (attrs[i] != nullptr) {
936         CHECK((i & 1) == 0);
937         if (attrs[i + 1] == nullptr) {
938             PLOGD("MediaCodec: attribute '%s' is null", attrs[i]);
939             return BAD_VALUE;
940         }
941 
942         if (strEq(attrs[i], "name")) {
943             a_name = attrs[++i];
944         } else if (strEq(attrs[i], "type")) {
945             a_type = attrs[++i];
946         } else if (strEq(attrs[i], "update")) {
947             a_update = attrs[++i];
948         } else if (strEq(attrs[i], "rank")) {
949             a_rank = attrs[++i];
950         } else if (strEq(attrs[i], "domain")) {
951             a_domain = attrs[++i];
952         } else if (strEq(attrs[i], "variant")) {
953             a_variant = attrs[++i];
954         } else if (strEq(attrs[i], "enabled")) {
955             a_enabled = attrs[++i];
956         } else {
957             PLOGD("MediaCodec: ignoring unrecognized attribute '%s'", attrs[i]);
958             ++i;
959         }
960         ++i;
961     }
962 
963     if (a_name == nullptr) {
964         PLOGD("MediaCodec with no 'name' attribute");
965         return BAD_VALUE;
966     }
967 
968     bool update = (a_update != nullptr) && parseBoolean(a_update);
969     if (a_domain != nullptr) {
970         // disable codecs with domain by default (unless updating)
971         if (!a_enabled && !update) {
972             a_enabled = "false";
973         }
974     }
975 
976     Result res = mState->enterMediaCodec(encoder, a_name, a_type, update);
977     if (res != OK) {
978         logAnyErrors(res);
979         return res;
980     }
981 
982     return updateMediaCodec(
983             a_rank, parseCommaSeparatedStringSet(a_domain),
984             parseCommaSeparatedStringSet(a_variant), a_enabled);
985 }
986 
987 MediaCodecsXmlParser::Impl::Result
enterMediaCodec(bool encoder,const char * name,const char * type,bool updating)988 MediaCodecsXmlParser::Impl::State::enterMediaCodec(
989         bool encoder, const char *name, const char *type, bool updating) {
990     // store name even in case of an error
991     CodecMap::iterator codecIt = mData->mCodecMap.find(name);
992     TypeMap::iterator typeIt;
993     if (codecIt == mData->mCodecMap.end()) { // New codec name
994         if (updating) {
995             std::string msg = "MediaCodec: cannot update non-existing codec: ";
996             msg = msg + name;
997             return { NAME_NOT_FOUND, msg };
998         }
999         // Create a new codec in mCodecMap
1000         codecIt = mData->mCodecMap.insert(Codec(name, CodecProperties())).first;
1001         if (type != nullptr) {
1002             typeIt = codecIt->second.typeMap.insert(Type(type, AttributeMap())).first;
1003         } else {
1004             typeIt = codecIt->second.typeMap.end();
1005         }
1006         codecIt->second.isEncoder = encoder;
1007         codecIt->second.order = mData->mCodecMap.size();
1008     } else { // Existing codec name
1009         if (!updating) {
1010             std::string msg = "MediaCodec: cannot add existing codec: ";
1011             msg = msg + name;
1012             return { ALREADY_EXISTS, msg };
1013         }
1014         if (type != nullptr) {
1015             typeIt = codecIt->second.typeMap.find(type);
1016             if (typeIt == codecIt->second.typeMap.end()) {
1017                 std::string msg = "MediaCodec: cannot update non-existing type for codec: ";
1018                 msg = msg + name;
1019                 return { NAME_NOT_FOUND, msg };
1020             }
1021         } else {
1022             // This should happen only when the codec has at most one type.
1023             typeIt = codecIt->second.typeMap.begin();
1024             if (typeIt == codecIt->second.typeMap.end()
1025                     || codecIt->second.typeMap.size() != 1) {
1026                 std::string msg = "MediaCodec: cannot update codec without type specified: ";
1027                 msg = msg + name;
1028                 return { BAD_VALUE, msg };
1029             }
1030         }
1031     }
1032     mCurrent.emplace_back(CodecAndType{name, codecIt, typeIt, updating});
1033     return OK;
1034 }
1035 
updateMediaCodec(const char * rank,const StringSet & domains,const StringSet & variants,const char * enabled)1036 status_t MediaCodecsXmlParser::Impl::Parser::updateMediaCodec(
1037         const char *rank, const StringSet &domains, const StringSet &variants,
1038         const char *enabled) {
1039     CHECK(mState->inCodec());
1040     CodecProperties &codec = mState->codec();
1041 
1042     if (rank != nullptr) {
1043         ALOGD_IF(!codec.rank.empty() && codec.rank != rank,
1044                 "codec '%s' rank changed from '%s' to '%s'",
1045                 mState->codecName().c_str(), codec.rank.c_str(), rank);
1046         codec.rank = rank;
1047     }
1048 
1049     codec.variantSet = variants;
1050 
1051     for (const std::string &domain : domains) {
1052         if (domain.size() && domain.at(0) == '!') {
1053             codec.domainSet.erase(domain.substr(1));
1054         } else {
1055             codec.domainSet.emplace(domain);
1056         }
1057     }
1058 
1059     if (enabled != nullptr) {
1060         if (parseBoolean(enabled)) {
1061             codec.quirkSet.erase("attribute::disabled");
1062             ALOGD("enabling %s", mState->codecName().c_str());
1063         } else {
1064             codec.quirkSet.emplace("attribute::disabled");
1065             ALOGD("disabling %s", mState->codecName().c_str());
1066         }
1067     }
1068     return OK;
1069 }
1070 
addQuirk(const char ** attrs,const char * prefix)1071 status_t MediaCodecsXmlParser::Impl::Parser::addQuirk(const char **attrs, const char *prefix) {
1072     CHECK(mState->inCodec());
1073     const char *a_name = nullptr;
1074 
1075     size_t i = 0;
1076     while (attrs[i] != nullptr) {
1077         CHECK((i & 1) == 0);
1078         if (attrs[i + 1] == nullptr) {
1079             PLOGD("Quirk: attribute '%s' is null", attrs[i]);
1080             return BAD_VALUE;
1081         }
1082 
1083         if (strEq(attrs[i], "name")) {
1084             a_name = attrs[++i];
1085         } else {
1086             PLOGD("Quirk: ignoring unrecognized attribute '%s'", attrs[i]);
1087             ++i;
1088         }
1089         ++i;
1090     }
1091 
1092     if (a_name == nullptr) {
1093         PLOGD("Quirk with no 'name' attribute");
1094         return BAD_VALUE;
1095     }
1096 
1097     std::string key = std::string(prefix ? : "") + a_name;
1098     mState->codec().quirkSet.emplace(key);
1099     ALOGV("adding %s to %s", key.c_str(), mState->codecName().c_str());
1100     return OK;
1101 }
1102 
enterType(const char ** attrs)1103 status_t MediaCodecsXmlParser::Impl::Parser::enterType(const char **attrs) {
1104     CHECK(mState->inCodec());
1105 
1106     const char *a_name = nullptr;
1107     const char *a_update = nullptr;
1108 
1109     size_t i = 0;
1110     while (attrs[i] != nullptr) {
1111         CHECK((i & 1) == 0);
1112         if (attrs[i + 1] == nullptr) {
1113             PLOGD("Type: attribute '%s' is null", attrs[i]);
1114             return BAD_VALUE;
1115         }
1116 
1117         if (strEq(attrs[i], "name")) {
1118             a_name = attrs[++i];
1119         } else if (strEq(attrs[i], "update")) {
1120             a_update = attrs[++i];
1121         } else {
1122             PLOGD("Type: ignoring unrecognized attribute '%s'", attrs[i]);
1123             ++i;
1124         }
1125         ++i;
1126     }
1127 
1128     if (a_name == nullptr) {
1129         PLOGD("Type with no 'name' attribute");
1130         return BAD_VALUE;
1131     }
1132 
1133     bool update = (a_update != nullptr) && parseBoolean(a_update);
1134     return mState->enterType(a_name, update);
1135 }
1136 
1137 MediaCodecsXmlParser::Impl::Result
enterType(const char * name,bool update)1138 MediaCodecsXmlParser::Impl::State::enterType(const char *name, bool update) {
1139     update = update || updating(); // handle parent
1140 
1141     CodecMap::iterator codecIt = mCurrent.back().mCodec;
1142     TypeMap::iterator typeIt = codecIt->second.typeMap.find(name);
1143     if (!update) {
1144         if (typeIt != codecIt->second.typeMap.end()) {
1145             return { ALREADY_EXISTS, "trying to update existing type '" + std::string(name) + "'" };
1146         }
1147         typeIt = codecIt->second.typeMap.insert(Type(name, AttributeMap())).first;
1148     } else if (typeIt == codecIt->second.typeMap.end()) {
1149         return { NAME_NOT_FOUND, "addType: updating non-existing type" };
1150     }
1151     mCurrent.push_back({ codecName(), codecIt, typeIt, update });
1152     CHECK(inType());
1153     return OK;
1154 }
1155 
addLimit(const char ** attrs)1156 status_t MediaCodecsXmlParser::Impl::Parser::addLimit(const char **attrs) {
1157     CHECK(mState->inType());
1158     const char* a_name = nullptr;
1159     const char* a_default = nullptr;
1160     const char* a_in = nullptr;
1161     const char* a_max = nullptr;
1162     const char* a_min = nullptr;
1163     const char* a_range = nullptr;
1164     const char* a_ranges = nullptr;
1165     const char* a_scale = nullptr;
1166     const char* a_value = nullptr;
1167 
1168     size_t i = 0;
1169     while (attrs[i] != nullptr) {
1170         CHECK((i & 1) == 0);
1171         if (attrs[i + 1] == nullptr) {
1172             PLOGD("Limit: attribute '%s' is null", attrs[i]);
1173             return BAD_VALUE;
1174         }
1175 
1176         if (strEq(attrs[i], "name")) {
1177             a_name = attrs[++i];
1178         } else if (strEq(attrs[i], "default")) {
1179             a_default = attrs[++i];
1180         } else if (strEq(attrs[i], "in")) {
1181             a_in = attrs[++i];
1182         } else if (strEq(attrs[i], "max")) {
1183             a_max = attrs[++i];
1184         } else if (strEq(attrs[i], "min")) {
1185             a_min = attrs[++i];
1186         } else if (strEq(attrs[i], "range")) {
1187             a_range = attrs[++i];
1188         } else if (strEq(attrs[i], "ranges")) {
1189             a_ranges = attrs[++i];
1190         } else if (strEq(attrs[i], "scale")) {
1191             a_scale = attrs[++i];
1192         } else if (strEq(attrs[i], "value")) {
1193             a_value = attrs[++i];
1194         } else {
1195             PLOGD("Limit: ignoring unrecognized limit: %s", attrs[i]);
1196             ++i;
1197         }
1198         ++i;
1199     }
1200 
1201     if (a_name == nullptr) {
1202         PLOGD("Limit with no 'name' attribute");
1203         return BAD_VALUE;
1204     }
1205 
1206     // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
1207     // measured-frame-rate, measured-blocks-per-second: range
1208     // quality: range + default + [scale]
1209     // complexity: range + default
1210     std::string key = a_name, value;
1211 
1212     // don't allow specifying more than one of value, range or min/max
1213     if ((a_value != nullptr) + (a_range != nullptr) + (a_ranges != nullptr)
1214             + (a_min != nullptr || a_max != nullptr) > 1) {
1215         PLOGD("Limit '%s' has multiple 'min'/'max', 'range', 'ranges' or 'value' attributes",
1216                 a_name);
1217         return BAD_VALUE;
1218     }
1219 
1220     // Min/max limits (only containing min or max attribute)
1221     //
1222     // Current "max" limits are "channel-count", "concurrent-instances".
1223     // There are no current "min" limits
1224     //
1225     // Range limits. "range" is specified in exactly one of the following forms:
1226     // 1) min-max
1227     // 2) value-value
1228     // 3) range
1229     //
1230     // Current range limits are "aspect-ratio", "bitrate", "block-count", "blocks-per-second",
1231     // "complexity", "frame-rate", "quality", "size", "measured-blocks-per-second",
1232     // "performance-point-*", "measured-frame-rate-*"
1233     //
1234     // Other limits (containing only value or ranges)
1235     //
1236     // Current ranges limit is "sample-rate"
1237     if ((a_min != nullptr) ^ (a_max != nullptr)) {
1238         // min/max limit
1239         if (a_max != nullptr) {
1240             key = "max-" + key;
1241             value = a_max;
1242         } else if (a_min != nullptr) {
1243             key = "min-" + key;
1244             value = a_min;
1245         }
1246     } else if (a_min != nullptr && a_max != nullptr) {
1247         // min-max
1248         key += "-range";
1249         value = a_min + std::string("-") + a_max;
1250     } else if (a_value != nullptr) {
1251         // value-value or value
1252         value = a_value;
1253         if (strEq(a_name, "aspect-ratio") ||
1254             strEq(a_name, "bitrate") ||
1255             strEq(a_name, "block-count") ||
1256             strEq(a_name, "blocks-per-second") ||
1257             strEq(a_name, "complexity") ||
1258             strEq(a_name, "frame-rate") ||
1259             strEq(a_name, "quality") ||
1260             strEq(a_name, "size") ||
1261             strEq(a_name, "measured-blocks-per-second") ||
1262             strHasPrefix(a_name, "performance-point-") ||
1263             strHasPrefix(a_name, "measured-frame-rate-")) {
1264             key += "-range";
1265             value += std::string("-") + a_value;
1266         }
1267     } else if (a_range != nullptr) {
1268         // range
1269         key += "-range";
1270         value = a_range;
1271     } else if (a_ranges != nullptr) {
1272         // ranges
1273         key += "-ranges";
1274         value = a_ranges;
1275     } else {
1276         PLOGD("Limit '%s' with no 'range', 'value' or 'min'/'max' attributes", a_name);
1277         return BAD_VALUE;
1278     }
1279 
1280     // handle 'in' attribute - this changes the key
1281     if (a_in != nullptr) {
1282         // Currently "aspect-ratio" uses in attribute
1283         const size_t a_in_len = strlen(a_in);
1284         key = std::string(a_in, a_in_len - a_in[a_in_len] == 's') + '-' + key;
1285     }
1286 
1287     // handle 'scale' attribute - this adds a new detail
1288     if (a_scale != nullptr) {
1289         mState->addDetail(a_name + std::string("-scale"), a_scale);
1290     } else if (strEq(a_name, "quality")) {
1291         // The default value of "quality-scale" is "linear" even if unspecified.
1292         mState->addDetail(a_name + std::string("-scale"), "linear");
1293     }
1294 
1295     // handle 'default' attribute - this adds a new detail
1296     if (a_default != nullptr) {
1297         mState->addDetail(a_name + std::string("-default"), a_default);
1298     }
1299 
1300     mState->addDetail(key, value);
1301     return OK;
1302 }
1303 
addDetail(const std::string & key,const std::string & value)1304 void MediaCodecsXmlParser::Impl::State::addDetail(
1305         const std::string &key, const std::string &value) {
1306     CHECK(inType());
1307     ALOGV("limit: %s = %s", key.c_str(), value.c_str());
1308     const StringSet &variants = mVariantsStack.back();
1309     if (variants.empty()) {
1310         type()[key] = value;
1311     } else {
1312         for (const std::string &variant : variants) {
1313             type()[variant + ":::" + key] = value;
1314         }
1315     }
1316 }
1317 
limitVariants(const char ** attrs)1318 status_t MediaCodecsXmlParser::Impl::Parser::limitVariants(const char **attrs) {
1319     const char* a_name = nullptr;
1320 
1321     size_t i = 0;
1322     while (attrs[i] != nullptr) {
1323         CHECK((i & 1) == 0);
1324         if (attrs[i + 1] == nullptr) {
1325             PLOGD("Variant: attribute '%s' is null", attrs[i]);
1326             return BAD_VALUE;
1327         }
1328         if (strEq(attrs[i], "name")) {
1329             a_name = attrs[++i];
1330         } else {
1331             PLOGD("Variant: ignoring unrecognized attribute: %s", attrs[i]);
1332             ++i;
1333         }
1334         ++i;
1335     }
1336 
1337     if (a_name == nullptr || *a_name == '\0') {
1338         PLOGD("Variant with no or empty 'name' attribute");
1339         return BAD_VALUE;
1340     }
1341 
1342     StringSet variants;
1343     for (const std::string &variant : parseCommaSeparatedStringSet(a_name)) {
1344         if (mState->variants().count(variant)) {
1345             variants.emplace(variant);
1346         } else {
1347             PLOGD("Variant: variant '%s' not in parent variants", variant.c_str());
1348             return BAD_VALUE;
1349         }
1350     }
1351     mState->enterVariants(variants);
1352     return OK;
1353 }
1354 
addFeature(const char ** attrs)1355 status_t MediaCodecsXmlParser::Impl::Parser::addFeature(const char **attrs) {
1356     CHECK(mState->inType());
1357     size_t i = 0;
1358     const char *a_name = nullptr;
1359     int32_t optional = -1;
1360     int32_t required = -1;
1361     const char *a_value = nullptr;
1362 
1363     while (attrs[i] != nullptr) {
1364         CHECK((i & 1) == 0);
1365         if (attrs[i + 1] == nullptr) {
1366             PLOGD("Feature: attribute '%s' is null", attrs[i]);
1367             return BAD_VALUE;
1368         }
1369 
1370         if (strEq(attrs[i], "name")) {
1371             a_name = attrs[++i];
1372         } else if (strEq(attrs[i], "optional")) {
1373             optional = parseBoolean(attrs[++i]) ? 1 : 0;
1374         } else if (strEq(attrs[i], "required")) {
1375             required = parseBoolean(attrs[++i]) ? 1 : 0;
1376         } else if (strEq(attrs[i], "value")) {
1377             a_value = attrs[++i];
1378         } else {
1379             PLOGD("Feature: ignoring unrecognized attribute '%s'", attrs[i]);
1380             ++i;
1381         }
1382         ++i;
1383     }
1384 
1385     // Every feature must have a name.
1386     if (a_name == nullptr) {
1387         PLOGD("Feature with no 'name' attribute");
1388         return BAD_VALUE;
1389     }
1390 
1391     if (a_value != nullptr) {
1392         if (optional != -1 || required != -1) {
1393             PLOGD("Feature '%s' has both value and optional/required attributes", a_name);
1394             return BAD_VALUE;
1395         }
1396     } else {
1397         if (optional == required && optional != -1) {
1398             PLOGD("Feature '%s' is both/neither optional and required", a_name);
1399             return BAD_VALUE;
1400         }
1401         a_value = (required == 1 || optional == 0) ? "1" : "0";
1402     }
1403 
1404     mState->addDetail(std::string("feature-") + a_name, a_value ? : "0");
1405     return OK;
1406 }
1407 
addMapping(const char ** attrs)1408 status_t MediaCodecsXmlParser::Impl::Parser::addMapping(const char **attrs) {
1409     CHECK(mState->inType());
1410     size_t i = 0;
1411     const char *a_name = nullptr;
1412     const char *a_value = nullptr;
1413     const char *a_kind = nullptr;
1414 
1415     while (attrs[i] != nullptr) {
1416         CHECK((i & 1) == 0);
1417         if (attrs[i + 1] == nullptr) {
1418             PLOGD("Mapping: attribute '%s' is null", attrs[i]);
1419             return BAD_VALUE;
1420         }
1421 
1422         if (strEq(attrs[i], "name")) {
1423             a_name = attrs[++i];
1424         } else if (strEq(attrs[i], "kind")) {
1425             a_kind = attrs[++i];
1426         } else if (strEq(attrs[i], "value")) {
1427             a_value = attrs[++i];
1428         } else {
1429             PLOGD("Mapping: ignoring unrecognized attribute '%s'", attrs[i]);
1430             ++i;
1431         }
1432         ++i;
1433     }
1434 
1435     // Every mapping must have all 3 fields
1436     if (a_name == nullptr) {
1437         PLOGD("Mapping with no 'name' attribute");
1438         return BAD_VALUE;
1439     }
1440 
1441     if (a_kind == nullptr) {
1442         PLOGD("Mapping with no 'kind' attribute");
1443         return BAD_VALUE;
1444     }
1445 
1446     if (a_value == nullptr) {
1447         PLOGD("Mapping with no 'value' attribute");
1448         return BAD_VALUE;
1449     }
1450 
1451     mState->addDetail(std::string("mapping-") + a_kind + "-" + a_name, a_value);
1452     return OK;
1453 }
1454 
addTuning(const char ** attrs)1455 status_t MediaCodecsXmlParser::Impl::Parser::addTuning(const char **attrs) {
1456     CHECK(mState->inType());
1457     size_t i = 0;
1458     const char *a_name = nullptr;
1459     const char *a_value = nullptr;
1460 
1461     while (attrs[i] != nullptr) {
1462         CHECK((i & 1) == 0);
1463         if (attrs[i + 1] == nullptr) {
1464             PLOGD("Mapping: attribute '%s' is null", attrs[i]);
1465             return BAD_VALUE;
1466         }
1467 
1468         if (strEq(attrs[i], "name")) {
1469             a_name = attrs[++i];
1470         } else if (strEq(attrs[i], "value")) {
1471             a_value = attrs[++i];
1472         } else {
1473             PLOGD("Tuning: ignoring unrecognized attribute '%s'", attrs[i]);
1474             ++i;
1475         }
1476         ++i;
1477     }
1478 
1479     // Every tuning must have both fields
1480     if (a_name == nullptr) {
1481         PLOGD("Tuning with no 'name' attribute");
1482         return BAD_VALUE;
1483     }
1484 
1485     if (a_value == nullptr) {
1486         PLOGD("Tuning with no 'value' attribute");
1487         return BAD_VALUE;
1488     }
1489 
1490     mState->addDetail(std::string("tuning-") + a_name, a_value);
1491     return OK;
1492 }
1493 
addAlias(const char ** attrs)1494 status_t MediaCodecsXmlParser::Impl::Parser::addAlias(const char **attrs) {
1495     CHECK(mState->inCodec());
1496     size_t i = 0;
1497     const char *a_name = nullptr;
1498 
1499     while (attrs[i] != nullptr) {
1500         CHECK((i & 1) == 0);
1501         if (attrs[i + 1] == nullptr) {
1502             PLOGD("Alias: attribute '%s' is null", attrs[i]);
1503             return BAD_VALUE;
1504         }
1505 
1506         if (strEq(attrs[i], "name")) {
1507             a_name = attrs[++i];
1508         } else {
1509             PLOGD("Alias: ignoring unrecognized attribute '%s'", attrs[i]);
1510             ++i;
1511         }
1512         ++i;
1513     }
1514 
1515     // Every feature must have a name.
1516     if (a_name == nullptr) {
1517         PLOGD("Alias with no 'name' attribute");
1518         return BAD_VALUE;
1519     }
1520 
1521     mState->codec().aliases.emplace_back(a_name);
1522     return OK;
1523 }
1524 
1525 const MediaCodecsXmlParser::AttributeMap&
getServiceAttributeMap() const1526 MediaCodecsXmlParser::getServiceAttributeMap() const {
1527     return mImpl->getServiceAttributeMap();
1528 }
1529 
1530 const MediaCodecsXmlParser::CodecMap&
getCodecMap() const1531 MediaCodecsXmlParser::getCodecMap() const {
1532     return mImpl->getCodecMap();
1533 }
1534 
1535 const MediaCodecsXmlParser::RoleMap&
getRoleMap() const1536 MediaCodecsXmlParser::getRoleMap() const {
1537     return mImpl->getRoleMap();
1538 }
1539 
1540 const MediaCodecsXmlParser::RoleMap&
getRoleMap() const1541 MediaCodecsXmlParser::Impl::getRoleMap() const {
1542     std::lock_guard<std::mutex> guard(mLock);
1543     if (mRoleMap.empty()) {
1544         generateRoleMap();
1545     }
1546     return mRoleMap;
1547 }
1548 
getCommonPrefix() const1549 const char* MediaCodecsXmlParser::getCommonPrefix() const {
1550     return mImpl->getCommonPrefix();
1551 }
1552 
getCommonPrefix() const1553 const char* MediaCodecsXmlParser::Impl::getCommonPrefix() const {
1554     std::lock_guard<std::mutex> guard(mLock);
1555     if (mCommonPrefix.empty()) {
1556         generateCommonPrefix();
1557     }
1558     return mCommonPrefix.data();
1559 }
1560 
getParsingStatus() const1561 status_t MediaCodecsXmlParser::getParsingStatus() const {
1562     return mImpl->getParsingStatus();
1563 }
1564 
generateRoleMap() const1565 void MediaCodecsXmlParser::Impl::generateRoleMap() const {
1566     for (const auto& codec : mData.mCodecMap) {
1567         const auto &codecName = codec.first;
1568         if (codecName == "<dummy>") {
1569             continue;
1570         }
1571         bool isEncoder = codec.second.isEncoder;
1572         size_t order = codec.second.order;
1573         std::string rank = codec.second.rank;
1574         const auto& typeMap = codec.second.typeMap;
1575         for (const auto& type : typeMap) {
1576             const auto& typeName = type.first;
1577             const char* roleName = GetComponentRole(isEncoder, typeName.data());
1578             if (roleName == nullptr) {
1579                 ALOGE("Cannot find the role for %s of type %s",
1580                         isEncoder ? "an encoder" : "a decoder",
1581                         typeName.data());
1582                 continue;
1583             }
1584             const auto& typeAttributeMap = type.second;
1585 
1586             auto roleIterator = mRoleMap.find(roleName);
1587             std::multimap<size_t, NodeInfo>* nodeList;
1588             if (roleIterator == mRoleMap.end()) {
1589                 RoleProperties roleProperties;
1590                 roleProperties.type = typeName;
1591                 roleProperties.isEncoder = isEncoder;
1592                 auto insertResult = mRoleMap.insert(
1593                         std::make_pair(roleName, roleProperties));
1594                 if (!insertResult.second) {
1595                     ALOGE("Cannot add role %s", roleName);
1596                     continue;
1597                 }
1598                 nodeList = &insertResult.first->second.nodeList;
1599             } else {
1600                 if (roleIterator->second.type != typeName) {
1601                     ALOGE("Role %s has mismatching types: %s and %s",
1602                             roleName,
1603                             roleIterator->second.type.data(),
1604                             typeName.data());
1605                     continue;
1606                 }
1607                 if (roleIterator->second.isEncoder != isEncoder) {
1608                     ALOGE("Role %s cannot be both an encoder and a decoder",
1609                             roleName);
1610                     continue;
1611                 }
1612                 nodeList = &roleIterator->second.nodeList;
1613             }
1614 
1615             NodeInfo nodeInfo;
1616             nodeInfo.name = codecName;
1617             // NOTE: no aliases are exposed in role info
1618             // attribute quirks are exposed as node attributes
1619             nodeInfo.attributeList.reserve(typeAttributeMap.size());
1620             for (const auto& attribute : typeAttributeMap) {
1621                 nodeInfo.attributeList.push_back(
1622                         Attribute{attribute.first, attribute.second});
1623             }
1624             for (const std::string &quirk : codec.second.quirkSet) {
1625                 if (strHasPrefix(quirk.c_str(), "attribute::")) {
1626                     nodeInfo.attributeList.push_back(Attribute{quirk, "present"});
1627                 }
1628             }
1629             if (!rank.empty()) {
1630                 nodeInfo.attributeList.push_back(Attribute{"rank", rank});
1631             }
1632             nodeList->insert(std::make_pair(
1633                     order, std::move(nodeInfo)));
1634         }
1635     }
1636 }
1637 
generateCommonPrefix() const1638 void MediaCodecsXmlParser::Impl::generateCommonPrefix() const {
1639     if (mData.mCodecMap.empty()) {
1640         return;
1641     }
1642     auto i = mData.mCodecMap.cbegin();
1643     auto first = i->first.cbegin();
1644     auto last = i->first.cend();
1645     for (++i; i != mData.mCodecMap.cend(); ++i) {
1646         last = std::mismatch(
1647                 first, last, i->first.cbegin(), i->first.cend()).first;
1648     }
1649     mCommonPrefix.insert(mCommonPrefix.begin(), first, last);
1650 }
1651 
1652 } // namespace android
1653