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