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 #include <utils/Log.h>
20 
21 #include <media/vndk/xmlparser/1.0/MediaCodecsXmlParser.h>
22 
23 #include <media/MediaCodecInfo.h>
24 
25 #include <media/stagefright/foundation/ADebug.h>
26 #include <media/stagefright/foundation/AMessage.h>
27 #include <media/stagefright/foundation/AUtils.h>
28 #include <media/stagefright/MediaErrors.h>
29 
30 #include <sys/stat.h>
31 
32 #include <expat.h>
33 #include <string>
34 
35 #define MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH 256
36 
37 namespace android {
38 
39 namespace { // Local variables and functions
40 
41 const char *kProfilingResults =
42         "/data/misc/media/media_codecs_profiling_results.xml";
43 
44 // Treblized media codec list will be located in /odm/etc or /vendor/etc.
45 const char *kConfigLocationList[] =
46         {"/odm/etc", "/vendor/etc", "/etc"};
47 constexpr int kConfigLocationListSize =
48         (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
49 
findMediaCodecListFileFullPath(const char * file_name,std::string * out_path)50 bool findMediaCodecListFileFullPath(
51         const char *file_name, std::string *out_path) {
52     for (int i = 0; i < kConfigLocationListSize; i++) {
53         *out_path = std::string(kConfigLocationList[i]) + "/" + file_name;
54         struct stat file_stat;
55         if (stat(out_path->c_str(), &file_stat) == 0 &&
56                 S_ISREG(file_stat.st_mode)) {
57             return true;
58         }
59     }
60     return false;
61 }
62 
63 // Find TypeInfo by name.
findTypeInfo(CodecInfo & codecInfo,const AString & typeName)64 std::vector<TypeInfo>::iterator findTypeInfo(
65         CodecInfo &codecInfo, const AString &typeName) {
66     return std::find_if(
67             codecInfo.mTypes.begin(), codecInfo.mTypes.end(),
68             [typeName](const auto &typeInfo) {
69                 return typeInfo.mName == typeName;
70             });
71 }
72 
73 // Convert a string into a boolean value.
ParseBoolean(const char * s)74 bool ParseBoolean(const char *s) {
75     if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
76         return true;
77     }
78     char *end;
79     unsigned long res = strtoul(s, &end, 10);
80     return *s != '\0' && *end == '\0' && res > 0;
81 }
82 
83 } // unnamed namespace
84 
MediaCodecsXmlParser()85 MediaCodecsXmlParser::MediaCodecsXmlParser() :
86     mInitCheck(NO_INIT),
87     mUpdate(false) {
88     std::string config_file_path;
89     if (findMediaCodecListFileFullPath(
90             "media_codecs.xml", &config_file_path)) {
91         parseTopLevelXMLFile(config_file_path.c_str(), false);
92     } else {
93         mInitCheck = NAME_NOT_FOUND;
94     }
95     if (findMediaCodecListFileFullPath(
96             "media_codecs_performance.xml", &config_file_path)) {
97         parseTopLevelXMLFile(config_file_path.c_str(), true);
98     }
99     parseTopLevelXMLFile(kProfilingResults, true);
100 }
101 
parseTopLevelXMLFile(const char * codecs_xml,bool ignore_errors)102 void MediaCodecsXmlParser::parseTopLevelXMLFile(
103         const char *codecs_xml, bool ignore_errors) {
104     // get href_base
105     const char *href_base_end = strrchr(codecs_xml, '/');
106     if (href_base_end != NULL) {
107         mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
108     }
109 
110     mInitCheck = OK; // keeping this here for safety
111     mCurrentSection = SECTION_TOPLEVEL;
112     mDepth = 0;
113 
114     parseXMLFile(codecs_xml);
115 
116     if (mInitCheck != OK) {
117         if (ignore_errors) {
118             mInitCheck = OK;
119             return;
120         }
121         mCodecInfos.clear();
122         return;
123     }
124 }
125 
~MediaCodecsXmlParser()126 MediaCodecsXmlParser::~MediaCodecsXmlParser() {
127 }
128 
initCheck() const129 status_t MediaCodecsXmlParser::initCheck() const {
130     return mInitCheck;
131 }
132 
parseXMLFile(const char * path)133 void MediaCodecsXmlParser::parseXMLFile(const char *path) {
134     FILE *file = fopen(path, "r");
135 
136     if (file == NULL) {
137         ALOGW("unable to open media codecs configuration xml file: %s", path);
138         mInitCheck = NAME_NOT_FOUND;
139         return;
140     }
141 
142     ALOGV("Start parsing %s", path);
143     XML_Parser parser = ::XML_ParserCreate(NULL);
144     CHECK(parser != NULL);
145 
146     ::XML_SetUserData(parser, this);
147     ::XML_SetElementHandler(
148             parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
149 
150     const int BUFF_SIZE = 512;
151     while (mInitCheck == OK) {
152         void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
153         if (buff == NULL) {
154             ALOGE("failed in call to XML_GetBuffer()");
155             mInitCheck = UNKNOWN_ERROR;
156             break;
157         }
158 
159         int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
160         if (bytes_read < 0) {
161             ALOGE("failed in call to read");
162             mInitCheck = ERROR_IO;
163             break;
164         }
165 
166         XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
167         if (status != XML_STATUS_OK) {
168             ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
169             mInitCheck = ERROR_MALFORMED;
170             break;
171         }
172 
173         if (bytes_read == 0) {
174             break;
175         }
176     }
177 
178     ::XML_ParserFree(parser);
179 
180     fclose(file);
181     file = NULL;
182 }
183 
184 // static
StartElementHandlerWrapper(void * me,const char * name,const char ** attrs)185 void MediaCodecsXmlParser::StartElementHandlerWrapper(
186         void *me, const char *name, const char **attrs) {
187     static_cast<MediaCodecsXmlParser *>(me)->startElementHandler(name, attrs);
188 }
189 
190 // static
EndElementHandlerWrapper(void * me,const char * name)191 void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) {
192     static_cast<MediaCodecsXmlParser *>(me)->endElementHandler(name);
193 }
194 
includeXMLFile(const char ** attrs)195 status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) {
196     const char *href = NULL;
197     size_t i = 0;
198     while (attrs[i] != NULL) {
199         if (!strcmp(attrs[i], "href")) {
200             if (attrs[i + 1] == NULL) {
201                 return -EINVAL;
202             }
203             href = attrs[i + 1];
204             ++i;
205         } else {
206             ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]);
207             return -EINVAL;
208         }
209         ++i;
210     }
211 
212     // For security reasons and for simplicity, file names can only contain
213     // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
214     for (i = 0; href[i] != '\0'; i++) {
215         if (href[i] == '.' || href[i] == '_' ||
216                 (href[i] >= '0' && href[i] <= '9') ||
217                 (href[i] >= 'A' && href[i] <= 'Z') ||
218                 (href[i] >= 'a' && href[i] <= 'z')) {
219             continue;
220         }
221         ALOGE("invalid include file name: %s", href);
222         return -EINVAL;
223     }
224 
225     AString filename = href;
226     if (!filename.startsWith("media_codecs_") ||
227         !filename.endsWith(".xml")) {
228         ALOGE("invalid include file name: %s", href);
229         return -EINVAL;
230     }
231     filename.insert(mHrefBase, 0);
232 
233     parseXMLFile(filename.c_str());
234     return mInitCheck;
235 }
236 
startElementHandler(const char * name,const char ** attrs)237 void MediaCodecsXmlParser::startElementHandler(
238         const char *name, const char **attrs) {
239     if (mInitCheck != OK) {
240         return;
241     }
242 
243     bool inType = true;
244 
245     if (!strcmp(name, "Include")) {
246         mInitCheck = includeXMLFile(attrs);
247         if (mInitCheck == OK) {
248             mPastSections.push(mCurrentSection);
249             mCurrentSection = SECTION_INCLUDE;
250         }
251         ++mDepth;
252         return;
253     }
254 
255     switch (mCurrentSection) {
256         case SECTION_TOPLEVEL:
257         {
258             if (!strcmp(name, "Decoders")) {
259                 mCurrentSection = SECTION_DECODERS;
260             } else if (!strcmp(name, "Encoders")) {
261                 mCurrentSection = SECTION_ENCODERS;
262             } else if (!strcmp(name, "Settings")) {
263                 mCurrentSection = SECTION_SETTINGS;
264             }
265             break;
266         }
267 
268         case SECTION_SETTINGS:
269         {
270             if (!strcmp(name, "Setting")) {
271                 mInitCheck = addSettingFromAttributes(attrs);
272             }
273             break;
274         }
275 
276         case SECTION_DECODERS:
277         {
278             if (!strcmp(name, "MediaCodec")) {
279                 mInitCheck =
280                     addMediaCodecFromAttributes(false /* encoder */, attrs);
281 
282                 mCurrentSection = SECTION_DECODER;
283             }
284             break;
285         }
286 
287         case SECTION_ENCODERS:
288         {
289             if (!strcmp(name, "MediaCodec")) {
290                 mInitCheck =
291                     addMediaCodecFromAttributes(true /* encoder */, attrs);
292 
293                 mCurrentSection = SECTION_ENCODER;
294             }
295             break;
296         }
297 
298         case SECTION_DECODER:
299         case SECTION_ENCODER:
300         {
301             if (!strcmp(name, "Quirk")) {
302                 mInitCheck = addQuirk(attrs);
303             } else if (!strcmp(name, "Type")) {
304                 mInitCheck = addTypeFromAttributes(attrs, (mCurrentSection == SECTION_ENCODER));
305                 mCurrentSection =
306                     (mCurrentSection == SECTION_DECODER
307                             ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
308             }
309         }
310         inType = false;
311         // fall through
312 
313         case SECTION_DECODER_TYPE:
314         case SECTION_ENCODER_TYPE:
315         {
316             // ignore limits and features specified outside of type
317             bool outside = !inType && mCurrentType == mCodecInfos[mCurrentName].mTypes.end();
318             if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
319                 ALOGW("ignoring %s specified outside of a Type", name);
320             } else if (!strcmp(name, "Limit")) {
321                 mInitCheck = addLimit(attrs);
322             } else if (!strcmp(name, "Feature")) {
323                 mInitCheck = addFeature(attrs);
324             }
325             break;
326         }
327 
328         default:
329             break;
330     }
331 
332     ++mDepth;
333 }
334 
endElementHandler(const char * name)335 void MediaCodecsXmlParser::endElementHandler(const char *name) {
336     if (mInitCheck != OK) {
337         return;
338     }
339 
340     switch (mCurrentSection) {
341         case SECTION_SETTINGS:
342         {
343             if (!strcmp(name, "Settings")) {
344                 mCurrentSection = SECTION_TOPLEVEL;
345             }
346             break;
347         }
348 
349         case SECTION_DECODERS:
350         {
351             if (!strcmp(name, "Decoders")) {
352                 mCurrentSection = SECTION_TOPLEVEL;
353             }
354             break;
355         }
356 
357         case SECTION_ENCODERS:
358         {
359             if (!strcmp(name, "Encoders")) {
360                 mCurrentSection = SECTION_TOPLEVEL;
361             }
362             break;
363         }
364 
365         case SECTION_DECODER_TYPE:
366         case SECTION_ENCODER_TYPE:
367         {
368             if (!strcmp(name, "Type")) {
369                 mCurrentSection =
370                     (mCurrentSection == SECTION_DECODER_TYPE
371                             ? SECTION_DECODER : SECTION_ENCODER);
372 
373                 mCurrentType = mCodecInfos[mCurrentName].mTypes.end();
374             }
375             break;
376         }
377 
378         case SECTION_DECODER:
379         {
380             if (!strcmp(name, "MediaCodec")) {
381                 mCurrentSection = SECTION_DECODERS;
382                 mCurrentName.clear();
383             }
384             break;
385         }
386 
387         case SECTION_ENCODER:
388         {
389             if (!strcmp(name, "MediaCodec")) {
390                 mCurrentSection = SECTION_ENCODERS;
391                 mCurrentName.clear();
392             }
393             break;
394         }
395 
396         case SECTION_INCLUDE:
397         {
398             if (!strcmp(name, "Include") && mPastSections.size() > 0) {
399                 mCurrentSection = mPastSections.top();
400                 mPastSections.pop();
401             }
402             break;
403         }
404 
405         default:
406             break;
407     }
408 
409     --mDepth;
410 }
411 
addSettingFromAttributes(const char ** attrs)412 status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) {
413     const char *name = NULL;
414     const char *value = NULL;
415     const char *update = NULL;
416 
417     size_t i = 0;
418     while (attrs[i] != NULL) {
419         if (!strcmp(attrs[i], "name")) {
420             if (attrs[i + 1] == NULL) {
421                 ALOGE("addSettingFromAttributes: name is null");
422                 return -EINVAL;
423             }
424             name = attrs[i + 1];
425             ++i;
426         } else if (!strcmp(attrs[i], "value")) {
427             if (attrs[i + 1] == NULL) {
428                 ALOGE("addSettingFromAttributes: value is null");
429                 return -EINVAL;
430             }
431             value = attrs[i + 1];
432             ++i;
433         } else if (!strcmp(attrs[i], "update")) {
434             if (attrs[i + 1] == NULL) {
435                 ALOGE("addSettingFromAttributes: update is null");
436                 return -EINVAL;
437             }
438             update = attrs[i + 1];
439             ++i;
440         } else {
441             ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]);
442             return -EINVAL;
443         }
444 
445         ++i;
446     }
447 
448     if (name == NULL || value == NULL) {
449         ALOGE("addSettingFromAttributes: name or value unspecified");
450         return -EINVAL;
451     }
452 
453     mUpdate = (update != NULL) && ParseBoolean(update);
454     if (mUpdate != (mGlobalSettings.count(name) > 0)) {
455         ALOGE("addSettingFromAttributes: updating non-existing setting");
456         return -EINVAL;
457     }
458     mGlobalSettings[name] = value;
459 
460     return OK;
461 }
462 
addMediaCodecFromAttributes(bool encoder,const char ** attrs)463 status_t MediaCodecsXmlParser::addMediaCodecFromAttributes(
464         bool encoder, const char **attrs) {
465     const char *name = NULL;
466     const char *type = NULL;
467     const char *update = NULL;
468 
469     size_t i = 0;
470     while (attrs[i] != NULL) {
471         if (!strcmp(attrs[i], "name")) {
472             if (attrs[i + 1] == NULL) {
473                 ALOGE("addMediaCodecFromAttributes: name is null");
474                 return -EINVAL;
475             }
476             name = attrs[i + 1];
477             ++i;
478         } else if (!strcmp(attrs[i], "type")) {
479             if (attrs[i + 1] == NULL) {
480                 ALOGE("addMediaCodecFromAttributes: type is null");
481                 return -EINVAL;
482             }
483             type = attrs[i + 1];
484             ++i;
485         } else if (!strcmp(attrs[i], "update")) {
486             if (attrs[i + 1] == NULL) {
487                 ALOGE("addMediaCodecFromAttributes: update is null");
488                 return -EINVAL;
489             }
490             update = attrs[i + 1];
491             ++i;
492         } else {
493             ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
494             return -EINVAL;
495         }
496 
497         ++i;
498     }
499 
500     if (name == NULL) {
501         ALOGE("addMediaCodecFromAttributes: name not found");
502         return -EINVAL;
503     }
504 
505     mUpdate = (update != NULL) && ParseBoolean(update);
506     if (mUpdate != (mCodecInfos.count(name) > 0)) {
507         ALOGE("addMediaCodecFromAttributes: updating non-existing codec or vice versa");
508         return -EINVAL;
509     }
510 
511     CodecInfo *info = &mCodecInfos[name];
512     if (mUpdate) {
513         // existing codec
514         mCurrentName = name;
515         mCurrentType = info->mTypes.begin();
516         if (type != NULL) {
517             // existing type
518             mCurrentType = findTypeInfo(*info, type);
519             if (mCurrentType == info->mTypes.end()) {
520                 ALOGE("addMediaCodecFromAttributes: updating non-existing type");
521                 return -EINVAL;
522             }
523         }
524     } else {
525         // new codec
526         mCurrentName = name;
527         mQuirks[name].clear();
528         info->mTypes.clear();
529         info->mTypes.emplace_back();
530         mCurrentType = --info->mTypes.end();
531         mCurrentType->mName = type;
532         info->mIsEncoder = encoder;
533     }
534 
535     return OK;
536 }
537 
addQuirk(const char ** attrs)538 status_t MediaCodecsXmlParser::addQuirk(const char **attrs) {
539     const char *name = NULL;
540 
541     size_t i = 0;
542     while (attrs[i] != NULL) {
543         if (!strcmp(attrs[i], "name")) {
544             if (attrs[i + 1] == NULL) {
545                 ALOGE("addQuirk: name is null");
546                 return -EINVAL;
547             }
548             name = attrs[i + 1];
549             ++i;
550         } else {
551             ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]);
552             return -EINVAL;
553         }
554 
555         ++i;
556     }
557 
558     if (name == NULL) {
559         ALOGE("addQuirk: name not found");
560         return -EINVAL;
561     }
562 
563     mQuirks[mCurrentName].emplace_back(name);
564     return OK;
565 }
566 
addTypeFromAttributes(const char ** attrs,bool encoder)567 status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) {
568     const char *name = NULL;
569     const char *update = NULL;
570 
571     size_t i = 0;
572     while (attrs[i] != NULL) {
573         if (!strcmp(attrs[i], "name")) {
574             if (attrs[i + 1] == NULL) {
575                 ALOGE("addTypeFromAttributes: name is null");
576                 return -EINVAL;
577             }
578             name = attrs[i + 1];
579             ++i;
580         } else if (!strcmp(attrs[i], "update")) {
581             if (attrs[i + 1] == NULL) {
582                 ALOGE("addTypeFromAttributes: update is null");
583                 return -EINVAL;
584             }
585             update = attrs[i + 1];
586             ++i;
587         } else {
588             ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]);
589             return -EINVAL;
590         }
591 
592         ++i;
593     }
594 
595     if (name == NULL) {
596         return -EINVAL;
597     }
598 
599     CodecInfo *info = &mCodecInfos[mCurrentName];
600     info->mIsEncoder = encoder;
601     mCurrentType = findTypeInfo(*info, name);
602     if (!mUpdate) {
603         if (mCurrentType != info->mTypes.end()) {
604             ALOGE("addTypeFromAttributes: re-defining existing type without update");
605             return -EINVAL;
606         }
607         info->mTypes.emplace_back();
608         mCurrentType = --info->mTypes.end();
609     } else if (mCurrentType == info->mTypes.end()) {
610         ALOGE("addTypeFromAttributes: updating non-existing type");
611         return -EINVAL;
612     }
613 
614     return OK;
615 }
616 
limitFoundMissingAttr(const AString & name,const char * attr,bool found=true)617 static status_t limitFoundMissingAttr(const AString &name, const char *attr, bool found = true) {
618     ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
619             (found ? "" : "no "), attr);
620     return -EINVAL;
621 }
622 
limitError(const AString & name,const char * msg)623 static status_t limitError(const AString &name, const char *msg) {
624     ALOGE("limit '%s' %s", name.c_str(), msg);
625     return -EINVAL;
626 }
627 
limitInvalidAttr(const AString & name,const char * attr,const AString & value)628 static status_t limitInvalidAttr(const AString &name, const char *attr, const AString &value) {
629     ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
630             attr, value.c_str());
631     return -EINVAL;
632 }
633 
addLimit(const char ** attrs)634 status_t MediaCodecsXmlParser::addLimit(const char **attrs) {
635     sp<AMessage> msg = new AMessage();
636 
637     size_t i = 0;
638     while (attrs[i] != NULL) {
639         if (attrs[i + 1] == NULL) {
640             ALOGE("addLimit: limit is not given");
641             return -EINVAL;
642         }
643 
644         // attributes with values
645         if (!strcmp(attrs[i], "name")
646                 || !strcmp(attrs[i], "default")
647                 || !strcmp(attrs[i], "in")
648                 || !strcmp(attrs[i], "max")
649                 || !strcmp(attrs[i], "min")
650                 || !strcmp(attrs[i], "range")
651                 || !strcmp(attrs[i], "ranges")
652                 || !strcmp(attrs[i], "scale")
653                 || !strcmp(attrs[i], "value")) {
654             msg->setString(attrs[i], attrs[i + 1]);
655             ++i;
656         } else {
657             ALOGE("addLimit: unrecognized limit: %s", attrs[i]);
658             return -EINVAL;
659         }
660         ++i;
661     }
662 
663     AString name;
664     if (!msg->findString("name", &name)) {
665         ALOGE("limit with no 'name' attribute");
666         return -EINVAL;
667     }
668 
669     // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
670     // measured-frame-rate, measured-blocks-per-second: range
671     // quality: range + default + [scale]
672     // complexity: range + default
673     bool found;
674     if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
675         ALOGW("ignoring null type");
676         return OK;
677     }
678 
679     if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
680             || name == "blocks-per-second" || name == "complexity"
681             || name == "frame-rate" || name == "quality" || name == "size"
682             || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
683         AString min, max;
684         if (msg->findString("min", &min) && msg->findString("max", &max)) {
685             min.append("-");
686             min.append(max);
687             if (msg->contains("range") || msg->contains("value")) {
688                 return limitError(name, "has 'min' and 'max' as well as 'range' or "
689                         "'value' attributes");
690             }
691             msg->setString("range", min);
692         } else if (msg->contains("min") || msg->contains("max")) {
693             return limitError(name, "has only 'min' or 'max' attribute");
694         } else if (msg->findString("value", &max)) {
695             min = max;
696             min.append("-");
697             min.append(max);
698             if (msg->contains("range")) {
699                 return limitError(name, "has both 'range' and 'value' attributes");
700             }
701             msg->setString("range", min);
702         }
703 
704         AString range, scale = "linear", def, in_;
705         if (!msg->findString("range", &range)) {
706             return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
707         }
708 
709         if ((name == "quality" || name == "complexity") ^
710                 (found = msg->findString("default", &def))) {
711             return limitFoundMissingAttr(name, "default", found);
712         }
713         if (name != "quality" && msg->findString("scale", &scale)) {
714             return limitFoundMissingAttr(name, "scale");
715         }
716         if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
717             return limitFoundMissingAttr(name, "in", found);
718         }
719 
720         if (name == "aspect-ratio") {
721             if (!(in_ == "pixels") && !(in_ == "blocks")) {
722                 return limitInvalidAttr(name, "in", in_);
723             }
724             in_.erase(5, 1); // (pixel|block)-aspect-ratio
725             in_.append("-");
726             in_.append(name);
727             name = in_;
728         }
729         if (name == "quality") {
730             mCurrentType->mDetails["quality-scale"] = scale;
731         }
732         if (name == "quality" || name == "complexity") {
733             AString tag = name;
734             tag.append("-default");
735             mCurrentType->mDetails[tag] = def;
736         }
737         AString tag = name;
738         tag.append("-range");
739         mCurrentType->mDetails[tag] = range;
740     } else {
741         AString max, value, ranges;
742         if (msg->contains("default")) {
743             return limitFoundMissingAttr(name, "default");
744         } else if (msg->contains("in")) {
745             return limitFoundMissingAttr(name, "in");
746         } else if ((name == "channel-count" || name == "concurrent-instances") ^
747                 (found = msg->findString("max", &max))) {
748             return limitFoundMissingAttr(name, "max", found);
749         } else if (msg->contains("min")) {
750             return limitFoundMissingAttr(name, "min");
751         } else if (msg->contains("range")) {
752             return limitFoundMissingAttr(name, "range");
753         } else if ((name == "sample-rate") ^
754                 (found = msg->findString("ranges", &ranges))) {
755             return limitFoundMissingAttr(name, "ranges", found);
756         } else if (msg->contains("scale")) {
757             return limitFoundMissingAttr(name, "scale");
758         } else if ((name == "alignment" || name == "block-size") ^
759                 (found = msg->findString("value", &value))) {
760             return limitFoundMissingAttr(name, "value", found);
761         }
762 
763         if (max.size()) {
764             AString tag = "max-";
765             tag.append(name);
766             mCurrentType->mDetails[tag] = max;
767         } else if (value.size()) {
768             mCurrentType->mDetails[name] = value;
769         } else if (ranges.size()) {
770             AString tag = name;
771             tag.append("-ranges");
772             mCurrentType->mDetails[tag] = ranges;
773         } else {
774             ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
775         }
776     }
777 
778     return OK;
779 }
780 
addFeature(const char ** attrs)781 status_t MediaCodecsXmlParser::addFeature(const char **attrs) {
782     size_t i = 0;
783     const char *name = NULL;
784     int32_t optional = -1;
785     int32_t required = -1;
786     const char *value = NULL;
787 
788     while (attrs[i] != NULL) {
789         if (attrs[i + 1] == NULL) {
790             ALOGE("addFeature: feature is not given");
791             return -EINVAL;
792         }
793 
794         // attributes with values
795         if (!strcmp(attrs[i], "name")) {
796             name = attrs[i + 1];
797             ++i;
798         } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
799             int value = (int)ParseBoolean(attrs[i + 1]);
800             if (!strcmp(attrs[i], "optional")) {
801                 optional = value;
802             } else {
803                 required = value;
804             }
805             ++i;
806         } else if (!strcmp(attrs[i], "value")) {
807             value = attrs[i + 1];
808             ++i;
809         } else {
810             ALOGE("addFeature: unrecognized attribute: %s", attrs[i]);
811             return -EINVAL;
812         }
813         ++i;
814     }
815     if (name == NULL) {
816         ALOGE("feature with no 'name' attribute");
817         return -EINVAL;
818     }
819 
820     if (optional == required && optional != -1) {
821         ALOGE("feature '%s' is both/neither optional and required", name);
822         return -EINVAL;
823     }
824 
825     if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
826         ALOGW("ignoring null type");
827         return OK;
828     }
829     if (value != NULL) {
830         mCurrentType->mStringFeatures[name] = value;
831     } else {
832         mCurrentType->mBoolFeatures[name] = (required == 1) || (optional == 0);
833     }
834     return OK;
835 }
836 
getGlobalSettings(std::map<AString,AString> * settings) const837 void MediaCodecsXmlParser::getGlobalSettings(
838         std::map<AString, AString> *settings) const {
839     settings->clear();
840     settings->insert(mGlobalSettings.begin(), mGlobalSettings.end());
841 }
842 
getCodecInfo(const char * name,CodecInfo * info) const843 status_t MediaCodecsXmlParser::getCodecInfo(const char *name, CodecInfo *info) const {
844     if (mCodecInfos.count(name) == 0) {
845         ALOGE("Codec not found with name '%s'", name);
846         return NAME_NOT_FOUND;
847     }
848     *info = mCodecInfos.at(name);
849     return OK;
850 }
851 
getQuirks(const char * name,std::vector<AString> * quirks) const852 status_t MediaCodecsXmlParser::getQuirks(const char *name, std::vector<AString> *quirks) const {
853     if (mQuirks.count(name) == 0) {
854         ALOGE("Codec not found with name '%s'", name);
855         return NAME_NOT_FOUND;
856     }
857     quirks->clear();
858     quirks->insert(quirks->end(), mQuirks.at(name).begin(), mQuirks.at(name).end());
859     return OK;
860 }
861 
862 }  // namespace android
863