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