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