Edit

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

Branch :

  • Show log

    Commit

  • Author : Ian Elliott
    Date : 2019-01-10 12:02:47
    Hash : b814bbb7
    Message : Temporarily disable calling GetSystemInfo for rules processing. A bug on Android is causing <something> to crash if the rules-processing code calls GetSystemInfo, which in turn calls Vulkan. While this is being investigated, we will temporarily disable calling GetSystemInfo (instead, insert dummy data that won't match any actual GPU), so that the rest of the rules processing will still work. Bug: angleproject:3063 Change-Id: I9976fd57ba001099b94ebe7186739477bf0c8f79 Reviewed-on: https://chromium-review.googlesource.com/c/1405709 Commit-Queue: Ian Elliott <ianelliott@google.com> Reviewed-by: Courtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: Cody Northrop <cnorthrop@google.com> Reviewed-by: Jamie Madill <jmadill@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 <list>
    #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__);
    #define VERBOSE(...) printf(__VA_ARGS__);
    #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() : mPart(""), mWildcard(true) {}
        StringPart(const std::string part) : mPart(part), mWildcard(false) {}
        ~StringPart() {}
        bool match(const StringPart &toCheck) const
        {
            return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart));
        }
    
      public:
        std::string mPart;
        bool mWildcard;
    };
    
    // 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() : mPart(0), mWildcard(true) {}
        IntegerPart(uint32_t part) : mPart(part), mWildcard(false) {}
        ~IntegerPart() {}
        bool match(const IntegerPart &toCheck) const
        {
            return (mWildcard || toCheck.mWildcard || (toCheck.mPart == mPart));
        }
    
      public:
        uint32_t mPart;
        bool mWildcard;
    };
    
    // 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:
        ListOf(const std::string listType) : mWildcard(true), mListType(listType) {}
        ~ListOf() { mList.clear(); }
        void addItem(const T &toAdd)
        {
            mList.push_back(toAdd);
            mWildcard = false;
        }
        bool match(const T &toCheck) const
        {
            VERBOSE("\t\t Within ListOf<%s> match: wildcards are %s and %s,\n", mListType.c_str(),
                    mWildcard ? "true" : "false", toCheck.mWildcard ? "true" : "false");
            if (mWildcard || toCheck.mWildcard)
            {
                return true;
            }
            for (const T &it : mList)
            {
                VERBOSE("\t\t   Within ListOf<%s> match: calling match on sub-item is %s,\n",
                        mListType.c_str(), it.match(toCheck) ? "true" : "false");
                if (it.match(toCheck))
                {
                    return true;
                }
            }
            return false;
        }
        bool match(const ListOf<T> &toCheck) const
        {
            VERBOSE("\t\t Within ListOf<%s> match: wildcards are %s and %s,\n", mListType.c_str(),
                    mWildcard ? "true" : "false", toCheck.mWildcard ? "true" : "false");
            if (mWildcard || toCheck.mWildcard)
            {
                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))
                {
                    return true;
                }
            }
            return false;
        }
        void logListOf(const std::string prefix, const std::string name) const
        {
            if (mWildcard)
            {
                VERBOSE("%sListOf%s is wildcarded to always match", prefix.c_str(), name.c_str());
            }
            else
            {
                VERBOSE("%sListOf%s has %d item(s):", 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), mWildcard(false)
        {}
        Version(uint32_t major, uint32_t minor, uint32_t subminor)
            : mMajor(major), mMinor(minor), mSubminor(subminor), mWildcard(false)
        {}
        Version(uint32_t major, uint32_t minor) : mMajor(major), mMinor(minor), mWildcard(false) {}
        Version(uint32_t major) : mMajor(major), mWildcard(false) {}
        Version() : mWildcard(true) {}
        Version(const Version &toCopy)
            : mMajor(toCopy.mMajor),
              mMinor(toCopy.mMinor),
              mSubminor(toCopy.mSubminor),
              mPatch(toCopy.mPatch),
              mWildcard(toCopy.mWildcard)
        {}
        ~Version() {}
    
        static Version *CreateVersionFromJson(const Json::Value &jObject)
        {
            Version *version = nullptr;
            // A major version must be provided before a minor, and so on:
            if (jObject.isMember(kJsonVerMajor) && jObject[kJsonVerMajor].isInt())
            {
                int major = jObject[kJsonVerMajor].asInt();
                if (jObject.isMember(kJsonVerMinor) && jObject[kJsonVerMinor].isInt())
                {
                    int minor = jObject[kJsonVerMinor].asInt();
                    if (jObject.isMember(kJsonVerSubMinor) && jObject[kJsonVerSubMinor].isInt())
                    {
                        int subMinor = jObject[kJsonVerSubMinor].asInt();
                        if (jObject.isMember(kJsonVerPatch) && jObject[kJsonVerPatch].isInt())
                        {
                            int patch = jObject[kJsonVerPatch].asInt();
                            version   = new Version(major, minor, subMinor, patch);
                        }
                        else
                        {
                            version = new Version(major, minor, subMinor);
                        }
                    }
                    else
                    {
                        version = new Version(major, minor);
                    }
                }
                else
                {
                    version = new Version(major);
                }
            }
            return version;
        }
    
        bool match(const Version &toCheck) const
        {
            VERBOSE("\t\t\t Within Version %d,%d,%d,%d match(%d,%d,%d,%d): wildcards are %s and %s,\n",
                    mMajor.mPart, mMinor.mPart, mSubminor.mPart, mPatch.mPart, toCheck.mMajor.mPart,
                    toCheck.mMinor.mPart, toCheck.mSubminor.mPart, toCheck.mPatch.mPart,
                    mWildcard ? "true" : "false", toCheck.mWildcard ? "true" : "false");
            if (!(mWildcard || toCheck.mWildcard))
            {
                VERBOSE("\t\t\t   mMajor match is %s, mMinor is %s, mSubminor is %s, mPatch is %s\n",
                        mMajor.match(toCheck.mMajor) ? "true" : "false",
                        mMinor.match(toCheck.mMinor) ? "true" : "false",
                        mSubminor.match(toCheck.mSubminor) ? "true" : "false",
                        mPatch.match(toCheck.mPatch) ? "true" : "false");
            }
            return (mWildcard || toCheck.mWildcard ||
                    (mMajor.match(toCheck.mMajor) && mMinor.match(toCheck.mMinor) &&
                     mSubminor.match(toCheck.mSubminor) && mPatch.match(toCheck.mPatch)));
        }
        std::string getString() const
        {
            if (mWildcard)
            {
                return "*";
            }
            else
            {
                char ret[100];
                // Must at least have a major version:
                if (!mMinor.mWildcard)
                {
                    if (!mSubminor.mWildcard)
                    {
                        if (!mPatch.mWildcard)
                        {
                            snprintf(ret, 100, "%d.%d.%d.%d", mMajor.mPart, mMinor.mPart,
                                     mSubminor.mPart, mPatch.mPart);
                        }
                        else
                        {
                            snprintf(ret, 100, "%d.%d.%d.*", mMajor.mPart, mMinor.mPart,
                                     mSubminor.mPart);
                        }
                    }
                    else
                    {
                        snprintf(ret, 100, "%d.%d.*", mMajor.mPart, mMinor.mPart);
                    }
                }
                else
                {
                    snprintf(ret, 100, "%d.*", mMajor.mPart);
                }
                std::string retString = ret;
                return retString;
            }
        }
    
      public:
        IntegerPart mMajor;
        IntegerPart mMinor;
        IntegerPart mSubminor;
        IntegerPart mPatch;
        bool mWildcard;
    };
    
    // 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(const std::string name, const Version &version)
            : mName(name), mVersion(version), mWildcard(false)
        {}
        Application(const std::string name) : mName(name), mVersion(), mWildcard(false) {}
        Application() : mName(), mVersion(), mWildcard(true) {}
        ~Application() {}
    
        static Application *CreateApplicationFromJson(const Json::Value &jObject)
        {
            Application *application = nullptr;
    
            // If an application is listed, the application's name is required:
            std::string appName = jObject[kJsonAppName].asString();
    
            // The application's version is optional:
            Version *version = Version::CreateVersionFromJson(jObject);
            if (version)
            {
                application = new Application(appName, *version);
                delete version;
            }
            else
            {
                application = new Application(appName);
            }
            return application;
        }
    
        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)");
            }
            else if (!mVersion.mWildcard)
            {
                VERBOSE("      Application \"%s\" (version: %s)", mName.mPart.c_str(),
                        mVersion.getString().c_str());
            }
            else
            {
                VERBOSE("      Application \"%s\"", mName.mPart.c_str());
            }
        }
    
      public:
        StringPart mName;
        Version mVersion;
        bool mWildcard;
    };
    
    // 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(const std::string vendor, uint32_t deviceId, const Version &version)
            : mVendor(vendor), mDeviceId(IntegerPart(deviceId)), mVersion(version), mWildcard(false)
        {}
        GPU(const std::string vendor, uint32_t deviceId)
            : mVendor(vendor), mDeviceId(IntegerPart(deviceId)), mVersion(), mWildcard(false)
        {}
        GPU(const std::string vendor) : mVendor(vendor), mDeviceId(), mVersion(), mWildcard(false) {}
        GPU() : mVendor(), mDeviceId(), mVersion(), mWildcard(true) {}
        bool match(const GPU &toCheck) const
        {
            VERBOSE("\t\t Within GPU match: wildcards are %s and %s,\n", mWildcard ? "true" : "false",
                    toCheck.mWildcard ? "true" : "false");
            VERBOSE("\t\t   mVendor = \"%s\" and toCheck.mVendor = \"%s\"\n", mVendor.mPart.c_str(),
                    toCheck.mVendor.mPart.c_str());
            VERBOSE("\t\t   mDeviceId = %d and toCheck.mDeviceId = %d\n", mDeviceId.mPart,
                    toCheck.mDeviceId.mPart);
            VERBOSE("\t\t   mVendor match is %s, mDeviceId is %s, mVersion is %s\n",
                    toCheck.mVendor.match(mVendor) ? "true" : "false",
                    toCheck.mDeviceId.match(mDeviceId) ? "true" : "false",
                    toCheck.mVersion.match(mVersion) ? "true" : "false");
            return (mWildcard || toCheck.mWildcard ||
                    (toCheck.mVendor.match(mVendor) && toCheck.mDeviceId.match(mDeviceId) &&
                     toCheck.mVersion.match(mVersion)));
        }
        ~GPU() {}
    
        static GPU *CreateGpuFromJson(const Json::Value &jObject)
        {
            GPU *gpu = nullptr;
    
            // If a GPU is listed, the vendor name is required:
            if (jObject.isMember(kJsonVendor) && jObject[kJsonVendor].isString())
            {
                std::string vendor = jObject[kJsonVendor].asString();
                // If a version is given, the deviceId is required:
                if (jObject.isMember(kJsonDeviceId) && jObject[kJsonDeviceId].isUInt())
                {
                    uint32_t deviceId = jObject[kJsonDeviceId].asUInt();
                    Version *version  = Version::CreateVersionFromJson(jObject);
                    if (version)
                    {
                        gpu = new GPU(vendor, deviceId, *version);
                        delete version;
                    }
                    else
                    {
                        gpu = new GPU(vendor, deviceId);
                    }
                }
                else
                {
                    gpu = new GPU(vendor);
                }
            }
            else
            {
                WARN("Asked to parse a GPU, but no vendor found");
            }
    
            return gpu;
        }
    
        void logItem() const
        {
            if (mWildcard)
            {
                VERBOSE("          Wildcard (i.e. will match all GPUs)");
            }
            else
            {
                if (!mDeviceId.mWildcard)
                {
                    if (!mVersion.mWildcard)
                    {
                        VERBOSE("\t     GPU vendor: %s, deviceId: 0x%x, version: %s",
                                mVendor.mPart.c_str(), mDeviceId.mPart, mVersion.getString().c_str());
                    }
                    else
                    {
                        VERBOSE("\t     GPU vendor: %s, deviceId: 0x%x", mVendor.mPart.c_str(),
                                mDeviceId.mPart);
                    }
                }
                else
                {
                    VERBOSE("\t     GPU vendor: %s", mVendor.mPart.c_str());
                }
            }
        }
    
      public:
        StringPart mVendor;
        IntegerPart mDeviceId;
        Version mVersion;
        bool mWildcard;
    };
    
    // 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(const std::string manufacturer, const std::string model)
            : mManufacturer(manufacturer), mModel(model), mGpuList("GPU"), mWildcard(false)
        {}
        Device(const std::string manufacturer)
            : mManufacturer(manufacturer), mModel(), mGpuList("GPU"), mWildcard(false)
        {}
        Device() : mManufacturer(), mModel(), mGpuList("GPU"), mWildcard(true) {}
        ~Device() {}
    
        static Device *CreateDeviceFromJson(const Json::Value &jObject)
        {
            Device *device = nullptr;
            if (jObject.isMember(kJsonManufacturer) && jObject[kJsonManufacturer].isString())
            {
                std::string manufacturerName = jObject[kJsonManufacturer].asString();
                // We don't let a model be specified without also specifying an Manufacturer:
                if (jObject.isMember(kJsonModel) && jObject[kJsonModel].isString())
                {
                    std::string model = jObject[kJsonModel].asString();
                    device            = new Device(manufacturerName, model);
                }
                else
                {
                    device = new Device(manufacturerName);
                }
            }
            else
            {
                // 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):
                device = new Device();
            }
            return device;
        }
    
        void addGPU(const GPU &gpu) { mGpuList.addItem(gpu); }
        bool match(const Device &toCheck) const
        {
            VERBOSE("\t Within Device match: wildcards are %s and %s,\n", mWildcard ? "true" : "false",
                    toCheck.mWildcard ? "true" : "false");
            if (!(mWildcard || toCheck.mWildcard))
            {
                VERBOSE("\t   Manufacturer match is %s, model is %s\n",
                        toCheck.mManufacturer.match(mManufacturer) ? "true" : "false",
                        toCheck.mModel.match(mModel) ? "true" : "false");
            }
            VERBOSE("\t   Need to check ListOf<GPU>\n");
            return ((mWildcard || toCheck.mWildcard ||
                     // The wildcards can override the Manufacturer/Model check, but not the GPU check
                     (toCheck.mManufacturer.match(mManufacturer) && toCheck.mModel.match(mModel))) &&
                    mGpuList.match(toCheck.mGpuList));
        }
        void logItem() const
        {
            if (mWildcard)
            {
                if (mGpuList.mWildcard)
                {
                    VERBOSE("      Wildcard (i.e. will match all devices)");
                    return;
                }
                else
                {
                    VERBOSE(
                        "      Device with any manufacturer and model"
                        ", and with the following GPUs:");
                }
            }
            else
            {
                if (!mModel.mWildcard)
                {
                    VERBOSE(
                        "      Device manufacturer: \"%s\" and model \"%s\""
                        ", and with the following GPUs:",
                        mManufacturer.mPart.c_str(), mModel.mPart.c_str());
                }
                else
                {
                    VERBOSE(
                        "      Device manufacturer: \"%s\""
                        ", and with the following GPUs:",
                        mManufacturer.mPart.c_str());
                }
            }
            mGpuList.logListOf("        ", "GPUs");
        }
    
      public:
        StringPart mManufacturer;
        StringPart mModel;
        ListOf<GPU> mGpuList;
        bool mWildcard;
    };
    
    // 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(appName)), mDevice(Device(deviceMfr, deviceModel))
        {}
        ~Scenario() {}
        void logScenario()
        {
            VERBOSE("  Scenario to compare against the rules");
            VERBOSE("    Application:");
            mApplication.logItem();
            VERBOSE("    Device:");
            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() {}
        void addApp(const Application &app) { mAppList.addItem(app); }
        void addDevice(const Device &dev) { mDevList.addItem(dev); }
        bool match(const Scenario &toCheck) const
        {
            VERBOSE("    Within \"%s\" Rule: application match is %s and device match is %s\n",
                    mDescription.c_str(), mAppList.match(toCheck.mApplication) ? "true" : "false",
                    mDevList.match(toCheck.mDevice) ? "true" : "false");
            return (mAppList.match(toCheck.mApplication) && mDevList.match(toCheck.mDevice));
        }
        bool getUseANGLE() const { return mUseANGLE; }
        void logRule() const
        {
            VERBOSE("  Rule: \"%s\" %s ANGLE", 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::Reader jReader;
            Json::Value jTopLevelObject;
            jReader.parse(jsonFileContents, jTopLevelObject);
            Json::Value jRules = jTopLevelObject[kJsonRules];
            for (unsigned int ruleIndex = 0; ruleIndex < jRules.size(); ruleIndex++)
            {
                Json::Value jRule           = jRules[ruleIndex];
                std::string ruleDescription = jRule[kJsonRule].asString();
                bool useANGLE               = jRule[kJsonUseANGLE].asBool();
                Rule *newRule               = new Rule(ruleDescription, useANGLE);
    
                Json::Value jApps = jRule[kJsonApplications];
                for (unsigned int appIndex = 0; appIndex < jApps.size(); appIndex++)
                {
                    Json::Value jApp    = jApps[appIndex];
                    Application *newApp = Application::CreateApplicationFromJson(jApp);
                    newRule->addApp(*newApp);
                    delete newApp;
                }
    
                Json::Value jDevs = jRule[kJsonDevices];
                for (unsigned int deviceIndex = 0; deviceIndex < jDevs.size(); deviceIndex++)
                {
                    Json::Value jDev = jDevs[deviceIndex];
                    Device *newDev   = Device::CreateDeviceFromJson(jDev);
    
                    Json::Value jGPUs = jDev[kJsonGPUs];
                    for (unsigned int gpuIndex = 0; gpuIndex < jGPUs.size(); gpuIndex++)
                    {
                        Json::Value jGPU = jGPUs[gpuIndex];
                        GPU *newGPU      = GPU::CreateGpuFromJson(jGPU);
                        if (newGPU)
                        {
                            newDev->addGPU(*newGPU);
                            delete newGPU;
                        }
                    }
                    newRule->addDevice(*newDev);
                    delete newDev;
                }
    
                rules->addRule(*newRule);
                delete newRule;
            }
    
            // Make sure there is at least one, default rule.  If not, add it here:
            int nRules = rules->mRuleList.size();
            if (nRules == 0)
            {
                Rule defaultRule("Default Rule", false);
                rules->addRule(defaultRule);
            }
            return rules;
        }
    
        void addRule(const Rule &rule) { mRuleList.push_back(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:",
                    static_cast<int>(mRuleList.size()));
    
            for (const Rule &rule : mRuleList)
            {
                VERBOSE("  Checking Rule: \"%s\" (to see whether there's a match)",
                        rule.mDescription.c_str());
                if (rule.match(toCheck))
                {
                    VERBOSE("  -> Rule matches.  Setting useANGLE to %s",
                            rule.getUseANGLE() ? "true" : "false");
                    useANGLE = rule.getUseANGLE();
                }
                else
                {
                    VERBOSE("  -> Rule doesn't match.");
                }
            }
    
            return useANGLE;
        }
        void logRules()
        {
            VERBOSE("Showing %d ANGLE-for-Android rules:", 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);
        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, gpuDriverVersion);
        scenario.mDevice.addGPU(gpuDriver);
        scenario.logScenario();
    
        bool rtn = rules->getUseANGLE(scenario);
        VERBOSE("Application \"%s\" should %s ANGLE", 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"