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