codec2: use media_codecs from apex and support variants/domains

- prefer XML from apex
- allow vendors/OEMs to override the enablement of codecs, media types
  domains and variant selectors
- propagate global settings from this info builder

Bug: 129710438
Change-Id: I0858ae982b4228ad1498cc1e7175956d8f5737af
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 5231b63..1919597 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -212,6 +212,76 @@
     }
 }
 
+class Switch {
+    enum Flags : uint8_t {
+        // flags
+        IS_ENABLED = (1 << 0),
+        BY_DEFAULT = (1 << 1),
+    };
+
+    constexpr Switch(uint8_t flags) : mFlags(flags) {}
+
+    uint8_t mFlags;
+
+public:
+    // have to create class due to this bool conversion operator...
+    constexpr operator bool() const {
+        return mFlags & IS_ENABLED;
+    }
+
+    constexpr Switch operator!() const {
+        return Switch(mFlags ^ IS_ENABLED);
+    }
+
+    static constexpr Switch DISABLED() { return 0; };
+    static constexpr Switch ENABLED() { return IS_ENABLED; };
+    static constexpr Switch DISABLED_BY_DEFAULT() { return BY_DEFAULT; };
+    static constexpr Switch ENABLED_BY_DEFAULT() { return IS_ENABLED | BY_DEFAULT; };
+
+    const char *toString(const char *def = "??") const {
+        switch (mFlags) {
+        case 0:                         return "0";
+        case IS_ENABLED:                return "1";
+        case BY_DEFAULT:                return "(0)";
+        case IS_ENABLED | BY_DEFAULT:   return "(1)";
+        default: return def;
+        }
+    }
+
+};
+
+const char *asString(const Switch &s, const char *def = "??") {
+    return s.toString(def);
+}
+
+Switch isSettingEnabled(
+        std::string setting, const MediaCodecsXmlParser::AttributeMap &settings,
+        Switch def = Switch::DISABLED_BY_DEFAULT()) {
+    const auto enablement = settings.find(setting);
+    if (enablement == settings.end()) {
+        return def;
+    }
+    return enablement->second == "1" ? Switch::ENABLED() : Switch::DISABLED();
+}
+
+Switch isVariantEnabled(
+        std::string variant, const MediaCodecsXmlParser::AttributeMap &settings) {
+    return isSettingEnabled("variant-" + variant, settings);
+}
+
+Switch isVariantExpressionEnabled(
+        std::string exp, const MediaCodecsXmlParser::AttributeMap &settings) {
+    if (!exp.empty() && exp.at(0) == '!') {
+        return !isVariantEnabled(exp.substr(1, exp.size() - 1), settings);
+    }
+    return isVariantEnabled(exp, settings);
+}
+
+Switch isDomainEnabled(
+        std::string domain, const MediaCodecsXmlParser::AttributeMap &settings) {
+    return isSettingEnabled("domain-" + domain, settings);
+}
+
 } // unnamed namespace
 
 status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
@@ -248,14 +318,33 @@
     // Obtain Codec2Client
     std::vector<Traits> traits = Codec2Client::ListComponents();
 
+    // parse APEX XML first, followed by vendor XML
     MediaCodecsXmlParser parser;
     parser.parseXmlFilesInSearchDirs(
+            parser.getDefaultXmlNames(),
+            { "/apex/com.android.media.swcodec/etc" });
+
+    // TODO: remove these c2-specific files once product moved to default file names
+    parser.parseXmlFilesInSearchDirs(
             { "media_codecs_c2.xml", "media_codecs_performance_c2.xml" });
+
+    // parse default XML files
+    parser.parseXmlFilesInSearchDirs();
+
     if (parser.getParsingStatus() != OK) {
         ALOGD("XML parser no good");
         return OK;
     }
 
+    MediaCodecsXmlParser::AttributeMap settings = parser.getServiceAttributeMap();
+    for (const auto &v : settings) {
+        if (!hasPrefix(v.first, "media-type-")
+                && !hasPrefix(v.first, "domain-")
+                && !hasPrefix(v.first, "variant-")) {
+            writer->addGlobalSetting(v.first.c_str(), v.second.c_str());
+        }
+    }
+
     for (const Traits& trait : traits) {
         C2Component::rank_t rank = trait.rank;
 
@@ -340,12 +429,42 @@
                 break;
             }
 
+            const MediaCodecsXmlParser::CodecProperties &codec =
+                parser.getCodecMap().at(nameOrAlias);
+
+            // verify that either the codec is explicitly enabled, or one of its domains is
+            bool codecEnabled = codec.quirkSet.find("attribute::disabled") == codec.quirkSet.end();
+            if (!codecEnabled) {
+                for (const std::string &domain : codec.domainSet) {
+                    const Switch enabled = isDomainEnabled(domain, settings);
+                    ALOGV("codec entry '%s' is in domain '%s' that is '%s'",
+                            nameOrAlias.c_str(), domain.c_str(), asString(enabled));
+                    if (enabled) {
+                        codecEnabled = true;
+                        break;
+                    }
+                }
+            }
+            // if codec has variants, also check that at least one of them is enabled
+            bool variantEnabled = codec.variantSet.empty();
+            for (const std::string &variant : codec.variantSet) {
+                const Switch enabled = isVariantExpressionEnabled(variant, settings);
+                ALOGV("codec entry '%s' has a variant '%s' that is '%s'",
+                        nameOrAlias.c_str(), variant.c_str(), asString(enabled));
+                if (enabled) {
+                    variantEnabled = true;
+                    break;
+                }
+            }
+            if (!codecEnabled || !variantEnabled) {
+                ALOGD("codec entry for '%s' is disabled", nameOrAlias.c_str());
+                continue;
+            }
+
             ALOGV("adding codec entry for '%s'", nameOrAlias.c_str());
             std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
             codecInfo->setName(nameOrAlias.c_str());
             codecInfo->setOwner(("codec2::" + trait.owner).c_str());
-            const MediaCodecsXmlParser::CodecProperties &codec =
-                parser.getCodecMap().at(nameOrAlias);
 
             bool encoder = trait.kind == C2Component::KIND_ENCODER;
             typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
@@ -372,6 +491,7 @@
                     rank = xmlRank;
                 }
             }
+            ALOGV("rank: %u", (unsigned)rank);
             codecInfo->setRank(rank);
 
             for (const std::string &alias : codec.aliases) {
@@ -381,12 +501,39 @@
 
             for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) {
                 const std::string &mediaType = typeIt->first;
+                const Switch typeEnabled = isSettingEnabled(
+                        "media-type-" + mediaType, settings, Switch::ENABLED_BY_DEFAULT());
+                const Switch domainTypeEnabled = isSettingEnabled(
+                        "media-type-" + mediaType + (encoder ? "-encoder" : "-decoder"),
+                        settings, Switch::ENABLED_BY_DEFAULT());
+                ALOGV("type '%s-%s' is '%s/%s'",
+                        mediaType.c_str(), (encoder ? "encoder" : "decoder"),
+                        asString(typeEnabled), asString(domainTypeEnabled));
+                if (!typeEnabled || !domainTypeEnabled) {
+                    ALOGD("media type '%s' for codec entry '%s' is disabled", mediaType.c_str(),
+                            nameOrAlias.c_str());
+                    continue;
+                }
+
+                ALOGI("adding type '%s'", typeIt->first.c_str());
                 const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second;
                 std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
                     codecInfo->addMediaType(mediaType.c_str());
-                for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) {
-                    std::string key, value;
-                    std::tie(key, value) = *attrIt;
+                for (const auto &v : attrMap) {
+                    std::string key = v.first;
+                    std::string value = v.second;
+
+                    size_t variantSep = key.find(":::");
+                    if (variantSep != std::string::npos) {
+                        std::string variant = key.substr(0, variantSep);
+                        const Switch enabled = isVariantExpressionEnabled(variant, settings);
+                        ALOGV("variant '%s' is '%s'", variant.c_str(), asString(enabled));
+                        if (!enabled) {
+                            continue;
+                        }
+                        key = key.substr(variantSep + 3);
+                    }
+
                     if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) {
                         int32_t intValue = 0;
                         // Ignore trailing bad characters and default to 0.