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 <utils/Log.h>
23 #include <media/stagefright/MediaErrors.h>
24 #include <media/stagefright/omx/OMXUtils.h>
25 #include <sys/stat.h>
26 #include <expat.h>
27 
28 #include <cctype>
29 #include <algorithm>
30 
31 namespace android {
32 
33 namespace {
34 
35 /**
36  * Search for a file in a list of search directories.
37  *
38  * For each string `searchDir` in `searchDirs`, `searchDir/fileName` will be
39  * tested whether it is a valid file name or not. If it is a valid file name,
40  * the concatenated name (`searchDir/fileName`) will be stored in the output
41  * variable `outPath`, and the function will return `true`. Otherwise, the
42  * search continues until the `nullptr` element in `searchDirs` is reached, at
43  * which point the function returns `false`.
44  *
45  * \param[in] searchDirs Null-terminated array of search paths.
46  * \param[in] fileName Name of the file to search.
47  * \param[out] outPath Full path of the file. `outPath` will hold a valid file
48  * name if the return value of this function is `true`.
49  * \return `true` if some element in `searchDirs` combined with `fileName` is a
50  * valid file name; `false` otherwise.
51  */
findFileInDirs(const char * const * searchDirs,const char * fileName,std::string * outPath)52 bool findFileInDirs(
53         const char* const* searchDirs,
54         const char *fileName,
55         std::string *outPath) {
56     for (; *searchDirs != nullptr; ++searchDirs) {
57         *outPath = std::string(*searchDirs) + "/" + fileName;
58         struct stat fileStat;
59         if (stat(outPath->c_str(), &fileStat) == 0 &&
60                 S_ISREG(fileStat.st_mode)) {
61             return true;
62         }
63     }
64     return false;
65 }
66 
strnEq(const char * s1,const char * s2,size_t count)67 bool strnEq(const char* s1, const char* s2, size_t count) {
68     return strncmp(s1, s2, count) == 0;
69 }
70 
strEq(const char * s1,const char * s2)71 bool strEq(const char* s1, const char* s2) {
72     return strcmp(s1, s2) == 0;
73 }
74 
striEq(const char * s1,const char * s2)75 bool striEq(const char* s1, const char* s2) {
76     return strcasecmp(s1, s2) == 0;
77 }
78 
strHasPrefix(const char * test,const char * prefix)79 bool strHasPrefix(const char* test, const char* prefix) {
80     return strnEq(test, prefix, strlen(prefix));
81 }
82 
parseBoolean(const char * s)83 bool parseBoolean(const char* s) {
84     return striEq(s, "y") ||
85             striEq(s, "yes") ||
86             striEq(s, "t") ||
87             striEq(s, "true") ||
88             striEq(s, "1");
89 }
90 
limitFoundMissingAttr(const char * name,const char * attr,bool found=true)91 status_t limitFoundMissingAttr(const char* name, const char *attr, bool found = true) {
92     ALOGE("limit '%s' with %s'%s' attribute", name,
93             (found ? "" : "no "), attr);
94     return -EINVAL;
95 }
96 
limitError(const char * name,const char * msg)97 status_t limitError(const char* name, const char *msg) {
98     ALOGE("limit '%s' %s", name, msg);
99     return -EINVAL;
100 }
101 
limitInvalidAttr(const char * name,const char * attr,const char * value)102 status_t limitInvalidAttr(const char* name, const char* attr, const char* value) {
103     ALOGE("limit '%s' with invalid '%s' attribute (%s)", name,
104             attr, value);
105     return -EINVAL;
106 }
107 
108 }; // unnamed namespace
109 
110 constexpr char const* MediaCodecsXmlParser::defaultSearchDirs[];
111 constexpr char const* MediaCodecsXmlParser::defaultMainXmlName;
112 constexpr char const* MediaCodecsXmlParser::defaultPerformanceXmlName;
113 constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
114 
MediaCodecsXmlParser(const char * const * searchDirs,const char * mainXmlName,const char * performanceXmlName,const char * profilingResultsXmlPath)115 MediaCodecsXmlParser::MediaCodecsXmlParser(
116         const char* const* searchDirs,
117         const char* mainXmlName,
118         const char* performanceXmlName,
119         const char* profilingResultsXmlPath) :
120     mParsingStatus(NO_INIT),
121     mUpdate(false),
122     mCodecCounter(0) {
123     std::string path;
124     if (findFileInDirs(searchDirs, mainXmlName, &path)) {
125         parseTopLevelXMLFile(path.c_str(), false);
126     } else {
127         ALOGE("Cannot find %s", mainXmlName);
128         mParsingStatus = NAME_NOT_FOUND;
129     }
130     if (findFileInDirs(searchDirs, performanceXmlName, &path)) {
131         parseTopLevelXMLFile(path.c_str(), true);
132     }
133     if (profilingResultsXmlPath != nullptr) {
134         parseTopLevelXMLFile(profilingResultsXmlPath, true);
135     }
136 }
137 
parseTopLevelXMLFile(const char * codecs_xml,bool ignore_errors)138 bool MediaCodecsXmlParser::parseTopLevelXMLFile(
139         const char *codecs_xml,
140         bool ignore_errors) {
141     // get href_base
142     const char *href_base_end = strrchr(codecs_xml, '/');
143     if (href_base_end != nullptr) {
144         mHrefBase = std::string(codecs_xml, href_base_end - codecs_xml + 1);
145     }
146 
147     mParsingStatus = OK; // keeping this here for safety
148     mCurrentSection = SECTION_TOPLEVEL;
149 
150     parseXMLFile(codecs_xml);
151 
152     if (mParsingStatus != OK) {
153         ALOGW("parseTopLevelXMLFile(%s) failed", codecs_xml);
154         if (ignore_errors) {
155             mParsingStatus = OK;
156             return false;
157         }
158         mCodecMap.clear();
159         return false;
160     }
161     return true;
162 }
163 
~MediaCodecsXmlParser()164 MediaCodecsXmlParser::~MediaCodecsXmlParser() {
165 }
166 
parseXMLFile(const char * path)167 void MediaCodecsXmlParser::parseXMLFile(const char *path) {
168     FILE *file = fopen(path, "r");
169 
170     if (file == nullptr) {
171         ALOGW("unable to open media codecs configuration xml file: %s", path);
172         mParsingStatus = NAME_NOT_FOUND;
173         return;
174     }
175 
176     XML_Parser parser = ::XML_ParserCreate(nullptr);
177     LOG_FATAL_IF(parser == nullptr, "XML_MediaCodecsXmlParserCreate() failed.");
178 
179     ::XML_SetUserData(parser, this);
180     ::XML_SetElementHandler(
181             parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
182 
183     static constexpr int BUFF_SIZE = 512;
184     while (mParsingStatus == OK) {
185         void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
186         if (buff == nullptr) {
187             ALOGE("failed in call to XML_GetBuffer()");
188             mParsingStatus = UNKNOWN_ERROR;
189             break;
190         }
191 
192         int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
193         if (bytes_read < 0) {
194             ALOGE("failed in call to read");
195             mParsingStatus = ERROR_IO;
196             break;
197         }
198 
199         XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
200         if (status != XML_STATUS_OK) {
201             ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
202             mParsingStatus = ERROR_MALFORMED;
203             break;
204         }
205 
206         if (bytes_read == 0) {
207             break;
208         }
209     }
210 
211     ::XML_ParserFree(parser);
212 
213     fclose(file);
214     file = nullptr;
215 }
216 
217 // static
StartElementHandlerWrapper(void * me,const char * name,const char ** attrs)218 void MediaCodecsXmlParser::StartElementHandlerWrapper(
219         void *me, const char *name, const char **attrs) {
220     static_cast<MediaCodecsXmlParser*>(me)->startElementHandler(name, attrs);
221 }
222 
223 // static
EndElementHandlerWrapper(void * me,const char * name)224 void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) {
225     static_cast<MediaCodecsXmlParser*>(me)->endElementHandler(name);
226 }
227 
includeXMLFile(const char ** attrs)228 status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) {
229     const char *href = nullptr;
230     size_t i = 0;
231     while (attrs[i] != nullptr) {
232         if (strEq(attrs[i], "href")) {
233             if (attrs[++i] == nullptr) {
234                 return -EINVAL;
235             }
236             href = attrs[i];
237         } else {
238             ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]);
239             return -EINVAL;
240         }
241         ++i;
242     }
243 
244     // For security reasons and for simplicity, file names can only contain
245     // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
246     for (i = 0; href[i] != '\0'; i++) {
247         if (href[i] == '.' || href[i] == '_' ||
248                 (href[i] >= '0' && href[i] <= '9') ||
249                 (href[i] >= 'A' && href[i] <= 'Z') ||
250                 (href[i] >= 'a' && href[i] <= 'z')) {
251             continue;
252         }
253         ALOGE("invalid include file name: %s", href);
254         return -EINVAL;
255     }
256 
257     std::string filename = href;
258     if (filename.compare(0, 13, "media_codecs_") != 0 ||
259             filename.compare(filename.size() - 4, 4, ".xml") != 0) {
260         ALOGE("invalid include file name: %s", href);
261         return -EINVAL;
262     }
263     filename.insert(0, mHrefBase);
264 
265     parseXMLFile(filename.c_str());
266     return mParsingStatus;
267 }
268 
startElementHandler(const char * name,const char ** attrs)269 void MediaCodecsXmlParser::startElementHandler(
270         const char *name, const char **attrs) {
271     if (mParsingStatus != OK) {
272         return;
273     }
274 
275     bool inType = true;
276 
277     if (strEq(name, "Include")) {
278         mParsingStatus = includeXMLFile(attrs);
279         if (mParsingStatus == OK) {
280             mSectionStack.push_back(mCurrentSection);
281             mCurrentSection = SECTION_INCLUDE;
282         }
283         return;
284     }
285 
286     switch (mCurrentSection) {
287         case SECTION_TOPLEVEL:
288         {
289             if (strEq(name, "Decoders")) {
290                 mCurrentSection = SECTION_DECODERS;
291             } else if (strEq(name, "Encoders")) {
292                 mCurrentSection = SECTION_ENCODERS;
293             } else if (strEq(name, "Settings")) {
294                 mCurrentSection = SECTION_SETTINGS;
295             }
296             break;
297         }
298 
299         case SECTION_SETTINGS:
300         {
301             if (strEq(name, "Setting")) {
302                 mParsingStatus = addSettingFromAttributes(attrs);
303             }
304             break;
305         }
306 
307         case SECTION_DECODERS:
308         {
309             if (strEq(name, "MediaCodec")) {
310                 mParsingStatus =
311                     addMediaCodecFromAttributes(false /* encoder */, attrs);
312 
313                 mCurrentSection = SECTION_DECODER;
314             }
315             break;
316         }
317 
318         case SECTION_ENCODERS:
319         {
320             if (strEq(name, "MediaCodec")) {
321                 mParsingStatus =
322                     addMediaCodecFromAttributes(true /* encoder */, attrs);
323 
324                 mCurrentSection = SECTION_ENCODER;
325             }
326             break;
327         }
328 
329         case SECTION_DECODER:
330         case SECTION_ENCODER:
331         {
332             if (strEq(name, "Quirk")) {
333                 mParsingStatus = addQuirk(attrs);
334             } else if (strEq(name, "Type")) {
335                 mParsingStatus = addTypeFromAttributes(attrs,
336                         (mCurrentSection == SECTION_ENCODER));
337                 mCurrentSection =
338                         (mCurrentSection == SECTION_DECODER ?
339                         SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
340             }
341         }
342         inType = false;
343         // fall through
344 
345         case SECTION_DECODER_TYPE:
346         case SECTION_ENCODER_TYPE:
347         {
348             // ignore limits and features specified outside of type
349             bool outside = !inType &&
350                     mCurrentType == mCurrentCodec->second.typeMap.end();
351             if (outside &&
352                     (strEq(name, "Limit") || strEq(name, "Feature"))) {
353                 ALOGW("ignoring %s specified outside of a Type", name);
354             } else if (strEq(name, "Limit")) {
355                 mParsingStatus = addLimit(attrs);
356             } else if (strEq(name, "Feature")) {
357                 mParsingStatus = addFeature(attrs);
358             }
359             break;
360         }
361 
362         default:
363             break;
364     }
365 
366 }
367 
endElementHandler(const char * name)368 void MediaCodecsXmlParser::endElementHandler(const char *name) {
369     if (mParsingStatus != OK) {
370         return;
371     }
372 
373     switch (mCurrentSection) {
374         case SECTION_SETTINGS:
375         {
376             if (strEq(name, "Settings")) {
377                 mCurrentSection = SECTION_TOPLEVEL;
378             }
379             break;
380         }
381 
382         case SECTION_DECODERS:
383         {
384             if (strEq(name, "Decoders")) {
385                 mCurrentSection = SECTION_TOPLEVEL;
386             }
387             break;
388         }
389 
390         case SECTION_ENCODERS:
391         {
392             if (strEq(name, "Encoders")) {
393                 mCurrentSection = SECTION_TOPLEVEL;
394             }
395             break;
396         }
397 
398         case SECTION_DECODER_TYPE:
399         case SECTION_ENCODER_TYPE:
400         {
401             if (strEq(name, "Type")) {
402                 mCurrentSection =
403                         (mCurrentSection == SECTION_DECODER_TYPE ?
404                         SECTION_DECODER : SECTION_ENCODER);
405 
406                 mCurrentType = mCurrentCodec->second.typeMap.end();
407             }
408             break;
409         }
410 
411         case SECTION_DECODER:
412         {
413             if (strEq(name, "MediaCodec")) {
414                 mCurrentSection = SECTION_DECODERS;
415                 mCurrentName.clear();
416             }
417             break;
418         }
419 
420         case SECTION_ENCODER:
421         {
422             if (strEq(name, "MediaCodec")) {
423                 mCurrentSection = SECTION_ENCODERS;
424                 mCurrentName.clear();
425             }
426             break;
427         }
428 
429         case SECTION_INCLUDE:
430         {
431             if (strEq(name, "Include") && (mSectionStack.size() > 0)) {
432                 mCurrentSection = mSectionStack.back();
433                 mSectionStack.pop_back();
434             }
435             break;
436         }
437 
438         default:
439             break;
440     }
441 
442 }
443 
addSettingFromAttributes(const char ** attrs)444 status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) {
445     const char *name = nullptr;
446     const char *value = nullptr;
447     const char *update = nullptr;
448 
449     size_t i = 0;
450     while (attrs[i] != nullptr) {
451         if (strEq(attrs[i], "name")) {
452             if (attrs[++i] == nullptr) {
453                 ALOGE("addSettingFromAttributes: name is null");
454                 return -EINVAL;
455             }
456             name = attrs[i];
457         } else if (strEq(attrs[i], "value")) {
458             if (attrs[++i] == nullptr) {
459                 ALOGE("addSettingFromAttributes: value is null");
460                 return -EINVAL;
461             }
462             value = attrs[i];
463         } else if (strEq(attrs[i], "update")) {
464             if (attrs[++i] == nullptr) {
465                 ALOGE("addSettingFromAttributes: update is null");
466                 return -EINVAL;
467             }
468             update = attrs[i];
469         } else {
470             ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]);
471             return -EINVAL;
472         }
473         ++i;
474     }
475 
476     if (name == nullptr || value == nullptr) {
477         ALOGE("addSettingFromAttributes: name or value unspecified");
478         return -EINVAL;
479     }
480 
481     // Boolean values are converted to "0" or "1".
482     if (strHasPrefix(name, "supports-")) {
483         value = parseBoolean(value) ? "1" : "0";
484     }
485 
486     mUpdate = (update != nullptr) && parseBoolean(update);
487     auto attribute = mServiceAttributeMap.find(name);
488     if (attribute == mServiceAttributeMap.end()) { // New attribute name
489         if (mUpdate) {
490             ALOGE("addSettingFromAttributes: updating non-existing setting");
491             return -EINVAL;
492         }
493         mServiceAttributeMap.insert(Attribute(name, value));
494     } else { // Existing attribute name
495         if (!mUpdate) {
496             ALOGE("addSettingFromAttributes: adding existing setting");
497         }
498         attribute->second = value;
499     }
500 
501     return OK;
502 }
503 
addMediaCodecFromAttributes(bool encoder,const char ** attrs)504 status_t MediaCodecsXmlParser::addMediaCodecFromAttributes(
505         bool encoder, const char **attrs) {
506     const char *name = nullptr;
507     const char *type = nullptr;
508     const char *update = nullptr;
509 
510     size_t i = 0;
511     while (attrs[i] != nullptr) {
512         if (strEq(attrs[i], "name")) {
513             if (attrs[++i] == nullptr) {
514                 ALOGE("addMediaCodecFromAttributes: name is null");
515                 return -EINVAL;
516             }
517             name = attrs[i];
518         } else if (strEq(attrs[i], "type")) {
519             if (attrs[++i] == nullptr) {
520                 ALOGE("addMediaCodecFromAttributes: type is null");
521                 return -EINVAL;
522             }
523             type = attrs[i];
524         } else if (strEq(attrs[i], "update")) {
525             if (attrs[++i] == nullptr) {
526                 ALOGE("addMediaCodecFromAttributes: update is null");
527                 return -EINVAL;
528             }
529             update = attrs[i];
530         } else {
531             ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
532             return -EINVAL;
533         }
534         ++i;
535     }
536 
537     if (name == nullptr) {
538         ALOGE("addMediaCodecFromAttributes: name not found");
539         return -EINVAL;
540     }
541 
542     mUpdate = (update != nullptr) && parseBoolean(update);
543     mCurrentCodec = mCodecMap.find(name);
544     if (mCurrentCodec == mCodecMap.end()) { // New codec name
545         if (mUpdate) {
546             ALOGE("addMediaCodecFromAttributes: updating non-existing codec");
547             return -EINVAL;
548         }
549         // Create a new codec in mCodecMap
550         mCurrentCodec = mCodecMap.insert(
551                 Codec(name, CodecProperties())).first;
552         if (type != nullptr) {
553             mCurrentType = mCurrentCodec->second.typeMap.insert(
554                     Type(type, AttributeMap())).first;
555         } else {
556             mCurrentType = mCurrentCodec->second.typeMap.end();
557         }
558         mCurrentCodec->second.isEncoder = encoder;
559         mCurrentCodec->second.order = mCodecCounter++;
560     } else { // Existing codec name
561         if (!mUpdate) {
562             ALOGE("addMediaCodecFromAttributes: adding existing codec");
563             return -EINVAL;
564         }
565         if (type != nullptr) {
566             mCurrentType = mCurrentCodec->second.typeMap.find(type);
567             if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
568                 ALOGE("addMediaCodecFromAttributes: updating non-existing type");
569                 return -EINVAL;
570             }
571         } else {
572             // This should happen only when the codec has at most one type.
573             mCurrentType = mCurrentCodec->second.typeMap.begin();
574         }
575     }
576 
577     return OK;
578 }
579 
addQuirk(const char ** attrs)580 status_t MediaCodecsXmlParser::addQuirk(const char **attrs) {
581     const char *name = nullptr;
582 
583     size_t i = 0;
584     while (attrs[i] != nullptr) {
585         if (strEq(attrs[i], "name")) {
586             if (attrs[++i] == nullptr) {
587                 ALOGE("addQuirk: name is null");
588                 return -EINVAL;
589             }
590             name = attrs[i];
591         } else {
592             ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]);
593             return -EINVAL;
594         }
595         ++i;
596     }
597 
598     if (name == nullptr) {
599         ALOGE("addQuirk: name not found");
600         return -EINVAL;
601     }
602 
603     mCurrentCodec->second.quirkSet.emplace(name);
604     return OK;
605 }
606 
addTypeFromAttributes(const char ** attrs,bool encoder)607 status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) {
608     const char *name = nullptr;
609     const char *update = nullptr;
610 
611     size_t i = 0;
612     while (attrs[i] != nullptr) {
613         if (strEq(attrs[i], "name")) {
614             if (attrs[++i] == nullptr) {
615                 ALOGE("addTypeFromAttributes: name is null");
616                 return -EINVAL;
617             }
618             name = attrs[i];
619         } else if (strEq(attrs[i], "update")) {
620             if (attrs[++i] == nullptr) {
621                 ALOGE("addTypeFromAttributes: update is null");
622                 return -EINVAL;
623             }
624             update = attrs[i];
625         } else {
626             ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]);
627             return -EINVAL;
628         }
629         ++i;
630     }
631 
632     if (name == nullptr) {
633         return -EINVAL;
634     }
635 
636     mCurrentCodec->second.isEncoder = encoder;
637     mCurrentType = mCurrentCodec->second.typeMap.find(name);
638     if (!mUpdate) {
639         if (mCurrentType != mCurrentCodec->second.typeMap.end()) {
640             ALOGE("addTypeFromAttributes: re-defining existing type without update");
641             return -EINVAL;
642         }
643         mCurrentType = mCurrentCodec->second.typeMap.insert(
644                 Type(name, AttributeMap())).first;
645     } else if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
646         ALOGE("addTypeFromAttributes: updating non-existing type");
647     }
648     return OK;
649 }
650 
addLimit(const char ** attrs)651 status_t MediaCodecsXmlParser::addLimit(const char **attrs) {
652     const char* a_name = nullptr;
653     const char* a_default = nullptr;
654     const char* a_in = nullptr;
655     const char* a_max = nullptr;
656     const char* a_min = nullptr;
657     const char* a_range = nullptr;
658     const char* a_ranges = nullptr;
659     const char* a_scale = nullptr;
660     const char* a_value = nullptr;
661 
662     size_t i = 0;
663     while (attrs[i] != nullptr) {
664         if (strEq(attrs[i], "name")) {
665             if (attrs[++i] == nullptr) {
666                 ALOGE("addLimit: name is null");
667                 return -EINVAL;
668             }
669             a_name = attrs[i];
670         } else if (strEq(attrs[i], "default")) {
671             if (attrs[++i] == nullptr) {
672                 ALOGE("addLimit: default is null");
673                 return -EINVAL;
674             }
675             a_default = attrs[i];
676         } else if (strEq(attrs[i], "in")) {
677             if (attrs[++i] == nullptr) {
678                 ALOGE("addLimit: in is null");
679                 return -EINVAL;
680             }
681             a_in = attrs[i];
682         } else if (strEq(attrs[i], "max")) {
683             if (attrs[++i] == nullptr) {
684                 ALOGE("addLimit: max is null");
685                 return -EINVAL;
686             }
687             a_max = attrs[i];
688         } else if (strEq(attrs[i], "min")) {
689             if (attrs[++i] == nullptr) {
690                 ALOGE("addLimit: min is null");
691                 return -EINVAL;
692             }
693             a_min = attrs[i];
694         } else if (strEq(attrs[i], "range")) {
695             if (attrs[++i] == nullptr) {
696                 ALOGE("addLimit: range is null");
697                 return -EINVAL;
698             }
699             a_range = attrs[i];
700         } else if (strEq(attrs[i], "ranges")) {
701             if (attrs[++i] == nullptr) {
702                 ALOGE("addLimit: ranges is null");
703                 return -EINVAL;
704             }
705             a_ranges = attrs[i];
706         } else if (strEq(attrs[i], "scale")) {
707             if (attrs[++i] == nullptr) {
708                 ALOGE("addLimit: scale is null");
709                 return -EINVAL;
710             }
711             a_scale = attrs[i];
712         } else if (strEq(attrs[i], "value")) {
713             if (attrs[++i] == nullptr) {
714                 ALOGE("addLimit: value is null");
715                 return -EINVAL;
716             }
717             a_value = attrs[i];
718         } else {
719             ALOGE("addLimit: unrecognized limit: %s", attrs[i]);
720             return -EINVAL;
721         }
722         ++i;
723     }
724 
725     if (a_name == nullptr) {
726         ALOGE("limit with no 'name' attribute");
727         return -EINVAL;
728     }
729 
730     // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
731     // measured-frame-rate, measured-blocks-per-second: range
732     // quality: range + default + [scale]
733     // complexity: range + default
734     if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
735         ALOGW("ignoring null type");
736         return OK;
737     }
738 
739     std::string range;
740     if (strEq(a_name, "aspect-ratio") ||
741             strEq(a_name, "bitrate") ||
742             strEq(a_name, "block-count") ||
743             strEq(a_name, "blocks-per-second") ||
744             strEq(a_name, "complexity") ||
745             strEq(a_name, "frame-rate") ||
746             strEq(a_name, "quality") ||
747             strEq(a_name, "size") ||
748             strEq(a_name, "measured-blocks-per-second") ||
749             strHasPrefix(a_name, "measured-frame-rate-")) {
750         // "range" is specified in exactly one of the following forms:
751         // 1) min-max
752         // 2) value-value
753         // 3) range
754         if (a_min != nullptr && a_max != nullptr) {
755             // min-max
756             if (a_range != nullptr || a_value != nullptr) {
757                 return limitError(a_name, "has 'min' and 'max' as well as 'range' or "
758                         "'value' attributes");
759             }
760             range = a_min;
761             range += '-';
762             range += a_max;
763         } else if (a_min != nullptr || a_max != nullptr) {
764             return limitError(a_name, "has only 'min' or 'max' attribute");
765         } else if (a_value != nullptr) {
766             // value-value
767             if (a_range != nullptr) {
768                 return limitError(a_name, "has both 'range' and 'value' attributes");
769             }
770             range = a_value;
771             range += '-';
772             range += a_value;
773         } else if (a_range == nullptr) {
774             return limitError(a_name, "with no 'range', 'value' or 'min'/'max' attributes");
775         } else {
776             // range
777             range = a_range;
778         }
779 
780         // "aspect-ratio" requires some special treatment.
781         if (strEq(a_name, "aspect-ratio")) {
782             // "aspect-ratio" must have "in".
783             if (a_in == nullptr) {
784                 return limitFoundMissingAttr(a_name, "in", false);
785             }
786             // "in" must be either "pixels" or "blocks".
787             if (!strEq(a_in, "pixels") && !strEq(a_in, "blocks")) {
788                 return limitInvalidAttr(a_name, "in", a_in);
789             }
790             // name will be "pixel-aspect-ratio-range" or
791             // "block-aspect-ratio-range".
792             mCurrentType->second[
793                     std::string(a_in).substr(0, strlen(a_in) - 1) +
794                     "-aspect-ratio-range"] = range;
795         } else {
796             // For everything else (apart from "aspect-ratio"), simply append
797             // "-range" to the name for the range-type property.
798             mCurrentType->second[std::string(a_name) + "-range"] = range;
799 
800             // Only "quality" may have "scale".
801             if (!strEq(a_name, "quality") && a_scale != nullptr) {
802                 return limitFoundMissingAttr(a_name, "scale");
803             } else if (strEq(a_name, "quality")) {
804                 // The default value of "quality-scale" is "linear".
805                 mCurrentType->second["quality-scale"] = a_scale == nullptr ?
806                         "linear" : a_scale;
807             }
808 
809             // "quality" and "complexity" must have "default".
810             // Other limits must not have "default".
811             if (strEq(a_name, "quality") || strEq(a_name, "complexity")) {
812                 if (a_default == nullptr) {
813                     return limitFoundMissingAttr(a_name, "default", false);
814                 }
815                 // name will be "quality-default" or "complexity-default".
816                 mCurrentType->second[std::string(a_name) + "-default"] = a_default;
817             } else if (a_default != nullptr) {
818                 return limitFoundMissingAttr(a_name, "default", true);
819             }
820         }
821     } else {
822         if (a_default != nullptr) {
823             return limitFoundMissingAttr(a_name, "default");
824         }
825         if (a_in != nullptr) {
826             return limitFoundMissingAttr(a_name, "in");
827         }
828         if (a_scale != nullptr) {
829             return limitFoundMissingAttr(a_name, "scale");
830         }
831         if (a_range != nullptr) {
832             return limitFoundMissingAttr(a_name, "range");
833         }
834         if (a_min != nullptr) {
835             return limitFoundMissingAttr(a_name, "min");
836         }
837 
838         if (a_max != nullptr) {
839             // "max" must exist if and only if name is "channel-count" or
840             // "concurrent-instances".
841             // "min" is not ncessary.
842             if (strEq(a_name, "channel-count") ||
843                     strEq(a_name, "concurrent-instances")) {
844                 mCurrentType->second[std::string("max-") + a_name] = a_max;
845             } else {
846                 return limitFoundMissingAttr(a_name, "max", false);
847             }
848         } else if (strEq(a_name, "channel-count") ||
849                 strEq(a_name, "concurrent-instances")) {
850             return limitFoundMissingAttr(a_name, "max");
851         }
852 
853         if (a_ranges != nullptr) {
854             // "ranges" must exist if and only if name is "sample-rate".
855             if (strEq(a_name, "sample-rate")) {
856                 mCurrentType->second["sample-rate-ranges"] = a_ranges;
857             } else {
858                 return limitFoundMissingAttr(a_name, "ranges", false);
859             }
860         } else if (strEq(a_name, "sample-rate")) {
861             return limitFoundMissingAttr(a_name, "ranges");
862         }
863 
864         if (a_value != nullptr) {
865             // "value" must exist if and only if name is "alignment" or
866             // "block-size".
867             if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
868                 mCurrentType->second[a_name] = a_value;
869             } else {
870                 return limitFoundMissingAttr(a_name, "value", false);
871             }
872         } else if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
873             return limitFoundMissingAttr(a_name, "value", false);
874         }
875 
876     }
877 
878     return OK;
879 }
880 
addFeature(const char ** attrs)881 status_t MediaCodecsXmlParser::addFeature(const char **attrs) {
882     size_t i = 0;
883     const char *name = nullptr;
884     int32_t optional = -1;
885     int32_t required = -1;
886     const char *value = nullptr;
887 
888     while (attrs[i] != nullptr) {
889         if (strEq(attrs[i], "name")) {
890             if (attrs[++i] == nullptr) {
891                 ALOGE("addFeature: name is null");
892                 return -EINVAL;
893             }
894             name = attrs[i];
895         } else if (strEq(attrs[i], "optional")) {
896             if (attrs[++i] == nullptr) {
897                 ALOGE("addFeature: optional is null");
898                 return -EINVAL;
899             }
900             optional = parseBoolean(attrs[i]) ? 1 : 0;
901         } else if (strEq(attrs[i], "required")) {
902             if (attrs[++i] == nullptr) {
903                 ALOGE("addFeature: required is null");
904                 return -EINVAL;
905             }
906             required = parseBoolean(attrs[i]) ? 1 : 0;
907         } else if (strEq(attrs[i], "value")) {
908             if (attrs[++i] == nullptr) {
909                 ALOGE("addFeature: value is null");
910                 return -EINVAL;
911             }
912             value = attrs[i];
913         } else {
914             ALOGE("addFeature: unrecognized attribute: %s", attrs[i]);
915             return -EINVAL;
916         }
917         ++i;
918     }
919 
920     // Every feature must have a name.
921     if (name == nullptr) {
922         ALOGE("feature with no 'name' attribute");
923         return -EINVAL;
924     }
925 
926     if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
927         ALOGW("ignoring null type");
928         return OK;
929     }
930 
931     if ((optional != -1) || (required != -1)) {
932         if (optional == required) {
933             ALOGE("feature '%s' is both/neither optional and required", name);
934             return -EINVAL;
935         }
936         if ((optional == 1) || (required == 1)) {
937             if (value != nullptr) {
938                 ALOGE("feature '%s' cannot have extra 'value'", name);
939                 return -EINVAL;
940             }
941             mCurrentType->second[std::string("feature-") + name] =
942                     optional == 1 ? "0" : "1";
943             return OK;
944         }
945     }
946     mCurrentType->second[std::string("feature-") + name] = value == nullptr ?
947             "0" : value;
948     return OK;
949 }
950 
951 const MediaCodecsXmlParser::AttributeMap&
getServiceAttributeMap() const952         MediaCodecsXmlParser::getServiceAttributeMap() const {
953     return mServiceAttributeMap;
954 }
955 
956 const MediaCodecsXmlParser::CodecMap&
getCodecMap() const957         MediaCodecsXmlParser::getCodecMap() const {
958     return mCodecMap;
959 }
960 
961 const MediaCodecsXmlParser::RoleMap&
getRoleMap() const962         MediaCodecsXmlParser::getRoleMap() const {
963     if (mRoleMap.empty()) {
964         generateRoleMap();
965     }
966     return mRoleMap;
967 }
968 
getCommonPrefix() const969 const char* MediaCodecsXmlParser::getCommonPrefix() const {
970     if (mCommonPrefix.empty()) {
971         generateCommonPrefix();
972     }
973     return mCommonPrefix.data();
974 }
975 
getParsingStatus() const976 status_t MediaCodecsXmlParser::getParsingStatus() const {
977     return mParsingStatus;
978 }
979 
generateRoleMap() const980 void MediaCodecsXmlParser::generateRoleMap() const {
981     for (const auto& codec : mCodecMap) {
982         const auto& codecName = codec.first;
983         bool isEncoder = codec.second.isEncoder;
984         size_t order = codec.second.order;
985         const auto& typeMap = codec.second.typeMap;
986         for (const auto& type : typeMap) {
987             const auto& typeName = type.first;
988             const char* roleName = GetComponentRole(isEncoder, typeName.data());
989             if (roleName == nullptr) {
990                 ALOGE("Cannot find the role for %s of type %s",
991                         isEncoder ? "an encoder" : "a decoder",
992                         typeName.data());
993                 continue;
994             }
995             const auto& typeAttributeMap = type.second;
996 
997             auto roleIterator = mRoleMap.find(roleName);
998             std::multimap<size_t, NodeInfo>* nodeList;
999             if (roleIterator == mRoleMap.end()) {
1000                 RoleProperties roleProperties;
1001                 roleProperties.type = typeName;
1002                 roleProperties.isEncoder = isEncoder;
1003                 auto insertResult = mRoleMap.insert(
1004                         std::make_pair(roleName, roleProperties));
1005                 if (!insertResult.second) {
1006                     ALOGE("Cannot add role %s", roleName);
1007                     continue;
1008                 }
1009                 nodeList = &insertResult.first->second.nodeList;
1010             } else {
1011                 if (roleIterator->second.type != typeName) {
1012                     ALOGE("Role %s has mismatching types: %s and %s",
1013                             roleName,
1014                             roleIterator->second.type.data(),
1015                             typeName.data());
1016                     continue;
1017                 }
1018                 if (roleIterator->second.isEncoder != isEncoder) {
1019                     ALOGE("Role %s cannot be both an encoder and a decoder",
1020                             roleName);
1021                     continue;
1022                 }
1023                 nodeList = &roleIterator->second.nodeList;
1024             }
1025 
1026             NodeInfo nodeInfo;
1027             nodeInfo.name = codecName;
1028             nodeInfo.attributeList.reserve(typeAttributeMap.size());
1029             for (const auto& attribute : typeAttributeMap) {
1030                 nodeInfo.attributeList.push_back(
1031                         Attribute{attribute.first, attribute.second});
1032             }
1033             nodeList->insert(std::make_pair(
1034                     std::move(order), std::move(nodeInfo)));
1035         }
1036     }
1037 }
1038 
generateCommonPrefix() const1039 void MediaCodecsXmlParser::generateCommonPrefix() const {
1040     if (mCodecMap.empty()) {
1041         return;
1042     }
1043     auto i = mCodecMap.cbegin();
1044     auto first = i->first.cbegin();
1045     auto last = i->first.cend();
1046     for (++i; i != mCodecMap.cend(); ++i) {
1047         last = std::mismatch(
1048                 first, last, i->first.cbegin(), i->first.cend()).first;
1049     }
1050     mCommonPrefix.insert(mCommonPrefix.begin(), first, last);
1051 }
1052 
1053 } // namespace android
1054 
1055