Merge the 2021-09-05 SPL branch from AOSP-Partner

* security-aosp-pi-release:
  Do not modify vector after getting references

Change-Id: I6be06a35a91061480826da9b4be5c34937479aee
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index c5b57c7..fb682ec 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -94,7 +94,32 @@
         OP_PROCESS_OUTGOING_CALLS = 54,
         OP_USE_FINGERPRINT = 55,
         OP_BODY_SENSORS = 56,
+        OP_READ_CELL_BROADCASTS = 57,
+        OP_MOCK_LOCATION = 58,
+        OP_READ_EXTERNAL_STORAGE = 59,
+        OP_WRITE_EXTERNAL_STORAGE = 60,
+        OP_TURN_SCREEN_ON = 61,
+        OP_GET_ACCOUNTS = 62,
+        OP_RUN_IN_BACKGROUND = 63,
         OP_AUDIO_ACCESSIBILITY_VOLUME = 64,
+        OP_READ_PHONE_NUMBERS = 65,
+        OP_REQUEST_INSTALL_PACKAGES = 66,
+        OP_PICTURE_IN_PICTURE = 67,
+        OP_INSTANT_APP_START_FOREGROUND = 68,
+        OP_ANSWER_PHONE_CALLS = 69,
+        OP_RUN_ANY_IN_BACKGROUND = 70,
+        OP_CHANGE_WIFI_STATE = 71,
+        OP_REQUEST_DELETE_PACKAGES = 72,
+        OP_BIND_ACCESSIBILITY_SERVICE = 73,
+        OP_ACCEPT_HANDOVER = 74,
+        OP_MANAGE_IPSEC_TUNNELS = 75,
+        OP_START_FOREGROUND = 76,
+        OP_BLUETOOTH_SCAN = 77,
+        OP_BLUETOOTH_CHANGE = 78,
+        OP_BOOT_COMPLETED = 79,
+        OP_NFC_CHANGE = 80,
+        OP_DATA_CONNECT_CHANGE = 81,
+        OP_SU = 82
     };
 
     AppOpsManager();
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 49ffc8f..651b7f6 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -23,6 +23,7 @@
 #include <memory>
 
 #include <cutils/native_handle.h>
+#include <cutils/properties.h>
 #include <log/log.h>
 #include <utils/StrongPointer.h>
 #include <ui/GraphicBuffer.h>
@@ -31,6 +32,8 @@
 #include <private/android/AHardwareBufferHelpers.h>
 #include <android/hardware/graphics/common/1.1/types.h>
 
+#include "../../opengl/libs/gles_workarounds.h"
+
 
 static constexpr int kFdBufferSize = 128 * sizeof(int);  // 128 ints
 
@@ -67,6 +70,17 @@
         return BAD_VALUE;
     }
 
+    // Refuse creating buffers that are proven to be unreliable (by CTS), unless
+    // the user opts in for experimental features.
+    if (!FP2GLESWorkarounds::isExperimentalGLES3Enabled()) {
+        // SingleLayer_ColorTest_GpuColorOutputCpuRead_R8G8B8_UNORM
+        if (desc->layers == 1 && desc->format == AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM) {
+            ALOGW("Refusing to create buffer with unreliable configuration (R8G8B8_UNORM) while "
+                "experimental GLES3 support is disabled.");
+            return BAD_VALUE;
+        }
+    }
+
     uint64_t usage =  AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
     sp<GraphicBuffer> gbuffer(new GraphicBuffer(
             desc->width, desc->height, format, desc->layers, usage,
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 5fbb3b2..2e5a876 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -68,6 +68,10 @@
         "libgrallocusage",
     ],
 
+    whole_static_libs: [
+        "libgles_workarounds",
+    ],
+
     header_libs: [
         "libnativebase_headers",
     ],
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 5200545..481bd7c 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -20,6 +20,8 @@
 #include <sys/types.h>
 
 #include <cutils/native_handle.h>
+#include <cutils/properties.h>
+#include <log/log.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/Vector.h>
@@ -67,6 +69,13 @@
         while (n) {
             n--;
             reply.read(s);
+            if (s.getType() == Sensor::TYPE_GYROSCOPE &&
+                !property_get_bool("persist.fp2.use_gyroscope", false)) {
+                ALOGW("BpSensorServer::getSensorList: Skipping sensor with insufficient precision: "
+                    "\"%s\", vendor \"%s\"",
+                    s.getName().c_str(), s.getVendor().c_str());
+                continue;
+            }
             v.add(s);
         }
         return v;
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index 2383516..e742bec 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -55,7 +55,10 @@
 
     // Set fifo event count zero for older devices which do not support batching. Fused
     // sensors also have their fifo counts set to zero.
-    if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) {
+    if (halVersion > SENSORS_DEVICE_API_VERSION_1_0
+        // HANDLE_ACCELERATION == 0. FP2 accelerometer doesn't support batching properly.
+        && mHandle != 0
+    ) {
         mFifoReservedEventCount = hwSensor.fifoReservedEventCount;
         mFifoMaxEventCount = hwSensor.fifoMaxEventCount;
     } else {
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index ff9d19e..f763496 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -23,6 +23,7 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-DADDNL_GRALLOC_10_USAGE_BITS=0x02000000U",
     ],
     cppflags: [
         "-Weverything",
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index b92cbf3..918251f 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -45,6 +45,12 @@
         // TODO(b/72323293, b/72703005): Remove these additional bits
         bits = bits | (1 << 10) | (1 << 13);
 
+#ifdef ADDNL_GRALLOC_10_USAGE_BITS
+        uint64_t addnl_bits = static_cast<uint64_t>(ADDNL_GRALLOC_10_USAGE_BITS);
+        ALOGI("Adding additional valid usage bits: 0x%" PRIx64, addnl_bits);
+        bits = bits | addnl_bits;
+#endif
+
         return bits;
     }();
     return valid10UsageBits;
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index d43c164..114319a 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -83,6 +83,14 @@
     include_dirs: ["bionic/libc/private"],
 }
 
+cc_library_static {
+    name: "libgles_workarounds",
+    defaults: ["gl_libs_defaults"],
+    srcs: [
+        "gles_workarounds.cpp"
+    ]
+}
+
 //##############################################################################
 // Build META EGL library
 //
@@ -104,6 +112,9 @@
         "libnativewindow",
         "libbacktrace",
     ],
+    static_libs: [
+        "libgles_workarounds",
+    ],
     target: {
         vendor: {
             exclude_shared_libs: ["libgraphicsenv"],
@@ -181,6 +192,9 @@
         },
     },
     shared_libs: ["libEGL"],
+    static_libs: [
+        "libgles_workarounds",
+    ],
 }
 
 //##############################################################################
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 91a3455..1e8d7ba 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -1,5 +1,6 @@
 /*
  ** Copyright 2007, The Android Open Source Project
+ ** Copyright 2018-2020, Fairphone B.V.
  **
  ** Licensed under the Apache License, Version 2.0 (the "License");
  ** you may not use this file except in compliance with the License.
@@ -25,6 +26,7 @@
 #include <dlfcn.h>
 
 #include <android/dlext.h>
+#include <cutils/compiler.h>
 #include <cutils/properties.h>
 #include <log/log.h>
 
@@ -35,6 +37,7 @@
 
 #include "egl_trace.h"
 #include "egldefs.h"
+#include "gles_workarounds.h"
 
 extern "C" {
   android_namespace_t* android_get_exported_namespace(const char*);
@@ -480,6 +483,30 @@
     return nullptr;
 }
 
+namespace
+{
+
+typedef EGLBoolean (*eglGetConfigAttrib_func_t)(
+    EGLDisplay display, EGLConfig config, EGLint attribute, EGLint * value);
+
+eglGetConfigAttrib_func_t eglGetConfigAttrib_func_ptr = nullptr;
+
+EGLBoolean eglGetConfigAttrib_wrapper(EGLDisplay display,
+    EGLConfig config,
+    EGLint attribute,
+    EGLint * value)
+{
+    if (CC_UNLIKELY(!eglGetConfigAttrib_func_ptr)) {
+        return EGL_FALSE;
+    }
+
+    const EGLBoolean result = eglGetConfigAttrib_func_ptr(display, config, attribute, value);
+
+    return FP2GLESWorkarounds::eglGetConfigAttrib(display, config, attribute, value, result);
+}
+
+}
+
 void *Loader::load_driver(const char* kind,
         egl_connection_t* cnx, uint32_t mask)
 {
@@ -510,6 +537,8 @@
         char const * const * api = egl_names;
         while (*api) {
             char const * name = *api;
+            // wrap eglGetConfigAttrib(), see eglGetConfigAttrib_wrapper()
+            const bool loading_eglGetConfigAttrib = (strcmp(name, "eglGetConfigAttrib") == 0);
             __eglMustCastToProperFunctionPointerType f =
                 (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
             if (f == NULL) {
@@ -519,6 +548,10 @@
                     f = (__eglMustCastToProperFunctionPointerType)0;
                 }
             }
+            if (f && loading_eglGetConfigAttrib) {
+                eglGetConfigAttrib_func_ptr = (eglGetConfigAttrib_func_t)f;
+                f = (__eglMustCastToProperFunctionPointerType)&eglGetConfigAttrib_wrapper;
+            }
             *curr++ = f;
             api++;
         }
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index f53cf3f..5eb1ee6 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -111,6 +111,32 @@
 
 // ----------------------------------------------------------------------------
 
+void egl_set_framework_error_for_currrent_context(GLenum error) {
+    EGLContext context = egl_tls_t::getContext();
+    if (context == EGL_NO_CONTEXT)
+        return;
+
+    egl_context_t * const c = get_context(context);
+    if (c == NULL) // this should never happen, by construction
+        return;
+
+    c->setFrameworkGLError(error);
+}
+
+GLenum egl_get_reset_framework_error_for_current_context() {
+    EGLContext context = egl_tls_t::getContext();
+    if (context == EGL_NO_CONTEXT)
+        return 0;
+
+    egl_context_t * const c = get_context(context);
+    if (c == NULL) // this should never happen, by construction
+        return 0;
+
+    return c->getResetFrameworkGLError();
+}
+
+// ----------------------------------------------------------------------------
+
 const GLubyte * egl_get_string_for_current_context(GLenum name) {
     // NOTE: returning NULL here will fall-back to the default
     // implementation.
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index f879254..306352e 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -18,6 +18,8 @@
 
 #include <sstream>
 
+#include "../gles_workarounds.h"
+
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -281,7 +283,8 @@
 egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
         egl_connection_t const* cnx, int version) :
     egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
-            config(config), read(0), draw(0), cnx(cnx), version(version) {
+            config(config), read(0), draw(0), cnx(cnx), version(version),
+            frameworkGLError(0) {
 }
 
 void egl_context_t::onLooseCurrent() {
@@ -318,10 +321,27 @@
             while (ss >> str) {
                 tokenized_gl_extensions.push_back(str);
             }
+
+            FP2GLESWorkarounds::filterEGLContextExtensions(
+                &gl_extensions,
+                &tokenized_gl_extensions
+            );
         }
     }
 }
 
+
+void egl_context_t::setFrameworkGLError(int32_t error) {
+    frameworkGLError = error;
+}
+
+int32_t egl_context_t::getResetFrameworkGLError() {
+    const int32_t currentError = frameworkGLError;
+    frameworkGLError = 0;
+    return currentError;
+}
+
+
 // ----------------------------------------------------------------------------
 }; // namespace android
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 4e1de5c..c6f478e 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -198,6 +198,15 @@
     void onLooseCurrent();
     void onMakeCurrent(EGLSurface draw, EGLSurface read);
 
+    // Store GL error flag set by the framework.
+    // Used for working around driver issues.
+    // This uses int32_t instead of GLenum, so that we don't depend on
+    // GL headers here.
+    void setFrameworkGLError(int32_t error);
+    // As per specification of glGetError(), retrieving the error flag
+    // also resets it.
+    int32_t getResetFrameworkGLError();
+
     EGLDisplay dpy;
     EGLContext context;
     EGLConfig config;
@@ -207,6 +216,9 @@
     int version;
     std::string gl_extensions;
     std::vector<std::string> tokenized_gl_extensions;
+
+private:
+    int32_t frameworkGLError;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index f7fde96..303b8f0 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -1,5 +1,6 @@
 /*
  ** Copyright 2007, The Android Open Source Project
+ ** Copyright 2018-2020, Fairphone B.V.
  **
  ** Licensed under the Apache License, Version 2.0 (the "License");
  ** you may not use this file except in compliance with the License.
@@ -18,12 +19,14 @@
 #include <errno.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <vector>
 
 #include <log/log.h>
 #include <cutils/properties.h>
 
 #include "../hooks.h"
 #include "../egl_impl.h"
+#include "../gles_workarounds.h"
 
 using namespace android;
 
@@ -292,12 +295,49 @@
  */
 
 extern "C" {
+    GLenum __glGetError();
     const GLubyte * __glGetString(GLenum name);
     const GLubyte * __glGetStringi(GLenum name, GLuint index);
     void __glGetBooleanv(GLenum pname, GLboolean * data);
     void __glGetFloatv(GLenum pname, GLfloat * data);
     void __glGetIntegerv(GLenum pname, GLint * data);
     void __glGetInteger64v(GLenum pname, GLint64 * data);
+    void __glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
+        GLsizei height, GLenum format, GLenum type, const void *pixels);
+    void __glTexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width,
+        GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type,
+        const void *pixels);
+    void __glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
+        GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
+        const void *pixels);
+    void __glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width,
+        GLsizei height);
+    void __glShaderSource(GLuint shader, GLsizei count, const GLchar *const*string,
+        const GLint *length);
+
+}
+
+namespace
+{
+
+template<typename T>
+void getAliasedPointSizeRange(T * data) {
+    data[0] = static_cast<T>(FP2GLESWorkarounds::GL_ALIASED_POINT_SIZE_MIN);
+    data[1] = static_cast<T>(FP2GLESWorkarounds::GL_ALIASED_POINT_SIZE_MAX);
+}
+
+}
+
+GLenum glGetError() {
+    // As per specification of glGetError(), it resets the error flag when being
+    // called. So make sure to always fetch and reset both the framework and
+    // driver error flag.
+    const GLenum frameworkError =
+        egl_get_reset_framework_error_for_current_context();
+    const GLenum driverError = __glGetError();
+
+    // Framework-defined errors override driver errors.
+    return frameworkError != 0 ? frameworkError : driverError;
 }
 
 const GLubyte * glGetString(GLenum name) {
@@ -306,7 +346,8 @@
         gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
         if(_c) ret = _c->glGetString(name);
     }
-    return ret;
+
+    return FP2GLESWorkarounds::glGetString(name, ret);
 }
 
 const GLubyte * glGetStringi(GLenum name, GLuint index) {
@@ -340,6 +381,11 @@
         }
     }
 
+    if (pname == GL_ALIASED_POINT_SIZE_RANGE) {
+        getAliasedPointSizeRange(data);
+        return;
+    }
+
     gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
     if (_c) _c->glGetFloatv(pname, data);
 }
@@ -353,6 +399,11 @@
         }
     }
 
+    if (pname == GL_ALIASED_POINT_SIZE_RANGE) {
+        getAliasedPointSizeRange(data);
+        return;
+    }
+
     gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
     if (_c) _c->glGetIntegerv(pname, data);
 }
@@ -366,6 +417,109 @@
         }
     }
 
+    if (pname == GL_ALIASED_POINT_SIZE_RANGE) {
+        getAliasedPointSizeRange(data);
+        return;
+    }
+
     gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
     if (_c) _c->glGetInteger64v(pname, data);
 }
+
+void glTexImage2D(GLenum target, GLint level,GLint internalformat, GLsizei width, GLsizei height,
+    GLint border, GLenum format, GLenum type, const void *pixels) {
+    const bool isValidRequest = FP2GLESWorkarounds::checkValidGlTexImage2D(
+        target, level, internalformat, width, height, border, format, type, pixels);
+    if (isValidRequest) {
+        __glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+    }
+}
+
+void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
+    GLsizei height, GLenum format, GLenum type, const void *pixels) {
+    const bool isValidRequest = FP2GLESWorkarounds::checkValidGlTexSubImage2D(
+        target, level, xoffset, yoffset, width, height, format, type, pixels);
+    if (isValidRequest) {
+        __glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
+    }
+}
+
+void glTexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height,
+    GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) {
+    const bool isValidRequest = FP2GLESWorkarounds::checkValidGlTexImage3D(
+        target, level, internalformat, width, height, depth, border, format, type, pixels);
+    if (isValidRequest) {
+        __glTexImage3D(target, level, internalformat, width, height, depth, border, format, type,
+            pixels);
+    }
+}
+
+void glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
+    GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) {
+    const bool isValidRequest = FP2GLESWorkarounds::checkValidGlTexSubImage3D(
+        target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
+    if (isValidRequest) {
+        __glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format,
+            type, pixels);
+    }
+}
+
+void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
+    const bool isValidRequest = FP2GLESWorkarounds::checkValidGlRenderbufferStorage(
+        target, internalformat, width, height);
+    if (isValidRequest) {
+        __glRenderbufferStorage(target, internalformat, width, height);
+    }
+}
+
+
+void glShaderSource(const GLuint shader, const GLsizei count, const GLchar *const*string,
+    const GLint *length) {
+
+    if (count < 0 || !string) {
+        // Let the driver handle error reporting for invalid parameters.
+        __glShaderSource(shader, count, string, length);
+        return;
+    }
+
+    bool anyModified = false;
+    std::vector<std::string> modifiedStrings(count);
+    std::vector<const GLchar*> modifiedStringPtrs(count, nullptr);
+    std::vector<GLint> modifiedLengths(count);
+    for (GLsizei i = 0; i < count; ++i) {
+        const GLsizei inputLength = length ? length[i] : -1;
+        const GLchar *const inputString = string[i];
+        // default-initialize with unmodified values
+        modifiedLengths[i] = inputLength;
+        modifiedStringPtrs[i] = inputString;
+
+        if (!inputString) {
+            // This case would be a bug. Let the driver handle it.
+            continue;
+        }
+        std::string str;
+        if (inputLength > 0) {
+            // Length is explicitly defined.
+            str = std::string(inputString, inputLength);
+        } else if (inputLength < 0) {
+            // String is null-terminated.
+            str = std::string(inputString);
+        } else {
+            // Unexpected, probably also a bug on application-side.
+            continue;
+        }
+        if (FP2GLESWorkarounds::adjustShaderString(str)) {
+            modifiedLengths[i] = str.length();
+            modifiedStrings[i] = std::move(str);
+            modifiedStringPtrs[i] = modifiedStrings[i].c_str();
+            anyModified = true;
+        }
+    }
+
+    if (!anyModified) {
+        __glShaderSource(shader, count, string, length);
+    } else {
+        __glShaderSource(shader, count,
+            modifiedStringPtrs.data(), modifiedLengths.data());
+    }
+}
diff --git a/opengl/libs/GLES2/gl2_api.in b/opengl/libs/GLES2/gl2_api.in
index a331572..dfb2ac5 100644
--- a/opengl/libs/GLES2/gl2_api.in
+++ b/opengl/libs/GLES2/gl2_api.in
@@ -178,7 +178,7 @@
 void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint *params) {
     CALL_GL_API(glGetBufferParameteriv, target, pname, params);
 }
-GLenum API_ENTRY(glGetError)(void) {
+GLenum API_ENTRY(__glGetError)(void) {
     CALL_GL_API_RETURN(glGetError);
 }
 void API_ENTRY(__glGetFloatv)(GLenum pname, GLfloat *data) {
@@ -280,7 +280,7 @@
 void API_ENTRY(glReleaseShaderCompiler)(void) {
     CALL_GL_API(glReleaseShaderCompiler);
 }
-void API_ENTRY(glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
+void API_ENTRY(__glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
     CALL_GL_API(glRenderbufferStorage, target, internalformat, width, height);
 }
 void API_ENTRY(glSampleCoverage)(GLfloat value, GLboolean invert) {
@@ -292,7 +292,7 @@
 void API_ENTRY(glShaderBinary)(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) {
     CALL_GL_API(glShaderBinary, count, shaders, binaryformat, binary, length);
 }
-void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) {
+void API_ENTRY(__glShaderSource)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) {
     CALL_GL_API(glShaderSource, shader, count, string, length);
 }
 void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) {
@@ -313,7 +313,7 @@
 void API_ENTRY(glStencilOpSeparate)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {
     CALL_GL_API(glStencilOpSeparate, face, sfail, dpfail, dppass);
 }
-void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {
+void API_ENTRY(__glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {
     CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, border, format, type, pixels);
 }
 void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) {
@@ -328,7 +328,7 @@
 void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint *params) {
     CALL_GL_API(glTexParameteriv, target, pname, params);
 }
-void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {
+void API_ENTRY(__glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {
     CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset, width, height, format, type, pixels);
 }
 void API_ENTRY(glUniform1f)(GLint location, GLfloat v0) {
@@ -430,10 +430,10 @@
 void API_ENTRY(glDrawRangeElements)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices) {
     CALL_GL_API(glDrawRangeElements, mode, start, end, count, type, indices);
 }
-void API_ENTRY(glTexImage3D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) {
+void API_ENTRY(__glTexImage3D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) {
     CALL_GL_API(glTexImage3D, target, level, internalformat, width, height, depth, border, format, type, pixels);
 }
-void API_ENTRY(glTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) {
+void API_ENTRY(__glTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) {
     CALL_GL_API(glTexSubImage3D, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
 }
 void API_ENTRY(glCopyTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index bacd4b4..562f923 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -1,5 +1,6 @@
 /*
  ** Copyright 2007, The Android Open Source Project
+ ** Copyright 2018-2020, Fairphone B.V.
  **
  ** Licensed under the Apache License, Version 2.0 (the "License");
  ** you may not use this file except in compliance with the License.
@@ -27,6 +28,7 @@
 
 #include "../hooks.h"
 #include "../egl_impl.h"
+#include "../gles_workarounds.h"
 
 using namespace android;
 
@@ -352,5 +354,6 @@
         gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
         ret = _c->glGetString(name);
     }
-    return ret;
+
+    return FP2GLESWorkarounds::glGetString(name, ret);
 }
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index a8855ef..981239f 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -27,6 +27,11 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
+// Allow framework to set GL error state on unreliable driver implementations.
+EGLAPI void egl_set_framework_error_for_currrent_context(GLenum error);
+// Get and reset the current GL error flag defined by the framework.
+EGLAPI GLenum egl_get_reset_framework_error_for_current_context();
+
 EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name);
 EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index);
 EGLAPI GLint egl_get_num_extensions_for_current_context();
diff --git a/opengl/libs/gles_workarounds.cpp b/opengl/libs/gles_workarounds.cpp
new file mode 100644
index 0000000..5283741
--- /dev/null
+++ b/opengl/libs/gles_workarounds.cpp
@@ -0,0 +1,427 @@
+#include "gles_workarounds.h"
+
+#include <cstring>
+#include <regex>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <cutils/properties.h>
+#include <log/log.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES3/gl3.h>
+
+#include "egl_impl.h"
+
+namespace
+{
+
+bool fp2_experimental_gles3 = false;
+
+bool check_fp2_experimental_gles3()
+{
+    static const char s_OpenGLESv30[] = "196608";
+    char prop[PROPERTY_VALUE_MAX];
+    property_get("ro.opengles.version", prop, "");
+    return strncmp(prop, s_OpenGLESv30, PROPERTY_VALUE_MAX) == 0;
+}
+
+void early_gles_workarounds_init()
+{
+    fp2_experimental_gles3 = check_fp2_experimental_gles3();
+}
+
+pthread_once_t once_control = PTHREAD_ONCE_INIT;
+const int sEarlyInitState = pthread_once(&once_control, &early_gles_workarounds_init);
+
+
+const std::vector<std::string> & unstableGLExtension()
+{
+    static const std::vector<std::string> exts = {
+        "GL_EXT_sRGB",
+        "GL_EXT_sRGB_write_control",
+        "GL_EXT_texture_sRGB_decode",
+    };
+    return exts;
+}
+
+const std::regex & unstableGLExtRegex()
+{
+    static const std::regex r = []() {
+        // Construct regex: match all unstable extension names.
+        std::string regexStr;
+        for (size_t i = 0; i < unstableGLExtension().size(); ++i) {
+            regexStr.append(unstableGLExtension()[i]);
+            if (i != unstableGLExtension().size() - 1) {
+                regexStr.append("|");
+            }
+        }
+        // Match whole words only and remove redundant space.
+        regexStr = "\\b(" + regexStr + ")\\b[ ]?";
+        return std::regex(regexStr);
+    }();
+    return r;
+}
+
+std::string filterStableExtensions(const std::string & extensions)
+{
+    return std::regex_replace(
+        extensions,
+        unstableGLExtRegex(),
+        "");
+}
+
+// Check for incompletely supported sRGB formats for textures.
+bool isValidTextureFormat(GLenum format, GLenum internalformat) {
+    if (fp2_experimental_gles3) {
+        // Experimental mode: Expose everything the driver implements.
+        return true;
+    }
+
+    // The "GL_SRGB*_EXT" enums below are introduced by the extension
+    // GL_EXT_sRGB. They are available as identical, non-*_EXT enum in GLES3 as
+    // well. GL_SRGB8 is available through GLES3 only.
+
+    if (format == GL_SRGB_EXT || format == GL_SRGB_ALPHA_EXT) {
+        return false;
+    }
+
+    if (internalformat == GL_SRGB8 || internalformat == GL_SRGB8_ALPHA8_EXT
+        || internalformat == GL_SRGB_EXT || internalformat == GL_SRGB_ALPHA_EXT) {
+        return false;
+    }
+
+    return true;
+}
+
+// Check for incompletely supported sRGB formats for renderbuffers.
+bool isValidRenderbufferFormat(GLenum internalformat) {
+    if (fp2_experimental_gles3) {
+        return true;
+    }
+
+    // GL_EXT_sRGB adds GL_SRGB8_ALPHA8_EXT for renderbuffers. All other
+    // GL_SRGB* enums get rejected by the driver, if used on renderbuffers.
+    if (internalformat == GL_SRGB8_ALPHA8_EXT) {
+        return false;
+    }
+
+    return true;
+}
+
+
+// The following two functions are taken from system/core/base/strings.cpp for
+// convenience. See ./Android.bp for comments on not adding additional link
+// dependencies.
+
+std::vector<std::string> SplitString(const std::string& s,
+                               const std::string& delimiters) {
+  if (delimiters.size() == 0u) {
+    return {};
+  }
+
+  std::vector<std::string> result;
+
+  size_t base = 0;
+  size_t found;
+  while (true) {
+    found = s.find_first_of(delimiters, base);
+    result.push_back(s.substr(base, found - base));
+    if (found == s.npos) break;
+    base = found + 1;
+  }
+
+  return result;
+}
+
+template <typename ContainerT, typename SeparatorT>
+std::string JoinStrings(const ContainerT& things, SeparatorT separator) {
+  if (things.empty()) {
+    return "";
+  }
+
+  std::ostringstream result;
+  result << *things.begin();
+  for (auto it = std::next(things.begin()); it != things.end(); ++it) {
+    result << separator << *it;
+  }
+  return result.str();
+}
+
+}
+
+
+bool FP2GLESWorkarounds::isExperimentalGLES3Enabled()
+{
+    return fp2_experimental_gles3;
+}
+
+EGLBoolean FP2GLESWorkarounds::eglGetConfigAttrib(
+    const EGLDisplay /* display */,
+    const EGLConfig /* config */,
+    const EGLint attribute,
+    EGLint * value,
+    const EGLBoolean driverResult)
+{
+    if (!value) {
+        return driverResult;
+    }
+
+    if ((attribute == EGL_RENDERABLE_TYPE) && !fp2_experimental_gles3) {
+        *value = *value & ~EGL_OPENGL_ES3_BIT_KHR;
+    }
+
+    return driverResult;
+}
+
+const GLubyte * FP2GLESWorkarounds::glGetString(const GLenum name, const GLubyte * driverString)
+{
+    if (fp2_experimental_gles3) {
+        return driverString;
+    }
+
+    if (name == GL_VERSION) {
+        struct VersionStringReplacement {
+            const char * broken_version_prefix;
+            const size_t check_length;
+            const char * fake_version_string;
+        };
+        const VersionStringReplacement versionStringReplacements[] = {
+            { "OpenGL ES 3.", 12, "OpenGL ES 2.0" },
+            { "OpenGL ES-CM 3.", 15, "OpenGL ES-CM 2.0" },
+            { NULL, 0, NULL }
+        };
+        const VersionStringReplacement * r = versionStringReplacements;
+        for (; r->broken_version_prefix != NULL; ++r) {
+            if (strncmp((const char *)driverString,
+                r->broken_version_prefix, r->check_length) == 0) {
+                return (const GLubyte *)r->fake_version_string;
+            }
+        }
+        return driverString;
+    }
+
+    return driverString;
+}
+
+
+bool FP2GLESWorkarounds::checkValidGlTexImage2D(GLenum /*target*/,
+    GLint /*level*/,
+    GLint internalformat,
+    GLsizei /*width*/,
+    GLsizei /*height*/,
+    GLint /*border*/,
+    GLenum format,
+    GLenum /*type*/,
+    const void * /*data*/)
+{
+    if (!isValidTextureFormat(format, internalformat)) {
+        // glTexImage2D and related report GL_INVALID_OPERATION if format,
+        // internal format and type don't match the expected combinations.
+        android::egl_set_framework_error_for_currrent_context(GL_INVALID_OPERATION);
+        return false;
+    }
+
+    return true;
+}
+
+bool FP2GLESWorkarounds::checkValidGlTexSubImage2D(GLenum /*target*/,
+    GLint /*level*/,
+    GLint /*xoffset*/,
+    GLint /*yoffset*/,
+    GLsizei /*width*/,
+    GLsizei /*height*/,
+    GLenum format,
+    GLenum /*type*/,
+    const void * /*data*/)
+{
+    if (!isValidTextureFormat(format, 0)) {
+        android::egl_set_framework_error_for_currrent_context(GL_INVALID_OPERATION);
+        return false;
+    }
+
+    return true;
+}
+
+bool FP2GLESWorkarounds::checkValidGlTexImage3D(GLenum /*target*/, GLint /*level*/,
+    GLint internalformat, GLsizei /*width*/, GLsizei /*height*/, GLsizei /*depth*/,
+    GLint /*border*/, GLenum format, GLenum /*type*/, const void */*pixels*/) {
+    if (!isValidTextureFormat(format, internalformat)) {
+        android::egl_set_framework_error_for_currrent_context(GL_INVALID_OPERATION);
+        return false;
+    }
+    return true;
+}
+
+bool FP2GLESWorkarounds::checkValidGlTexSubImage3D(GLenum /*target*/, GLint /*level*/,
+    GLint /*xoffset*/, GLint /*yoffset*/, GLint /*zoffset*/, GLsizei /*width*/, GLsizei /*height*/,
+    GLsizei /*depth*/, GLenum format, GLenum /*type*/,
+    const void */*pixels*/) {
+    if (!isValidTextureFormat(format, 0)) {
+        android::egl_set_framework_error_for_currrent_context(GL_INVALID_OPERATION);
+        return false;
+    }
+    return true;
+}
+
+bool FP2GLESWorkarounds::checkValidGlRenderbufferStorage(GLenum /*target*/,
+    GLenum internalformat,
+    GLsizei /*width*/,
+    GLsizei /*height*/)
+{
+    if (!isValidRenderbufferFormat(internalformat)) {
+        // glRenderbufferStorage reports GL_INVALID_ENUM in case of wrong
+        // internal format.
+        android::egl_set_framework_error_for_currrent_context(GL_INVALID_ENUM);
+        return false;
+    }
+
+    return true;
+}
+
+namespace {
+
+enum class FixResult {
+    noMatch,
+    applied,
+    unsupportedCode,
+};
+
+/** Replace a function call on a vec3 by separate calls on each component.
+ *
+ * This helps fixing issues with dFdx/dFdy in GLSL on Adreno 330 that produces
+ * wrong results only when called on a vec3.
+ *
+ * Parameters:
+ *  pos: Starting position to search from; will be updated to the end of the
+ *    current search.
+ */
+FixResult replace_func_vec3_call(std::string& source, const std::string& funcName, size_t& pos) {
+    const size_t callStartPos = source.find(funcName, pos);
+    if (callStartPos == std::string::npos) {
+        return FixResult::noMatch;
+    }
+    // Find opening and closing brackets
+    pos = callStartPos + funcName.length();
+    for (; pos < source.length(); ++pos) {
+        switch (source[pos]) {
+            case ' ':
+            case '\t':
+            case '\n':
+            case '\r':
+                continue;
+            case '(':
+                break;
+            default:
+                return FixResult::unsupportedCode;
+        }
+        break;
+    }
+    if (pos == source.length()) {
+        pos = std::string::npos;
+        return FixResult::unsupportedCode;
+    }
+    const size_t openingPos = pos;
+    const size_t closingPos = source.find(")", openingPos);
+    if (closingPos == std::string::npos) {
+        return FixResult::unsupportedCode;
+    }
+
+    // Extract the parameter part of call like `func(var.xyz)`:
+    const std::string paramStr = source.substr(
+        openingPos + 1, closingPos - openingPos - 1);
+    // Match `some_variable_42.swizzle`, where `swizzle` can use either of the
+    // sets `xyzw`, `rgba` or `stpq` (according to GLSL specs). We are looking
+    // for cases where the result is a vec3, thus typically `.xyz`.
+    static const std::regex paramRegex(
+        R"(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\.\s*([xyzwrgbastpq]{3,3})\s*)");
+    std::smatch match;
+    if (!std::regex_match(paramStr, match, paramRegex) || (match.size() != 3)) {
+        return FixResult::unsupportedCode;
+    }
+    const std::string& var = match[1];
+    const std::string& swizzle = match[2];
+    const std::string fixedCall = "vec3("
+        + funcName + "(" + var + "." + swizzle[0] + "), "
+        + funcName + "(" + var + "." + swizzle[1] + "), "
+        + funcName + "(" + var + "." + swizzle[2] + "))";
+    source.replace(callStartPos, closingPos - callStartPos + 1, fixedCall);
+    pos = callStartPos + fixedCall.length();
+    return FixResult::applied;
+}
+
+}
+
+bool FP2GLESWorkarounds::adjustShaderString(std::string& shaderString) {
+    // This is an extremely simplistic implementation. To do this properly, we
+    // would need a complete syntax analysis.
+    int appliedCount = 0;
+    int unsupportedCount = 0;
+    auto lines = SplitString(shaderString, "\n\r");
+    for (auto & line : lines) {
+        if (line.length() > 0 && line[0] == '#') {
+            continue;
+        }
+        const auto commentStart = line.find("//");
+        if (commentStart != std::string::npos) {
+            line.resize(commentStart);
+        }
+        auto loopFix = [&appliedCount, &unsupportedCount] (
+            std::string& source, const std::string& funcName)
+            {
+                size_t pos = 0;
+                while (pos != source.length()) {
+                    switch (replace_func_vec3_call(source, funcName, pos)) {
+                        case FixResult::unsupportedCode:
+                            ++unsupportedCount;
+                            // fall-through
+                        case FixResult::noMatch:
+                            return;
+                        case FixResult::applied:
+                            ++appliedCount;
+                            break;
+                    }
+                }
+            };
+        loopFix(line, "dFdx");
+        loopFix(line, "dFdy");
+    }
+    if (appliedCount) {
+        shaderString = JoinStrings(lines, "\n");
+    }
+    if (appliedCount != 0 || unsupportedCount != 0) {
+        ALOGI("glShaderSource: Found calls to dFdx/dFdy with vec3 parameter "
+            "(broken on current Adreno 330 driver):");
+        ALOGI("    Fixed cases: %i", appliedCount);
+        ALOGI("    Unsupported code/syntax: %i", unsupportedCount);
+    }
+    return appliedCount != 0;
+}
+
+void FP2GLESWorkarounds::filterEGLContextExtensions(
+    std::string * gl_extensions,
+    std::vector<std::string> * tokenized_gl_extensions)
+{
+    if (fp2_experimental_gles3) {
+        return;
+    }
+
+    // EGL stores extensions both in a string (separated by space) and a vector.
+    // Filter unstable extensions from both lists.
+    for (auto && ext : unstableGLExtension())
+    {
+        const auto it = std::find(
+            tokenized_gl_extensions->begin(),
+            tokenized_gl_extensions->end(),
+            ext + " "
+        );
+        if (it != tokenized_gl_extensions->end()) {
+            tokenized_gl_extensions->erase(it);
+        }
+    }
+    *gl_extensions = filterStableExtensions(*gl_extensions);
+}
diff --git a/opengl/libs/gles_workarounds.h b/opengl/libs/gles_workarounds.h
new file mode 100644
index 0000000..224c51a
--- /dev/null
+++ b/opengl/libs/gles_workarounds.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020, Fairphone B.V.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+// Do not include the version-specific GL headers here. Instead, forward-declare
+// the required type declarations:
+using EGLBoolean    = unsigned int;
+using EGLConfig     = void*;
+using EGLDisplay    = void*;
+using EGLint        = int32_t;
+using GLenum        = unsigned int;
+using GLint         = int32_t;
+using GLfloat       = float;
+using GLsizei       = int32_t;
+using GLubyte       = uint8_t;
+
+
+/** Workaround broken and incomplete graphics driver behavior on the FP2.
+ *
+ * The OpenGL ES implementation of the driver for the Fairphone FP2 is
+ * incomplete, unstable, and causes various tests to fail on Android 7 and 9.
+ * Falling back to OpenGL ES 2.0 instead of 3.0 is more stable in many
+ * situations. Therefore, OpenGL ES 3.0 is treated as experimental feature from
+ * Android 7 onwards.
+ *
+ * This class gathers fixes and workarounds for bugs and incomplete
+ * implementations in the outdated graphics driver. Based on the
+ * ro.opengles.version system property, it operates in two modes:
+ *
+ * - OpenGL ES 2.0: Hide meta data that hints to OpenGL ES 3.0 support in
+ *   strings and feature flags.
+ * - Experimental OpenGL ES 3.0: Keep exposing GLES support as reported by the
+ *   driver and only try applying fixes where possible.
+ */
+class FP2GLESWorkarounds {
+public:
+    /** Check whether experimental OpenGL ES 3.0 support is enabled in the system.*/
+    static bool isExperimentalGLES3Enabled();
+
+    // Global GL constants
+
+    /** Actual values for GL_ALIASED_POINT_SIZE_RANGE
+     *
+     * The driver reports range (1.0, 1023.0), but it actually renders points up
+     * to size 4092. This causes conformance tests to fail, because the driver
+     * is supposed to clamp requested point sizes to the range it reports. Fix
+     * this by reporting values that match the actual behavior of the driver.
+     * Test: dEQP-GLES2.functional.rasterization.limits#points
+     */
+    static constexpr GLfloat GL_ALIASED_POINT_SIZE_MIN = 1.f;
+    static constexpr GLfloat GL_ALIASED_POINT_SIZE_MAX = 4092.f;
+
+    /** Hide GLES3 support from context attributes if needed.
+     *
+     * Remove EGL_OPENGL_ES3_BIT_KHR from EGL_RENDERABLE_TYPE unless
+     * experimental GLES3 support is enabled.
+     */
+    static EGLBoolean eglGetConfigAttrib(
+        EGLDisplay display,
+        EGLConfig config,
+        EGLint attribute,
+        EGLint * value,
+        EGLBoolean driverResult);
+
+    /** Adjust driver-reported strings if needed.
+     *
+     * Following changes are made, unless experimental GLES3 support is enabled:
+     *  - Adjust version strings to report OpenGL ES 2.0 only.
+     */
+    static const GLubyte * glGetString(GLenum name, const GLubyte * driverString);
+
+
+    /** Check for valid texture parameters and set GL error flag if needed.
+     *
+     * This checks if the requested texture is valid according to the current
+     * set of exposed driver features. If it is not valid, this will set the
+     * value returned by the next call to glGetError() at framework level
+     * accordingly.
+     */
+    static bool checkValidGlTexImage2D(GLenum target, GLint level, GLint internalformat,
+        GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * data);
+    static bool checkValidGlTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+        GLsizei width, GLsizei height, GLenum format, GLenum type, const void * data);
+    static bool checkValidGlTexImage3D(GLenum target, GLint level, GLint internalformat,
+        GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type,
+        const void *pixels);
+    static bool checkValidGlTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+        GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
+        const void *pixels);
+    static bool checkValidGlRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width,
+        GLsizei height);
+
+    /** Apply workarounds to shader sources.
+     *
+     * Modify `shaderString` to fix known issues with Adreno 330 drivers.
+     * `shaderString` must be a copy of one of the strings passed into a call to
+     * `glShaderSource`. It will be modified in-place.
+     *
+     * Returns true if modifications were done.
+     */
+    static bool adjustShaderString(std::string& shaderString);
+
+    /** Filter extension list in internal EGL data structures.
+     *
+     * OpenGL (ES) extensions need to be handled by thread and context. Use
+     * EGL's existing logic for that and filter their internal data structures
+     * directly.
+     *
+     * Note: Add a call to this function in egl_object.cpp directly, so that the
+     * extensions are filtered direct where EGL's internal data structures are
+     * initialized.
+     */
+    static void filterEGLContextExtensions(
+        std::string * gl_extensions,
+        std::vector<std::string> * tokenized_gl_extensions);
+};