Edit

kc3-lang/angle/src/feature_support_util/feature_support_util.cpp

Branch :

  • Show log

    Commit

  • Author : Jordan Bayles
    Date : 2020-06-24 15:12:17
    Hash : 1265dbe6
    Message : Update JsonCpp usage This patch updates usages of the JsonCpp library, including the following changes: 1. Removed unused version variable from the DEPS file. 2. Removed deprecated writer and reader usages, in favor of the new builder pattern inside JsonCpp. 3. Modernized usage of the Json::Value types, including some rewrites to avoid unnecessary heap allocations, and using the new foreach iterators. Bug: chromium:983223 Change-Id: If26abc8be677d905183a23498fbb81256854525c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2265093 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org> Commit-Queue: Geoff Lang <geofflang@chromium.org>

  • src/feature_support_util/feature_support_util.cpp
  • //
    // Copyright 2018 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    // feature_support_util.cpp: Helps client APIs make decisions based on rules
    // data files.  For example, the Android EGL loader uses this library to
    // determine whether to use ANGLE or a native GLES driver.
    
    #include "feature_support_util.h"
    #include <json/json.h>
    #include <string.h>
    #include "common/platform.h"
    #if defined(ANGLE_PLATFORM_ANDROID)
    #    include <android/log.h>
    #    include <unistd.h>
    #endif
    #include <fstream>
    #include <ios>
    #include <list>
    #include <memory>
    #include <sstream>
    #include <utility>
    #include <vector>
    #include "../gpu_info_util/SystemInfo.h"
    
    namespace angle
    {
    
    #if defined(ANGLE_PLATFORM_ANDROID)
    // Define ANGLE_FEATURE_UTIL_LOG_VERBOSE if you want VERBOSE to output
    // ANGLE_FEATURE_UTIL_LOG_VERBOSE is automatically defined when is_debug = true
    
    #    define ERR(...) __android_log_print(ANDROID_LOG_ERROR, "ANGLE", __VA_ARGS__)
    #    define WARN(...) __android_log_print(ANDROID_LOG_WARN, "ANGLE", __VA_ARGS__)
    #    define INFO(...) __android_log_print(ANDROID_LOG_INFO, "ANGLE", __VA_ARGS__)
    #    define DEBUG(...) __android_log_print(ANDROID_LOG_DEBUG, "ANGLE", __VA_ARGS__)
    #    ifdef ANGLE_FEATURE_UTIL_LOG_VERBOSE
    #        define VERBOSE(...) __android_log_print(ANDROID_LOG_VERBOSE, "ANGLE", __VA_ARGS__)
    #    else
    #        define VERBOSE(...) ((void)0)
    #    endif
    #else  // defined(ANDROID)
    #    define ERR(...) printf(__VA_ARGS__)
    #    define WARN(...) printf(__VA_ARGS__)
    #    define INFO(...) printf(__VA_ARGS__)
    #    define DEBUG(...) printf(__VA_ARGS__)
    // Uncomment for debugging.
    //#    define VERBOSE(...) printf(__VA_ARGS__)
    #    define VERBOSE(...)
    #endif  // defined(ANDROID)
    
    // JSON values are generally composed of either:
    //  - Objects, which are a set of comma-separated string:value pairs (note the recursive nature)
    //  - Arrays, which are a set of comma-separated values.
    // We'll call the string in a string:value pair the "identifier".  These identifiers are defined
    // below, as follows:
    
    // The JSON identifier for the top-level set of rules.  This is an object, the value of which is an
    // array of rules.  The rules will be processed in order.  If a rule matches, the rule's version of
    // the answer (true or false) becomes the new answer.  After all rules are processed, the
    // most-recent answer is the final answer.
    constexpr char kJsonRules[] = "Rules";
    // The JSON identifier for a given rule.  A rule is an object, the first string:value pair is this
    // identifier (i.e. "Rule") as the string and the value is a user-firendly description of the rule:
    constexpr char kJsonRule[] = "Rule";
    // Within a rule, the JSON identifier for the answer--whether or not to use ANGLE.  The value is a
    // boolean (i.e. true or false).
    constexpr char kJsonUseANGLE[] = "UseANGLE";
    
    // Within a rule, the JSON identifier for describing one or more applications.  The value is an
    // array of objects, each object of which can specify attributes of an application.
    constexpr char kJsonApplications[] = "Applications";
    // Within an object that describes the attributes of an application, the JSON identifier for the
    // name of the application (e.g. "com.google.maps").  The value is a string.  If any other
    // attributes will be specified, this must be the first attribute specified in the object.
    constexpr char kJsonAppName[] = "AppName";
    
    // Within a rule, the JSON identifier for describing one or more devices.  The value is an
    // array of objects, each object of which can specify attributes of a device.
    constexpr char kJsonDevices[] = "Devices";
    // Within an object that describes the attributes of a device, the JSON identifier for the
    // manufacturer of the device.  The value is a string.  If any other non-GPU attributes will be
    // specified, this must be the first attribute specified in the object.
    constexpr char kJsonManufacturer[] = "Manufacturer";
    // Within an object that describes the attributes of a device, the JSON identifier for the
    // model of the device.  The value is a string.
    constexpr char kJsonModel[] = "Model";
    
    // Within an object that describes the attributes of a device, the JSON identifier for describing
    // one or more GPUs/drivers used in the device.  The value is an
    // array of objects, each object of which can specify attributes of a GPU and its driver.
    constexpr char kJsonGPUs[] = "GPUs";
    // Within an object that describes the attributes of a GPU and driver, the JSON identifier for the
    // vendor of the device/driver.  The value is a string.  If any other attributes will be specified,
    // this must be the first attribute specified in the object.
    constexpr char kJsonVendor[] = "Vendor";
    // Within an object that describes the attributes of a GPU and driver, the JSON identifier for the
    // deviceId of the device.  The value is an unsigned integer.  If the driver version will be
    // specified, this must preceded the version attributes specified in the object.
    constexpr char kJsonDeviceId[] = "DeviceId";
    
    // Within an object that describes the attributes of either an application or a GPU, the JSON
    // identifier for the major version of that application or GPU driver.  The value is a positive
    // integer number.  Not specifying a major version implies a wildcard for all values of a version.
    constexpr char kJsonVerMajor[] = "VerMajor";
    // Within an object that describes the attributes of either an application or a GPU, the JSON
    // identifier for the minor version of that application or GPU driver.  The value is a positive
    // integer number.  In order to specify a minor version, it must be specified immediately after the
    // major number associated with it.  Not specifying a minor version implies a wildcard for the
    // minor, subminor, and patch values of a version.
    constexpr char kJsonVerMinor[] = "VerMinor";
    // Within an object that describes the attributes of either an application or a GPU, the JSON
    // identifier for the subminor version of that application or GPU driver.  The value is a positive
    // integer number.  In order to specify a subminor version, it must be specified immediately after
    // the minor number associated with it.  Not specifying a subminor version implies a wildcard for
    // the subminor and patch values of a version.
    constexpr char kJsonVerSubMinor[] = "VerSubMinor";
    // Within an object that describes the attributes of either an application or a GPU, the JSON
    // identifier for the patch version of that application or GPU driver.  The value is a positive
    // integer number.  In order to specify a patch version, it must be specified immediately after the
    // subminor number associated with it.  Not specifying a patch version implies a wildcard for the
    // patch value of a version.
    constexpr char kJsonVerPatch[] = "VerPatch";
    
    // This encapsulates a std::string.  The default constructor (not given a string) assumes that this
    // is a wildcard (i.e. will match all other StringPart objects).
    class StringPart
    {
      public:
        StringPart() = default;
        explicit StringPart(const std::string part) : mPart(part), mWildcard(false) {}
        ~StringPart() = default;
    
        static StringPart FromJson(const Json::Value &parent, const char *key)
        {
            if (parent.isMember(key) && parent[key].isString())
            {
                return StringPart(parent[key].asString());
            }
            return {};
        }
    
        bool match(const StringPart &toCheck) const
        {
            return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart));
        }
    
      public:
        std::string mPart;
        bool mWildcard = true;
    };
    
    // This encapsulates a 32-bit unsigned integer.  The default constructor (not given a number)
    // assumes that this is a wildcard (i.e. will match all other IntegerPart objects).
    class IntegerPart
    {
      public:
        IntegerPart() = default;
        explicit IntegerPart(uint32_t part) : mPart(part), mWildcard(false) {}
        ~IntegerPart() = default;
    
        static IntegerPart FromJson(const Json::Value &parent, const char *key)
        {
            if (parent.isMember(key) && parent[key].isInt())
            {
                return IntegerPart(parent[key].asInt());
            }
            return {};
        }
    
        bool match(const IntegerPart &toCheck) const
        {
            return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart));
        }
    
      public:
        uint32_t mPart = 0;
        bool mWildcard = true;
    };
    
    // This encapsulates a list of other classes, each of which will have a match() and logItem()
    // method.  The common constructor (given a type, but not any list items) assumes that this is
    // a wildcard (i.e. will match all other ListOf<t> objects).
    template <class T>
    class ListOf
    {
      public:
        explicit ListOf(const std::string listType) : mWildcard(true), mListType(listType) {}
        ~ListOf() { mList.clear(); }
        void addItem(T &&toAdd)
        {
            mList.push_back(std::move(toAdd));
            mWildcard = false;
        }
        bool match(const T &toCheck) const
        {
            VERBOSE("\t\t Matching ListOf<%s> against item:\n", mListType.c_str());
            if (mWildcard || toCheck.mWildcard)
            {
                VERBOSE("\t\t\t Successful match due to wildcard.\n");
                return true;
            }
            for (const T &it : mList)
            {
                if (it.match(toCheck))
                {
                    VERBOSE("\t\t\t Successful match due to list item match.\n");
                    return true;
                }
            }
            VERBOSE("\t\t\t Failed to match.\n");
            return false;
        }
        bool match(const ListOf<T> &toCheck) const
        {
            VERBOSE("\t\t Matching ListOf<%s>:\n", mListType.c_str());
            if (mWildcard || toCheck.mWildcard)
            {
                VERBOSE("\t\t\t Successful match due to wildcard.\n");
                return true;
            }
            // If we make it to here, both this and toCheck have at least one item in their mList.
            for (const T &it : toCheck.mList)
            {
                if (match(it))
                {
                    VERBOSE("\t\t\t Successful match due to list item match.\n");
                    return true;
                }
            }
            VERBOSE("\t\t\t Failed to match list.\n");
            return false;
        }
        void logListOf(const std::string prefix, const std::string name) const
        {
            if (mWildcard)
            {
                VERBOSE("%sListOf%s is wildcarded to always match\n", prefix.c_str(), name.c_str());
            }
            else
            {
                VERBOSE("%sListOf%s has %d item(s):\n", prefix.c_str(), name.c_str(),
                        static_cast<int>(mList.size()));
                for (auto &it : mList)
                {
                    it.logItem();
                }
            }
        }
    
        bool mWildcard;
    
      private:
        std::string mListType;
        std::vector<T> mList;
    };
    
    // This encapsulates up-to four 32-bit unsigned integers, that represent a potentially-complex
    // version number.  The default constructor (not given any numbers) assumes that this is a wildcard
    // (i.e. will match all other Version objects).  Each part of a Version is stored in an IntegerPart
    // class, and so may be wildcarded as well.
    class Version
    {
      public:
        Version(uint32_t major, uint32_t minor, uint32_t subminor, uint32_t patch)
            : mMajor(major), mMinor(minor), mSubminor(subminor), mPatch(patch)
        {}
    
        Version()                = default;
        Version(const Version &) = default;
        Version(Version &&)      = default;
        Version &operator=(const Version &) = default;
        Version &operator=(Version &&) = default;
        ~Version()                     = default;
    
        static Version FromJson(const Json::Value &jObject)
        {
            Version version;
            version.mMajor = IntegerPart::FromJson(jObject, kJsonVerMajor);
            if (version.mMajor.mWildcard)
            {
                return version;
            }
            // Revision fields are only checked if their parent version field
            // is set.
            version.mMinor = IntegerPart::FromJson(jObject, kJsonVerMinor);
            if (version.mMinor.mWildcard)
            {
                return version;
            }
    
            version.mSubminor = IntegerPart::FromJson(jObject, kJsonVerSubMinor);
            if (version.mSubminor.mWildcard)
            {
                return version;
            }
    
            version.mPatch = IntegerPart::FromJson(jObject, kJsonVerPatch);
            return version;
        }
    
        bool match(const Version &toCheck) const
        {
            VERBOSE("\t\t\t Matching Version %s against %s\n", getString().c_str(),
                    toCheck.getString().c_str());
            return (isWildcard() || toCheck.isWildcard() ||
                    (mMajor.match(toCheck.mMajor) && mMinor.match(toCheck.mMinor) &&
                     mSubminor.match(toCheck.mSubminor) && mPatch.match(toCheck.mPatch)));
        }
    
        std::string getString() const
        {
            if (mMajor.mWildcard)
            {
                return "*";
            }
    
            std::ostringstream ss;
            ss << mMajor.mPart;
            // Must at least have a major version:
            if (!mMinor.mWildcard)
            {
                ss << "." << mMinor.mPart;
                if (!mSubminor.mWildcard)
                {
                    ss << "." << mSubminor.mPart;
    
                    if (!mPatch.mWildcard)
                    {
                        ss << "." << mPatch.mPart;
                    }
                }
            }
            if (mPatch.mWildcard)
            {
                ss << ".*";
            }
            return ss.str();
        }
    
        bool isWildcard() const { return mMajor.mWildcard; }
    
      public:
        IntegerPart mMajor;
        IntegerPart mMinor;
        IntegerPart mSubminor;
        IntegerPart mPatch;
    };
    
    // This encapsulates an application, and potentially the application's Version.  The default
    // constructor (not given any values) assumes that this is a wildcard (i.e. will match all
    // other Application objects).  Each part of an Application is stored in a class that may
    // also be wildcarded.
    class Application
    {
      public:
        Application(StringPart name, Version version = {})
            : mName(name), mVersion(version), mWildcard(false)
        {}
        Application()  = default;
        ~Application() = default;
    
        static bool FromJson(const Json::Value &jObject, Application *out)
        {
            // If an application is listed, the application's name is required:
            auto name = StringPart::FromJson(jObject, kJsonAppName);
            if (name.mWildcard)
            {
                return false;
            }
            auto version = Version::FromJson(jObject);
            *out         = Application{std::move(name), std::move(version)};
            return true;
        }
    
        bool match(const Application &toCheck) const
        {
            return (mWildcard || toCheck.mWildcard ||
                    (toCheck.mName.match(mName) && toCheck.mVersion.match(mVersion)));
        }
        void logItem() const
        {
            if (mWildcard)
            {
                VERBOSE("      Wildcard (i.e. will match all applications)\n");
            }
            else if (!mVersion.isWildcard())
            {
                VERBOSE("      Application \"%s\" (version: %s)\n", mName.mPart.c_str(),
                        mVersion.getString().c_str());
            }
            else
            {
                VERBOSE("      Application \"%s\"\n", mName.mPart.c_str());
            }
        }
    
      public:
        StringPart mName;
        Version mVersion;
        bool mWildcard = true;
    };
    
    // This encapsulates a GPU and its driver.  The default constructor (not given any values) assumes
    // that this is a wildcard (i.e. will match all other GPU objects).  Each part of a GPU is stored
    // in a class that may also be wildcarded.
    class GPU
    {
      public:
        GPU(StringPart vendor, IntegerPart deviceId, Version version)
            : mVendor(std::move(vendor)),
              mDeviceId(std::move(deviceId)),
              mVersion(version),
              mWildcard(false)
        {}
        GPU(std::string vendor, uint32_t deviceId, Version version)
            : GPU(StringPart(std::move(vendor)), IntegerPart(deviceId), std::move(version))
        {}
        GPU()  = default;
        ~GPU() = default;
        bool match(const GPU &toCheck) const
        {
            VERBOSE("\t\t Matching %s \n\t\t  against %s\n", toString().c_str(),
                    toCheck.toString().c_str());
            return (mWildcard || toCheck.mWildcard ||
                    (toCheck.mVendor.match(mVendor) && toCheck.mDeviceId.match(mDeviceId) &&
                     toCheck.mVersion.match(mVersion)));
        }
    
        // Returns true if out is set to a valid GPU instance.
        static bool CreateGpuFromJson(const Json::Value &jObject, GPU *out)
        {
            // If a GPU is listed, the vendor name is required:
            auto vendor = StringPart::FromJson(jObject, kJsonVendor);
            if (vendor.mWildcard)
            {
                WARN("Asked to parse a GPU, but no vendor found.\n");
                return false;
            }
    
            auto deviceId = IntegerPart::FromJson(jObject, kJsonDeviceId);
            auto version  = Version::FromJson(jObject);
            *out          = GPU{std::move(vendor), std::move(deviceId), std::move(version)};
            return true;
        }
    
        std::string toString() const
        {
            if (mWildcard)
            {
                return std::string("Wildcard (i.e. will match all GPUs)");
            }
    
            std::ostringstream ss;
            ss << "GPU vendor: " << mVendor.mPart;
            if (!mDeviceId.mWildcard)
            {
                ss << ", deviceId: " << std::hex << mDeviceId.mPart;
            }
            ss << ", version: " << mVersion.getString();
            return ss.str();
        }
    
        void logItem() const { VERBOSE("\t     %s\n", toString().c_str()); }
    
      public:
        StringPart mVendor;
        IntegerPart mDeviceId;
        Version mVersion;
        bool mWildcard = true;
    };
    
    // This encapsulates a device, and potentially the device's model and/or a list of GPUs/drivers
    // associated with the Device.  The default constructor (not given any values) assumes that this is
    // a wildcard (i.e. will match all other Device objects).  Each part of a Device is stored in a
    // class that may also be wildcarded.
    class Device
    {
      public:
        Device(StringPart manufacturer, StringPart model)
            : mManufacturer(std::move(manufacturer)),
              mModel(std::move(model)),
              mGpuList("GPU"),
              mWildcard(false)
        {}
        Device() : mGpuList("GPU") {}
        ~Device() = default;
    
        static Device FromJson(const Json::Value &jObject)
        {
            auto manufacturer = StringPart::FromJson(jObject, kJsonManufacturer);
            if (!manufacturer.mWildcard)
            {
                // We don't let a model be specified without also specifying a manufacturer:
                auto model = StringPart::FromJson(jObject, kJsonModel);
                return Device(std::move(manufacturer), std::move(model));
            }
            // This case is not treated as an error because a rule may wish to only call out one or
            // more GPUs, but not any specific manufacturer (e.g. for any manufacturer's device
            // that uses a GPU from Vendor-A, with DeviceID-Foo, and with driver version 1.2.3.4):
            return Device();
        }
    
        void addGPU(GPU &&gpu) { mGpuList.addItem(std::move(gpu)); }
        bool match(const Device &toCheck) const
        {
            // GPU lists must always match, even when wildcards are used.
            VERBOSE("\t   Checking ListOf<GPU>:\n");
            if (!mGpuList.match(toCheck.mGpuList))
            {
                VERBOSE("\t Failed to match due to mismatched GPU list.\n");
                return false;
            }
            if (mWildcard || toCheck.mWildcard)
            {
                VERBOSE("\t  Matching due to wildcard.\n");
                return true;
            }
            if (toCheck.mManufacturer.match(mManufacturer) && toCheck.mModel.match(mModel))
            {
                VERBOSE("\t  Matching due to manufacturer and model match.\n");
                return true;
            }
            return false;
        }
        void logItem() const
        {
            if (mWildcard)
            {
                if (mGpuList.mWildcard)
                {
                    VERBOSE("      Wildcard (i.e. will match all devices)\n");
                    return;
                }
                else
                {
                    VERBOSE(
                        "      Device with any manufacturer and model"
                        ", and with the following GPUs:\n");
                }
            }
            else
            {
                if (!mModel.mWildcard)
                {
                    VERBOSE(
                        "      Device manufacturer: \"%s\" and model \"%s\""
                        ", and with the following GPUs:\n",
                        mManufacturer.mPart.c_str(), mModel.mPart.c_str());
                }
                else
                {
                    VERBOSE(
                        "      Device manufacturer: \"%s\""
                        ", and with the following GPUs:\n",
                        mManufacturer.mPart.c_str());
                }
            }
            mGpuList.logListOf("        ", "GPUs");
        }
    
      public:
        StringPart mManufacturer;
        StringPart mModel;
        ListOf<GPU> mGpuList;
        bool mWildcard = true;
    };
    
    // This encapsulates a particular scenario to check against the rules.  A Scenario is similar to a
    // Rule, except that a Rule has an answer and potentially many wildcards, and a Scenario is the
    // fully-specified combination of an Application and a Device that is proposed to be run with
    // ANGLE.  It is compared with the list of Rules.
    class Scenario
    {
      public:
        Scenario(const char *appName, const char *deviceMfr, const char *deviceModel)
            : mApplication(Application(StringPart(appName))),
              mDevice(Device(StringPart(deviceMfr), StringPart(deviceModel)))
        {}
        ~Scenario() = default;
        void logScenario()
        {
            VERBOSE("  Scenario to compare against the rules:\n");
            VERBOSE("    Application:\n");
            mApplication.logItem();
            VERBOSE("    Device:\n");
            mDevice.logItem();
        }
    
      public:
        Application mApplication;
        Device mDevice;
    };
    
    // This encapsulates a Rule that provides an answer based on whether a particular Scenario matches
    // the Rule.  A Rule always has an answer, but can potentially wildcard every item in it (i.e.
    // match every scenario).
    class Rule
    {
      public:
        Rule(const std::string description, bool useANGLE)
            : mDescription(description),
              mAppList("Application"),
              mDevList("Device"),
              mUseANGLE(useANGLE)
        {}
        ~Rule() = default;
        void addApp(Application &&app) { mAppList.addItem(std::move(app)); }
        void addDevice(Device &&dev) { mDevList.addItem(std::move(dev)); }
        bool match(const Scenario &toCheck) const
        {
            VERBOSE("    Matching rule \"%s\" against scenario:\n", mDescription.c_str());
            if (!mAppList.match(toCheck.mApplication))
            {
                VERBOSE("\tFailed to match rule due to mismatched application.\n");
                return false;
            }
            if (!mDevList.match(toCheck.mDevice))
            {
                VERBOSE("\tFailed to match rule due to mismatched device.\n");
                return false;
            }
            VERBOSE("\tSuccessfully matched rule.");
            return true;
        }
        bool getUseANGLE() const { return mUseANGLE; }
        void logRule() const
        {
            VERBOSE("  Rule: \"%s\" %s ANGLE\n", mDescription.c_str(),
                    mUseANGLE ? "enables" : "disables");
            mAppList.logListOf("    ", "Applications");
            mDevList.logListOf("    ", "Devices");
        }
    
        std::string mDescription;
        ListOf<Application> mAppList;
        ListOf<Device> mDevList;
        bool mUseANGLE;
    };
    
    // This encapsulates a list of Rules that Scenarios are matched against.  A Scenario is compared
    // with each Rule, in order.  Any time a Scenario matches a Rule, the current answer is overridden
    // with the answer of the matched Rule.
    class RuleList
    {
      public:
        RuleList() {}
        ~RuleList() { mRuleList.clear(); }
    
        static RuleList *ReadRulesFromJsonString(const std::string jsonFileContents)
        {
            RuleList *rules = new RuleList;
    
            // Open the file and start parsing it:
            Json::CharReaderBuilder builder;
            // Json::CharReaderBuilder::strictMode(&builder.settings_);
            std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
    
            Json::Value jTopLevelObject;
            std::string errorMessage;
            const bool succeeded = reader->parse(&*jsonFileContents.begin(), &*jsonFileContents.end(),
                                                 &jTopLevelObject, &errorMessage);
            if (!succeeded)
            {
                VERBOSE("Failed to parse rules from json file. Error: %s\n", errorMessage.c_str());
                return nullptr;
            }
    
            for (const auto &jRule : jTopLevelObject[kJsonRules])
            {
                std::string ruleDescription = jRule[kJsonRule].asString();
                bool useANGLE               = jRule[kJsonUseANGLE].asBool();
                Rule newRule(std::move(ruleDescription), useANGLE);
    
                for (const auto &jApp : jRule[kJsonApplications])
                {
                    Application app;
                    if (Application::FromJson(jApp, &app))
                    {
                        newRule.addApp(std::move(app));
                    }
                }
    
                for (const auto &jDev : jRule[kJsonDevices])
                {
                    Device newDev = Device::FromJson(jDev);
                    for (const auto &jGPU : jDev[kJsonGPUs])
                    {
                        GPU newGPU;
                        if (GPU::CreateGpuFromJson(jGPU, &newGPU))
                        {
                            newDev.addGPU(std::move(newGPU));
                        }
                    }
                    newRule.addDevice(std::move(newDev));
                }
    
                rules->addRule(std::move(newRule));
            }
    
            // Make sure there is at least one, default rule.  If not, add it here:
            if (rules->mRuleList.empty())
            {
                Rule defaultRule("Default Rule", false);
                rules->addRule(std::move(defaultRule));
            }
            return rules;
        }
    
        void addRule(Rule &&rule) { mRuleList.push_back(std::move(rule)); }
        bool getUseANGLE(const Scenario &toCheck)
        {
            // Initialize useANGLE to the system-wide default (that should be set in the default
            // rule, but just in case, set it here too):
            bool useANGLE = false;
            VERBOSE("Checking scenario against %d ANGLE-for-Android rules:\n",
                    static_cast<int>(mRuleList.size()));
    
            for (const Rule &rule : mRuleList)
            {
                VERBOSE("  Checking Rule: \"%s\" (to see whether there's a match)\n",
                        rule.mDescription.c_str());
                if (rule.match(toCheck))
                {
                    VERBOSE("  -> Rule matches.  Updating useANGLE to %s.\n",
                            rule.getUseANGLE() ? "true" : "false");
                    // The ANGLE rules are ordered from least to greatest specificity, meaning that
                    // the last rule with a match should dictate whether or not ANGLE should be
                    // recommended for use.
                    useANGLE = rule.getUseANGLE();
                }
                else
                {
                    VERBOSE("  -> Rule doesn't match.\n");
                }
            }
            return useANGLE;
        }
        void logRules()
        {
            VERBOSE("Showing %d ANGLE-for-Android rules:\n", static_cast<int>(mRuleList.size()));
            for (const Rule &rule : mRuleList)
            {
                rule.logRule();
            }
        }
    
      public:
        std::vector<Rule> mRuleList;
    };
    
    }  // namespace angle
    
    extern "C" {
    
    using namespace angle;
    
    // This function is part of the version-2 API:
    ANGLE_EXPORT bool ANGLEGetFeatureSupportUtilAPIVersion(unsigned int *versionToUse)
    {
        if (!versionToUse || (*versionToUse < kFeatureVersion_LowestSupported))
        {
            // The versionToUse is either nullptr or is less than the lowest version supported, which
            // is an error.
            return false;
        }
        if (*versionToUse > kFeatureVersion_HighestSupported)
        {
            // The versionToUse is greater than the highest version supported; change it to the
            // highest version supported (caller will decide if it can use that version).
            *versionToUse = kFeatureVersion_HighestSupported;
        }
        return true;
    }
    
    // This function is part of the version-2 API:
    ANGLE_EXPORT bool ANGLEAndroidParseRulesString(const char *rulesString,
                                                   RulesHandle *rulesHandle,
                                                   int *rulesVersion)
    {
        if (!rulesString || !rulesHandle || !rulesVersion)
        {
            return false;
        }
    
        std::string rulesFileContents = rulesString;
        RuleList *rules               = RuleList::ReadRulesFromJsonString(rulesFileContents);
        if (!rules)
        {
            return false;
        }
    
        rules->logRules();
    
        *rulesHandle  = rules;
        *rulesVersion = 0;
        return true;
    }
    
    // This function is part of the version-2 API:
    ANGLE_EXPORT bool ANGLEGetSystemInfo(SystemInfoHandle *systemInfoHandle)
    {
        if (!systemInfoHandle)
        {
            return false;
        }
    
        // TODO (http://anglebug.com/3036): Restore the real code
        angle::SystemInfo *systemInfo = new angle::SystemInfo;
        systemInfo->gpus.resize(1);
        GPUDeviceInfo &gpu = systemInfo->gpus[0];
        gpu.vendorId       = 0xFEFEFEFE;
        gpu.deviceId       = 0xFEEEFEEE;
        gpu.driverVendor   = "Foo";
        gpu.driverVersion  = "1.2.3.4";
    
        *systemInfoHandle = systemInfo;
        return true;
    }
    
    // This function is part of the version-2 API:
    ANGLE_EXPORT bool ANGLEAddDeviceInfoToSystemInfo(const char *deviceMfr,
                                                     const char *deviceModel,
                                                     SystemInfoHandle systemInfoHandle)
    {
        angle::SystemInfo *systemInfo = static_cast<angle::SystemInfo *>(systemInfoHandle);
        if (!deviceMfr || !deviceModel || !systemInfo)
        {
            return false;
        }
    
        systemInfo->machineManufacturer = deviceMfr;
        systemInfo->machineModelName    = deviceModel;
        return true;
    }
    
    // This function is part of the version-2 API:
    ANGLE_EXPORT bool ANGLEShouldBeUsedForApplication(const RulesHandle rulesHandle,
                                                      int rulesVersion,
                                                      const SystemInfoHandle systemInfoHandle,
                                                      const char *appName)
    {
        RuleList *rules               = static_cast<RuleList *>(rulesHandle);
        angle::SystemInfo *systemInfo = static_cast<angle::SystemInfo *>(systemInfoHandle);
        if (!rules || !systemInfo || !appName || (systemInfo->gpus.size() != 1))
        {
            return false;
        }
    
        Scenario scenario(appName, systemInfo->machineManufacturer.c_str(),
                          systemInfo->machineModelName.c_str());
        Version gpuDriverVersion(systemInfo->gpus[0].detailedDriverVersion.major,
                                 systemInfo->gpus[0].detailedDriverVersion.minor,
                                 systemInfo->gpus[0].detailedDriverVersion.subMinor,
                                 systemInfo->gpus[0].detailedDriverVersion.patch);
        GPU gpuDriver(systemInfo->gpus[0].driverVendor, systemInfo->gpus[0].deviceId,
                      std::move(gpuDriverVersion));
        scenario.mDevice.addGPU(std::move(gpuDriver));
        scenario.logScenario();
    
        bool rtn = rules->getUseANGLE(std::move(scenario));
        VERBOSE("Application \"%s\" should %s ANGLE.\n", appName, rtn ? "use" : "NOT use");
    
        return rtn;
    }
    
    // This function is part of the version-2 API:
    ANGLE_EXPORT void ANGLEFreeRulesHandle(const RulesHandle rulesHandle)
    {
        RuleList *rules = static_cast<RuleList *>(rulesHandle);
        if (rules)
        {
            delete rules;
        }
    }
    
    // This function is part of the version-2 API:
    ANGLE_EXPORT void ANGLEFreeSystemInfoHandle(const SystemInfoHandle systemInfoHandle)
    {
        angle::SystemInfo *systemInfo = static_cast<angle::SystemInfo *>(systemInfoHandle);
        if (systemInfo)
        {
            delete systemInfo;
        }
    }
    
    }  // extern "C"