Merge changes from topic "rootless_debug_gles"

* changes:
  Rootless Debug for GLES
  Split platform functions from entrypoints
  Rename eglApi.cpp to egl_platform_entries.cpp
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 4da30e9..bab87ac 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -23,6 +23,7 @@
 
     shared_libs: [
         "liblog",
+        "libcutils",
     ],
 
     export_include_dirs: ["include"],
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 2a7d76e..3328ad7 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -18,9 +18,12 @@
 #define LOG_TAG "GraphicsEnv"
 #include <graphicsenv/GraphicsEnv.h>
 
+#include <sys/prctl.h>
+
 #include <mutex>
 
 #include <android/dlext.h>
+#include <cutils/properties.h>
 #include <log/log.h>
 
 // TODO(b/37049319) Get this from a header once one exists
@@ -46,6 +49,14 @@
     return env;
 }
 
+int GraphicsEnv::getCanLoadSystemLibraries() {
+    if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+        // Return an integer value since this crosses library boundaries
+        return 1;
+    }
+    return 0;
+}
+
 void GraphicsEnv::setDriverPath(const std::string path) {
     if (!mDriverPath.empty()) {
         ALOGV("ignoring attempt to change driver path from '%s' to '%s'",
@@ -181,4 +192,10 @@
 const char* android_getAngleAppPref() {
     return android::GraphicsEnv::getInstance().getAngleAppPref();
 }
+const char* android_getLayerPaths() {
+    return android::GraphicsEnv::getInstance().getLayerPaths().c_str();
+}
+const char* android_getDebugLayers() {
+    return android::GraphicsEnv::getInstance().getDebugLayers().c_str();
+}
 }
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 00e8fc0..1783429 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -29,6 +29,8 @@
 public:
     static GraphicsEnv& getInstance();
 
+    int getCanLoadSystemLibraries();
+
     // Set a search path for loading graphics drivers. The path is a list of
     // directories separated by ':'. A directory can be contained in a zip file
     // (drivers must be stored uncompressed and page aligned); such elements
@@ -85,11 +87,13 @@
  *    will be removed soon.
  */
 extern "C" {
-android_namespace_t* android_getDriverNamespace();
-android_namespace_t* android_getAngleNamespace();
-const char* android_getAngleAppName();
-const char* android_getAngleAppPref();
-bool android_getAngleDeveloperOptIn();
+    android_namespace_t* android_getDriverNamespace();
+    android_namespace_t* android_getAngleNamespace();
+    const char* android_getAngleAppName();
+    const char* android_getAngleAppPref();
+    bool android_getAngleDeveloperOptIn();
+    const char* android_getLayerPaths();
+    const char* android_getDebugLayers();
 }
 
 #endif // ANDROID_UI_GRAPHICS_ENV_H
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 2a6dee4..583aec9 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -140,8 +140,10 @@
         "EGL/egl_cache.cpp",
         "EGL/egl_display.cpp",
         "EGL/egl_object.cpp",
+        "EGL/egl_layers.cpp",
         "EGL/egl.cpp",
         "EGL/eglApi.cpp",
+        "EGL/egl_platform_entries.cpp",
         "EGL/Loader.cpp",
         "EGL/egl_angle_platform.cpp",
     ],
@@ -149,8 +151,11 @@
         "libvndksupport",
         "android.hardware.configstore@1.0",
         "android.hardware.configstore-utils",
+        "libbase",
         "libhidlbase",
         "libhidltransport",
+        "libnativebridge",
+        "libnativeloader",
         "libutils",
     ],
     static_libs: [
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 018d979..5aa03a7 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -33,6 +33,7 @@
 #endif
 #include <vndksupport/linker.h>
 
+#include "egl_platform_entries.h"
 #include "egl_trace.h"
 #include "egldefs.h"
 
@@ -237,12 +238,12 @@
 
     setEmulatorGlesValue();
 
-    dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2);
+    dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2 | PLATFORM);
     if (dso) {
         hnd = new driver_t(dso);
     } else {
         // Always load EGL first
-        dso = load_driver("EGL", cnx, EGL);
+        dso = load_driver("EGL", cnx, EGL | PLATFORM);
         if (dso) {
             hnd = new driver_t(dso);
             hnd->set( load_driver("GLESv1_CM", cnx, GLESv1_CM), GLESv1_CM );
@@ -623,6 +624,25 @@
             return nullptr;
     }
 
+    if (mask & PLATFORM) {
+        // For each entrypoint tracked by the platform
+        char const* const* entries = platform_names;
+        EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
+
+        while (*entries) {
+            const char* name = *entries;
+            EGLFuncPointer f = FindPlatformImplAddr(name);
+
+            if (f == nullptr) {
+                // If no entry found, update the lookup table: sPlatformImplMap
+                ALOGE("No entry found in platform lookup table for %s", name);
+            }
+
+            *curr++ = f;
+            entries++;
+        }
+    }
+
     if (mask & EGL) {
         getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");
 
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 6a32bb3..93c33a8 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -33,7 +33,8 @@
     enum {
         EGL         = 0x01,
         GLESv1_CM   = 0x02,
-        GLESv2      = 0x04
+        GLESv2      = 0x04,
+        PLATFORM    = 0x08
     };
     struct driver_t {
         explicit driver_t(void* gles);
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index e292b80..01795e0 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -30,11 +30,10 @@
 #include "egl_tls.h"
 #include "egl_display.h"
 #include "egl_object.h"
+#include "egl_layers.h"
 #include "CallStack.h"
 #include "Loader.h"
 
-typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
-
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
@@ -167,6 +166,10 @@
     return (GLint)c->tokenized_gl_extensions.size();
 }
 
+egl_connection_t* egl_get_connection() {
+    return &gEGLImpl;
+}
+
 // ----------------------------------------------------------------------------
 
 // this mutex protects:
@@ -192,6 +195,14 @@
         cnx->dso = loader.open(cnx);
     }
 
+    // Check to see if any layers are enabled and route functions through them
+    if (cnx->dso) {
+        // Layers can be enabled long after the drivers have been loaded.
+        // They will only be initialized once.
+        LayerLoader& layer_loader(LayerLoader::getInstance());
+        layer_loader.InitLayers(cnx);
+    }
+
     return cnx->dso ? EGL_TRUE : EGL_FALSE;
 }
 
@@ -257,6 +268,11 @@
     nullptr
 };
 
+char const * const platform_names[] = {
+    #include "platform_entries.in"
+    nullptr
+};
+
 #undef GL_ENTRY
 #undef EGL_ENTRY
 
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index d2dc514..8202c4e 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1,5 +1,5 @@
 /*
- ** Copyright 2007, The Android Open Source Project
+ ** Copyright 2018, The Android Open Source Project
  **
  ** Licensed under the Apache License, Version 2.0 (the "License");
  ** you may not use this file except in compliance with the License.
@@ -16,1185 +16,198 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <ctype.h>
-#include <dlfcn.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <hardware/gralloc1.h>
-
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
-#include <EGL/eglext_angle.h>
-
-#include <android/hardware_buffer.h>
-#include <private/android/AHardwareBufferHelpers.h>
-
-#include <cutils/compiler.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-
-#include <condition_variable>
-#include <deque>
-#include <mutex>
-#include <unordered_map>
-#include <string>
-#include <thread>
 
 #include "../egl_impl.h"
 
-#include "egl_display.h"
-#include "egl_object.h"
+#include "egl_layers.h"
+#include "egl_platform_entries.h"
 #include "egl_tls.h"
 #include "egl_trace.h"
 
 using namespace android;
 
-// ----------------------------------------------------------------------------
-
 namespace android {
 
-using nsecs_t = int64_t;
+extern EGLBoolean egl_init_drivers();
 
-struct extention_map_t {
-    const char* name;
-    __eglMustCastToProperFunctionPointerType address;
-};
+} // namespace android
 
-/*
- * This is the list of EGL extensions exposed to applications.
- *
- * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL
- * wrapper and are always available.
- *
- * The rest (gExtensionString) depend on support in the EGL driver, and are
- * only available if the driver supports them. However, some of these must be
- * supported because they are used by the Android system itself; these are
- * listed as mandatory below and are required by the CDD. The system *assumes*
- * the mandatory extensions are present and may not function properly if some
- * are missing.
- *
- * NOTE: Both strings MUST have a single space as the last character.
- */
-
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
-
-// clang-format off
-// Extensions implemented by the EGL wrapper.
-char const * const gBuiltinExtensionString =
-        "EGL_KHR_get_all_proc_addresses "
-        "EGL_ANDROID_presentation_time "
-        "EGL_KHR_swap_buffers_with_damage "
-        "EGL_ANDROID_get_native_client_buffer "
-        "EGL_ANDROID_front_buffer_auto_refresh "
-        "EGL_ANDROID_get_frame_timestamps "
-        "EGL_EXT_surface_SMPTE2086_metadata "
-        "EGL_EXT_surface_CTA861_3_metadata "
-        ;
-
-// Whitelist of extensions exposed to applications if implemented in the vendor driver.
-char const * const gExtensionString  =
-        "EGL_KHR_image "                        // mandatory
-        "EGL_KHR_image_base "                   // mandatory
-        "EGL_EXT_image_gl_colorspace "
-        "EGL_KHR_image_pixmap "
-        "EGL_KHR_lock_surface "
-        "EGL_KHR_gl_colorspace "
-        "EGL_KHR_gl_texture_2D_image "
-        "EGL_KHR_gl_texture_3D_image "
-        "EGL_KHR_gl_texture_cubemap_image "
-        "EGL_KHR_gl_renderbuffer_image "
-        "EGL_KHR_reusable_sync "
-        "EGL_KHR_fence_sync "
-        "EGL_KHR_create_context "
-        "EGL_KHR_config_attribs "
-        "EGL_KHR_surfaceless_context "
-        "EGL_KHR_stream "
-        "EGL_KHR_stream_fifo "
-        "EGL_KHR_stream_producer_eglsurface "
-        "EGL_KHR_stream_consumer_gltexture "
-        "EGL_KHR_stream_cross_process_fd "
-        "EGL_EXT_create_context_robustness "
-        "EGL_NV_system_time "
-        "EGL_ANDROID_image_native_buffer "      // mandatory
-        "EGL_KHR_wait_sync "                    // strongly recommended
-        "EGL_ANDROID_recordable "               // mandatory
-        "EGL_KHR_partial_update "               // strongly recommended
-        "EGL_EXT_pixel_format_float "
-        "EGL_EXT_buffer_age "                   // strongly recommended with partial_update
-        "EGL_KHR_create_context_no_error "
-        "EGL_KHR_mutable_render_buffer "
-        "EGL_EXT_yuv_surface "
-        "EGL_EXT_protected_content "
-        "EGL_IMG_context_priority "
-        "EGL_KHR_no_config_context "
-        ;
-// clang-format on
-
-// extensions not exposed to applications but used by the ANDROID system
-//      "EGL_ANDROID_blob_cache "               // strongly recommended
-//      "EGL_IMG_hibernate_process "            // optional
-//      "EGL_ANDROID_native_fence_sync "        // strongly recommended
-//      "EGL_ANDROID_framebuffer_target "       // mandatory for HWC 1.1
-
-/*
- * EGL Extensions entry-points exposed to 3rd party applications
- * (keep in sync with gExtensionString above)
- *
- */
-static const extention_map_t sExtensionMap[] = {
-    // EGL_KHR_lock_surface
-    { "eglLockSurfaceKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
-    { "eglUnlockSurfaceKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
-
-    // EGL_KHR_image, EGL_KHR_image_base
-    { "eglCreateImageKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
-    { "eglDestroyImageKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
-
-    // EGL_KHR_reusable_sync, EGL_KHR_fence_sync
-    { "eglCreateSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
-    { "eglDestroySyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
-    { "eglClientWaitSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
-    { "eglSignalSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
-    { "eglGetSyncAttribKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
-
-    // EGL_NV_system_time
-    { "eglGetSystemTimeFrequencyNV",
-            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
-    { "eglGetSystemTimeNV",
-            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
-
-    // EGL_KHR_wait_sync
-    { "eglWaitSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
-
-    // EGL_ANDROID_presentation_time
-    { "eglPresentationTimeANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
-
-    // EGL_KHR_swap_buffers_with_damage
-    { "eglSwapBuffersWithDamageKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
-
-    // EGL_ANDROID_get_native_client_buffer
-    { "eglGetNativeClientBufferANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
-
-    // EGL_KHR_partial_update
-    { "eglSetDamageRegionKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
-
-    { "eglCreateStreamKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
-    { "eglDestroyStreamKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
-    { "eglStreamAttribKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
-    { "eglQueryStreamKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
-    { "eglQueryStreamu64KHR",
-            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
-    { "eglQueryStreamTimeKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
-    { "eglCreateStreamProducerSurfaceKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
-    { "eglStreamConsumerGLTextureExternalKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
-    { "eglStreamConsumerAcquireKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
-    { "eglStreamConsumerReleaseKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
-    { "eglGetStreamFileDescriptorKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
-    { "eglCreateStreamFromFileDescriptorKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
-
-    // EGL_ANDROID_get_frame_timestamps
-    { "eglGetNextFrameIdANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
-    { "eglGetCompositorTimingANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
-    { "eglGetCompositorTimingSupportedANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
-    { "eglGetFrameTimestampsANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
-    { "eglGetFrameTimestampSupportedANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
-
-    // EGL_ANDROID_native_fence_sync
-    { "eglDupNativeFenceFDANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
-};
-
-/*
- * These extensions entry-points should not be exposed to applications.
- * They're used internally by the Android EGL layer.
- */
-#define FILTER_EXTENSIONS(procname) \
-        (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") ||    \
-         !strcmp((procname), "eglHibernateProcessIMG")      ||    \
-         !strcmp((procname), "eglAwakenProcessIMG"))
-
-// accesses protected by sExtensionMapMutex
-static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
-
-static int sGLExtentionSlot = 0;
-static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
-
-static void(*findProcAddress(const char* name,
-        const extention_map_t* map, size_t n))() {
-    for (uint32_t i=0 ; i<n ; i++) {
-        if (!strcmp(name, map[i].name)) {
-            return map[i].address;
-        }
-    }
-    return nullptr;
+static inline void clearError() {
+    egl_tls_t::clearError();
 }
 
-// ----------------------------------------------------------------------------
-
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
-extern EGLBoolean egl_init_drivers();
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
-extern gl_hooks_t gHooksTrace;
-
-} // namespace android;
-
-
-// ----------------------------------------------------------------------------
-
-static inline void clearError() { egl_tls_t::clearError(); }
-static inline EGLContext getContext() { return egl_tls_t::getContext(); }
-
-// ----------------------------------------------------------------------------
-
-EGLDisplay eglGetDisplay(EGLNativeDisplayType display)
-{
+EGLDisplay eglGetDisplay(EGLNativeDisplayType display) {
     ATRACE_CALL();
     clearError();
 
-    uintptr_t index = reinterpret_cast<uintptr_t>(display);
-    if (index >= NUM_DISPLAYS) {
-        return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
-    }
-
     if (egl_init_drivers() == EGL_FALSE) {
         return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
     }
 
-    EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display);
-    return dpy;
+    // Call down the chain, which usually points directly to the impl
+    // but may also be routed through layers
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetDisplay(display);
 }
 
-// ----------------------------------------------------------------------------
-// Initialization
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
-{
+EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) {
     clearError();
 
-    egl_display_ptr dp = get_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-
-    EGLBoolean res = dp->initialize(major, minor);
-
-    return res;
-}
-
-EGLBoolean eglTerminate(EGLDisplay dpy)
-{
-    // NOTE: don't unload the drivers b/c some APIs can be called
-    // after eglTerminate() has been called. eglTerminate() only
-    // terminates an EGLDisplay, not a EGL itself.
-
-    clearError();
-
-    egl_display_ptr dp = get_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-
-    EGLBoolean res = dp->terminate();
-
-    return res;
-}
-
-// ----------------------------------------------------------------------------
-// configuration
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglGetConfigs(   EGLDisplay dpy,
-                            EGLConfig *configs,
-                            EGLint config_size, EGLint *num_config)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    if (num_config==nullptr) {
-        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
-    }
-
-    EGLBoolean res = EGL_FALSE;
-    *num_config = 0;
-
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso) {
-        res = cnx->egl.eglGetConfigs(
-                dp->disp.dpy, configs, config_size, num_config);
-    }
-
-    return res;
+    return cnx->platform.eglInitialize(dpy, major, minor);
 }
 
-EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
-                            EGLConfig *configs, EGLint config_size,
-                            EGLint *num_config)
-{
+EGLBoolean eglTerminate(EGLDisplay dpy) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    if (num_config==nullptr) {
-        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
-    }
-
-    EGLBoolean res = EGL_FALSE;
-    *num_config = 0;
-
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso) {
-        if (attrib_list) {
-            char value[PROPERTY_VALUE_MAX];
-            property_get("debug.egl.force_msaa", value, "false");
-
-            if (!strcmp(value, "true")) {
-                size_t attribCount = 0;
-                EGLint attrib = attrib_list[0];
-
-                // Only enable MSAA if the context is OpenGL ES 2.0 and
-                // if no caveat is requested
-                const EGLint *attribRendererable = nullptr;
-                const EGLint *attribCaveat = nullptr;
-
-                // Count the number of attributes and look for
-                // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
-                while (attrib != EGL_NONE) {
-                    attrib = attrib_list[attribCount];
-                    switch (attrib) {
-                        case EGL_RENDERABLE_TYPE:
-                            attribRendererable = &attrib_list[attribCount];
-                            break;
-                        case EGL_CONFIG_CAVEAT:
-                            attribCaveat = &attrib_list[attribCount];
-                            break;
-                        default:
-                            break;
-                    }
-                    attribCount++;
-                }
-
-                if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
-                        (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
-
-                    // Insert 2 extra attributes to force-enable MSAA 4x
-                    EGLint aaAttribs[attribCount + 4];
-                    aaAttribs[0] = EGL_SAMPLE_BUFFERS;
-                    aaAttribs[1] = 1;
-                    aaAttribs[2] = EGL_SAMPLES;
-                    aaAttribs[3] = 4;
-
-                    memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
-
-                    EGLint numConfigAA;
-                    EGLBoolean resAA = cnx->egl.eglChooseConfig(
-                            dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
-
-                    if (resAA == EGL_TRUE && numConfigAA > 0) {
-                        ALOGD("Enabling MSAA 4x");
-                        *num_config = numConfigAA;
-                        return resAA;
-                    }
-                }
-            }
-        }
-
-        res = cnx->egl.eglChooseConfig(
-                dp->disp.dpy, attrib_list, configs, config_size, num_config);
-    }
-    return res;
+    return cnx->platform.eglTerminate(dpy);
 }
 
-EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
-        EGLint attribute, EGLint *value)
-{
+EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size,
+                         EGLint* num_config) {
     clearError();
 
-    egl_connection_t* cnx = nullptr;
-    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (!dp) return EGL_FALSE;
-
-    return cnx->egl.eglGetConfigAttrib(
-            dp->disp.dpy, config, attribute, value);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetConfigs(dpy, configs, config_size, num_config);
 }
 
-// ----------------------------------------------------------------------------
-// surfaces
-// ----------------------------------------------------------------------------
-
-// Translates EGL color spaces to Android data spaces.
-static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) {
-    if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
-        return HAL_DATASPACE_UNKNOWN;
-    } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
-        return HAL_DATASPACE_SRGB;
-    } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
-        return HAL_DATASPACE_DISPLAY_P3;
-    } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) {
-        return HAL_DATASPACE_DISPLAY_P3_LINEAR;
-    } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) {
-        return HAL_DATASPACE_V0_SCRGB;
-    } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
-        return HAL_DATASPACE_V0_SCRGB_LINEAR;
-    } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
-        return HAL_DATASPACE_BT2020_LINEAR;
-    } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
-        return HAL_DATASPACE_BT2020_PQ;
-    }
-    return HAL_DATASPACE_UNKNOWN;
-}
-
-// Get the colorspace value that should be reported from queries. When the colorspace
-// is unknown (no attribute passed), default to reporting LINEAR.
-static EGLint getReportedColorSpace(EGLint colorspace) {
-    return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace;
-}
-
-// Returns a list of color spaces understood by the vendor EGL driver.
-static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp,
-                                                android_pixel_format format) {
-    std::vector<EGLint> colorSpaces;
-    if (!dp->hasColorSpaceSupport) return colorSpaces;
-
-    // OpenGL drivers only support sRGB encoding with 8-bit formats.
-    // RGB_888 is never returned by getNativePixelFormat, but is included for completeness.
-    const bool formatSupportsSRGBEncoding =
-        format == HAL_PIXEL_FORMAT_RGBA_8888 || format == HAL_PIXEL_FORMAT_RGBX_8888 ||
-        format == HAL_PIXEL_FORMAT_RGB_888;
-    const bool formatIsFloatingPoint = format == HAL_PIXEL_FORMAT_RGBA_FP16;
-
-    if (formatSupportsSRGBEncoding) {
-        // sRGB and linear are always supported when color space support is present.
-        colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
-        colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
-        // DCI-P3 uses the sRGB transfer function, so it's only relevant for 8-bit formats.
-        if (findExtension(dp->disp.queryString.extensions,
-                              "EGL_EXT_gl_colorspace_display_p3")) {
-            colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
-        }
-    }
-
-    // According to the spec, scRGB is only supported for floating point formats.
-    // For non-linear scRGB, the application is responsible for applying the
-    // transfer function.
-    if (formatIsFloatingPoint) {
-        if (findExtension(dp->disp.queryString.extensions,
-                  "EGL_EXT_gl_colorspace_scrgb")) {
-            colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT);
-        }
-        if (findExtension(dp->disp.queryString.extensions,
-                  "EGL_EXT_gl_colorspace_scrgb_linear")) {
-            colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
-        }
-    }
-
-    // BT2020 can be used with any pixel format. PQ encoding must be applied by the
-    // application and does not affect the behavior of OpenGL.
-    if (findExtension(dp->disp.queryString.extensions,
-                          "EGL_EXT_gl_colorspace_bt2020_linear")) {
-        colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
-    }
-    if (findExtension(dp->disp.queryString.extensions,
-                          "EGL_EXT_gl_colorspace_bt2020_pq")) {
-        colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
-    }
-
-    // Linear DCI-P3 simply uses different primaries than standard RGB and thus
-    // can be used with any pixel format.
-    if (findExtension(dp->disp.queryString.extensions,
-                          "EGL_EXT_gl_colorspace_display_p3_linear")) {
-        colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
-    }
-    return colorSpaces;
-}
-
-// Cleans up color space related parameters that the driver does not understand.
-// If there is no color space attribute in attrib_list, colorSpace is left
-// unmodified.
-static EGLBoolean processAttributes(egl_display_ptr dp, NativeWindowType window,
-                                    android_pixel_format format, const EGLint* attrib_list,
-                                    EGLint* colorSpace,
-                                    std::vector<EGLint>* strippedAttribList) {
-    for (const EGLint* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
-        bool copyAttribute = true;
-        if (attr[0] == EGL_GL_COLORSPACE_KHR) {
-            // Fail immediately if the driver doesn't have color space support at all.
-            if (!dp->hasColorSpaceSupport) return false;
-            *colorSpace = attr[1];
-
-            // Strip the attribute if the driver doesn't understand it.
-            copyAttribute = false;
-            std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp, format);
-            for (auto driverColorSpace : driverColorSpaces) {
-                if (attr[1] == driverColorSpace) {
-                    copyAttribute = true;
-                    break;
-                }
-            }
-
-            // If the driver doesn't understand it, we should map sRGB-encoded P3 to
-            // sRGB rather than just dropping the colorspace on the floor.
-            // For this format, the driver is expected to apply the sRGB
-            // transfer function during framebuffer operations.
-            if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
-                strippedAttribList->push_back(attr[0]);
-                strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR);
-            }
-        }
-        if (copyAttribute) {
-            strippedAttribList->push_back(attr[0]);
-            strippedAttribList->push_back(attr[1]);
-        }
-    }
-    // Terminate the attribute list.
-    strippedAttribList->push_back(EGL_NONE);
-
-    // If the passed color space has wide color gamut, check whether the target native window
-    // supports wide color.
-    const bool colorSpaceIsNarrow =
-        *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR ||
-        *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR ||
-        *colorSpace == EGL_UNKNOWN;
-    if (window && !colorSpaceIsNarrow) {
-        bool windowSupportsWideColor = true;
-        // Ordinarily we'd put a call to native_window_get_wide_color_support
-        // at the beginning of the function so that we'll have the
-        // result when needed elsewhere in the function.
-        // However, because eglCreateWindowSurface is called by SurfaceFlinger and
-        // SurfaceFlinger is required to answer the call below we would
-        // end up in a deadlock situation. By moving the call to only happen
-        // if the application has specifically asked for wide-color we avoid
-        // the deadlock with SurfaceFlinger since it will not ask for a
-        // wide-color surface.
-        int err = native_window_get_wide_color_support(window, &windowSupportsWideColor);
-
-        if (err) {
-            ALOGE("processAttributes: invalid window (win=%p) "
-                  "failed (%#x) (already connected to another API?)",
-                  window, err);
-            return false;
-        }
-        if (!windowSupportsWideColor) {
-            // Application has asked for a wide-color colorspace but
-            // wide-color support isn't available on the display the window is on.
-            return false;
-        }
-    }
-    return true;
-}
-
-// Gets the native pixel format corrsponding to the passed EGLConfig.
-void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
-                          android_pixel_format* format) {
-    // Set the native window's buffers format to match what this config requests.
-    // Whether to use sRGB gamma is not part of the EGLconfig, but is part
-    // of our native format. So if sRGB gamma is requested, we have to
-    // modify the EGLconfig's format before setting the native window's
-    // format.
-
-    EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
-    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType);
-
-    EGLint a = 0;
-    EGLint r, g, b;
-    r = g = b = 0;
-    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
-    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
-    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
-    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
-    EGLint colorDepth = r + g + b;
-
-    // Today, the driver only understands sRGB and linear on 888X
-    // formats. Strip other colorspaces from the attribute list and
-    // only use them to set the dataspace via
-    // native_window_set_buffers_dataspace
-    // if pixel format is RGBX 8888
-    //    TBD: Can test for future extensions that indicate that driver
-    //    handles requested color space and we can let it through.
-    //    allow SRGB and LINEAR. All others need to be stripped.
-    // else if 565, 4444
-    //    TBD: Can we assume these are supported if 8888 is?
-    // else if FP16 or 1010102
-    //    strip colorspace from attribs.
-    // endif
-    if (a == 0) {
-        if (colorDepth <= 16) {
-            *format = HAL_PIXEL_FORMAT_RGB_565;
-        } else {
-            if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
-                if (colorDepth > 24) {
-                    *format = HAL_PIXEL_FORMAT_RGBA_1010102;
-                } else {
-                    *format = HAL_PIXEL_FORMAT_RGBX_8888;
-                }
-            } else {
-                *format = HAL_PIXEL_FORMAT_RGBA_FP16;
-            }
-        }
-    } else {
-        if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
-            if (colorDepth > 24) {
-                *format = HAL_PIXEL_FORMAT_RGBA_1010102;
-            } else {
-                *format = HAL_PIXEL_FORMAT_RGBA_8888;
-            }
-        } else {
-            *format = HAL_PIXEL_FORMAT_RGBA_FP16;
-        }
-    }
-}
-
-EGLBoolean sendSurfaceMetadata(egl_surface_t* s) {
-    android_smpte2086_metadata smpteMetadata;
-    if (s->getSmpte2086Metadata(smpteMetadata)) {
-        int err =
-                native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata);
-        s->resetSmpte2086Metadata();
-        if (err != 0) {
-            ALOGE("error setting native window smpte2086 metadata: %s (%d)",
-                  strerror(-err), err);
-            return EGL_FALSE;
-        }
-    }
-    android_cta861_3_metadata cta8613Metadata;
-    if (s->getCta8613Metadata(cta8613Metadata)) {
-        int err =
-                native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata);
-        s->resetCta8613Metadata();
-        if (err != 0) {
-            ALOGE("error setting native window CTS 861.3 metadata: %s (%d)",
-                  strerror(-err), err);
-            return EGL_FALSE;
-        }
-    }
-    return EGL_TRUE;
-}
-
-EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
-                                    NativeWindowType window,
-                                    const EGLint *attrib_list)
-{
-    const EGLint *origAttribList = attrib_list;
+EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs,
+                           EGLint config_size, EGLint* num_config) {
     clearError();
 
-    egl_connection_t* cnx = nullptr;
-    egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (dp) {
-        if (!window) {
-            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
-        }
-
-        int value = 0;
-        window->query(window, NATIVE_WINDOW_IS_VALID, &value);
-        if (!value) {
-            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
-        }
-
-        // NOTE: When using Vulkan backend, the Vulkan runtime makes all the
-        // native_window_* calls, so don't do them here.
-        if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
-            int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
-            if (result < 0) {
-                ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
-                      "failed (%#x) (already connected to another API?)",
-                      window, result);
-                return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
-            }
-        }
-
-        EGLDisplay iDpy = dp->disp.dpy;
-        android_pixel_format format;
-        getNativePixelFormat(iDpy, cnx, config, &format);
-
-        // now select correct colorspace and dataspace based on user's attribute list
-        EGLint colorSpace = EGL_UNKNOWN;
-        std::vector<EGLint> strippedAttribList;
-        if (!processAttributes(dp, window, format, attrib_list, &colorSpace,
-                               &strippedAttribList)) {
-            ALOGE("error invalid colorspace: %d", colorSpace);
-            return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
-        }
-        attrib_list = strippedAttribList.data();
-
-        if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
-            int err = native_window_set_buffers_format(window, format);
-            if (err != 0) {
-                ALOGE("error setting native window pixel format: %s (%d)",
-                      strerror(-err), err);
-                native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
-                return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
-            }
-
-            android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace);
-            if (dataSpace != HAL_DATASPACE_UNKNOWN) {
-                err = native_window_set_buffers_data_space(window, dataSpace);
-                if (err != 0) {
-                    ALOGE("error setting native window pixel dataSpace: %s (%d)", strerror(-err),
-                          err);
-                    native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
-                    return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
-                }
-            }
-        }
-
-        // the EGL spec requires that a new EGLSurface default to swap interval
-        // 1, so explicitly set that on the window here.
-        ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
-        anw->setSwapInterval(anw, 1);
-
-        EGLSurface surface = cnx->egl.eglCreateWindowSurface(
-                iDpy, config, window, attrib_list);
-        if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s =
-                    new egl_surface_t(dp.get(), config, window, surface,
-                                      getReportedColorSpace(colorSpace), cnx);
-            return s;
-        }
-
-        // EGLSurface creation failed
-        if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
-            native_window_set_buffers_format(window, 0);
-            native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
-        }
-    }
-    return EGL_NO_SURFACE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglChooseConfig(dpy, attrib_list, configs, config_size, num_config);
 }
 
-EGLSurface eglCreatePixmapSurface(  EGLDisplay dpy, EGLConfig config,
-                                    NativePixmapType pixmap,
-                                    const EGLint *attrib_list)
-{
+EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value) {
     clearError();
 
-    egl_connection_t* cnx = nullptr;
-    egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (dp) {
-        EGLDisplay iDpy = dp->disp.dpy;
-        android_pixel_format format;
-        getNativePixelFormat(iDpy, cnx, config, &format);
-
-        // now select a corresponding sRGB format if needed
-        EGLint colorSpace = EGL_UNKNOWN;
-        std::vector<EGLint> strippedAttribList;
-        if (!processAttributes(dp, nullptr, format, attrib_list, &colorSpace,
-                               &strippedAttribList)) {
-            ALOGE("error invalid colorspace: %d", colorSpace);
-            return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
-        }
-        attrib_list = strippedAttribList.data();
-
-        EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
-                dp->disp.dpy, config, pixmap, attrib_list);
-        if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s =
-                    new egl_surface_t(dp.get(), config, nullptr, surface,
-                                      getReportedColorSpace(colorSpace), cnx);
-            return s;
-        }
-    }
-    return EGL_NO_SURFACE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetConfigAttrib(dpy, config, attribute, value);
 }
 
-EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
-                                    const EGLint *attrib_list)
-{
+EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
+                                  const EGLint* attrib_list) {
     clearError();
 
-    egl_connection_t* cnx = nullptr;
-    egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (dp) {
-        EGLDisplay iDpy = dp->disp.dpy;
-        android_pixel_format format;
-        getNativePixelFormat(iDpy, cnx, config, &format);
-
-        // Select correct colorspace based on user's attribute list
-        EGLint colorSpace = EGL_UNKNOWN;
-        std::vector<EGLint> strippedAttribList;
-        if (!processAttributes(dp, nullptr, format, attrib_list, &colorSpace,
-                               &strippedAttribList)) {
-            ALOGE("error invalid colorspace: %d", colorSpace);
-            return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
-        }
-        attrib_list = strippedAttribList.data();
-
-        EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
-                dp->disp.dpy, config, attrib_list);
-        if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s =
-                    new egl_surface_t(dp.get(), config, nullptr, surface,
-                                      getReportedColorSpace(colorSpace), cnx);
-            return s;
-        }
-    }
-    return EGL_NO_SURFACE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreateWindowSurface(dpy, config, window, attrib_list);
 }
 
-EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface)
-{
+EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap,
+                                  const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t * const s = get_surface(surface);
-    EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
-    if (result == EGL_TRUE) {
-        _s.terminate();
-    }
-    return result;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreatePixmapSurface(dpy, config, pixmap, attrib_list);
 }
 
-EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface,
-                            EGLint attribute, EGLint *value)
-{
+EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreatePbufferSurface(dpy, config, attrib_list);
+}
 
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) {
+    clearError();
 
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->getColorSpaceAttribute(attribute, value)) {
-        return EGL_TRUE;
-    } else if (s->getSmpte2086Attribute(attribute, value)) {
-        return EGL_TRUE;
-    } else if (s->getCta8613Attribute(attribute, value)) {
-        return EGL_TRUE;
-    }
-    return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglDestroySurface(dpy, surface);
+}
+
+EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglQuerySurface(dpy, surface, attribute, value);
 }
 
 void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {
     ATRACE_CALL();
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return;
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-    }
+    egl_connection_t* const cnx = &gEGLImpl;
+    cnx->platform.eglBeginFrame(dpy, surface);
 }
 
-// ----------------------------------------------------------------------------
-// Contexts
-// ----------------------------------------------------------------------------
-
-EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
-                            EGLContext share_list, const EGLint *attrib_list)
-{
+EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_list,
+                            const EGLint* attrib_list) {
     clearError();
 
-    egl_connection_t* cnx = nullptr;
-    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (dp) {
-        if (share_list != EGL_NO_CONTEXT) {
-            if (!ContextRef(dp.get(), share_list).get()) {
-                return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
-            }
-            egl_context_t* const c = get_context(share_list);
-            share_list = c->context;
-        }
-        EGLContext context = cnx->egl.eglCreateContext(
-                dp->disp.dpy, config, share_list, attrib_list);
-        if (context != EGL_NO_CONTEXT) {
-            // figure out if it's a GLESv1 or GLESv2
-            int version = 0;
-            if (attrib_list) {
-                while (*attrib_list != EGL_NONE) {
-                    GLint attr = *attrib_list++;
-                    GLint value = *attrib_list++;
-                    if (attr == EGL_CONTEXT_CLIENT_VERSION) {
-                        if (value == 1) {
-                            version = egl_connection_t::GLESv1_INDEX;
-                        } else if (value == 2 || value == 3) {
-                            version = egl_connection_t::GLESv2_INDEX;
-                        }
-                    }
-                };
-            }
-            egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
-                    version);
-            return c;
-        }
-    }
-    return EGL_NO_CONTEXT;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreateContext(dpy, config, share_list, attrib_list);
 }
 
-EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
-{
+EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp)
-        return EGL_FALSE;
-
-    ContextRef _c(dp.get(), ctx);
-    if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    egl_context_t * const c = get_context(ctx);
-    EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
-    if (result == EGL_TRUE) {
-        _c.terminate();
-    }
-    return result;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglDestroyContext(dpy, ctx);
 }
 
-EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
-                            EGLSurface read, EGLContext ctx)
-{
+EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
     clearError();
 
-    egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglMakeCurrent(dpy, draw, read, ctx);
+}
 
-    // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
-    // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
-    // a valid but uninitialized display.
-    if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
-         (draw != EGL_NO_SURFACE) ) {
-        if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
-    }
+EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) {
+    clearError();
 
-    // get a reference to the object passed in
-    ContextRef _c(dp.get(), ctx);
-    SurfaceRef _d(dp.get(), draw);
-    SurfaceRef _r(dp.get(), read);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglQueryContext(dpy, ctx, attribute, value);
+}
 
-    // validate the context (if not EGL_NO_CONTEXT)
-    if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
-        // EGL_NO_CONTEXT is valid
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-    }
+EGLContext eglGetCurrentContext(void) {
+    clearError();
 
-    // these are the underlying implementation's object
-    EGLContext impl_ctx  = EGL_NO_CONTEXT;
-    EGLSurface impl_draw = EGL_NO_SURFACE;
-    EGLSurface impl_read = EGL_NO_SURFACE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCurrentContext();
+}
 
-    // these are our objects structs passed in
-    egl_context_t       * c = nullptr;
-    egl_surface_t const * d = nullptr;
-    egl_surface_t const * r = nullptr;
+EGLSurface eglGetCurrentSurface(EGLint readdraw) {
+    clearError();
 
-    // these are the current objects structs
-    egl_context_t * cur_c = get_context(getContext());
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCurrentSurface(readdraw);
+}
 
-    if (ctx != EGL_NO_CONTEXT) {
-        c = get_context(ctx);
-        impl_ctx = c->context;
+EGLDisplay eglGetCurrentDisplay(void) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCurrentDisplay();
+}
+
+EGLBoolean eglWaitGL(void) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglWaitGL();
+}
+
+EGLBoolean eglWaitNative(EGLint engine) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglWaitNative(engine);
+}
+
+EGLint eglGetError(void) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->platform.eglGetError) {
+        return cnx->platform.eglGetError();
     } else {
-        // no context given, use the implementation of the current context
-        if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
-            // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
-            return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE);
-        }
-        if (cur_c == nullptr) {
-            // no current context
-            // not an error, there is just no current context.
-            return EGL_TRUE;
-        }
+        return egl_tls_t::getError();
     }
-
-    // retrieve the underlying implementation's draw EGLSurface
-    if (draw != EGL_NO_SURFACE) {
-        if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-        d = get_surface(draw);
-        impl_draw = d->surface;
-    }
-
-    // retrieve the underlying implementation's read EGLSurface
-    if (read != EGL_NO_SURFACE) {
-        if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-        r = get_surface(read);
-        impl_read = r->surface;
-    }
-
-
-    EGLBoolean result = dp->makeCurrent(c, cur_c,
-            draw, read, ctx,
-            impl_draw, impl_read, impl_ctx);
-
-    if (result == EGL_TRUE) {
-        if (c) {
-            setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
-            egl_tls_t::setContext(ctx);
-            _c.acquire();
-            _r.acquire();
-            _d.acquire();
-        } else {
-            setGLHooksThreadSpecific(&gHooksNoContext);
-            egl_tls_t::setContext(EGL_NO_CONTEXT);
-        }
-    } else {
-        // this will ALOGE the error
-        egl_connection_t* const cnx = &gEGLImpl;
-        result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE);
-    }
-    return result;
 }
 
-
-EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
-                            EGLint attribute, EGLint *value)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    ContextRef _c(dp.get(), ctx);
-    if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    egl_context_t * const c = get_context(ctx);
-    return c->cnx->egl.eglQueryContext(
-            dp->disp.dpy, c->context, attribute, value);
-
-}
-
-EGLContext eglGetCurrentContext(void)
-{
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would correctly return EGL_NO_CONTEXT.
-
-    clearError();
-
-    EGLContext ctx = getContext();
-    return ctx;
-}
-
-EGLSurface eglGetCurrentSurface(EGLint readdraw)
-{
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would correctly return EGL_NO_SURFACE.
-
-    clearError();
-
-    EGLContext ctx = getContext();
-    if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
-        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
-        switch (readdraw) {
-            case EGL_READ: return c->read;
-            case EGL_DRAW: return c->draw;
-            default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
-        }
-    }
-    return EGL_NO_SURFACE;
-}
-
-EGLDisplay eglGetCurrentDisplay(void)
-{
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would correctly return EGL_NO_DISPLAY.
-
-    clearError();
-
-    EGLContext ctx = getContext();
-    if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
-        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
-        return c->dpy;
-    }
-    return EGL_NO_DISPLAY;
-}
-
-EGLBoolean eglWaitGL(void)
-{
-    clearError();
-
-    egl_connection_t* const cnx = &gEGLImpl;
-    if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    return cnx->egl.eglWaitGL();
-}
-
-EGLBoolean eglWaitNative(EGLint engine)
-{
-    clearError();
-
-    egl_connection_t* const cnx = &gEGLImpl;
-    if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    return cnx->egl.eglWaitNative(engine);
-}
-
-EGLint eglGetError(void)
-{
-    EGLint err = EGL_SUCCESS;
-    egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso) {
-        err = cnx->egl.eglGetError();
-    }
-    if (err == EGL_SUCCESS) {
-        err = egl_tls_t::getError();
-    }
-    return err;
-}
-
-static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
-        const char* procname) {
-    const egl_connection_t* cnx = &gEGLImpl;
-    void* proc = nullptr;
-
-    proc = dlsym(cnx->libEgl, procname);
-    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
-    proc = dlsym(cnx->libGles2, procname);
-    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
-    proc = dlsym(cnx->libGles1, procname);
-    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
-    return nullptr;
-}
-
-__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
-{
+__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char* procname) {
     // eglGetProcAddress() could be the very first function called
     // in which case we must make sure we've initialized ourselves, this
     // happens the first time egl_get_display() is called.
@@ -1203,436 +216,87 @@
 
     if (egl_init_drivers() == EGL_FALSE) {
         setError(EGL_BAD_PARAMETER, NULL);
-        return  nullptr;
-    }
-
-    if (FILTER_EXTENSIONS(procname)) {
         return nullptr;
     }
 
-    __eglMustCastToProperFunctionPointerType addr;
-    addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap));
-    if (addr) return addr;
-
-    addr = findBuiltinWrapper(procname);
-    if (addr) return addr;
-
-    // this protects accesses to sGLExtentionMap and sGLExtentionSlot
-    pthread_mutex_lock(&sExtensionMapMutex);
-
-        /*
-         * Since eglGetProcAddress() is not associated to anything, it needs
-         * to return a function pointer that "works" regardless of what
-         * the current context is.
-         *
-         * For this reason, we return a "forwarder", a small stub that takes
-         * care of calling the function associated with the context
-         * currently bound.
-         *
-         * We first look for extensions we've already resolved, if we're seeing
-         * this extension for the first time, we go through all our
-         * implementations and call eglGetProcAddress() and record the
-         * result in the appropriate implementation hooks and return the
-         * address of the forwarder corresponding to that hook set.
-         *
-         */
-
-        const std::string name(procname);
-
-    auto& extentionMap = sGLExtentionMap;
-    auto pos = extentionMap.find(name);
-        addr = (pos != extentionMap.end()) ? pos->second : nullptr;
-        const int slot = sGLExtentionSlot;
-
-        ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
-                "no more slots for eglGetProcAddress(\"%s\")",
-                procname);
-
-        if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
-            bool found = false;
-
-            egl_connection_t* const cnx = &gEGLImpl;
-            if (cnx->dso && cnx->egl.eglGetProcAddress) {
-                // Extensions are independent of the bound context
-                addr =
-                cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
-                cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] =
-                        cnx->egl.eglGetProcAddress(procname);
-                if (addr) found = true;
-            }
-
-            if (found) {
-                addr = gExtensionForwarders[slot];
-                extentionMap[name] = addr;
-                sGLExtentionSlot++;
-            }
-        }
-
-    pthread_mutex_unlock(&sExtensionMapMutex);
-    return addr;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetProcAddress(procname);
 }
 
-class FrameCompletionThread {
-public:
-
-    static void queueSync(EGLSyncKHR sync) {
-        static FrameCompletionThread thread;
-
-        char name[64];
-
-        std::lock_guard<std::mutex> lock(thread.mMutex);
-        snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued);
-        ATRACE_NAME(name);
-
-        thread.mQueue.push_back(sync);
-        thread.mCondition.notify_one();
-        thread.mFramesQueued++;
-        ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size()));
-    }
-
-private:
-
-    FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
-        std::thread thread(&FrameCompletionThread::loop, this);
-        thread.detach();
-    }
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wmissing-noreturn"
-    void loop() {
-        while (true) {
-            threadLoop();
-        }
-    }
-#pragma clang diagnostic pop
-
-    void threadLoop() {
-        EGLSyncKHR sync;
-        uint32_t frameNum;
-        {
-            std::unique_lock<std::mutex> lock(mMutex);
-            while (mQueue.empty()) {
-                mCondition.wait(lock);
-            }
-            sync = mQueue[0];
-            frameNum = mFramesCompleted;
-        }
-        EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-        {
-            char name[64];
-            snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum);
-            ATRACE_NAME(name);
-
-            EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
-            if (result == EGL_FALSE) {
-                ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
-            } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
-                ALOGE("FrameCompletion: timeout waiting for fence");
-            }
-            eglDestroySyncKHR(dpy, sync);
-        }
-        {
-            std::lock_guard<std::mutex> lock(mMutex);
-            mQueue.pop_front();
-            mFramesCompleted++;
-            ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size()));
-        }
-    }
-
-    uint32_t mFramesQueued;
-    uint32_t mFramesCompleted;
-    std::deque<EGLSyncKHR> mQueue;
-    std::condition_variable mCondition;
-    std::mutex mMutex;
-};
-
-EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw,
-        EGLint *rects, EGLint n_rects)
-{
+EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+                                       EGLint n_rects) {
     ATRACE_CALL();
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), draw);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t* const s = get_surface(draw);
-
-    if (CC_UNLIKELY(dp->traceGpuCompletion)) {
-        EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
-        if (sync != EGL_NO_SYNC_KHR) {
-            FrameCompletionThread::queueSync(sync);
-        }
-    }
-
-    if (CC_UNLIKELY(dp->finishOnSwap)) {
-        uint32_t pixel;
-        egl_context_t * const c = get_context( egl_tls_t::getContext() );
-        if (c) {
-            // glReadPixels() ensures that the frame is complete
-            s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
-                    GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
-        }
-    }
-
-    if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
-        if (!sendSurfaceMetadata(s)) {
-            native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL);
-            return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE);
-        }
-    }
-
-    if (n_rects == 0) {
-        return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
-    }
-
-    std::vector<android_native_rect_t> androidRects((size_t)n_rects);
-    for (int r = 0; r < n_rects; ++r) {
-        int offset = r * 4;
-        int x = rects[offset];
-        int y = rects[offset + 1];
-        int width = rects[offset + 2];
-        int height = rects[offset + 3];
-        android_native_rect_t androidRect;
-        androidRect.left = x;
-        androidRect.top = y + height;
-        androidRect.right = x + width;
-        androidRect.bottom = y;
-        androidRects.push_back(androidRect);
-    }
-    if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
-        native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(),
-                                         androidRects.size());
-    }
-
-    if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
-        return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
-                rects, n_rects);
-    } else {
-        return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
-    }
-}
-
-EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
-{
-    return eglSwapBuffersWithDamageKHR(dpy, surface, nullptr, 0);
-}
-
-EGLBoolean eglCopyBuffers(  EGLDisplay dpy, EGLSurface surface,
-                            NativePixmapType target)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t const * const s = get_surface(surface);
-    return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
-}
-
-const char* eglQueryString(EGLDisplay dpy, EGLint name)
-{
-    clearError();
-
-    // Generate an error quietly when client extensions (as defined by
-    // EGL_EXT_client_extensions) are queried.  We do not want to rely on
-    // validate_display to generate the error as validate_display would log
-    // the error, which can be misleading.
-    //
-    // If we want to support EGL_EXT_client_extensions later, we can return
-    // the client extension string here instead.
-    if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS)
-        return setErrorQuiet(EGL_BAD_DISPLAY, (const char*)nullptr);
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return (const char *) nullptr;
-
-    switch (name) {
-        case EGL_VENDOR:
-            return dp->getVendorString();
-        case EGL_VERSION:
-            return dp->getVersionString();
-        case EGL_EXTENSIONS:
-            return dp->getExtensionString();
-        case EGL_CLIENT_APIS:
-            return dp->getClientApiString();
-        default:
-            break;
-    }
-    return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
-}
-
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return (const char *) nullptr;
-
-    switch (name) {
-        case EGL_VENDOR:
-            return dp->disp.queryString.vendor;
-        case EGL_VERSION:
-            return dp->disp.queryString.version;
-        case EGL_EXTENSIONS:
-            return dp->disp.queryString.extensions;
-        case EGL_CLIENT_APIS:
-            return dp->disp.queryString.clientApi;
-        default:
-            break;
-    }
-    return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
-}
-
-// ----------------------------------------------------------------------------
-// EGL 1.1
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglSurfaceAttrib(
-        EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t * const s = get_surface(surface);
-
-    if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
-        if (!s->getNativeWindow()) {
-            setError(EGL_BAD_SURFACE, EGL_FALSE);
-        }
-        int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
-        return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    if (attribute == EGL_TIMESTAMPS_ANDROID) {
-        if (!s->getNativeWindow()) {
-            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-        }
-        int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
-        return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    if (s->setSmpte2086Attribute(attribute, value)) {
-        return EGL_TRUE;
-    } else if (s->setCta8613Attribute(attribute, value)) {
-        return EGL_TRUE;
-    } else if (s->cnx->egl.eglSurfaceAttrib) {
-        return s->cnx->egl.eglSurfaceAttrib(
-                dp->disp.dpy, s->surface, attribute, value);
-    }
-    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglBindTexImage(
-        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->cnx->egl.eglBindTexImage) {
-        return s->cnx->egl.eglBindTexImage(
-                dp->disp.dpy, s->surface, buffer);
-    }
-    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglReleaseTexImage(
-        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->cnx->egl.eglReleaseTexImage) {
-        return s->cnx->egl.eglReleaseTexImage(
-                dp->disp.dpy, s->surface, buffer);
-    }
-    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean res = EGL_TRUE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglSwapInterval) {
-        res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval);
-    }
-
-    return res;
+    return cnx->platform.eglSwapBuffersWithDamageKHR(dpy, draw, rects, n_rects);
 }
 
-
-// ----------------------------------------------------------------------------
-// EGL 1.2
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglWaitClient(void)
-{
+EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
+    ATRACE_CALL();
     clearError();
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    EGLBoolean res;
-    if (cnx->egl.eglWaitClient) {
-        res = cnx->egl.eglWaitClient();
-    } else {
-        res = cnx->egl.eglWaitGL();
-    }
-    return res;
+    return cnx->platform.eglSwapBuffers(dpy, surface);
 }
 
-EGLBoolean eglBindAPI(EGLenum api)
-{
+EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) {
     clearError();
 
-    if (egl_init_drivers() == EGL_FALSE) {
-        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
-    }
-
-    // bind this API on all EGLs
-    EGLBoolean res = EGL_TRUE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglBindAPI) {
-        res = cnx->egl.eglBindAPI(api);
-    }
-    return res;
+    return cnx->platform.eglCopyBuffers(dpy, surface, target);
 }
 
-EGLenum eglQueryAPI(void)
-{
+const char* eglQueryString(EGLDisplay dpy, EGLint name) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglQueryString(dpy, name);
+}
+
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglQueryStringImplementationANDROID(dpy, name);
+}
+
+EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglSurfaceAttrib(dpy, surface, attribute, value);
+}
+
+EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglBindTexImage(dpy, surface, buffer);
+}
+
+EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglReleaseTexImage(dpy, surface, buffer);
+}
+
+EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglSwapInterval(dpy, interval);
+}
+
+EGLBoolean eglWaitClient(void) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglWaitClient();
+}
+
+EGLBoolean eglBindAPI(EGLenum api) {
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
@@ -1640,806 +304,282 @@
     }
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglQueryAPI) {
-        return cnx->egl.eglQueryAPI();
-    }
-
-    // or, it can only be OpenGL ES
-    return EGL_OPENGL_ES_API;
+    return cnx->platform.eglBindAPI(api);
 }
 
-EGLBoolean eglReleaseThread(void)
-{
+EGLenum eglQueryAPI(void) {
+    clearError();
+
+    if (egl_init_drivers() == EGL_FALSE) {
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglQueryAPI();
+}
+
+EGLBoolean eglReleaseThread(void) {
     clearError();
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglReleaseThread) {
-        cnx->egl.eglReleaseThread();
-    }
-
-    // If there is context bound to the thread, release it
-    egl_display_t::loseCurrent(get_context(getContext()));
-
-    egl_tls_t::clearTLS();
-    return EGL_TRUE;
+    return cnx->platform.eglReleaseThread();
 }
 
-EGLSurface eglCreatePbufferFromClientBuffer(
-          EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
-          EGLConfig config, const EGLint *attrib_list)
-{
+EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
+                                            EGLConfig config, const EGLint* attrib_list) {
     clearError();
 
-    egl_connection_t* cnx = nullptr;
-    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (!dp) return EGL_FALSE;
-    if (cnx->egl.eglCreatePbufferFromClientBuffer) {
-        return cnx->egl.eglCreatePbufferFromClientBuffer(
-                dp->disp.dpy, buftype, buffer, config, attrib_list);
-    }
-    return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreatePbufferFromClientBuffer(dpy, buftype, buffer, config,
+                                                          attrib_list);
 }
 
-// ----------------------------------------------------------------------------
-// EGL_EGLEXT_VERSION 3
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface,
-        const EGLint *attrib_list)
-{
+EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->cnx->egl.eglLockSurfaceKHR) {
-        return s->cnx->egl.eglLockSurfaceKHR(
-                dp->disp.dpy, s->surface, attrib_list);
-    }
-    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglLockSurfaceKHR(dpy, surface, attrib_list);
 }
 
-EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface)
-{
+EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->cnx->egl.eglUnlockSurfaceKHR) {
-        return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
-    }
-    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglUnlockSurfaceKHR(dpy, surface);
 }
 
 EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
-        EGLClientBuffer buffer, const EGLint *attrib_list)
-{
+                              EGLClientBuffer buffer, const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_IMAGE_KHR;
-
-    ContextRef _c(dp.get(), ctx);
-    egl_context_t * const c = _c.get();
-
-    EGLImageKHR result = EGL_NO_IMAGE_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglCreateImageKHR) {
-        result = cnx->egl.eglCreateImageKHR(
-                dp->disp.dpy,
-                c ? c->context : EGL_NO_CONTEXT,
-                target, buffer, attrib_list);
-    }
-    return result;
+    return cnx->platform.eglCreateImageKHR(dpy, ctx, target, buffer, attrib_list);
 }
 
-EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
-{
+EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglDestroyImageKHR) {
-        result = cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img);
-    }
-    return result;
+    return cnx->platform.eglDestroyImageKHR(dpy, img);
 }
 
-// ----------------------------------------------------------------------------
-// EGL_EGLEXT_VERSION 5
-// ----------------------------------------------------------------------------
-
-
-EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
-{
+EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_SYNC_KHR;
-
-    EGLSyncKHR result = EGL_NO_SYNC_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglCreateSyncKHR) {
-        result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list);
-    }
-    return result;
+    return cnx->platform.eglCreateSyncKHR(dpy, type, attrib_list);
 }
 
-EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
-{
+EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglDestroySyncKHR) {
-        result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync);
-    }
-    return result;
+    return cnx->platform.eglDestroySyncKHR(dpy, sync);
 }
 
 EGLBoolean eglSignalSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglSignalSyncKHR) {
-        result = cnx->egl.eglSignalSyncKHR(
-                dp->disp.dpy, sync, mode);
-    }
-    return result;
+    return cnx->platform.eglSignalSyncKHR(dpy, sync, mode);
 }
 
-EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync,
-        EGLint flags, EGLTimeKHR timeout)
-{
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLint result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) {
-        result = cnx->egl.eglClientWaitSyncKHR(
-                dp->disp.dpy, sync, flags, timeout);
-    }
-    return result;
+    return cnx->platform.eglClientWaitSyncKHR(dpy, sync, flags, timeout);
 }
 
-EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync,
-        EGLint attribute, EGLint *value)
-{
+EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint* value) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) {
-        result = cnx->egl.eglGetSyncAttribKHR(
-                dp->disp.dpy, sync, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglGetSyncAttribKHR(dpy, sync, attribute, value);
 }
 
-EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint *attrib_list)
-{
+EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_STREAM_KHR;
-
-    EGLStreamKHR result = EGL_NO_STREAM_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
-        result = cnx->egl.eglCreateStreamKHR(
-                dp->disp.dpy, attrib_list);
-    }
-    return result;
+    return cnx->platform.eglCreateStreamKHR(dpy, attrib_list);
 }
 
-EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream)
-{
+EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
-        result = cnx->egl.eglDestroyStreamKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglDestroyStreamKHR(dpy, stream);
 }
 
-EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLint value)
-{
+EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                              EGLint value) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
-        result = cnx->egl.eglStreamAttribKHR(
-                dp->disp.dpy, stream, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglStreamAttribKHR(dpy, stream, attribute, value);
 }
 
-EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLint *value)
-{
+EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                             EGLint* value) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
-        result = cnx->egl.eglQueryStreamKHR(
-                dp->disp.dpy, stream, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglQueryStreamKHR(dpy, stream, attribute, value);
 }
 
-EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLuint64KHR *value)
-{
+EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                                EGLuint64KHR* value) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
-        result = cnx->egl.eglQueryStreamu64KHR(
-                dp->disp.dpy, stream, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglQueryStreamu64KHR(dpy, stream, attribute, value);
 }
 
-EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLTimeKHR *value)
-{
+EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                                 EGLTimeKHR* value) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
-        result = cnx->egl.eglQueryStreamTimeKHR(
-                dp->disp.dpy, stream, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglQueryStreamTimeKHR(dpy, stream, attribute, value);
 }
 
-EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config,
-        EGLStreamKHR stream, const EGLint *attrib_list)
-{
+EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream,
+                                             const EGLint* attrib_list) {
     clearError();
 
-    egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_SURFACE;
-
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
-        EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
-                dp->disp.dpy, config, stream, attrib_list);
-        if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
-                                                 EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
-            return s;
-        }
-    }
-    return EGL_NO_SURFACE;
+    return cnx->platform.eglCreateStreamProducerSurfaceKHR(dpy, config, stream, attrib_list);
 }
 
-EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy,
-        EGLStreamKHR stream)
-{
+EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy, EGLStreamKHR stream) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
-        result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglStreamConsumerGLTextureExternalKHR(dpy, stream);
 }
 
-EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy,
-        EGLStreamKHR stream)
-{
+EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy, EGLStreamKHR stream) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
-        result = cnx->egl.eglStreamConsumerAcquireKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglStreamConsumerAcquireKHR(dpy, stream);
 }
 
-EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy,
-        EGLStreamKHR stream)
-{
+EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy, EGLStreamKHR stream) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
-        result = cnx->egl.eglStreamConsumerReleaseKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglStreamConsumerReleaseKHR(dpy, stream);
 }
 
-EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(
-        EGLDisplay dpy, EGLStreamKHR stream)
-{
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(EGLDisplay dpy, EGLStreamKHR stream) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
-
-    EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
-        result = cnx->egl.eglGetStreamFileDescriptorKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglGetStreamFileDescriptorKHR(dpy, stream);
 }
 
-EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(
-        EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
-{
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(EGLDisplay dpy,
+                                                  EGLNativeFileDescriptorKHR file_descriptor) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_STREAM_KHR;
-
-    EGLStreamKHR result = EGL_NO_STREAM_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
-        result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
-                dp->disp.dpy, file_descriptor);
-    }
-    return result;
+    return cnx->platform.eglCreateStreamFromFileDescriptorKHR(dpy, file_descriptor);
 }
 
-// ----------------------------------------------------------------------------
-// EGL_EGLEXT_VERSION 15
-// ----------------------------------------------------------------------------
-
 EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) {
     clearError();
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-    EGLint result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglWaitSyncKHR) {
-        result = cnx->egl.eglWaitSyncKHR(dp->disp.dpy, sync, flags);
-    }
-    return result;
+    return cnx->platform.eglWaitSyncKHR(dpy, sync, flags);
 }
 
-// ----------------------------------------------------------------------------
-// ANDROID extensions
-// ----------------------------------------------------------------------------
-
-EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync)
-{
+EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
-
-    EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) {
-        result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync);
-    }
-    return result;
+    return cnx->platform.eglDupNativeFenceFDANDROID(dpy, sync);
 }
 
-EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface,
-        EGLnsecsANDROID time)
-{
+EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface, EGLnsecsANDROID time) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return EGL_FALSE;
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-    native_window_set_buffers_timestamp(s->getNativeWindow(), time);
-
-    return EGL_TRUE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglPresentationTimeANDROID(dpy, surface, time);
 }
 
-EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) {
+EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer* buffer) {
     clearError();
-    // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
-    // this function cannot be implemented when this libEGL is built for
-    // vendors.
-#ifndef __ANDROID_VNDK__
-    if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
-    return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
-#else
-    return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
-#endif
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetNativeClientBufferANDROID(buffer);
 }
 
-// ----------------------------------------------------------------------------
-// NVIDIA extensions
-// ----------------------------------------------------------------------------
-EGLuint64NV eglGetSystemTimeFrequencyNV()
-{
+EGLuint64NV eglGetSystemTimeFrequencyNV() {
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
         return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
     }
 
-    EGLuint64NV ret = 0;
     egl_connection_t* const cnx = &gEGLImpl;
-
-    if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) {
-        return cnx->egl.eglGetSystemTimeFrequencyNV();
-    }
-
-    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+    return cnx->platform.eglGetSystemTimeFrequencyNV();
 }
 
-EGLuint64NV eglGetSystemTimeNV()
-{
+EGLuint64NV eglGetSystemTimeNV() {
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
         return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
     }
 
-    EGLuint64NV ret = 0;
     egl_connection_t* const cnx = &gEGLImpl;
-
-    if (cnx->dso && cnx->egl.eglGetSystemTimeNV) {
-        return cnx->egl.eglGetSystemTimeNV();
-    }
-
-    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+    return cnx->platform.eglGetSystemTimeNV();
 }
 
-// ----------------------------------------------------------------------------
-// Partial update extension
-// ----------------------------------------------------------------------------
-EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface,
-        EGLint *rects, EGLint n_rects)
-{
+EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, EGLint* rects,
+                                 EGLint n_rects) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        return EGL_FALSE;
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->cnx->egl.eglSetDamageRegionKHR) {
-        return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
-                rects, n_rects);
-    }
-
-    return EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglSetDamageRegionKHR(dpy, surface, rects, n_rects);
 }
 
-EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
-            EGLuint64KHR *frameId) {
+EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR* frameId) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-
-    if (!s->getNativeWindow()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    uint64_t nextFrameId = 0;
-    int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId);
-
-    if (ret != 0) {
-        // This should not happen. Return an error that is not in the spec
-        // so it's obvious something is very wrong.
-        ALOGE("eglGetNextFrameId: Unexpected error.");
-        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
-    }
-
-    *frameId = nextFrameId;
-    return EGL_TRUE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetNextFrameIdANDROID(dpy, surface, frameId);
 }
 
-EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface,
-        EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
-{
+EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps,
+                                         const EGLint* names, EGLnsecsANDROID* values) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-
-    if (!s->getNativeWindow()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    nsecs_t* compositeDeadline = nullptr;
-    nsecs_t* compositeInterval = nullptr;
-    nsecs_t* compositeToPresentLatency = nullptr;
-
-    for (int i = 0; i < numTimestamps; i++) {
-        switch (names[i]) {
-            case EGL_COMPOSITE_DEADLINE_ANDROID:
-                compositeDeadline = &values[i];
-                break;
-            case EGL_COMPOSITE_INTERVAL_ANDROID:
-                compositeInterval = &values[i];
-                break;
-            case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
-                compositeToPresentLatency = &values[i];
-                break;
-            default:
-                return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
-        }
-    }
-
-    int ret = native_window_get_compositor_timing(s->getNativeWindow(),
-            compositeDeadline, compositeInterval, compositeToPresentLatency);
-
-    switch (ret) {
-      case 0:
-        return EGL_TRUE;
-      case -ENOSYS:
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-      default:
-        // This should not happen. Return an error that is not in the spec
-        // so it's obvious something is very wrong.
-        ALOGE("eglGetCompositorTiming: Unexpected error.");
-        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
-    }
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCompositorTimingANDROID(dpy, surface, numTimestamps, names, values);
 }
 
-EGLBoolean eglGetCompositorTimingSupportedANDROID(
-        EGLDisplay dpy, EGLSurface surface, EGLint name)
-{
+EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint name) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-
-    ANativeWindow* window = s->getNativeWindow();
-    if (!window) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    switch (name) {
-        case EGL_COMPOSITE_DEADLINE_ANDROID:
-        case EGL_COMPOSITE_INTERVAL_ANDROID:
-        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
-            return EGL_TRUE;
-        default:
-            return EGL_FALSE;
-    }
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCompositorTimingSupportedANDROID(dpy, surface, name);
 }
 
-EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
-        EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
-        EGLnsecsANDROID *values)
-{
+EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId,
+                                        EGLint numTimestamps, const EGLint* timestamps,
+                                        EGLnsecsANDROID* values) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-
-    if (!s->getNativeWindow()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    nsecs_t* requestedPresentTime = nullptr;
-    nsecs_t* acquireTime = nullptr;
-    nsecs_t* latchTime = nullptr;
-    nsecs_t* firstRefreshStartTime = nullptr;
-    nsecs_t* gpuCompositionDoneTime = nullptr;
-    nsecs_t* lastRefreshStartTime = nullptr;
-    nsecs_t* displayPresentTime = nullptr;
-    nsecs_t* dequeueReadyTime = nullptr;
-    nsecs_t* releaseTime = nullptr;
-
-    for (int i = 0; i < numTimestamps; i++) {
-        switch (timestamps[i]) {
-            case EGL_REQUESTED_PRESENT_TIME_ANDROID:
-                requestedPresentTime = &values[i];
-                break;
-            case EGL_RENDERING_COMPLETE_TIME_ANDROID:
-                acquireTime = &values[i];
-                break;
-            case EGL_COMPOSITION_LATCH_TIME_ANDROID:
-                latchTime = &values[i];
-                break;
-            case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
-                firstRefreshStartTime = &values[i];
-                break;
-            case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
-                lastRefreshStartTime = &values[i];
-                break;
-            case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
-                gpuCompositionDoneTime = &values[i];
-                break;
-            case EGL_DISPLAY_PRESENT_TIME_ANDROID:
-                displayPresentTime = &values[i];
-                break;
-            case EGL_DEQUEUE_READY_TIME_ANDROID:
-                dequeueReadyTime = &values[i];
-                break;
-            case EGL_READS_DONE_TIME_ANDROID:
-                releaseTime = &values[i];
-                break;
-            default:
-                return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
-        }
-    }
-
-    int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
-            requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
-            lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
-            dequeueReadyTime, releaseTime);
-
-    switch (ret) {
-        case 0:
-            return EGL_TRUE;
-        case -ENOENT:
-            return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
-        case -ENOSYS:
-            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-        case -EINVAL:
-            return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
-        default:
-            // This should not happen. Return an error that is not in the spec
-            // so it's obvious something is very wrong.
-            ALOGE("eglGetFrameTimestamps: Unexpected error.");
-            return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
-    }
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetFrameTimestampsANDROID(dpy, surface, frameId, numTimestamps,
+                                                      timestamps, values);
 }
 
-EGLBoolean eglGetFrameTimestampSupportedANDROID(
-        EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
-{
+EGLBoolean eglGetFrameTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface,
+                                                EGLint timestamp) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-
-    ANativeWindow* window = s->getNativeWindow();
-    if (!window) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    switch (timestamp) {
-        case EGL_COMPOSITE_DEADLINE_ANDROID:
-        case EGL_COMPOSITE_INTERVAL_ANDROID:
-        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
-        case EGL_REQUESTED_PRESENT_TIME_ANDROID:
-        case EGL_RENDERING_COMPLETE_TIME_ANDROID:
-        case EGL_COMPOSITION_LATCH_TIME_ANDROID:
-        case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
-        case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
-        case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
-        case EGL_DEQUEUE_READY_TIME_ANDROID:
-        case EGL_READS_DONE_TIME_ANDROID:
-            return EGL_TRUE;
-        case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
-            int value = 0;
-            window->query(window,
-                    NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
-            return value == 0 ? EGL_FALSE : EGL_TRUE;
-        }
-        default:
-            return EGL_FALSE;
-    }
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetFrameTimestampSupportedANDROID(dpy, surface, timestamp);
 }
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
new file mode 100644
index 0000000..6900b8b
--- /dev/null
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -0,0 +1,433 @@
+/*
+ ** Copyright 2018, The Android Open Source Project
+ **
+ ** 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.
+ */
+
+#include "egl_layers.h"
+
+#include <EGL/egl.h>
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android/dlext.h>
+#include <cutils/properties.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <log/log.h>
+#include <nativebridge/native_bridge.h>
+#include <nativeloader/native_loader.h>
+#include <sys/prctl.h>
+
+namespace android {
+
+// GLES Layers
+//
+// - Layer discovery -
+// 1. Check for debug layer list from GraphicsEnv
+// 2. If none enabled, check system properties
+//
+// - Layer initializing -
+// TODO: ADD DETAIL ABOUT NEW INTERFACES
+// - InitializeLayer (provided by layer, called by loader)
+// - GetLayerProcAddress (provided by layer, called by loader)
+// - getNextLayerProcAddress (provided by loader, called by layer)
+//
+// 1. Walk through defs for egl and each gl version
+// 2. Call GetLayerProcAddress passing the name and the target hook entry point
+//   - This tells the layer the next point in the chain it should call
+// 3. Replace the hook with the layer's entry point
+//    - All entryoints will be present, anything unsupported by the driver will
+//      have gl_unimplemented
+//
+// - Extension layering -
+//  Not all functions are known to Android, so libEGL handles extensions.
+//  They are looked up by applications using eglGetProcAddress
+//  Layers can look them up with getNextLayerProcAddress
+
+const int kFuncCount = sizeof(platform_impl_t) / sizeof(char*) + sizeof(egl_t) / sizeof(char*) +
+        sizeof(gl_hooks_t) / sizeof(char*);
+
+typedef struct FunctionTable {
+    EGLFuncPointer x[kFuncCount];
+    EGLFuncPointer& operator[](int i) { return x[i]; }
+} FunctionTable;
+
+// TODO: Move these to class
+std::unordered_map<std::string, int> func_indices;
+// func_indices.reserve(kFuncCount);
+
+std::unordered_map<int, std::string> func_names;
+// func_names.reserve(kFuncCount);
+
+std::vector<FunctionTable> layer_functions;
+
+const void* getNextLayerProcAddress(void* layer_id, const char* name) {
+    // Use layer_id to find funcs for layer below current
+    // This is the same key provided in InitializeLayer
+    auto next_layer_funcs = reinterpret_cast<FunctionTable*>(layer_id);
+    EGLFuncPointer val;
+
+    if (func_indices.find(name) == func_indices.end()) {
+        // No entry for this function - it is an extension
+        // call down the GPA chain directly to the impl
+        ALOGV("getNextLayerProcAddress servicing %s", name);
+
+        // Look up which GPA we should use
+        int gpaIndex = func_indices["eglGetProcAddress"];
+        EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
+
+        ALOGV("Calling down the GPA chain (%llu) for %s", (unsigned long long)gpaNext, name);
+
+        // Call it for the requested function
+        typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
+        PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);
+
+        val = reinterpret_cast<EGLFuncPointer>(next(name));
+        ALOGV("Got back %llu for %s", (unsigned long long)val, name);
+
+        // We should store it now, but to do that, we need to move func_idx to the class so we can
+        // increment it separately
+        // TODO: Move func_idx to class and store the result of GPA
+        return reinterpret_cast<void*>(val);
+    }
+
+    // int index = func_indices[name];
+    // val = (*next_layer_funcs)[index];
+    // return reinterpret_cast<void*>(val);
+    return reinterpret_cast<void*>((*next_layer_funcs)[func_indices[name]]);
+}
+
+void SetupFuncMaps(FunctionTable& functions, char const* const* entries, EGLFuncPointer* curr,
+                   int& func_idx) {
+    while (*entries) {
+        const char* name = *entries;
+
+        // Some names overlap, only fill with initial entry
+        // This does mean that some indices will not be used
+        if (func_indices.find(name) == func_indices.end()) {
+            func_names[func_idx] = name;
+            func_indices[name] = func_idx;
+        }
+
+        // Populate layer_functions once with initial value
+        // These values will arrive in priority order, starting with platform entries
+        if (functions[func_idx] == nullptr) {
+            functions[func_idx] = *curr;
+        }
+
+        entries++;
+        curr++;
+        func_idx++;
+    }
+}
+
+LayerLoader& LayerLoader::getInstance() {
+    // This function is mutex protected in egl_init_drivers_locked and eglGetProcAddressImpl
+    static LayerLoader layer_loader;
+
+    if (!layer_loader.layers_loaded_) layer_loader.LoadLayers();
+
+    return layer_loader;
+}
+
+const char kSystemLayerLibraryDir[] = "/data/local/debug/gles";
+
+std::string LayerLoader::GetDebugLayers() {
+    // Layers can be specified at the Java level in GraphicsEnvironemnt
+    // gpu_debug_layers = layer1:layer2:layerN
+    std::string debug_layers = android_getDebugLayers();
+
+    if (debug_layers.empty()) {
+        // Only check system properties if Java settings are empty
+        char prop[PROPERTY_VALUE_MAX];
+        property_get("debug.gles.layers", prop, "");
+        debug_layers = prop;
+    }
+
+    return debug_layers;
+}
+
+EGLFuncPointer LayerLoader::ApplyLayer(layer_setup_func layer_setup, const char* name,
+                                       EGLFuncPointer next) {
+    // Walk through our list of LayerSetup functions (they will already be in reverse order) to
+    // build up a call chain from the driver
+
+    EGLFuncPointer layer_entry = next;
+
+    layer_entry = layer_setup(name, layer_entry);
+
+    if (next != layer_entry) {
+        ALOGV("We succeeded, replacing hook (%llu) with layer entry (%llu), for %s",
+              (unsigned long long)next, (unsigned long long)layer_entry, name);
+    }
+
+    return layer_entry;
+}
+
+EGLFuncPointer LayerLoader::ApplyLayers(const char* name, EGLFuncPointer next) {
+    if (!layers_loaded_ || layer_setup_.empty()) return next;
+
+    ALOGV("ApplyLayers called for %s with next (%llu), current_layer_ (%i)", name,
+          (unsigned long long)next, current_layer_);
+
+    EGLFuncPointer val = next;
+
+    // Only ApplyLayers for layers that have been setup, not all layers yet
+    for (unsigned i = 0; i < current_layer_; i++) {
+        ALOGV("ApplyLayers: Calling ApplyLayer with i = %i for %s with next (%llu)", i, name,
+              (unsigned long long)next);
+        val = ApplyLayer(layer_setup_[i], name, val);
+    }
+
+    ALOGV("ApplyLayers returning %llu for %s", (unsigned long long)val, name);
+
+    return val;
+}
+
+void LayerLoader::LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer* curr,
+                                       char const* const* entries) {
+    while (*entries) {
+        char const* name = *entries;
+
+        EGLFuncPointer prev = *curr;
+
+        // Pass the existing entry point into the layer, replace the call with return value
+        *curr = ApplyLayer(layer_setup, name, *curr);
+
+        if (prev != *curr) {
+            ALOGV("LayerPlatformEntries: Replaced (%llu) with platform entry (%llu), for %s",
+                  (unsigned long long)prev, (unsigned long long)*curr, name);
+        } else {
+            ALOGV("LayerPlatformEntries: No change(%llu) for %s, which means layer did not "
+                  "intercept",
+                  (unsigned long long)prev, name);
+        }
+
+        curr++;
+        entries++;
+    }
+}
+
+void LayerLoader::LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer* curr,
+                                     char const* const* entries) {
+    while (*entries) {
+        char const* name = *entries;
+        EGLFuncPointer prev = *curr;
+
+        // Only apply layers to driver entries if not handled by the platform
+        if (FindPlatformImplAddr(name) == nullptr) {
+            // Pass the existing entry point into the layer, replace the call with return value
+            *curr = ApplyLayer(layer_setup, name, *prev);
+
+            if (prev != *curr) {
+                ALOGV("LayerDriverEntries: Replaced (%llu) with platform entry (%llu), for %s",
+                      (unsigned long long)prev, (unsigned long long)*curr, name);
+            }
+
+        } else {
+            ALOGV("LayerDriverEntries: Skipped (%llu) for %s", (unsigned long long)prev, name);
+        }
+
+        curr++;
+        entries++;
+    }
+}
+
+bool LayerLoader::Initialized() {
+    return initialized_;
+}
+
+void LayerLoader::InitLayers(egl_connection_t* cnx) {
+    if (!layers_loaded_) return;
+
+    if (initialized_) return;
+
+    if (layer_setup_.empty()) {
+        initialized_ = true;
+        return;
+    }
+
+    // Include the driver in layer_functions
+    layer_functions.resize(layer_setup_.size() + 1);
+
+    // Walk through the initial lists and create layer_functions[0]
+    int func_idx = 0;
+    char const* const* entries;
+    EGLFuncPointer* curr;
+
+    entries = platform_names;
+    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
+    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+    ALOGV("InitLayers: func_idx after platform_names: %i", func_idx);
+
+    entries = egl_names;
+    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
+    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+    ALOGV("InitLayers: func_idx after egl_names: %i", func_idx);
+
+    entries = gl_names;
+    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
+    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+    ALOGV("InitLayers: func_idx after gl_names: %i", func_idx);
+
+    // Walk through each layer's entry points per API, starting just above the driver
+    for (current_layer_ = 0; current_layer_ < layer_setup_.size(); current_layer_++) {
+        // Init the layer with a key that points to layer just below it
+        layer_init_[current_layer_](reinterpret_cast<void*>(&layer_functions[current_layer_]),
+                                    reinterpret_cast<PFNEGLGETNEXTLAYERPROCADDRESSPROC>(
+                                            getNextLayerProcAddress));
+
+        // Check functions implemented by the platform
+        func_idx = 0;
+        entries = platform_names;
+        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
+        LayerPlatformEntries(layer_setup_[current_layer_], curr, entries);
+
+        // Populate next function table after layers have been applied
+        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+
+        // EGL
+        entries = egl_names;
+        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
+        LayerDriverEntries(layer_setup_[current_layer_], curr, entries);
+
+        // Populate next function table after layers have been applied
+        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+
+        // GLES 2+
+        // NOTE: We route calls to GLESv2 hooks, not GLESv1, so layering does not support GLES 1.x
+        // If it were added in the future, a different layer initialization model would be needed,
+        // that defers loading GLES entrypoints until after eglMakeCurrent, so two phase
+        // initialization.
+        entries = gl_names;
+        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
+        LayerDriverEntries(layer_setup_[current_layer_], curr, entries);
+
+        // Populate next function table after layers have been applied
+        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+    }
+
+    // We only want to apply layers once
+    initialized_ = true;
+}
+
+void LayerLoader::LoadLayers() {
+    std::string debug_layers = GetDebugLayers();
+
+    // If no layers are specified, we're done
+    if (debug_layers.empty()) return;
+
+    // Only enable the system search path for non-user builds
+    std::string system_path;
+    if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+        system_path = kSystemLayerLibraryDir;
+    }
+
+    ALOGI("Debug layer list: %s", debug_layers.c_str());
+    std::vector<std::string> layers = android::base::Split(debug_layers, ":");
+
+    // Load the layers in reverse order so we start with the driver's entrypoint and work our way up
+    for (int32_t i = layers.size() - 1; i >= 0; i--) {
+        // Check each layer path for the layer
+        std::vector<std::string> paths = android::base::Split(android_getLayerPaths(), ":");
+
+        if (!system_path.empty()) {
+            // Prepend the system paths so they override other layers
+            auto it = paths.begin();
+            paths.insert(it, system_path);
+        }
+
+        bool layer_found = false;
+        for (uint32_t j = 0; j < paths.size() && !layer_found; j++) {
+            std::string layer;
+
+            ALOGI("Searching %s for GLES layers", paths[j].c_str());
+
+            // Realpath will return null for non-existent files
+            android::base::Realpath(paths[j] + "/" + layers[i], &layer);
+
+            if (!layer.empty()) {
+                layer_found = true;
+                ALOGI("GLES layer found: %s", layer.c_str());
+
+                // Load the layer
+                //
+                // TODO: This code is common with Vulkan loader, refactor
+                //
+                // Libraries in the system layer library dir can't be loaded into
+                // the application namespace. That causes compatibility problems, since
+                // any symbol dependencies will be resolved by system libraries. They
+                // can't safely use libc++_shared, for example. Which is one reason
+                // (among several) we only allow them in non-user builds.
+                void* handle = nullptr;
+                auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
+                if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
+                    bool native_bridge = false;
+                    std::string error_message;
+                    handle = OpenNativeLibrary(app_namespace, layer.c_str(), &native_bridge,
+                                               &error_message);
+                    if (!handle) {
+                        ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
+                              error_message.c_str());
+                        return;
+                    }
+
+                } else {
+                    handle = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL);
+                }
+
+                if (handle) {
+                    ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)handle,
+                          layers[i].c_str());
+                } else {
+                    // If the layer is found but can't be loaded, try setenforce 0
+                    const char* dlsym_error = dlerror();
+                    ALOGE("Failed to load layer %s with error: %s", layer.c_str(), dlsym_error);
+                    return;
+                }
+
+                // Find the layer's Initialize function
+                std::string init_func = "InitializeLayer";
+                ALOGV("Looking for entrypoint %s", init_func.c_str());
+
+                layer_init_func LayerInit =
+                        reinterpret_cast<layer_init_func>(dlsym(handle, init_func.c_str()));
+                if (LayerInit) {
+                    ALOGV("Found %s for layer %s", init_func.c_str(), layer.c_str());
+                    layer_init_.push_back(LayerInit);
+                } else {
+                    ALOGE("Failed to dlsym %s for layer %s", init_func.c_str(), layer.c_str());
+                    return;
+                }
+
+                // Find the layer's setup function
+                std::string setup_func = "GetLayerProcAddress";
+                ALOGV("Looking for entrypoint %s", setup_func.c_str());
+
+                layer_setup_func LayerSetup =
+                        reinterpret_cast<layer_setup_func>(dlsym(handle, setup_func.c_str()));
+                if (LayerSetup) {
+                    ALOGV("Found %s for layer %s", setup_func.c_str(), layer.c_str());
+                    layer_setup_.push_back(LayerSetup);
+                } else {
+                    ALOGE("Failed to dlsym %s for layer %s", setup_func.c_str(), layer.c_str());
+                    return;
+                }
+            }
+        }
+    }
+    // Track this so we only attempt to load these once
+    layers_loaded_ = true;
+}
+
+} // namespace android
diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h
new file mode 100644
index 0000000..e401b44
--- /dev/null
+++ b/opengl/libs/EGL/egl_layers.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_EGL_LAYERS_H
+#define ANDROID_EGL_LAYERS_H
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <EGL/egldefs.h>
+
+#include "egl_platform_entries.h"
+
+typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
+
+namespace android {
+
+class LayerLoader {
+public:
+    static LayerLoader& getInstance();
+    ~LayerLoader(){};
+
+    typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*);
+    typedef EGLFuncPointer (*layer_init_func)(
+            const void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address);
+    typedef EGLFuncPointer (*layer_setup_func)(const char* name, EGLFuncPointer next);
+
+    void LoadLayers();
+    void InitLayers(egl_connection_t*);
+    void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+    void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+    bool Initialized();
+    std::string GetDebugLayers();
+
+    EGLFuncPointer GetGpaNext(unsigned i);
+    EGLFuncPointer ApplyLayer(layer_setup_func layer_setup, const char* name, EGLFuncPointer next);
+    EGLFuncPointer ApplyLayers(const char* name, EGLFuncPointer next);
+
+    std::vector<layer_init_func> layer_init_;
+    std::vector<layer_setup_func> layer_setup_;
+
+private:
+    LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0){};
+    bool layers_loaded_;
+    bool initialized_;
+    unsigned current_layer_;
+};
+
+}; // namespace android
+
+#endif // ANDROID_EGL_LAYERS_H
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
new file mode 100644
index 0000000..0d69a65
--- /dev/null
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -0,0 +1,2475 @@
+/*
+ ** Copyright 2007, The Android Open Source Project
+ **
+ ** 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "egl_platform_entries.h"
+
+#include <ctype.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hardware/gralloc1.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <EGL/eglext_angle.h>
+
+#include <android/hardware_buffer.h>
+#include <android-base/strings.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <private/android/AHardwareBufferHelpers.h>
+
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <log/log.h>
+
+#include <condition_variable>
+#include <deque>
+#include <mutex>
+#include <unordered_map>
+#include <string>
+#include <thread>
+
+#include "../egl_impl.h"
+
+#include "egl_display.h"
+#include "egl_object.h"
+#include "egl_layers.h"
+#include "egl_tls.h"
+#include "egl_trace.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+using nsecs_t = int64_t;
+
+struct extention_map_t {
+    const char* name;
+    __eglMustCastToProperFunctionPointerType address;
+};
+
+/*
+ * This is the list of EGL extensions exposed to applications.
+ *
+ * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL
+ * wrapper and are always available.
+ *
+ * The rest (gExtensionString) depend on support in the EGL driver, and are
+ * only available if the driver supports them. However, some of these must be
+ * supported because they are used by the Android system itself; these are
+ * listed as mandatory below and are required by the CDD. The system *assumes*
+ * the mandatory extensions are present and may not function properly if some
+ * are missing.
+ *
+ * NOTE: Both strings MUST have a single space as the last character.
+ */
+
+extern char const * const gBuiltinExtensionString;
+extern char const * const gExtensionString;
+
+// clang-format off
+// Extensions implemented by the EGL wrapper.
+char const * const gBuiltinExtensionString =
+        "EGL_KHR_get_all_proc_addresses "
+        "EGL_ANDROID_presentation_time "
+        "EGL_KHR_swap_buffers_with_damage "
+        "EGL_ANDROID_get_native_client_buffer "
+        "EGL_ANDROID_front_buffer_auto_refresh "
+        "EGL_ANDROID_get_frame_timestamps "
+        "EGL_EXT_surface_SMPTE2086_metadata "
+        "EGL_EXT_surface_CTA861_3_metadata "
+        ;
+
+// Whitelist of extensions exposed to applications if implemented in the vendor driver.
+char const * const gExtensionString  =
+        "EGL_KHR_image "                        // mandatory
+        "EGL_KHR_image_base "                   // mandatory
+        "EGL_EXT_image_gl_colorspace "
+        "EGL_KHR_image_pixmap "
+        "EGL_KHR_lock_surface "
+        "EGL_KHR_gl_colorspace "
+        "EGL_KHR_gl_texture_2D_image "
+        "EGL_KHR_gl_texture_3D_image "
+        "EGL_KHR_gl_texture_cubemap_image "
+        "EGL_KHR_gl_renderbuffer_image "
+        "EGL_KHR_reusable_sync "
+        "EGL_KHR_fence_sync "
+        "EGL_KHR_create_context "
+        "EGL_KHR_config_attribs "
+        "EGL_KHR_surfaceless_context "
+        "EGL_KHR_stream "
+        "EGL_KHR_stream_fifo "
+        "EGL_KHR_stream_producer_eglsurface "
+        "EGL_KHR_stream_consumer_gltexture "
+        "EGL_KHR_stream_cross_process_fd "
+        "EGL_EXT_create_context_robustness "
+        "EGL_NV_system_time "
+        "EGL_ANDROID_image_native_buffer "      // mandatory
+        "EGL_KHR_wait_sync "                    // strongly recommended
+        "EGL_ANDROID_recordable "               // mandatory
+        "EGL_KHR_partial_update "               // strongly recommended
+        "EGL_EXT_pixel_format_float "
+        "EGL_EXT_buffer_age "                   // strongly recommended with partial_update
+        "EGL_KHR_create_context_no_error "
+        "EGL_KHR_mutable_render_buffer "
+        "EGL_EXT_yuv_surface "
+        "EGL_EXT_protected_content "
+        "EGL_IMG_context_priority "
+        "EGL_KHR_no_config_context "
+        ;
+// clang-format on
+
+// extensions not exposed to applications but used by the ANDROID system
+//      "EGL_ANDROID_blob_cache "               // strongly recommended
+//      "EGL_IMG_hibernate_process "            // optional
+//      "EGL_ANDROID_native_fence_sync "        // strongly recommended
+//      "EGL_ANDROID_framebuffer_target "       // mandatory for HWC 1.1
+
+/*
+ * EGL Extensions entry-points exposed to 3rd party applications
+ * (keep in sync with gExtensionString above)
+ *
+ */
+static const extention_map_t sExtensionMap[] = {
+    // EGL_KHR_lock_surface
+    { "eglLockSurfaceKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
+    { "eglUnlockSurfaceKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
+
+    // EGL_KHR_image, EGL_KHR_image_base
+    { "eglCreateImageKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
+    { "eglDestroyImageKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
+
+    // EGL_KHR_reusable_sync, EGL_KHR_fence_sync
+    { "eglCreateSyncKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
+    { "eglDestroySyncKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
+    { "eglClientWaitSyncKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
+    { "eglSignalSyncKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
+    { "eglGetSyncAttribKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
+
+    // EGL_NV_system_time
+    { "eglGetSystemTimeFrequencyNV",
+            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
+    { "eglGetSystemTimeNV",
+            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
+
+    // EGL_KHR_wait_sync
+    { "eglWaitSyncKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
+
+    // EGL_ANDROID_presentation_time
+    { "eglPresentationTimeANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
+
+    // EGL_KHR_swap_buffers_with_damage
+    { "eglSwapBuffersWithDamageKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
+
+    // EGL_ANDROID_get_native_client_buffer
+    { "eglGetNativeClientBufferANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
+
+    // EGL_KHR_partial_update
+    { "eglSetDamageRegionKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
+
+    { "eglCreateStreamKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
+    { "eglDestroyStreamKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
+    { "eglStreamAttribKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
+    { "eglQueryStreamKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
+    { "eglQueryStreamu64KHR",
+            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
+    { "eglQueryStreamTimeKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
+    { "eglCreateStreamProducerSurfaceKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
+    { "eglStreamConsumerGLTextureExternalKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
+    { "eglStreamConsumerAcquireKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
+    { "eglStreamConsumerReleaseKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
+    { "eglGetStreamFileDescriptorKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
+    { "eglCreateStreamFromFileDescriptorKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+
+    // EGL_ANDROID_get_frame_timestamps
+    { "eglGetNextFrameIdANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
+    { "eglGetCompositorTimingANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
+    { "eglGetCompositorTimingSupportedANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
+    { "eglGetFrameTimestampsANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+    { "eglGetFrameTimestampSupportedANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
+
+    // EGL_ANDROID_native_fence_sync
+    { "eglDupNativeFenceFDANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
+};
+
+/*
+ * These extensions entry-points should not be exposed to applications.
+ * They're used internally by the Android EGL layer.
+ */
+#define FILTER_EXTENSIONS(procname) \
+        (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") ||    \
+         !strcmp((procname), "eglHibernateProcessIMG")      ||    \
+         !strcmp((procname), "eglAwakenProcessIMG"))
+
+// accesses protected by sExtensionMapMutex
+static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
+
+static int sGLExtentionSlot = 0;
+static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void(*findProcAddress(const char* name,
+        const extention_map_t* map, size_t n))() {
+    for (uint32_t i=0 ; i<n ; i++) {
+        if (!strcmp(name, map[i].name)) {
+            return map[i].address;
+        }
+    }
+    return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
+extern EGLBoolean egl_init_drivers();
+extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
+extern gl_hooks_t gHooksTrace;
+
+// ----------------------------------------------------------------------------
+
+static inline EGLContext getContext() { return egl_tls_t::getContext(); }
+
+// ----------------------------------------------------------------------------
+
+EGLDisplay eglGetDisplayImpl(EGLNativeDisplayType display)
+{
+    uintptr_t index = reinterpret_cast<uintptr_t>(display);
+    if (index >= NUM_DISPLAYS) {
+        return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
+    }
+
+    EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display);
+    return dpy;
+}
+
+// ----------------------------------------------------------------------------
+// Initialization
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor)
+{
+    egl_display_ptr dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+    EGLBoolean res = dp->initialize(major, minor);
+
+    return res;
+}
+
+EGLBoolean eglTerminateImpl(EGLDisplay dpy)
+{
+    // NOTE: don't unload the drivers b/c some APIs can be called
+    // after eglTerminate() has been called. eglTerminate() only
+    // terminates an EGLDisplay, not a EGL itself.
+
+    egl_display_ptr dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+    EGLBoolean res = dp->terminate();
+
+    return res;
+}
+
+// ----------------------------------------------------------------------------
+// configuration
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglGetConfigsImpl(EGLDisplay dpy,
+                             EGLConfig *configs,
+                             EGLint config_size, EGLint *num_config)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    if (num_config==nullptr) {
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+    }
+
+    EGLBoolean res = EGL_FALSE;
+    *num_config = 0;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        res = cnx->egl.eglGetConfigs(
+                dp->disp.dpy, configs, config_size, num_config);
+    }
+
+    return res;
+}
+
+EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list,
+                                EGLConfig *configs, EGLint config_size,
+                                EGLint *num_config)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    if (num_config==nullptr) {
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+    }
+
+    EGLBoolean res = EGL_FALSE;
+    *num_config = 0;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        if (attrib_list) {
+            char value[PROPERTY_VALUE_MAX];
+            property_get("debug.egl.force_msaa", value, "false");
+
+            if (!strcmp(value, "true")) {
+                size_t attribCount = 0;
+                EGLint attrib = attrib_list[0];
+
+                // Only enable MSAA if the context is OpenGL ES 2.0 and
+                // if no caveat is requested
+                const EGLint *attribRendererable = nullptr;
+                const EGLint *attribCaveat = nullptr;
+
+                // Count the number of attributes and look for
+                // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
+                while (attrib != EGL_NONE) {
+                    attrib = attrib_list[attribCount];
+                    switch (attrib) {
+                        case EGL_RENDERABLE_TYPE:
+                            attribRendererable = &attrib_list[attribCount];
+                            break;
+                        case EGL_CONFIG_CAVEAT:
+                            attribCaveat = &attrib_list[attribCount];
+                            break;
+                        default:
+                            break;
+                    }
+                    attribCount++;
+                }
+
+                if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
+                        (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+
+                    // Insert 2 extra attributes to force-enable MSAA 4x
+                    EGLint aaAttribs[attribCount + 4];
+                    aaAttribs[0] = EGL_SAMPLE_BUFFERS;
+                    aaAttribs[1] = 1;
+                    aaAttribs[2] = EGL_SAMPLES;
+                    aaAttribs[3] = 4;
+
+                    memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
+
+                    EGLint numConfigAA;
+                    EGLBoolean resAA = cnx->egl.eglChooseConfig(
+                            dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
+
+                    if (resAA == EGL_TRUE && numConfigAA > 0) {
+                        ALOGD("Enabling MSAA 4x");
+                        *num_config = numConfigAA;
+                        return resAA;
+                    }
+                }
+            }
+        }
+
+        res = cnx->egl.eglChooseConfig(
+                dp->disp.dpy, attrib_list, configs, config_size, num_config);
+    }
+    return res;
+}
+
+EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config,
+        EGLint attribute, EGLint *value)
+{
+    egl_connection_t* cnx = nullptr;
+    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (!dp) return EGL_FALSE;
+
+    return cnx->egl.eglGetConfigAttrib(
+            dp->disp.dpy, config, attribute, value);
+}
+
+// ----------------------------------------------------------------------------
+// surfaces
+// ----------------------------------------------------------------------------
+
+// Translates EGL color spaces to Android data spaces.
+static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) {
+    if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
+        return HAL_DATASPACE_UNKNOWN;
+    } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
+        return HAL_DATASPACE_SRGB;
+    } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
+        return HAL_DATASPACE_DISPLAY_P3;
+    } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) {
+        return HAL_DATASPACE_DISPLAY_P3_LINEAR;
+    } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) {
+        return HAL_DATASPACE_V0_SCRGB;
+    } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
+        return HAL_DATASPACE_V0_SCRGB_LINEAR;
+    } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
+        return HAL_DATASPACE_BT2020_LINEAR;
+    } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
+        return HAL_DATASPACE_BT2020_PQ;
+    }
+    return HAL_DATASPACE_UNKNOWN;
+}
+
+// Get the colorspace value that should be reported from queries. When the colorspace
+// is unknown (no attribute passed), default to reporting LINEAR.
+static EGLint getReportedColorSpace(EGLint colorspace) {
+    return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace;
+}
+
+// Returns a list of color spaces understood by the vendor EGL driver.
+static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp,
+                                                android_pixel_format format) {
+    std::vector<EGLint> colorSpaces;
+    if (!dp->hasColorSpaceSupport) return colorSpaces;
+
+    // OpenGL drivers only support sRGB encoding with 8-bit formats.
+    // RGB_888 is never returned by getNativePixelFormat, but is included for completeness.
+    const bool formatSupportsSRGBEncoding =
+        format == HAL_PIXEL_FORMAT_RGBA_8888 || format == HAL_PIXEL_FORMAT_RGBX_8888 ||
+        format == HAL_PIXEL_FORMAT_RGB_888;
+    const bool formatIsFloatingPoint = format == HAL_PIXEL_FORMAT_RGBA_FP16;
+
+    if (formatSupportsSRGBEncoding) {
+        // sRGB and linear are always supported when color space support is present.
+        colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+        colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
+        // DCI-P3 uses the sRGB transfer function, so it's only relevant for 8-bit formats.
+        if (findExtension(dp->disp.queryString.extensions,
+                              "EGL_EXT_gl_colorspace_display_p3")) {
+            colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
+        }
+    }
+
+    // According to the spec, scRGB is only supported for floating point formats.
+    // For non-linear scRGB, the application is responsible for applying the
+    // transfer function.
+    if (formatIsFloatingPoint) {
+        if (findExtension(dp->disp.queryString.extensions,
+                  "EGL_EXT_gl_colorspace_scrgb")) {
+            colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT);
+        }
+        if (findExtension(dp->disp.queryString.extensions,
+                  "EGL_EXT_gl_colorspace_scrgb_linear")) {
+            colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
+        }
+    }
+
+    // BT2020 can be used with any pixel format. PQ encoding must be applied by the
+    // application and does not affect the behavior of OpenGL.
+    if (findExtension(dp->disp.queryString.extensions,
+                          "EGL_EXT_gl_colorspace_bt2020_linear")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
+    }
+    if (findExtension(dp->disp.queryString.extensions,
+                          "EGL_EXT_gl_colorspace_bt2020_pq")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
+    }
+
+    // Linear DCI-P3 simply uses different primaries than standard RGB and thus
+    // can be used with any pixel format.
+    if (findExtension(dp->disp.queryString.extensions,
+                          "EGL_EXT_gl_colorspace_display_p3_linear")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
+    }
+    return colorSpaces;
+}
+
+// Cleans up color space related parameters that the driver does not understand.
+// If there is no color space attribute in attrib_list, colorSpace is left
+// unmodified.
+static EGLBoolean processAttributes(egl_display_ptr dp, NativeWindowType window,
+                                    android_pixel_format format, const EGLint* attrib_list,
+                                    EGLint* colorSpace,
+                                    std::vector<EGLint>* strippedAttribList) {
+    for (const EGLint* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
+        bool copyAttribute = true;
+        if (attr[0] == EGL_GL_COLORSPACE_KHR) {
+            // Fail immediately if the driver doesn't have color space support at all.
+            if (!dp->hasColorSpaceSupport) return false;
+            *colorSpace = attr[1];
+
+            // Strip the attribute if the driver doesn't understand it.
+            copyAttribute = false;
+            std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp, format);
+            for (auto driverColorSpace : driverColorSpaces) {
+                if (attr[1] == driverColorSpace) {
+                    copyAttribute = true;
+                    break;
+                }
+            }
+
+            // If the driver doesn't understand it, we should map sRGB-encoded P3 to
+            // sRGB rather than just dropping the colorspace on the floor.
+            // For this format, the driver is expected to apply the sRGB
+            // transfer function during framebuffer operations.
+            if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
+                strippedAttribList->push_back(attr[0]);
+                strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+            }
+        }
+        if (copyAttribute) {
+            strippedAttribList->push_back(attr[0]);
+            strippedAttribList->push_back(attr[1]);
+        }
+    }
+    // Terminate the attribute list.
+    strippedAttribList->push_back(EGL_NONE);
+
+    // If the passed color space has wide color gamut, check whether the target native window
+    // supports wide color.
+    const bool colorSpaceIsNarrow =
+        *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR ||
+        *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR ||
+        *colorSpace == EGL_UNKNOWN;
+    if (window && !colorSpaceIsNarrow) {
+        bool windowSupportsWideColor = true;
+        // Ordinarily we'd put a call to native_window_get_wide_color_support
+        // at the beginning of the function so that we'll have the
+        // result when needed elsewhere in the function.
+        // However, because eglCreateWindowSurface is called by SurfaceFlinger and
+        // SurfaceFlinger is required to answer the call below we would
+        // end up in a deadlock situation. By moving the call to only happen
+        // if the application has specifically asked for wide-color we avoid
+        // the deadlock with SurfaceFlinger since it will not ask for a
+        // wide-color surface.
+        int err = native_window_get_wide_color_support(window, &windowSupportsWideColor);
+
+        if (err) {
+            ALOGE("processAttributes: invalid window (win=%p) "
+                  "failed (%#x) (already connected to another API?)",
+                  window, err);
+            return false;
+        }
+        if (!windowSupportsWideColor) {
+            // Application has asked for a wide-color colorspace but
+            // wide-color support isn't available on the display the window is on.
+            return false;
+        }
+    }
+    return true;
+}
+
+// Gets the native pixel format corrsponding to the passed EGLConfig.
+void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
+                          android_pixel_format* format) {
+    // Set the native window's buffers format to match what this config requests.
+    // Whether to use sRGB gamma is not part of the EGLconfig, but is part
+    // of our native format. So if sRGB gamma is requested, we have to
+    // modify the EGLconfig's format before setting the native window's
+    // format.
+
+    EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType);
+
+    EGLint a = 0;
+    EGLint r, g, b;
+    r = g = b = 0;
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
+    EGLint colorDepth = r + g + b;
+
+    // Today, the driver only understands sRGB and linear on 888X
+    // formats. Strip other colorspaces from the attribute list and
+    // only use them to set the dataspace via
+    // native_window_set_buffers_dataspace
+    // if pixel format is RGBX 8888
+    //    TBD: Can test for future extensions that indicate that driver
+    //    handles requested color space and we can let it through.
+    //    allow SRGB and LINEAR. All others need to be stripped.
+    // else if 565, 4444
+    //    TBD: Can we assume these are supported if 8888 is?
+    // else if FP16 or 1010102
+    //    strip colorspace from attribs.
+    // endif
+    if (a == 0) {
+        if (colorDepth <= 16) {
+            *format = HAL_PIXEL_FORMAT_RGB_565;
+        } else {
+            if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+                if (colorDepth > 24) {
+                    *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+                } else {
+                    *format = HAL_PIXEL_FORMAT_RGBX_8888;
+                }
+            } else {
+                *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+            }
+        }
+    } else {
+        if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+            if (colorDepth > 24) {
+                *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+            } else {
+                *format = HAL_PIXEL_FORMAT_RGBA_8888;
+            }
+        } else {
+            *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+        }
+    }
+}
+
+EGLBoolean sendSurfaceMetadata(egl_surface_t* s) {
+    android_smpte2086_metadata smpteMetadata;
+    if (s->getSmpte2086Metadata(smpteMetadata)) {
+        int err =
+                native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata);
+        s->resetSmpte2086Metadata();
+        if (err != 0) {
+            ALOGE("error setting native window smpte2086 metadata: %s (%d)",
+                  strerror(-err), err);
+            return EGL_FALSE;
+        }
+    }
+    android_cta861_3_metadata cta8613Metadata;
+    if (s->getCta8613Metadata(cta8613Metadata)) {
+        int err =
+                native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata);
+        s->resetCta8613Metadata();
+        if (err != 0) {
+            ALOGE("error setting native window CTS 861.3 metadata: %s (%d)",
+                  strerror(-err), err);
+            return EGL_FALSE;
+        }
+    }
+    return EGL_TRUE;
+}
+
+EGLSurface eglCreateWindowSurfaceImpl(  EGLDisplay dpy, EGLConfig config,
+                                        NativeWindowType window,
+                                        const EGLint *attrib_list)
+{
+    const EGLint *origAttribList = attrib_list;
+
+    egl_connection_t* cnx = nullptr;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        if (!window) {
+            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+        }
+
+        int value = 0;
+        window->query(window, NATIVE_WINDOW_IS_VALID, &value);
+        if (!value) {
+            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+        }
+
+        // NOTE: When using Vulkan backend, the Vulkan runtime makes all the
+        // native_window_* calls, so don't do them here.
+        if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+            int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+            if (result < 0) {
+                ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
+                      "failed (%#x) (already connected to another API?)",
+                      window, result);
+                return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
+            }
+        }
+
+        EGLDisplay iDpy = dp->disp.dpy;
+        android_pixel_format format;
+        getNativePixelFormat(iDpy, cnx, config, &format);
+
+        // now select correct colorspace and dataspace based on user's attribute list
+        EGLint colorSpace = EGL_UNKNOWN;
+        std::vector<EGLint> strippedAttribList;
+        if (!processAttributes(dp, window, format, attrib_list, &colorSpace,
+                               &strippedAttribList)) {
+            ALOGE("error invalid colorspace: %d", colorSpace);
+            return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+        }
+        attrib_list = strippedAttribList.data();
+
+        if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+            int err = native_window_set_buffers_format(window, format);
+            if (err != 0) {
+                ALOGE("error setting native window pixel format: %s (%d)",
+                      strerror(-err), err);
+                native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+                return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+            }
+
+            android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace);
+            if (dataSpace != HAL_DATASPACE_UNKNOWN) {
+                err = native_window_set_buffers_data_space(window, dataSpace);
+                if (err != 0) {
+                    ALOGE("error setting native window pixel dataSpace: %s (%d)", strerror(-err),
+                          err);
+                    native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+                    return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+                }
+            }
+        }
+
+        // the EGL spec requires that a new EGLSurface default to swap interval
+        // 1, so explicitly set that on the window here.
+        ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
+        anw->setSwapInterval(anw, 1);
+
+        EGLSurface surface = cnx->egl.eglCreateWindowSurface(
+                iDpy, config, window, attrib_list);
+        if (surface != EGL_NO_SURFACE) {
+            egl_surface_t* s =
+                    new egl_surface_t(dp.get(), config, window, surface,
+                                      getReportedColorSpace(colorSpace), cnx);
+            return s;
+        }
+
+        // EGLSurface creation failed
+        if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+            native_window_set_buffers_format(window, 0);
+            native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePixmapSurfaceImpl(  EGLDisplay dpy, EGLConfig config,
+                                        NativePixmapType pixmap,
+                                        const EGLint *attrib_list)
+{
+    egl_connection_t* cnx = nullptr;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        EGLDisplay iDpy = dp->disp.dpy;
+        android_pixel_format format;
+        getNativePixelFormat(iDpy, cnx, config, &format);
+
+        // now select a corresponding sRGB format if needed
+        EGLint colorSpace = EGL_UNKNOWN;
+        std::vector<EGLint> strippedAttribList;
+        if (!processAttributes(dp, nullptr, format, attrib_list, &colorSpace,
+                               &strippedAttribList)) {
+            ALOGE("error invalid colorspace: %d", colorSpace);
+            return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+        }
+        attrib_list = strippedAttribList.data();
+
+        EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
+                dp->disp.dpy, config, pixmap, attrib_list);
+        if (surface != EGL_NO_SURFACE) {
+            egl_surface_t* s =
+                    new egl_surface_t(dp.get(), config, nullptr, surface,
+                                      getReportedColorSpace(colorSpace), cnx);
+            return s;
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePbufferSurfaceImpl( EGLDisplay dpy, EGLConfig config,
+                                        const EGLint *attrib_list)
+{
+    egl_connection_t* cnx = nullptr;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        EGLDisplay iDpy = dp->disp.dpy;
+        android_pixel_format format;
+        getNativePixelFormat(iDpy, cnx, config, &format);
+
+        // Select correct colorspace based on user's attribute list
+        EGLint colorSpace = EGL_UNKNOWN;
+        std::vector<EGLint> strippedAttribList;
+        if (!processAttributes(dp, nullptr, format, attrib_list, &colorSpace,
+                               &strippedAttribList)) {
+            ALOGE("error invalid colorspace: %d", colorSpace);
+            return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+        }
+        attrib_list = strippedAttribList.data();
+
+        EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
+                dp->disp.dpy, config, attrib_list);
+        if (surface != EGL_NO_SURFACE) {
+            egl_surface_t* s =
+                    new egl_surface_t(dp.get(), config, nullptr, surface,
+                                      getReportedColorSpace(colorSpace), cnx);
+            return s;
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t * const s = get_surface(surface);
+    EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
+    if (result == EGL_TRUE) {
+        _s.terminate();
+    }
+    return result;
+}
+
+EGLBoolean eglQuerySurfaceImpl( EGLDisplay dpy, EGLSurface surface,
+                                EGLint attribute, EGLint *value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->getColorSpaceAttribute(attribute, value)) {
+        return EGL_TRUE;
+    } else if (s->getSmpte2086Attribute(attribute, value)) {
+        return EGL_TRUE;
+    } else if (s->getCta8613Attribute(attribute, value)) {
+        return EGL_TRUE;
+    }
+    return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value);
+}
+
+void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return;
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        setError(EGL_BAD_SURFACE, EGL_FALSE);
+    }
+}
+
+// ----------------------------------------------------------------------------
+// Contexts
+// ----------------------------------------------------------------------------
+
+EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config,
+                                EGLContext share_list, const EGLint *attrib_list)
+{
+    egl_connection_t* cnx = nullptr;
+    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        if (share_list != EGL_NO_CONTEXT) {
+            if (!ContextRef(dp.get(), share_list).get()) {
+                return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
+            }
+            egl_context_t* const c = get_context(share_list);
+            share_list = c->context;
+        }
+        EGLContext context = cnx->egl.eglCreateContext(
+                dp->disp.dpy, config, share_list, attrib_list);
+        if (context != EGL_NO_CONTEXT) {
+            // figure out if it's a GLESv1 or GLESv2
+            int version = 0;
+            if (attrib_list) {
+                while (*attrib_list != EGL_NONE) {
+                    GLint attr = *attrib_list++;
+                    GLint value = *attrib_list++;
+                    if (attr == EGL_CONTEXT_CLIENT_VERSION) {
+                        if (value == 1) {
+                            version = egl_connection_t::GLESv1_INDEX;
+                        } else if (value == 2 || value == 3) {
+                            version = egl_connection_t::GLESv2_INDEX;
+                        }
+                    }
+                };
+            }
+            egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
+                    version);
+            return c;
+        }
+    }
+    return EGL_NO_CONTEXT;
+}
+
+EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp)
+        return EGL_FALSE;
+
+    ContextRef _c(dp.get(), ctx);
+    if (!_c.get())
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+    egl_context_t * const c = get_context(ctx);
+    EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
+    if (result == EGL_TRUE) {
+        _c.terminate();
+    }
+    return result;
+}
+
+EGLBoolean eglMakeCurrentImpl(  EGLDisplay dpy, EGLSurface draw,
+                                EGLSurface read, EGLContext ctx)
+{
+    egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+    // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
+    // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
+    // a valid but uninitialized display.
+    if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
+         (draw != EGL_NO_SURFACE) ) {
+        if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+
+    // get a reference to the object passed in
+    ContextRef _c(dp.get(), ctx);
+    SurfaceRef _d(dp.get(), draw);
+    SurfaceRef _r(dp.get(), read);
+
+    // validate the context (if not EGL_NO_CONTEXT)
+    if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
+        // EGL_NO_CONTEXT is valid
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+    }
+
+    // these are the underlying implementation's object
+    EGLContext impl_ctx  = EGL_NO_CONTEXT;
+    EGLSurface impl_draw = EGL_NO_SURFACE;
+    EGLSurface impl_read = EGL_NO_SURFACE;
+
+    // these are our objects structs passed in
+    egl_context_t       * c = nullptr;
+    egl_surface_t const * d = nullptr;
+    egl_surface_t const * r = nullptr;
+
+    // these are the current objects structs
+    egl_context_t * cur_c = get_context(getContext());
+
+    if (ctx != EGL_NO_CONTEXT) {
+        c = get_context(ctx);
+        impl_ctx = c->context;
+    } else {
+        // no context given, use the implementation of the current context
+        if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
+            // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
+            return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE);
+        }
+        if (cur_c == nullptr) {
+            // no current context
+            // not an error, there is just no current context.
+            return EGL_TRUE;
+        }
+    }
+
+    // retrieve the underlying implementation's draw EGLSurface
+    if (draw != EGL_NO_SURFACE) {
+        if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        d = get_surface(draw);
+        impl_draw = d->surface;
+    }
+
+    // retrieve the underlying implementation's read EGLSurface
+    if (read != EGL_NO_SURFACE) {
+        if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        r = get_surface(read);
+        impl_read = r->surface;
+    }
+
+
+    EGLBoolean result = dp->makeCurrent(c, cur_c,
+            draw, read, ctx,
+            impl_draw, impl_read, impl_ctx);
+
+    if (result == EGL_TRUE) {
+        if (c) {
+            setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
+            egl_tls_t::setContext(ctx);
+            _c.acquire();
+            _r.acquire();
+            _d.acquire();
+        } else {
+            setGLHooksThreadSpecific(&gHooksNoContext);
+            egl_tls_t::setContext(EGL_NO_CONTEXT);
+        }
+    } else {
+        // this will ALOGE the error
+        egl_connection_t* const cnx = &gEGLImpl;
+        result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE);
+    }
+    return result;
+}
+
+EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx,
+                                EGLint attribute, EGLint *value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    ContextRef _c(dp.get(), ctx);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+    egl_context_t * const c = get_context(ctx);
+    return c->cnx->egl.eglQueryContext(
+            dp->disp.dpy, c->context, attribute, value);
+
+}
+
+EGLContext eglGetCurrentContextImpl(void)
+{
+    // could be called before eglInitialize(), but we wouldn't have a context
+    // then, and this function would correctly return EGL_NO_CONTEXT.
+    EGLContext ctx = getContext();
+    return ctx;
+}
+
+EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw)
+{
+    // could be called before eglInitialize(), but we wouldn't have a context
+    // then, and this function would correctly return EGL_NO_SURFACE.
+
+    EGLContext ctx = getContext();
+    if (ctx) {
+        egl_context_t const * const c = get_context(ctx);
+        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
+        switch (readdraw) {
+            case EGL_READ: return c->read;
+            case EGL_DRAW: return c->draw;
+            default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLDisplay eglGetCurrentDisplayImpl(void)
+{
+    // could be called before eglInitialize(), but we wouldn't have a context
+    // then, and this function would correctly return EGL_NO_DISPLAY.
+
+    EGLContext ctx = getContext();
+    if (ctx) {
+        egl_context_t const * const c = get_context(ctx);
+        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
+        return c->dpy;
+    }
+    return EGL_NO_DISPLAY;
+}
+
+EGLBoolean eglWaitGLImpl(void)
+{
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+    return cnx->egl.eglWaitGL();
+}
+
+EGLBoolean eglWaitNativeImpl(EGLint engine)
+{
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+    return cnx->egl.eglWaitNative(engine);
+}
+
+EGLint eglGetErrorImpl(void)
+{
+    EGLint err = EGL_SUCCESS;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        err = cnx->egl.eglGetError();
+    }
+    if (err == EGL_SUCCESS) {
+        err = egl_tls_t::getError();
+    }
+    return err;
+}
+
+static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
+        const char* procname) {
+    const egl_connection_t* cnx = &gEGLImpl;
+    void* proc = nullptr;
+
+    proc = dlsym(cnx->libEgl, procname);
+    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+    proc = dlsym(cnx->libGles2, procname);
+    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+    proc = dlsym(cnx->libGles1, procname);
+    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+    return nullptr;
+}
+
+__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname)
+{
+    if (FILTER_EXTENSIONS(procname)) {
+        return nullptr;
+    }
+
+    __eglMustCastToProperFunctionPointerType addr;
+    addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap));
+    if (addr) return addr;
+
+    addr = findBuiltinWrapper(procname);
+    if (addr) return addr;
+
+    // this protects accesses to sGLExtentionMap and sGLExtentionSlot
+    pthread_mutex_lock(&sExtensionMapMutex);
+
+    /*
+     * Since eglGetProcAddress() is not associated to anything, it needs
+     * to return a function pointer that "works" regardless of what
+     * the current context is.
+     *
+     * For this reason, we return a "forwarder", a small stub that takes
+     * care of calling the function associated with the context
+     * currently bound.
+     *
+     * We first look for extensions we've already resolved, if we're seeing
+     * this extension for the first time, we go through all our
+     * implementations and call eglGetProcAddress() and record the
+     * result in the appropriate implementation hooks and return the
+     * address of the forwarder corresponding to that hook set.
+     *
+     */
+
+    const std::string name(procname);
+
+    auto& extentionMap = sGLExtentionMap;
+    auto pos = extentionMap.find(name);
+    addr = (pos != extentionMap.end()) ? pos->second : nullptr;
+    const int slot = sGLExtentionSlot;
+
+    ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
+             "no more slots for eglGetProcAddress(\"%s\")",
+             procname);
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    LayerLoader& layer_loader(LayerLoader::getInstance());
+
+    if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
+
+        if (cnx->dso && cnx->egl.eglGetProcAddress) {
+
+            // Extensions are independent of the bound context
+            addr = cnx->egl.eglGetProcAddress(procname);
+            if (addr) {
+
+                // purposefully track the bottom of the stack in extensionMap
+                extentionMap[name] = addr;
+
+                // Apply layers
+                addr = layer_loader.ApplyLayers(procname, addr);
+
+                // Track the top most entry point
+                cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+                cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+                addr = gExtensionForwarders[slot];
+                sGLExtentionSlot++;
+            }
+        }
+
+    } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
+
+        // We've seen this func before, but we tracked the bottom, so re-apply layers
+        // More layers might have been enabled
+        addr = layer_loader.ApplyLayers(procname, addr);
+
+        // Track the top most entry point
+        cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+        cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+        addr = gExtensionForwarders[slot];
+    }
+
+    pthread_mutex_unlock(&sExtensionMapMutex);
+    return addr;
+}
+
+class FrameCompletionThread {
+public:
+
+    static void queueSync(EGLSyncKHR sync) {
+        static FrameCompletionThread thread;
+
+        char name[64];
+
+        std::lock_guard<std::mutex> lock(thread.mMutex);
+        snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued);
+        ATRACE_NAME(name);
+
+        thread.mQueue.push_back(sync);
+        thread.mCondition.notify_one();
+        thread.mFramesQueued++;
+        ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size()));
+    }
+
+private:
+
+    FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
+        std::thread thread(&FrameCompletionThread::loop, this);
+        thread.detach();
+    }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+    void loop() {
+        while (true) {
+            threadLoop();
+        }
+    }
+#pragma clang diagnostic pop
+
+    void threadLoop() {
+        EGLSyncKHR sync;
+        uint32_t frameNum;
+        {
+            std::unique_lock<std::mutex> lock(mMutex);
+            while (mQueue.empty()) {
+                mCondition.wait(lock);
+            }
+            sync = mQueue[0];
+            frameNum = mFramesCompleted;
+        }
+        EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        {
+            char name[64];
+            snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum);
+            ATRACE_NAME(name);
+
+            EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
+            if (result == EGL_FALSE) {
+                ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
+            } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+                ALOGE("FrameCompletion: timeout waiting for fence");
+            }
+            eglDestroySyncKHR(dpy, sync);
+        }
+        {
+            std::lock_guard<std::mutex> lock(mMutex);
+            mQueue.pop_front();
+            mFramesCompleted++;
+            ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size()));
+        }
+    }
+
+    uint32_t mFramesQueued;
+    uint32_t mFramesCompleted;
+    std::deque<EGLSyncKHR> mQueue;
+    std::condition_variable mCondition;
+    std::mutex mMutex;
+};
+
+EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw,
+        EGLint *rects, EGLint n_rects)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), draw);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t* const s = get_surface(draw);
+
+    if (CC_UNLIKELY(dp->traceGpuCompletion)) {
+        EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
+        if (sync != EGL_NO_SYNC_KHR) {
+            FrameCompletionThread::queueSync(sync);
+        }
+    }
+
+    if (CC_UNLIKELY(dp->finishOnSwap)) {
+        uint32_t pixel;
+        egl_context_t * const c = get_context( egl_tls_t::getContext() );
+        if (c) {
+            // glReadPixels() ensures that the frame is complete
+            s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
+                    GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
+        }
+    }
+
+    if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+        if (!sendSurfaceMetadata(s)) {
+            native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL);
+            return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE);
+        }
+    }
+
+    if (n_rects == 0) {
+        return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+    }
+
+    std::vector<android_native_rect_t> androidRects((size_t)n_rects);
+    for (int r = 0; r < n_rects; ++r) {
+        int offset = r * 4;
+        int x = rects[offset];
+        int y = rects[offset + 1];
+        int width = rects[offset + 2];
+        int height = rects[offset + 3];
+        android_native_rect_t androidRect;
+        androidRect.left = x;
+        androidRect.top = y + height;
+        androidRect.right = x + width;
+        androidRect.bottom = y;
+        androidRects.push_back(androidRect);
+    }
+    if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+        native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(),
+                                         androidRects.size());
+    }
+
+    if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
+        return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
+                rects, n_rects);
+    } else {
+        return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+    }
+}
+
+EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface)
+{
+    return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0);
+}
+
+EGLBoolean eglCopyBuffersImpl(  EGLDisplay dpy, EGLSurface surface,
+                                NativePixmapType target)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const * const s = get_surface(surface);
+    return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
+}
+
+const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name)
+{
+    // Generate an error quietly when client extensions (as defined by
+    // EGL_EXT_client_extensions) are queried.  We do not want to rely on
+    // validate_display to generate the error as validate_display would log
+    // the error, which can be misleading.
+    //
+    // If we want to support EGL_EXT_client_extensions later, we can return
+    // the client extension string here instead.
+    if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS)
+        return setErrorQuiet(EGL_BAD_DISPLAY, (const char*)nullptr);
+
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return (const char *) nullptr;
+
+    switch (name) {
+        case EGL_VENDOR:
+            return dp->getVendorString();
+        case EGL_VERSION:
+            return dp->getVersionString();
+        case EGL_EXTENSIONS:
+            return dp->getExtensionString();
+        case EGL_CLIENT_APIS:
+            return dp->getClientApiString();
+        default:
+            break;
+    }
+    return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+}
+
+EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return (const char *) nullptr;
+
+    switch (name) {
+        case EGL_VENDOR:
+            return dp->disp.queryString.vendor;
+        case EGL_VERSION:
+            return dp->disp.queryString.version;
+        case EGL_EXTENSIONS:
+            return dp->disp.queryString.extensions;
+        case EGL_CLIENT_APIS:
+            return dp->disp.queryString.clientApi;
+        default:
+            break;
+    }
+    return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+}
+
+// ----------------------------------------------------------------------------
+// EGL 1.1
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglSurfaceAttribImpl(
+        EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t * const s = get_surface(surface);
+
+    if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
+        if (!s->getNativeWindow()) {
+            setError(EGL_BAD_SURFACE, EGL_FALSE);
+        }
+        int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
+        return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    if (attribute == EGL_TIMESTAMPS_ANDROID) {
+        if (!s->getNativeWindow()) {
+            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        }
+        int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
+        return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    if (s->setSmpte2086Attribute(attribute, value)) {
+        return EGL_TRUE;
+    } else if (s->setCta8613Attribute(attribute, value)) {
+        return EGL_TRUE;
+    } else if (s->cnx->egl.eglSurfaceAttrib) {
+        return s->cnx->egl.eglSurfaceAttrib(
+                dp->disp.dpy, s->surface, attribute, value);
+    }
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglBindTexImageImpl(
+        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->egl.eglBindTexImage) {
+        return s->cnx->egl.eglBindTexImage(
+                dp->disp.dpy, s->surface, buffer);
+    }
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglReleaseTexImageImpl(
+        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->egl.eglReleaseTexImage) {
+        return s->cnx->egl.eglReleaseTexImage(
+                dp->disp.dpy, s->surface, buffer);
+    }
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean res = EGL_TRUE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglSwapInterval) {
+        res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval);
+    }
+
+    return res;
+}
+
+
+// ----------------------------------------------------------------------------
+// EGL 1.2
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglWaitClientImpl(void)
+{
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+    EGLBoolean res;
+    if (cnx->egl.eglWaitClient) {
+        res = cnx->egl.eglWaitClient();
+    } else {
+        res = cnx->egl.eglWaitGL();
+    }
+    return res;
+}
+
+EGLBoolean eglBindAPIImpl(EGLenum api)
+{
+    // bind this API on all EGLs
+    EGLBoolean res = EGL_TRUE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglBindAPI) {
+        res = cnx->egl.eglBindAPI(api);
+    }
+    return res;
+}
+
+EGLenum eglQueryAPIImpl(void)
+{
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglQueryAPI) {
+        return cnx->egl.eglQueryAPI();
+    }
+
+    // or, it can only be OpenGL ES
+    return EGL_OPENGL_ES_API;
+}
+
+EGLBoolean eglReleaseThreadImpl(void)
+{
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglReleaseThread) {
+        cnx->egl.eglReleaseThread();
+    }
+
+    // If there is context bound to the thread, release it
+    egl_display_t::loseCurrent(get_context(getContext()));
+
+    egl_tls_t::clearTLS();
+    return EGL_TRUE;
+}
+
+EGLSurface eglCreatePbufferFromClientBufferImpl(
+          EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
+          EGLConfig config, const EGLint *attrib_list)
+{
+    egl_connection_t* cnx = nullptr;
+    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (!dp) return EGL_FALSE;
+    if (cnx->egl.eglCreatePbufferFromClientBuffer) {
+        return cnx->egl.eglCreatePbufferFromClientBuffer(
+                dp->disp.dpy, buftype, buffer, config, attrib_list);
+    }
+    return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 3
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface,
+        const EGLint *attrib_list)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->egl.eglLockSurfaceKHR) {
+        return s->cnx->egl.eglLockSurfaceKHR(
+                dp->disp.dpy, s->surface, attrib_list);
+    }
+    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->egl.eglUnlockSurfaceKHR) {
+        return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
+    }
+    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+}
+
+EGLImageKHR eglCreateImageKHRImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+        EGLClientBuffer buffer, const EGLint *attrib_list)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_IMAGE_KHR;
+
+    ContextRef _c(dp.get(), ctx);
+    egl_context_t * const c = _c.get();
+
+    EGLImageKHR result = EGL_NO_IMAGE_KHR;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateImageKHR) {
+        result = cnx->egl.eglCreateImageKHR(
+                dp->disp.dpy,
+                c ? c->context : EGL_NO_CONTEXT,
+                target, buffer, attrib_list);
+    }
+    return result;
+}
+
+EGLBoolean eglDestroyImageKHRImpl(EGLDisplay dpy, EGLImageKHR img)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglDestroyImageKHR) {
+        result = cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img);
+    }
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 5
+// ----------------------------------------------------------------------------
+
+
+EGLSyncKHR eglCreateSyncKHRImpl(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_SYNC_KHR;
+
+    EGLSyncKHR result = EGL_NO_SYNC_KHR;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateSyncKHR) {
+        result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list);
+    }
+    return result;
+}
+
+EGLBoolean eglDestroySyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglDestroySyncKHR) {
+        result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync);
+    }
+    return result;
+}
+
+EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglSignalSyncKHR) {
+        result = cnx->egl.eglSignalSyncKHR(
+                dp->disp.dpy, sync, mode);
+    }
+    return result;
+}
+
+EGLint eglClientWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync,
+        EGLint flags, EGLTimeKHR timeout)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLint result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) {
+        result = cnx->egl.eglClientWaitSyncKHR(
+                dp->disp.dpy, sync, flags, timeout);
+    }
+    return result;
+}
+
+EGLBoolean eglGetSyncAttribKHRImpl(EGLDisplay dpy, EGLSyncKHR sync,
+        EGLint attribute, EGLint *value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) {
+        result = cnx->egl.eglGetSyncAttribKHR(
+                dp->disp.dpy, sync, attribute, value);
+    }
+    return result;
+}
+
+EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_STREAM_KHR;
+
+    EGLStreamKHR result = EGL_NO_STREAM_KHR;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
+        result = cnx->egl.eglCreateStreamKHR(
+                dp->disp.dpy, attrib_list);
+    }
+    return result;
+}
+
+EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
+        result = cnx->egl.eglDestroyStreamKHR(
+                dp->disp.dpy, stream);
+    }
+    return result;
+}
+
+EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+        EGLenum attribute, EGLint value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
+        result = cnx->egl.eglStreamAttribKHR(
+                dp->disp.dpy, stream, attribute, value);
+    }
+    return result;
+}
+
+EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+        EGLenum attribute, EGLint *value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
+        result = cnx->egl.eglQueryStreamKHR(
+                dp->disp.dpy, stream, attribute, value);
+    }
+    return result;
+}
+
+EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+        EGLenum attribute, EGLuint64KHR *value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
+        result = cnx->egl.eglQueryStreamu64KHR(
+                dp->disp.dpy, stream, attribute, value);
+    }
+    return result;
+}
+
+EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+        EGLenum attribute, EGLTimeKHR *value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
+        result = cnx->egl.eglQueryStreamTimeKHR(
+                dp->disp.dpy, stream, attribute, value);
+    }
+    return result;
+}
+
+EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config,
+        EGLStreamKHR stream, const EGLint *attrib_list)
+{
+    egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_SURFACE;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
+        EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
+                dp->disp.dpy, config, stream, attrib_list);
+        if (surface != EGL_NO_SURFACE) {
+            egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
+                                                 EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
+            return s;
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy,
+        EGLStreamKHR stream)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
+        result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
+                dp->disp.dpy, stream);
+    }
+    return result;
+}
+
+EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy,
+        EGLStreamKHR stream)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
+        result = cnx->egl.eglStreamConsumerAcquireKHR(
+                dp->disp.dpy, stream);
+    }
+    return result;
+}
+
+EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy,
+        EGLStreamKHR stream)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
+        result = cnx->egl.eglStreamConsumerReleaseKHR(
+                dp->disp.dpy, stream);
+    }
+    return result;
+}
+
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(
+        EGLDisplay dpy, EGLStreamKHR stream)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
+
+    EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
+        result = cnx->egl.eglGetStreamFileDescriptorKHR(
+                dp->disp.dpy, stream);
+    }
+    return result;
+}
+
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(
+        EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_STREAM_KHR;
+
+    EGLStreamKHR result = EGL_NO_STREAM_KHR;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
+        result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
+                dp->disp.dpy, file_descriptor);
+    }
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 15
+// ----------------------------------------------------------------------------
+
+EGLint eglWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+    EGLint result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglWaitSyncKHR) {
+        result = cnx->egl.eglWaitSyncKHR(dp->disp.dpy, sync, flags);
+    }
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// ANDROID extensions
+// ----------------------------------------------------------------------------
+
+EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
+
+    EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) {
+        result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync);
+    }
+    return result;
+}
+
+EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+        EGLnsecsANDROID time)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return EGL_FALSE;
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+    native_window_set_buffers_timestamp(s->getNativeWindow(), time);
+
+    return EGL_TRUE;
+}
+
+EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) {
+    // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
+    // this function cannot be implemented when this libEGL is built for
+    // vendors.
+#ifndef __ANDROID_VNDK__
+    if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
+    return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
+#else
+    return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// NVIDIA extensions
+// ----------------------------------------------------------------------------
+EGLuint64NV eglGetSystemTimeFrequencyNVImpl()
+{
+    EGLuint64NV ret = 0;
+    egl_connection_t* const cnx = &gEGLImpl;
+
+    if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) {
+        return cnx->egl.eglGetSystemTimeFrequencyNV();
+    }
+
+    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+}
+
+EGLuint64NV eglGetSystemTimeNVImpl()
+{
+    EGLuint64NV ret = 0;
+    egl_connection_t* const cnx = &gEGLImpl;
+
+    if (cnx->dso && cnx->egl.eglGetSystemTimeNV) {
+        return cnx->egl.eglGetSystemTimeNV();
+    }
+
+    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+}
+
+// ----------------------------------------------------------------------------
+// Partial update extension
+// ----------------------------------------------------------------------------
+EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface,
+        EGLint *rects, EGLint n_rects)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        setError(EGL_BAD_DISPLAY, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->egl.eglSetDamageRegionKHR) {
+        return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
+                rects, n_rects);
+    }
+
+    return EGL_FALSE;
+}
+
+EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+            EGLuint64KHR *frameId) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    if (!s->getNativeWindow()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    uint64_t nextFrameId = 0;
+    int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId);
+
+    if (ret != 0) {
+        // This should not happen. Return an error that is not in the spec
+        // so it's obvious something is very wrong.
+        ALOGE("eglGetNextFrameId: Unexpected error.");
+        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+
+    *frameId = nextFrameId;
+    return EGL_TRUE;
+}
+
+EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+        EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    if (!s->getNativeWindow()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    nsecs_t* compositeDeadline = nullptr;
+    nsecs_t* compositeInterval = nullptr;
+    nsecs_t* compositeToPresentLatency = nullptr;
+
+    for (int i = 0; i < numTimestamps; i++) {
+        switch (names[i]) {
+            case EGL_COMPOSITE_DEADLINE_ANDROID:
+                compositeDeadline = &values[i];
+                break;
+            case EGL_COMPOSITE_INTERVAL_ANDROID:
+                compositeInterval = &values[i];
+                break;
+            case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+                compositeToPresentLatency = &values[i];
+                break;
+            default:
+                return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+        }
+    }
+
+    int ret = native_window_get_compositor_timing(s->getNativeWindow(),
+            compositeDeadline, compositeInterval, compositeToPresentLatency);
+
+    switch (ret) {
+      case 0:
+        return EGL_TRUE;
+      case -ENOSYS:
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+      default:
+        // This should not happen. Return an error that is not in the spec
+        // so it's obvious something is very wrong.
+        ALOGE("eglGetCompositorTiming: Unexpected error.");
+        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+}
+
+EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(
+        EGLDisplay dpy, EGLSurface surface, EGLint name)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    ANativeWindow* window = s->getNativeWindow();
+    if (!window) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    switch (name) {
+        case EGL_COMPOSITE_DEADLINE_ANDROID:
+        case EGL_COMPOSITE_INTERVAL_ANDROID:
+        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+            return EGL_TRUE;
+        default:
+            return EGL_FALSE;
+    }
+}
+
+EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+        EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
+        EGLnsecsANDROID *values)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    if (!s->getNativeWindow()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    nsecs_t* requestedPresentTime = nullptr;
+    nsecs_t* acquireTime = nullptr;
+    nsecs_t* latchTime = nullptr;
+    nsecs_t* firstRefreshStartTime = nullptr;
+    nsecs_t* gpuCompositionDoneTime = nullptr;
+    nsecs_t* lastRefreshStartTime = nullptr;
+    nsecs_t* displayPresentTime = nullptr;
+    nsecs_t* dequeueReadyTime = nullptr;
+    nsecs_t* releaseTime = nullptr;
+
+    for (int i = 0; i < numTimestamps; i++) {
+        switch (timestamps[i]) {
+            case EGL_REQUESTED_PRESENT_TIME_ANDROID:
+                requestedPresentTime = &values[i];
+                break;
+            case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+                acquireTime = &values[i];
+                break;
+            case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+                latchTime = &values[i];
+                break;
+            case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+                firstRefreshStartTime = &values[i];
+                break;
+            case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+                lastRefreshStartTime = &values[i];
+                break;
+            case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
+                gpuCompositionDoneTime = &values[i];
+                break;
+            case EGL_DISPLAY_PRESENT_TIME_ANDROID:
+                displayPresentTime = &values[i];
+                break;
+            case EGL_DEQUEUE_READY_TIME_ANDROID:
+                dequeueReadyTime = &values[i];
+                break;
+            case EGL_READS_DONE_TIME_ANDROID:
+                releaseTime = &values[i];
+                break;
+            default:
+                return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+        }
+    }
+
+    int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
+            requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
+            lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
+            dequeueReadyTime, releaseTime);
+
+    switch (ret) {
+        case 0:
+            return EGL_TRUE;
+        case -ENOENT:
+            return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
+        case -ENOSYS:
+            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        case -EINVAL:
+            return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+        default:
+            // This should not happen. Return an error that is not in the spec
+            // so it's obvious something is very wrong.
+            ALOGE("eglGetFrameTimestamps: Unexpected error.");
+            return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+}
+
+EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(
+        EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    ANativeWindow* window = s->getNativeWindow();
+    if (!window) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    switch (timestamp) {
+        case EGL_COMPOSITE_DEADLINE_ANDROID:
+        case EGL_COMPOSITE_INTERVAL_ANDROID:
+        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+        case EGL_REQUESTED_PRESENT_TIME_ANDROID:
+        case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+        case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+        case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+        case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+        case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
+        case EGL_DEQUEUE_READY_TIME_ANDROID:
+        case EGL_READS_DONE_TIME_ANDROID:
+            return EGL_TRUE;
+        case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
+            int value = 0;
+            window->query(window,
+                    NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+            return value == 0 ? EGL_FALSE : EGL_TRUE;
+        }
+        default:
+            return EGL_FALSE;
+    }
+}
+
+const GLubyte * glGetStringImpl(GLenum name) {
+    const GLubyte * ret = egl_get_string_for_current_context(name);
+    if (ret == NULL) {
+        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+        if(_c) ret = _c->glGetString(name);
+    }
+    return ret;
+}
+
+const GLubyte * glGetStringiImpl(GLenum name, GLuint index) {
+    const GLubyte * ret = egl_get_string_for_current_context(name, index);
+    if (ret == NULL) {
+        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+        if(_c) ret = _c->glGetStringi(name, index);
+    }
+    return ret;
+}
+
+void glGetBooleanvImpl(GLenum pname, GLboolean * data) {
+    if (pname == GL_NUM_EXTENSIONS) {
+        int num_exts = egl_get_num_extensions_for_current_context();
+        if (num_exts >= 0) {
+            *data = num_exts > 0 ? GL_TRUE : GL_FALSE;
+            return;
+        }
+    }
+
+    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    if (_c) _c->glGetBooleanv(pname, data);
+}
+
+void glGetFloatvImpl(GLenum pname, GLfloat * data) {
+    if (pname == GL_NUM_EXTENSIONS) {
+        int num_exts = egl_get_num_extensions_for_current_context();
+        if (num_exts >= 0) {
+            *data = (GLfloat)num_exts;
+            return;
+        }
+    }
+
+    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    if (_c) _c->glGetFloatv(pname, data);
+}
+
+void glGetIntegervImpl(GLenum pname, GLint * data) {
+    if (pname == GL_NUM_EXTENSIONS) {
+        int num_exts = egl_get_num_extensions_for_current_context();
+        if (num_exts >= 0) {
+            *data = (GLint)num_exts;
+            return;
+        }
+    }
+
+    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    if (_c) _c->glGetIntegerv(pname, data);
+}
+
+void glGetInteger64vImpl(GLenum pname, GLint64 * data) {
+    if (pname == GL_NUM_EXTENSIONS) {
+        int num_exts = egl_get_num_extensions_for_current_context();
+        if (num_exts >= 0) {
+            *data = (GLint64)num_exts;
+            return;
+        }
+    }
+
+    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    if (_c) _c->glGetInteger64v(pname, data);
+}
+
+struct implementation_map_t {
+    const char* name;
+    EGLFuncPointer address;
+};
+
+static const implementation_map_t sPlatformImplMap[] = {
+    { "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl },
+    { "eglInitialize", (EGLFuncPointer)&eglInitializeImpl },
+    { "eglTerminate", (EGLFuncPointer)&eglTerminateImpl },
+    { "eglGetConfigs", (EGLFuncPointer)&eglGetConfigsImpl },
+    { "eglChooseConfig", (EGLFuncPointer)&eglChooseConfigImpl },
+    { "eglGetConfigAttrib", (EGLFuncPointer)&eglGetConfigAttribImpl },
+    { "eglCreateWindowSurface", (EGLFuncPointer)&eglCreateWindowSurfaceImpl },
+    { "eglCreatePixmapSurface", (EGLFuncPointer)&eglCreatePixmapSurfaceImpl },
+    { "eglCreatePbufferSurface", (EGLFuncPointer)&eglCreatePbufferSurfaceImpl },
+    { "eglDestroySurface", (EGLFuncPointer)&eglDestroySurfaceImpl },
+    { "eglQuerySurface", (EGLFuncPointer)&eglQuerySurfaceImpl },
+    { "eglBeginFrame", (EGLFuncPointer)&eglBeginFrameImpl },
+    { "eglCreateContext", (EGLFuncPointer)&eglCreateContextImpl },
+    { "eglDestroyContext", (EGLFuncPointer)&eglDestroyContextImpl },
+    { "eglMakeCurrent", (EGLFuncPointer)&eglMakeCurrentImpl },
+    { "eglQueryContext", (EGLFuncPointer)&eglQueryContextImpl },
+    { "eglGetCurrentContext", (EGLFuncPointer)&eglGetCurrentContextImpl },
+    { "eglGetCurrentSurface", (EGLFuncPointer)&eglGetCurrentSurfaceImpl },
+    { "eglGetCurrentDisplay", (EGLFuncPointer)&eglGetCurrentDisplayImpl },
+    { "eglWaitGL", (EGLFuncPointer)&eglWaitGLImpl },
+    { "eglWaitNative", (EGLFuncPointer)&eglWaitNativeImpl },
+    { "eglGetError", (EGLFuncPointer)&eglGetErrorImpl },
+    { "eglSwapBuffersWithDamageKHR", (EGLFuncPointer)&eglSwapBuffersWithDamageKHRImpl },
+    { "eglGetProcAddress", (EGLFuncPointer)&eglGetProcAddressImpl },
+    { "eglSwapBuffers", (EGLFuncPointer)&eglSwapBuffersImpl },
+    { "eglCopyBuffers", (EGLFuncPointer)&eglCopyBuffersImpl },
+    { "eglQueryString", (EGLFuncPointer)&eglQueryStringImpl },
+    { "eglQueryStringImplementationANDROID", (EGLFuncPointer)&eglQueryStringImplementationANDROIDImpl },
+    { "eglSurfaceAttrib", (EGLFuncPointer)&eglSurfaceAttribImpl },
+    { "eglBindTexImage", (EGLFuncPointer)&eglBindTexImageImpl },
+    { "eglReleaseTexImage", (EGLFuncPointer)&eglReleaseTexImageImpl },
+    { "eglSwapInterval", (EGLFuncPointer)&eglSwapIntervalImpl },
+    { "eglWaitClient", (EGLFuncPointer)&eglWaitClientImpl },
+    { "eglBindAPI", (EGLFuncPointer)&eglBindAPIImpl },
+    { "eglQueryAPI", (EGLFuncPointer)&eglQueryAPIImpl },
+    { "eglReleaseThread", (EGLFuncPointer)&eglReleaseThreadImpl },
+    { "eglCreatePbufferFromClientBuffer", (EGLFuncPointer)&eglCreatePbufferFromClientBufferImpl },
+    { "eglLockSurfaceKHR", (EGLFuncPointer)&eglLockSurfaceKHRImpl },
+    { "eglUnlockSurfaceKHR", (EGLFuncPointer)&eglUnlockSurfaceKHRImpl },
+    { "eglCreateImageKHR", (EGLFuncPointer)&eglCreateImageKHRImpl },
+    { "eglDestroyImageKHR", (EGLFuncPointer)&eglDestroyImageKHRImpl },
+    { "eglCreateSyncKHR", (EGLFuncPointer)&eglCreateSyncKHRImpl },
+    { "eglDestroySyncKHR", (EGLFuncPointer)&eglDestroySyncKHRImpl },
+    { "eglSignalSyncKHR", (EGLFuncPointer)&eglSignalSyncKHRImpl },
+    { "eglClientWaitSyncKHR", (EGLFuncPointer)&eglClientWaitSyncKHRImpl },
+    { "eglGetSyncAttribKHR", (EGLFuncPointer)&eglGetSyncAttribKHRImpl },
+    { "eglCreateStreamKHR", (EGLFuncPointer)&eglCreateStreamKHRImpl },
+    { "eglDestroyStreamKHR", (EGLFuncPointer)&eglDestroyStreamKHRImpl },
+    { "eglStreamAttribKHR", (EGLFuncPointer)&eglStreamAttribKHRImpl },
+    { "eglQueryStreamKHR", (EGLFuncPointer)&eglQueryStreamKHRImpl },
+    { "eglQueryStreamu64KHR", (EGLFuncPointer)&eglQueryStreamu64KHRImpl },
+    { "eglQueryStreamTimeKHR", (EGLFuncPointer)&eglQueryStreamTimeKHRImpl },
+    { "eglCreateStreamProducerSurfaceKHR", (EGLFuncPointer)&eglCreateStreamProducerSurfaceKHRImpl },
+    { "eglStreamConsumerGLTextureExternalKHR", (EGLFuncPointer)&eglStreamConsumerGLTextureExternalKHRImpl },
+    { "eglStreamConsumerAcquireKHR", (EGLFuncPointer)&eglStreamConsumerAcquireKHRImpl },
+    { "eglStreamConsumerReleaseKHR", (EGLFuncPointer)&eglStreamConsumerReleaseKHRImpl },
+    { "eglGetStreamFileDescriptorKHR", (EGLFuncPointer)&eglGetStreamFileDescriptorKHRImpl },
+    { "eglCreateStreamFromFileDescriptorKHR", (EGLFuncPointer)&eglCreateStreamFromFileDescriptorKHRImpl },
+    { "eglWaitSyncKHR", (EGLFuncPointer)&eglWaitSyncKHRImpl },
+    { "eglDupNativeFenceFDANDROID", (EGLFuncPointer)&eglDupNativeFenceFDANDROIDImpl },
+    { "eglPresentationTimeANDROID", (EGLFuncPointer)&eglPresentationTimeANDROIDImpl },
+    { "eglGetNativeClientBufferANDROID", (EGLFuncPointer)&eglGetNativeClientBufferANDROIDImpl },
+    { "eglGetSystemTimeFrequencyNV", (EGLFuncPointer)&eglGetSystemTimeFrequencyNVImpl },
+    { "eglGetSystemTimeNV", (EGLFuncPointer)&eglGetSystemTimeNVImpl },
+    { "eglSetDamageRegionKHR", (EGLFuncPointer)&eglSetDamageRegionKHRImpl },
+    { "eglGetNextFrameIdANDROID", (EGLFuncPointer)&eglGetNextFrameIdANDROIDImpl },
+    { "eglGetCompositorTimingANDROID", (EGLFuncPointer)&eglGetCompositorTimingANDROIDImpl },
+    { "eglGetCompositorTimingSupportedANDROID", (EGLFuncPointer)&eglGetCompositorTimingSupportedANDROIDImpl },
+    { "eglGetFrameTimestampsANDROID", (EGLFuncPointer)&eglGetFrameTimestampsANDROIDImpl },
+    { "eglGetFrameTimestampSupportedANDROID", (EGLFuncPointer)&eglGetFrameTimestampSupportedANDROIDImpl },
+    { "glGetString", (EGLFuncPointer)&glGetStringImpl },
+    { "glGetStringi", (EGLFuncPointer)&glGetStringiImpl },
+    { "glGetBooleanv", (EGLFuncPointer)&glGetBooleanvImpl },
+    { "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl },
+    { "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl },
+    { "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl },
+};
+
+EGLFuncPointer FindPlatformImplAddr(const char* name)
+{
+    static const bool DEBUG = false;
+
+    if (name == nullptr) {
+        ALOGV("FindPlatformImplAddr called with null name");
+        return nullptr;
+    }
+
+    for (int i = 0; i < NELEM(sPlatformImplMap); i++) {
+        if (sPlatformImplMap[i].name == nullptr) {
+            ALOGV("FindPlatformImplAddr found nullptr for sPlatformImplMap[%i].name (%s)", i, name);
+            return nullptr;
+        }
+        if (!strcmp(name, sPlatformImplMap[i].name)) {
+            ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name);
+            return sPlatformImplMap[i].address;
+        }
+    }
+
+    ALOGV("FindPlatformImplAddr did not find an entry for %s", name);
+    return nullptr;
+}
+} // namespace android
diff --git a/opengl/libs/EGL/egl_platform_entries.h b/opengl/libs/EGL/egl_platform_entries.h
new file mode 100644
index 0000000..7cd80d6
--- /dev/null
+++ b/opengl/libs/EGL/egl_platform_entries.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_EGLAPI_H
+#define ANDROID_EGLAPI_H
+
+#include <EGL/egl.h>
+
+typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
+
+namespace android {
+
+EGLFuncPointer FindPlatformImplAddr(const char* name);
+
+}; // namespace android
+
+#endif // ANDROID_EGLAPI_H
+
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 4990af6..1a88678 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -38,12 +38,16 @@
     };
 
     inline egl_connection_t() : dso(nullptr) { }
+
     void *              dso;
     gl_hooks_t *        hooks[2];
     EGLint              major;
     EGLint              minor;
     egl_t               egl;
 
+    // Functions implemented or redirected by platform libraries
+    platform_impl_t     platform;
+
     void*               libEgl;
     void*               libGles1;
     void*               libGles2;
@@ -63,6 +67,7 @@
 
 extern char const * const gl_names[];
 extern char const * const egl_names[];
+extern char const * const platform_names[];
 
 extern egl_connection_t gEGLImpl;
 
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index f7fde96..65f50f5 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -301,71 +301,31 @@
 }
 
 const GLubyte * glGetString(GLenum name) {
-    const GLubyte * ret = egl_get_string_for_current_context(name);
-    if (ret == NULL) {
-        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-        if(_c) ret = _c->glGetString(name);
-    }
-    return ret;
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetString(name);
 }
 
 const GLubyte * glGetStringi(GLenum name, GLuint index) {
-    const GLubyte * ret = egl_get_string_for_current_context(name, index);
-    if (ret == NULL) {
-        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-        if(_c) ret = _c->glGetStringi(name, index);
-    }
-    return ret;
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetStringi(name, index);
 }
 
 void glGetBooleanv(GLenum pname, GLboolean * data) {
-    if (pname == GL_NUM_EXTENSIONS) {
-        int num_exts = egl_get_num_extensions_for_current_context();
-        if (num_exts >= 0) {
-            *data = num_exts > 0 ? GL_TRUE : GL_FALSE;
-            return;
-        }
-    }
-
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-    if (_c) _c->glGetBooleanv(pname, data);
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetBooleanv(pname, data);
 }
 
 void glGetFloatv(GLenum pname, GLfloat * data) {
-    if (pname == GL_NUM_EXTENSIONS) {
-        int num_exts = egl_get_num_extensions_for_current_context();
-        if (num_exts >= 0) {
-            *data = (GLfloat)num_exts;
-            return;
-        }
-    }
-
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-    if (_c) _c->glGetFloatv(pname, data);
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetFloatv(pname, data);
 }
 
 void glGetIntegerv(GLenum pname, GLint * data) {
-    if (pname == GL_NUM_EXTENSIONS) {
-        int num_exts = egl_get_num_extensions_for_current_context();
-        if (num_exts >= 0) {
-            *data = (GLint)num_exts;
-            return;
-        }
-    }
-
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-    if (_c) _c->glGetIntegerv(pname, data);
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetIntegerv(pname, data);
 }
 
 void glGetInteger64v(GLenum pname, GLint64 * data) {
-    if (pname == GL_NUM_EXTENSIONS) {
-        int num_exts = egl_get_num_extensions_for_current_context();
-        if (num_exts >= 0) {
-            *data = (GLint64)num_exts;
-            return;
-        }
-    }
-
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-    if (_c) _c->glGetInteger64v(pname, data);
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetInteger64v(pname, data);
 }
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index a8855ef..0af0501 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -21,15 +21,18 @@
 #include <EGL/eglext.h>
 #include <EGL/eglplatform.h>
 
+#include "EGL/egldefs.h"
 #include "hooks.h"
 
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
 
+
 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();
+EGLAPI egl_connection_t* egl_get_connection();
 
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index 81dbe0e..63a0e14 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -59,6 +59,10 @@
 #define GL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__);
 #define EGL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__);
 
+struct platform_impl_t {
+    #include "platform_entries.in"
+};
+
 struct egl_t {
     #include "EGL/egl_entries.in"
 };
diff --git a/opengl/libs/platform_entries.in b/opengl/libs/platform_entries.in
new file mode 100644
index 0000000..b28f6cc
--- /dev/null
+++ b/opengl/libs/platform_entries.in
@@ -0,0 +1,76 @@
+EGL_ENTRY(EGLDisplay, eglGetDisplay, EGLNativeDisplayType)
+EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*)
+EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay)
+EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*)
+EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*)
+EGL_ENTRY(EGLBoolean, eglGetConfigAttrib, EGLDisplay, EGLConfig, EGLint, EGLint*)
+EGL_ENTRY(EGLSurface, eglCreateWindowSurface, EGLDisplay, EGLConfig, NativeWindowType, const EGLint*)
+EGL_ENTRY(EGLSurface, eglCreatePixmapSurface, EGLDisplay, EGLConfig, NativePixmapType, const EGLint*)
+EGL_ENTRY(EGLSurface, eglCreatePbufferSurface, EGLDisplay, EGLConfig, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroySurface, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglQuerySurface, EGLDisplay, EGLSurface, EGLint, EGLint*)
+EGL_ENTRY(void, eglBeginFrame, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLContext, eglCreateContext, EGLDisplay, EGLConfig, EGLContext, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyContext, EGLDisplay, EGLContext)
+EGL_ENTRY(EGLBoolean, eglMakeCurrent, EGLDisplay, EGLSurface, EGLSurface, EGLContext)
+EGL_ENTRY(EGLBoolean, eglQueryContext, EGLDisplay, EGLContext, EGLint, EGLint*)
+EGL_ENTRY(EGLContext, eglGetCurrentContext, void)
+EGL_ENTRY(EGLSurface, eglGetCurrentSurface, EGLint)
+EGL_ENTRY(EGLDisplay, eglGetCurrentDisplay, void)
+EGL_ENTRY(EGLBoolean, eglWaitGL, void)
+EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint)
+EGL_ENTRY(EGLint, eglGetError, void)
+EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char*)
+EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint*, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType)
+EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint)
+EGL_ENTRY(const char*, eglQueryStringImplementationANDROID, EGLDisplay, EGLint)
+EGL_ENTRY(EGLBoolean, eglSurfaceAttrib, EGLDisplay, EGLSurface, EGLint, EGLint)
+EGL_ENTRY(EGLBoolean, eglBindTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglReleaseTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapInterval, EGLDisplay, EGLint)
+EGL_ENTRY(EGLBoolean, eglWaitClient, void)
+EGL_ENTRY(EGLBoolean, eglBindAPI, EGLenum)
+EGL_ENTRY(EGLenum, eglQueryAPI, void)
+EGL_ENTRY(EGLBoolean, eglReleaseThread, void)
+EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglUnlockSurfaceKHR, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLImageKHR, eglCreateImageKHR, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyImageKHR, EGLDisplay, EGLImageKHR)
+EGL_ENTRY(EGLSyncKHR, eglCreateSyncKHR, EGLDisplay, EGLenum, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroySyncKHR, EGLDisplay, EGLSyncKHR)
+EGL_ENTRY(EGLBoolean, eglSignalSyncKHR, EGLDisplay, EGLSyncKHR, EGLenum)
+EGL_ENTRY(EGLint, eglClientWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttribKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLint*)
+EGL_ENTRY(EGLStreamKHR, eglCreateStreamKHR, EGLDisplay, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyStreamKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamAttribKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint)
+EGL_ENTRY(EGLBoolean, eglQueryStreamKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint*)
+EGL_ENTRY(EGLBoolean, eglQueryStreamu64KHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLuint64KHR*)
+EGL_ENTRY(EGLBoolean, eglQueryStreamTimeKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLTimeKHR*)
+EGL_ENTRY(EGLSurface, eglCreateStreamProducerSurfaceKHR, EGLDisplay, EGLConfig, EGLStreamKHR, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerGLTextureExternalKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerAcquireKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerReleaseKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLNativeFileDescriptorKHR, eglGetStreamFileDescriptorKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR, EGLDisplay, EGLNativeFileDescriptorKHR)
+EGL_ENTRY(EGLint, eglWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint)
+EGL_ENTRY(EGLint, eglDupNativeFenceFDANDROID, EGLDisplay, EGLSyncKHR)
+EGL_ENTRY(EGLBoolean, eglPresentationTimeANDROID, EGLDisplay, EGLSurface, EGLnsecsANDROID)
+EGL_ENTRY(EGLClientBuffer, eglGetNativeClientBufferANDROID, const AHardwareBuffer*)
+EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void)
+EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void)
+EGL_ENTRY(EGLBoolean, eglSetDamageRegionKHR, EGLDisplay, EGLSurface, EGLint*, EGLint)
+EGL_ENTRY(EGLBoolean, eglGetNextFrameIdANDROID, EGLDisplay, EGLSurface, EGLuint64KHR*)
+EGL_ENTRY(EGLBoolean, eglGetCompositorTimingANDROID, EGLDisplay, EGLSurface, EGLint, const EGLint*, EGLnsecsANDROID*)
+EGL_ENTRY(EGLBoolean, eglGetCompositorTimingSupportedANDROID, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglGetFrameTimestampsANDROID, EGLDisplay, EGLSurface, EGLuint64KHR, EGLint, const EGLint*, EGLnsecsANDROID*)
+EGL_ENTRY(EGLBoolean, eglGetFrameTimestampSupportedANDROID, EGLDisplay, EGLSurface, EGLint)
+GL_ENTRY(const GLubyte*, glGetString, GLenum)
+GL_ENTRY(const GLubyte*, glGetStringi, GLenum, GLuint)
+GL_ENTRY(void, glGetBooleanv, GLenum, GLboolean*)
+GL_ENTRY(void, glGetFloatv, GLenum, GLfloat*)
+GL_ENTRY(void, glGetIntegerv, GLenum, GLint*)
+GL_ENTRY(void, glGetInteger64v, GLenum, GLint64*)