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