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