Merge "Revert "SurfaceFlinger: Share ownership of layers between State and Handle.""
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index e897482..19c2830 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -224,6 +224,11 @@
     { "pagecache",  "Page cache", 0, {
         { REQ,      "events/filemap/enable" },
     } },
+    { "memory",  "Memory", 0, {
+        { OPT,      "events/kmem/rss_stat/enable" },
+        { OPT,      "events/kmem/ion_heap_grow/enable" },
+        { OPT,      "events/kmem/ion_heap_shrink/enable" },
+    } },
 };
 
 struct TracingVendorCategory {
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index e185cbe..9aa1075 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -121,7 +121,6 @@
         "kill",
         "librank",
         "logcat",
-        "logcompressor",
         "lsmod",
         "lsof",
         "netstat",
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 2470a1d..e4990b0 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -805,7 +805,7 @@
                                 PLOG(WARNING) << "Failed to chmod " << p->fts_path;
                             }
                         }
-                        // Intentional fall through to also set GID
+                        [[fallthrough]]; // also set GID
                     case FTS_F:
                         if (chown(p->fts_path, -1, expected) != 0) {
                             PLOG(WARNING) << "Failed to chown " << p->fts_path;
@@ -1442,7 +1442,7 @@
                     && !strcmp(p->fts_parent->fts_parent->fts_parent->fts_name, "Android")) {
                 p->fts_number = 1;
             }
-            // Fall through to count the directory
+            [[fallthrough]]; // to count the directory
         case FTS_DEFAULT:
         case FTS_F:
         case FTS_SL:
@@ -1857,13 +1857,14 @@
                         }
                     }
                 }
-                // Fall through to always count against total
+                [[fallthrough]]; // always count against total
             case FTS_D:
                 // Ignore data belonging to specific apps
                 p->fts_number = p->fts_parent->fts_number;
                 if (p->fts_level == 1 && !strcmp(p->fts_name, "Android")) {
                     p->fts_number = 1;
                 }
+                [[fallthrough]]; // always count against total
             case FTS_DEFAULT:
             case FTS_SL:
             case FTS_SLNONE:
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 48f9eb4..6b9cf0d 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -974,7 +974,7 @@
             if (chmod(p->fts_path, target_mode) != 0) {
                 PLOG(WARNING) << "Failed to chmod " << p->fts_path;
             }
-            // Intentional fall through to also set GID
+            [[fallthrough]]; // to also set GID
         case FTS_F:
             if (chown(p->fts_path, -1, gid) != 0) {
                 PLOG(WARNING) << "Failed to chown " << p->fts_path;
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 8c3ff74..7f5177b 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -94,7 +94,8 @@
  *     int fd = ASharedMemory_create("memory", 128);
  *
  *     // By default it has PROT_READ | PROT_WRITE | PROT_EXEC.
- *     char *buffer = (char *) mmap(NULL, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ *     size_t memSize = ASharedMemory_getSize(fd);
+ *     char *buffer = (char *) mmap(NULL, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  *
  *     strcpy(buffer, "This is an example."); // trivially initialize content
  *
diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
index 1a9018a..e37c388 100644
--- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
@@ -42,8 +42,8 @@
 /**
  * analog using std::shared_ptr for internally held refcount
  *
- * ref must be called at least one time during the lifetime of this object. The recommended way to construct
- * this object is with SharedRefBase::make.
+ * ref must be called at least one time during the lifetime of this object. The recommended way to
+ * construct this object is with SharedRefBase::make.
  */
 class SharedRefBase {
 public:
@@ -77,7 +77,7 @@
     /**
      * Convenience method for making an object directly with a reference.
      */
-    template<class T, class... Args>
+    template <class T, class... Args>
     static std::shared_ptr<T> make(Args&&... args) {
         T* t = new T(std::forward<Args>(args)...);
         return t->template ref<T>();
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 0e97b50..d36b3c0 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -51,6 +51,99 @@
 void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29);
 
 /**
+ * This is called to allocate an array with a given length. If allocation fails, null should be
+ * returned.
+ */
+typedef void* (*AParcel_arrayReallocator)(void* vectorData, size_t length);
+
+// @START-PRIMITIVE-VECTOR-GETTERS
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef int32_t* (*AParcel_int32ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef uint32_t* (*AParcel_uint32ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef int64_t* (*AParcel_int64ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef uint64_t* (*AParcel_uint64ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef float* (*AParcel_floatArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef double* (*AParcel_doubleArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef bool (*AParcel_boolArrayGetter)(const void* arrayData, size_t index);
+
+/**
+ * This is called to set an underlying value in an arrayData object at index.
+ */
+typedef void (*AParcel_boolArraySetter)(void* arrayData, size_t index, bool value);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef char16_t* (*AParcel_charArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef int8_t* (*AParcel_byteArrayGetter)(void* arrayData);
+
+// @END-PRIMITIVE-VECTOR-GETTERS
+
+/**
+ * This is called to allocate a buffer
+ *
+ * The length here includes the space required to insert a '\0' for a properly formed c-str. If the
+ * buffer returned from this function is retStr, it will be filled by AParcel_readString with the
+ * data from the remote process, and it will be filled such that retStr[length] == '\0'.
+ *
+ * If allocation fails, null should be returned.
+ */
+typedef void* (*AParcel_stringReallocator)(void* stringData, size_t length);
+
+/**
+ * This is called to get the buffer from a stringData object.
+ */
+typedef char* (*AParcel_stringGetter)(void* stringData);
+
+/**
  * Writes an AIBinder to the next location in a non-null parcel. Can be null.
  */
 binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) __INTRODUCED_IN(29);
@@ -95,22 +188,6 @@
         __INTRODUCED_IN(29);
 
 /**
- * This is called to allocate a buffer
- *
- * The length here includes the space required to insert a '\0' for a properly formed c-str. If the
- * buffer returned from this function is retStr, it will be filled by AParcel_readString with the
- * data from the remote process, and it will be filled such that retStr[length] == '\0'.
- *
- * If allocation fails, null should be returned.
- */
-typedef void* (*AParcel_string_reallocator)(void* stringData, size_t length);
-
-/**
- * This is called to get the buffer from a stringData object.
- */
-typedef char* (*AParcel_string_getter)(void* stringData);
-
-/**
  * Reads and allocates string value from the next location in a non-null parcel.
  *
  * Data is passed to the string allocator once the string size is known. This data should be used to
@@ -121,11 +198,11 @@
  * If this function returns a success, the buffer returned by allocator when passed stringData will
  * contain a null-terminated c-str read from the binder.
  */
-binder_status_t AParcel_readString(const AParcel* parcel, AParcel_string_reallocator reallocator,
-                                   AParcel_string_getter getter, void** stringData)
+binder_status_t AParcel_readString(const AParcel* parcel, AParcel_stringReallocator reallocator,
+                                   AParcel_stringGetter getter, void** stringData)
         __INTRODUCED_IN(29);
 
-// @START
+// @START-PRIMITIVE-READ-WRITE
 /**
  * Writes int32_t value to the next location in a non-null parcel.
  */
@@ -216,7 +293,125 @@
  */
 binder_status_t AParcel_readByte(const AParcel* parcel, int8_t* value) __INTRODUCED_IN(29);
 
-// @END
+/**
+ * Writes an array of int32_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* value, size_t length)
+        __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of uint32_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* value, size_t length)
+        __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of int64_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* value, size_t length)
+        __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of uint64_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* value, size_t length)
+        __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of float to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* value, size_t length)
+        __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of double to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* value, size_t length)
+        __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of bool to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData,
+                                       AParcel_boolArrayGetter getter, size_t length)
+        __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of char16_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* value, size_t length)
+        __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of int8_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* value, size_t length)
+        __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of int32_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readInt32Array(const AParcel* parcel, void** arrayData,
+                                       AParcel_arrayReallocator reallocator,
+                                       AParcel_int32ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of uint32_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readUint32Array(const AParcel* parcel, void** arrayData,
+                                        AParcel_arrayReallocator reallocator,
+                                        AParcel_uint32ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of int64_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readInt64Array(const AParcel* parcel, void** arrayData,
+                                       AParcel_arrayReallocator reallocator,
+                                       AParcel_int64ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of uint64_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readUint64Array(const AParcel* parcel, void** arrayData,
+                                        AParcel_arrayReallocator reallocator,
+                                        AParcel_uint64ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of float from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readFloatArray(const AParcel* parcel, void** arrayData,
+                                       AParcel_arrayReallocator reallocator,
+                                       AParcel_floatArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of double from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void** arrayData,
+                                        AParcel_arrayReallocator reallocator,
+                                        AParcel_doubleArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of bool from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readBoolArray(const AParcel* parcel, void** arrayData,
+                                      AParcel_arrayReallocator reallocator,
+                                      AParcel_boolArraySetter setter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of char16_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readCharArray(const AParcel* parcel, void** arrayData,
+                                      AParcel_arrayReallocator reallocator,
+                                      AParcel_charArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of int8_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readByteArray(const AParcel* parcel, void** arrayData,
+                                      AParcel_arrayReallocator reallocator,
+                                      AParcel_byteArrayGetter getter) __INTRODUCED_IN(29);
+
+// @END-PRIMITIVE-READ-WRITE
 
 #endif //__ANDROID_API__ >= __ANDROID_API_Q__
 __END_DECLS
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
index d3e6cae..faeb78f 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
@@ -31,14 +31,246 @@
 #ifdef __cplusplus
 
 #include <string>
+#include <vector>
 
 namespace ndk {
 
 /**
+ * This resizes a std::vector of some underlying type to the given length.
+ */
+template <typename T>
+static inline void* AParcel_stdVectorReallocator(void* vectorData, size_t length) {
+    std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
+    if (length > vec->max_size()) return nullptr;
+
+    vec->resize(length);
+    return vec;
+}
+
+/**
+ * This retrieves the underlying contiguous vector from a corresponding vectorData.
+ */
+template <typename T>
+static inline T* AParcel_stdVectorGetter(void* vectorData) {
+    std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
+    return vec->data();
+}
+
+/**
+ * This retrieves the underlying value in a vector which may not be contiguous at index from a
+ * corresponding vectorData.
+ */
+template <typename T>
+static inline T AParcel_stdVectorGetter(const void* vectorData, size_t index) {
+    const std::vector<T>* vec = static_cast<const std::vector<T>*>(vectorData);
+    return (*vec)[index];
+}
+
+/**
+ * This sets the underlying value in a corresponding vectorData which may not be contiguous at
+ * index.
+ */
+template <typename T>
+static inline void AParcel_stdVectorSetter(void* vectorData, size_t index, T value) {
+    std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
+    (*vec)[index] = value;
+}
+
+/**
+ * Writes a vector to the next location in a non-null parcel.
+ */
+template <typename T>
+static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<T>& vec);
+
+/**
+ * Reads a vector to the next location in a non-null parcel.
+ */
+template <typename T>
+static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<T>* vec);
+
+// @START
+/**
+ * Writes a vector of int32_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<int32_t>(AParcel* parcel,
+                                                    const std::vector<int32_t>& vec) {
+    return AParcel_writeInt32Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of int32_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<int32_t>(const AParcel* parcel,
+                                                   std::vector<int32_t>* vec) {
+    void* vectorData = static_cast<void*>(vec);
+    return AParcel_readInt32Array(parcel, &vectorData, &AParcel_stdVectorReallocator<int32_t>,
+                                  AParcel_stdVectorGetter<int32_t>);
+}
+
+/**
+ * Writes a vector of uint32_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<uint32_t>(AParcel* parcel,
+                                                     const std::vector<uint32_t>& vec) {
+    return AParcel_writeUint32Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of uint32_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<uint32_t>(const AParcel* parcel,
+                                                    std::vector<uint32_t>* vec) {
+    void* vectorData = static_cast<void*>(vec);
+    return AParcel_readUint32Array(parcel, &vectorData, &AParcel_stdVectorReallocator<uint32_t>,
+                                   AParcel_stdVectorGetter<uint32_t>);
+}
+
+/**
+ * Writes a vector of int64_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<int64_t>(AParcel* parcel,
+                                                    const std::vector<int64_t>& vec) {
+    return AParcel_writeInt64Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of int64_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<int64_t>(const AParcel* parcel,
+                                                   std::vector<int64_t>* vec) {
+    void* vectorData = static_cast<void*>(vec);
+    return AParcel_readInt64Array(parcel, &vectorData, &AParcel_stdVectorReallocator<int64_t>,
+                                  AParcel_stdVectorGetter<int64_t>);
+}
+
+/**
+ * Writes a vector of uint64_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<uint64_t>(AParcel* parcel,
+                                                     const std::vector<uint64_t>& vec) {
+    return AParcel_writeUint64Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of uint64_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<uint64_t>(const AParcel* parcel,
+                                                    std::vector<uint64_t>* vec) {
+    void* vectorData = static_cast<void*>(vec);
+    return AParcel_readUint64Array(parcel, &vectorData, &AParcel_stdVectorReallocator<uint64_t>,
+                                   AParcel_stdVectorGetter<uint64_t>);
+}
+
+/**
+ * Writes a vector of float to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<float>(AParcel* parcel, const std::vector<float>& vec) {
+    return AParcel_writeFloatArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of float from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<float>(const AParcel* parcel, std::vector<float>* vec) {
+    void* vectorData = static_cast<void*>(vec);
+    return AParcel_readFloatArray(parcel, &vectorData, &AParcel_stdVectorReallocator<float>,
+                                  AParcel_stdVectorGetter<float>);
+}
+
+/**
+ * Writes a vector of double to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<double>(AParcel* parcel,
+                                                   const std::vector<double>& vec) {
+    return AParcel_writeDoubleArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of double from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<double>(const AParcel* parcel, std::vector<double>* vec) {
+    void* vectorData = static_cast<void*>(vec);
+    return AParcel_readDoubleArray(parcel, &vectorData, &AParcel_stdVectorReallocator<double>,
+                                   AParcel_stdVectorGetter<double>);
+}
+
+/**
+ * Writes a vector of bool to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<bool>(AParcel* parcel, const std::vector<bool>& vec) {
+    return AParcel_writeBoolArray(parcel, static_cast<const void*>(&vec),
+                                  AParcel_stdVectorGetter<bool>, vec.size());
+}
+
+/**
+ * Reads a vector of bool from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<bool>(const AParcel* parcel, std::vector<bool>* vec) {
+    void* vectorData = static_cast<void*>(vec);
+    return AParcel_readBoolArray(parcel, &vectorData, &AParcel_stdVectorReallocator<bool>,
+                                 AParcel_stdVectorSetter<bool>);
+}
+
+/**
+ * Writes a vector of char16_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<char16_t>(AParcel* parcel,
+                                                     const std::vector<char16_t>& vec) {
+    return AParcel_writeCharArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of char16_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<char16_t>(const AParcel* parcel,
+                                                    std::vector<char16_t>* vec) {
+    void* vectorData = static_cast<void*>(vec);
+    return AParcel_readCharArray(parcel, &vectorData, &AParcel_stdVectorReallocator<char16_t>,
+                                 AParcel_stdVectorGetter<char16_t>);
+}
+
+/**
+ * Writes a vector of int8_t to the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_writeVector<int8_t>(AParcel* parcel,
+                                                   const std::vector<int8_t>& vec) {
+    return AParcel_writeByteArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of int8_t from the next location in a non-null parcel.
+ */
+template <>
+inline binder_status_t AParcel_readVector<int8_t>(const AParcel* parcel, std::vector<int8_t>* vec) {
+    void* vectorData = static_cast<void*>(vec);
+    return AParcel_readByteArray(parcel, &vectorData, &AParcel_stdVectorReallocator<int8_t>,
+                                 AParcel_stdVectorGetter<int8_t>);
+}
+
+// @END
+
+/**
  * Takes a std::string and reallocates it to the specified length. For use with AParcel_readString.
  * See use below in AParcel_readString.
  */
-static inline void* AParcel_std_string_reallocator(void* stringData, size_t length) {
+static inline void* AParcel_stdStringReallocator(void* stringData, size_t length) {
     std::string* str = static_cast<std::string*>(stringData);
     str->resize(length - 1);
     return stringData;
@@ -47,7 +279,7 @@
 /**
  * Takes a std::string and returns the inner char*.
  */
-static inline char* AParcel_std_string_getter(void* stringData) {
+static inline char* AParcel_stdStringGetter(void* stringData) {
     std::string* str = static_cast<std::string*>(stringData);
     return &(*str)[0];
 }
@@ -64,10 +296,31 @@
  */
 static inline binder_status_t AParcel_readString(const AParcel* parcel, std::string* str) {
     void* stringData = static_cast<void*>(str);
-    return AParcel_readString(parcel, AParcel_std_string_reallocator, AParcel_std_string_getter,
+    return AParcel_readString(parcel, AParcel_stdStringReallocator, AParcel_stdStringGetter,
                               &stringData);
 }
 
+template <typename T>
+static inline binder_status_t AParcel_writeVectorSize(AParcel* parcel, const std::vector<T>& vec) {
+    if (vec.size() > INT32_MAX) {
+        return STATUS_BAD_VALUE;
+    }
+
+    return AParcel_writeInt32(parcel, static_cast<int32_t>(vec.size()));
+}
+
+template <typename T>
+static inline binder_status_t AParcel_resizeVector(const AParcel* parcel, std::vector<T>* vec) {
+    int32_t size;
+    binder_status_t err = AParcel_readInt32(parcel, &size);
+
+    if (err != STATUS_OK) return err;
+    if (size < 0) return STATUS_UNEXPECTED_NULL;
+
+    vec->resize(static_cast<size_t>(size));
+    return STATUS_OK;
+}
+
 } // namespace ndk
 
 #endif // __cplusplus
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 2a1bff1..f84814f 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -24,30 +24,48 @@
     AIBinder_Weak_promote;
     AParcel_delete;
     AParcel_readBool;
+    AParcel_readBoolArray;
     AParcel_readByte;
+    AParcel_readByteArray;
     AParcel_readChar;
+    AParcel_readCharArray;
     AParcel_readDouble;
+    AParcel_readDoubleArray;
     AParcel_readFloat;
+    AParcel_readFloatArray;
     AParcel_readInt32;
+    AParcel_readInt32Array;
     AParcel_readInt64;
+    AParcel_readInt64Array;
     AParcel_readNullableStrongBinder;
     AParcel_readStatusHeader;
     AParcel_readString;
     AParcel_readStrongBinder;
     AParcel_readUint32;
+    AParcel_readUint32Array;
     AParcel_readUint64;
+    AParcel_readUint64Array;
     AParcel_writeBool;
+    AParcel_writeBoolArray;
     AParcel_writeByte;
+    AParcel_writeByteArray;
     AParcel_writeChar;
+    AParcel_writeCharArray;
     AParcel_writeDouble;
+    AParcel_writeDoubleArray;
     AParcel_writeFloat;
+    AParcel_writeFloatArray;
     AParcel_writeInt32;
+    AParcel_writeInt32Array;
     AParcel_writeInt64;
+    AParcel_writeInt64Array;
     AParcel_writeStatusHeader;
     AParcel_writeString;
     AParcel_writeStrongBinder;
     AParcel_writeUint32;
+    AParcel_writeUint32Array;
     AParcel_writeUint64;
+    AParcel_writeUint64Array;
     AStatus_delete;
     AStatus_fromExceptionCode;
     AStatus_fromExceptionCodeWithMessage;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 3e03e90..29094db 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -31,6 +31,163 @@
 using ::android::sp;
 using ::android::status_t;
 
+template <typename T>
+using ContiguousArrayGetter = T* (*)(void* arrayData);
+template <typename T>
+using ArrayGetter = T (*)(const void* arrayData, size_t index);
+template <typename T>
+using ArraySetter = void (*)(void* arrayData, size_t index, T value);
+
+template <typename T>
+binder_status_t WriteArray(AParcel* parcel, const T* array, size_t length) {
+    if (length > std::numeric_limits<int32_t>::max()) return STATUS_BAD_VALUE;
+
+    Parcel* rawParcel = parcel->get();
+
+    status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
+    if (status != STATUS_OK) return PruneStatusT(status);
+
+    int32_t size = 0;
+    if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY;
+
+    void* const data = rawParcel->writeInplace(size);
+    if (data == nullptr) return STATUS_NO_MEMORY;
+
+    memcpy(data, array, size);
+
+    return STATUS_OK;
+}
+
+// Each element in a char16_t array is converted to an int32_t (not packed).
+template <>
+binder_status_t WriteArray<char16_t>(AParcel* parcel, const char16_t* array, size_t length) {
+    if (length > std::numeric_limits<int32_t>::max()) return STATUS_BAD_VALUE;
+
+    Parcel* rawParcel = parcel->get();
+
+    status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
+    if (status != STATUS_OK) return PruneStatusT(status);
+
+    int32_t size = 0;
+    if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY;
+
+    for (int32_t i = 0; i < length; i++) {
+        status = rawParcel->writeChar(array[i]);
+
+        if (status != STATUS_OK) return PruneStatusT(status);
+    }
+
+    return STATUS_OK;
+}
+
+template <typename T>
+binder_status_t ReadArray(const AParcel* parcel, void** arrayData,
+                          AParcel_arrayReallocator reallocator, ContiguousArrayGetter<T> getter) {
+    const Parcel* rawParcel = parcel->get();
+
+    int32_t length;
+    status_t status = rawParcel->readInt32(&length);
+
+    if (status != STATUS_OK) return PruneStatusT(status);
+    if (length < 0) return STATUS_UNEXPECTED_NULL;
+
+    *arrayData = reallocator(*arrayData, length);
+    if (*arrayData == nullptr) return STATUS_NO_MEMORY;
+
+    if (length == 0) return STATUS_OK;
+
+    T* array = getter(*arrayData);
+    if (array == nullptr) return STATUS_NO_MEMORY;
+
+    int32_t size = 0;
+    if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY;
+
+    const void* data = rawParcel->readInplace(size);
+    if (data == nullptr) return STATUS_NO_MEMORY;
+
+    memcpy(array, data, size);
+
+    return STATUS_OK;
+}
+
+// Each element in a char16_t array is converted to an int32_t (not packed)
+template <>
+binder_status_t ReadArray<char16_t>(const AParcel* parcel, void** arrayData,
+                                    AParcel_arrayReallocator reallocator,
+                                    ContiguousArrayGetter<char16_t> getter) {
+    const Parcel* rawParcel = parcel->get();
+
+    int32_t length;
+    status_t status = rawParcel->readInt32(&length);
+
+    if (status != STATUS_OK) return PruneStatusT(status);
+    if (length < 0) return STATUS_UNEXPECTED_NULL;
+
+    *arrayData = reallocator(*arrayData, length);
+    if (*arrayData == nullptr) return STATUS_NO_MEMORY;
+
+    if (length == 0) return STATUS_OK;
+
+    char16_t* array = getter(*arrayData);
+    if (array == nullptr) return STATUS_NO_MEMORY;
+
+    int32_t size = 0;
+    if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY;
+
+    for (int32_t i = 0; i < length; i++) {
+        status = rawParcel->readChar(array + i);
+
+        if (status != STATUS_OK) return PruneStatusT(status);
+    }
+
+    return STATUS_OK;
+}
+
+template <typename T>
+binder_status_t WriteArray(AParcel* parcel, const void* arrayData, ArrayGetter<T> getter,
+                           size_t length, status_t (Parcel::*write)(T)) {
+    if (length > std::numeric_limits<int32_t>::max()) return STATUS_BAD_VALUE;
+
+    Parcel* rawParcel = parcel->get();
+
+    status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
+    if (status != STATUS_OK) return PruneStatusT(status);
+
+    for (size_t i = 0; i < length; i++) {
+        status = (rawParcel->*write)(getter(arrayData, i));
+
+        if (status != STATUS_OK) return PruneStatusT(status);
+    }
+
+    return STATUS_OK;
+}
+
+template <typename T>
+binder_status_t ReadArray(const AParcel* parcel, void** arrayData,
+                          AParcel_arrayReallocator reallocator, ArraySetter<T> setter,
+                          status_t (Parcel::*read)(T*) const) {
+    const Parcel* rawParcel = parcel->get();
+
+    int32_t length;
+    status_t status = rawParcel->readInt32(&length);
+
+    if (status != STATUS_OK) return PruneStatusT(status);
+    if (length < 0) return STATUS_UNEXPECTED_NULL;
+
+    *arrayData = reallocator(*arrayData, length);
+    if (*arrayData == nullptr) return STATUS_NO_MEMORY;
+
+    for (size_t i = 0; i < length; i++) {
+        T readTarget;
+        status = (rawParcel->*read)(&readTarget);
+        if (status != STATUS_OK) return PruneStatusT(status);
+
+        setter(*arrayData, i, readTarget);
+    }
+
+    return STATUS_OK;
+}
+
 void AParcel_delete(AParcel* parcel) {
     delete parcel;
 }
@@ -98,8 +255,8 @@
     return STATUS_OK;
 }
 
-binder_status_t AParcel_readString(const AParcel* parcel, AParcel_string_reallocator reallocator,
-                                   AParcel_string_getter getter, void** stringData) {
+binder_status_t AParcel_readString(const AParcel* parcel, AParcel_stringReallocator reallocator,
+                                   AParcel_stringGetter getter, void** stringData) {
     size_t len16;
     const char16_t* str16 = parcel->get()->readString16Inplace(&len16);
 
@@ -122,10 +279,15 @@
     }
 
     *stringData = reallocator(*stringData, len8);
+
+    if (*stringData == nullptr) {
+        return STATUS_NO_MEMORY;
+    }
+
     char* str8 = getter(*stringData);
 
     if (str8 == nullptr) {
-        LOG(WARNING) << __func__ << ": AParcel_string_allocator failed to allocate.";
+        LOG(WARNING) << __func__ << ": AParcel_stringReallocator failed to allocate.";
         return STATUS_NO_MEMORY;
     }
 
@@ -227,4 +389,95 @@
     return PruneStatusT(status);
 }
 
+binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* value, size_t length) {
+    return WriteArray<int32_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* value, size_t length) {
+    return WriteArray<uint32_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* value, size_t length) {
+    return WriteArray<int64_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* value, size_t length) {
+    return WriteArray<uint64_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* value, size_t length) {
+    return WriteArray<float>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* value, size_t length) {
+    return WriteArray<double>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData,
+                                       AParcel_boolArrayGetter getter, size_t length) {
+    return WriteArray<bool>(parcel, arrayData, getter, length, &Parcel::writeBool);
+}
+
+binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* value, size_t length) {
+    return WriteArray<char16_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* value, size_t length) {
+    return WriteArray<int8_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_readInt32Array(const AParcel* parcel, void** arrayData,
+                                       AParcel_arrayReallocator reallocator,
+                                       AParcel_int32ArrayGetter getter) {
+    return ReadArray<int32_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readUint32Array(const AParcel* parcel, void** arrayData,
+                                        AParcel_arrayReallocator reallocator,
+                                        AParcel_uint32ArrayGetter getter) {
+    return ReadArray<uint32_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readInt64Array(const AParcel* parcel, void** arrayData,
+                                       AParcel_arrayReallocator reallocator,
+                                       AParcel_int64ArrayGetter getter) {
+    return ReadArray<int64_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readUint64Array(const AParcel* parcel, void** arrayData,
+                                        AParcel_arrayReallocator reallocator,
+                                        AParcel_uint64ArrayGetter getter) {
+    return ReadArray<uint64_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readFloatArray(const AParcel* parcel, void** arrayData,
+                                       AParcel_arrayReallocator reallocator,
+                                       AParcel_floatArrayGetter getter) {
+    return ReadArray<float>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void** arrayData,
+                                        AParcel_arrayReallocator reallocator,
+                                        AParcel_doubleArrayGetter getter) {
+    return ReadArray<double>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readBoolArray(const AParcel* parcel, void** arrayData,
+                                      AParcel_arrayReallocator reallocator,
+                                      AParcel_boolArraySetter setter) {
+    return ReadArray<bool>(parcel, arrayData, reallocator, setter, &Parcel::readBool);
+}
+
+binder_status_t AParcel_readCharArray(const AParcel* parcel, void** arrayData,
+                                      AParcel_arrayReallocator reallocator,
+                                      AParcel_charArrayGetter getter) {
+    return ReadArray<char16_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readByteArray(const AParcel* parcel, void** arrayData,
+                                      AParcel_arrayReallocator reallocator,
+                                      AParcel_byteArrayGetter getter) {
+    return ReadArray<int8_t>(parcel, arrayData, reallocator, getter);
+}
+
 // @END
diff --git a/libs/binder/ndk/scripts/gen_parcel_helper.py b/libs/binder/ndk/scripts/gen_parcel_helper.py
index bbd3e5d..45f8d06 100755
--- a/libs/binder/ndk/scripts/gen_parcel_helper.py
+++ b/libs/binder/ndk/scripts/gen_parcel_helper.py
@@ -30,13 +30,15 @@
     ("Byte", "int8_t"),
 ]
 
-def replaceFileTags(path, content):
+non_contiguously_addressable = {"Bool"}
+
+def replaceFileTags(path, content, start_tag, end_tag):
     print("Updating", path)
     with open(path, "r+") as f:
         lines = f.readlines()
 
-        start = lines.index("// @START\n")
-        end = lines.index("// @END\n")
+        start = lines.index("// @" + start_tag + "\n")
+        end = lines.index("// @" + end_tag + "\n")
 
         if end <= start or start < 0 or end < 0:
             print("Failed to find tags in", path)
@@ -59,8 +61,10 @@
 
     print("Updating auto-generated code")
 
+    pre_header = ""
     header = ""
     source = ""
+    cpp_helper = ""
 
     for pretty, cpp in data_types:
         header += "/**\n"
@@ -82,8 +86,97 @@
         source += "    return PruneStatusT(status);\n"
         source += "}\n\n"
 
-    replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", header)
-    replaceFileTags(ROOT + "parcel.cpp", source)
+    for pretty, cpp in data_types:
+        nca = pretty in non_contiguously_addressable
+
+        arg_type = "const " + cpp + "* value"
+        if nca: arg_type = "const void* arrayData, AParcel_" + pretty.lower() + "ArrayGetter getter"
+        args = "value, length"
+        if nca: args = "arrayData, getter, length, &Parcel::write" + pretty
+
+        header += "/**\n"
+        header += " * Writes an array of " + cpp + " to the next location in a non-null parcel.\n"
+        header += " */\n"
+        header += "binder_status_t AParcel_write" + pretty + "Array(AParcel* parcel, " + arg_type + ", size_t length) __INTRODUCED_IN(29);\n\n"
+        source += "binder_status_t AParcel_write" + pretty + "Array(AParcel* parcel, " + arg_type + ", size_t length) {\n"
+        source += "    return WriteArray<" + cpp + ">(parcel, " + args + ");\n";
+        source += "}\n\n"
+
+    for pretty, cpp in data_types:
+        nca = pretty in non_contiguously_addressable
+
+        extra_getter_args = ""
+        if nca: extra_getter_args = ", size_t index"
+        getter_return = cpp + "*"
+        if nca: getter_return = cpp
+        getter_array_data = "void* arrayData"
+        if nca: getter_array_data = "const void* arrayData"
+
+        getter_type = "AParcel_" + pretty.lower() + "ArrayGetter"
+        setter_type = "AParcel_" + pretty.lower() + "ArraySetter"
+
+        pre_header += "/**\n"
+        pre_header += " * This is called to get the underlying data from an arrayData object.\n"
+        pre_header += " *\n"
+        pre_header += " * This will never be called for an empty array.\n"
+        pre_header += " */\n"
+        pre_header += "typedef " + getter_return + " (*" + getter_type + ")(" + getter_array_data + extra_getter_args + ");\n\n"
+
+        if nca:
+            pre_header += "/**\n"
+            pre_header += " * This is called to set an underlying value in an arrayData object at index.\n"
+            pre_header += " */\n"
+            pre_header += "typedef void (*" + setter_type + ")(void* arrayData, size_t index, " + cpp + " value);\n\n"
+
+        read_using = "getter"
+        if nca: read_using = "setter"
+        read_type = getter_type
+        if nca: read_type = setter_type
+
+        arguments = ["const AParcel* parcel"]
+        arguments += ["void** arrayData"]
+        arguments += ["AParcel_arrayReallocator reallocator"]
+        arguments += [read_type + " " + read_using]
+        arguments = ", ".join(arguments)
+
+        header += "/**\n"
+        header += " * Reads an array of " + cpp + " from the next location in a non-null parcel.\n"
+        header += " */\n"
+        header += "binder_status_t AParcel_read" + pretty + "Array(" + arguments + ") __INTRODUCED_IN(29);\n\n"
+        source += "binder_status_t AParcel_read" + pretty + "Array(" + arguments + ") {\n"
+        additional_args = ""
+        if nca: additional_args = ", &Parcel::read" + pretty
+        source += "    return ReadArray<" + cpp + ">(parcel, arrayData, reallocator, " + read_using + additional_args + ");\n";
+        source += "}\n\n"
+
+        cpp_helper += "/**\n"
+        cpp_helper += " * Writes a vector of " + cpp + " to the next location in a non-null parcel.\n"
+        cpp_helper += " */\n"
+        cpp_helper += "template<>\n"
+        cpp_helper += "inline binder_status_t AParcel_writeVector<" + cpp + ">(AParcel* parcel, const std::vector<" + cpp + ">& vec) {\n"
+        write_args = "vec.data()"
+        if nca: write_args = "static_cast<const void*>(&vec), AParcel_stdVectorGetter<" + cpp + ">"
+        cpp_helper += "    return AParcel_write" + pretty + "Array(parcel, " + write_args + ", vec.size());\n"
+        cpp_helper += "}\n\n"
+
+        cpp_helper += "/**\n"
+        cpp_helper += " * Reads a vector of " + cpp + " from the next location in a non-null parcel.\n"
+        cpp_helper += " */\n"
+        cpp_helper += "template<>\n"
+        cpp_helper += "inline binder_status_t AParcel_readVector<" + cpp + ">(const AParcel* parcel, std::vector<" + cpp + ">* vec) {\n"
+        cpp_helper += "    void* vectorData = static_cast<void*>(vec);\n"
+        read_args = []
+        read_args += ["parcel"]
+        read_args += ["&vectorData"]
+        read_args += ["&AParcel_stdVectorReallocator<" + cpp + ">"]
+        read_args += ["AParcel_stdVector" + read_using.capitalize() + "<" + cpp + ">"]
+        cpp_helper += "    return AParcel_read" + pretty + "Array(" + ", ".join(read_args) + ");\n"
+        cpp_helper += "}\n\n"
+
+    replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", pre_header, "START-PRIMITIVE-VECTOR-GETTERS", "END-PRIMITIVE-VECTOR-GETTERS")
+    replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", header, "START-PRIMITIVE-READ-WRITE", "END-PRIMITIVE-READ-WRITE")
+    replaceFileTags(ROOT + "parcel.cpp", source, "START", "END")
+    replaceFileTags(ROOT + "include_ndk/android/binder_parcel_utils.h", cpp_helper, "START", "END")
 
     print("Updating DONE.")
 
diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/main_client.cpp
index 22bf1e5..6945cac 100644
--- a/libs/binder/ndk/test/main_client.cpp
+++ b/libs/binder/ndk/test/main_client.cpp
@@ -134,3 +134,4 @@
 
 #include <android/binder_auto_utils.h>
 #include <android/binder_interface_utils.h>
+#include <android/binder_parcel_utils.h>
diff --git a/libs/binder/ndk/update.sh b/libs/binder/ndk/update.sh
index 49b4730..9a4577f 100755
--- a/libs/binder/ndk/update.sh
+++ b/libs/binder/ndk/update.sh
@@ -18,6 +18,6 @@
 set -ex
 
 # This script makes sure that the source code is in sync with the various scripts
-./scripts/init_map.sh > libbinder_ndk.map.txt
 ./scripts/gen_parcel_helper.py
 ./scripts/format.sh
+./scripts/init_map.sh > libbinder_ndk.map.txt
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..e53f7fd 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'",
@@ -57,7 +68,9 @@
 }
 
 void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
-                               const std::string appPref, bool developerOptIn) {
+                               const std::string appPref, bool developerOptIn,
+                               const int rulesFd, const long rulesOffset,
+                               const long rulesLength) {
     if (!mAnglePath.empty()) {
         ALOGV("ignoring attempt to change ANGLE path from '%s' to '%s'", mAnglePath.c_str(),
               path.c_str());
@@ -83,6 +96,13 @@
     }
 
     mAngleDeveloperOptIn = developerOptIn;
+
+    ALOGV("setting ANGLE rules file descriptor to '%i'", rulesFd);
+    mAngleRulesFd = rulesFd;
+    ALOGV("setting ANGLE rules offset to '%li'", rulesOffset);
+    mAngleRulesOffset = rulesOffset;
+    ALOGV("setting ANGLE rules length to '%li'", rulesLength);
+    mAngleRulesLength = rulesLength;
 }
 
 void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) {
@@ -113,6 +133,18 @@
     return mAngleAppPref.c_str();
 }
 
+int GraphicsEnv::getAngleRulesFd() {
+    return mAngleRulesFd;
+}
+
+long GraphicsEnv::getAngleRulesOffset() {
+    return mAngleRulesOffset;
+}
+
+long GraphicsEnv::getAngleRulesLength() {
+    return mAngleRulesLength;
+}
+
 const std::string GraphicsEnv::getLayerPaths(){
     return mLayerPaths;
 }
@@ -181,4 +213,19 @@
 const char* android_getAngleAppPref() {
     return android::GraphicsEnv::getInstance().getAngleAppPref();
 }
+int android_getAngleRulesFd() {
+   return android::GraphicsEnv::getInstance().getAngleRulesFd();
+}
+long android_getAngleRulesOffset() {
+   return android::GraphicsEnv::getInstance().getAngleRulesOffset();
+}
+long android_getAngleRulesLength() {
+   return android::GraphicsEnv::getInstance().getAngleRulesLength();
+}
+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..404823a 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
@@ -43,11 +45,15 @@
     // in the search path must have a '!' after the zip filename, e.g.
     //     /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
     void setAngleInfo(const std::string path, const std::string appName, const std::string appPref,
-                      bool devOptIn);
+                      bool devOptIn, const int rulesFd, const long rulesOffset,
+                      const long rulesLength);
     android_namespace_t* getAngleNamespace();
     const char* getAngleAppName();
     const char* getAngleAppPref();
     bool getAngleDeveloperOptIn();
+    int getAngleRulesFd();
+    long getAngleRulesOffset();
+    long getAngleRulesLength();
 
     void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths);
     NativeLoaderNamespace* getAppNamespace();
@@ -64,6 +70,9 @@
     std::string mAngleAppName;
     std::string mAngleAppPref;
     bool mAngleDeveloperOptIn;
+    int mAngleRulesFd;
+    long mAngleRulesOffset;
+    long mAngleRulesLength;
     std::string mDebugLayers;
     std::string mLayerPaths;
     android_namespace_t* mDriverNamespace = nullptr;
@@ -85,11 +94,16 @@
  *    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();
+    int android_getAngleRulesFd();
+    long android_getAngleRulesOffset();
+    long android_getAngleRulesLength();
+    const char* android_getLayerPaths();
+    const char* android_getDebugLayers();
 }
 
 #endif // ANDROID_UI_GRAPHICS_ENV_H
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp
index 3dac037..1a7c2d3 100644
--- a/libs/gui/BufferHubProducer.cpp
+++ b/libs/gui/BufferHubProducer.cpp
@@ -400,19 +400,8 @@
         ALOGE("attachBuffer: DetachedBufferHandle cannot be NULL.");
         return BAD_VALUE;
     }
-    auto detached_buffer = BufferHubBuffer::Import(std::move(detached_handle->handle()));
-    if (detached_buffer == nullptr) {
-        ALOGE("attachBuffer: BufferHubBuffer cannot be NULL.");
-        return BAD_VALUE;
-    }
-    auto status_or_handle = detached_buffer->Promote();
-    if (!status_or_handle.ok()) {
-        ALOGE("attachBuffer: Failed to promote a BufferHubBuffer into a BufferProducer, error=%d.",
-              status_or_handle.error());
-        return BAD_VALUE;
-    }
     std::shared_ptr<BufferProducer> buffer_producer =
-            BufferProducer::Import(status_or_handle.take());
+            BufferProducer::Import(std::move(detached_handle->handle()));
     if (buffer_producer == nullptr) {
         ALOGE("attachBuffer: Failed to import BufferProducer.");
         return BAD_VALUE;
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 24b1986..0a0c8ca 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -78,7 +78,7 @@
 
     shared_libs: [
         "android.hardware.graphics.allocator@2.0",
-        "android.hardware.graphics.common@1.1",
+        "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@2.1",
         "android.hardware.configstore@1.0",
@@ -97,7 +97,7 @@
     ],
 
     export_shared_lib_headers: [
-        "android.hardware.graphics.common@1.1",
+        "android.hardware.graphics.common@1.2",
     ],
 
     static_libs: [
@@ -144,6 +144,9 @@
         "libhardware_headers",
         "libui_headers",
     ],
+
+    // TODO(b/117568153): Temporarily opt out using libcrt.
+    no_libcrt: true,
 }
 
 cc_library_headers {
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index 606386c..44a947e 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -41,7 +41,6 @@
 
 #include <poll.h>
 
-using android::dvr::BufferHubMetadata;
 using android::dvr::BufferTraits;
 using android::dvr::DetachedBufferRPC;
 using android::dvr::NativeHandleWrapper;
@@ -160,7 +159,7 @@
 
     // If all imports succeed, replace the previous buffer and id.
     mId = bufferId;
-    mBfferStateBit = bufferTraits.buffer_state_bit();
+    mBufferStateBit = bufferTraits.buffer_state_bit();
 
     // TODO(b/112012161) Set up shared fences.
     ALOGD("BufferHubBuffer::ImportGraphicBuffer: id=%d, buffer_state=%" PRIx64 ".", id(),
@@ -175,26 +174,6 @@
     return poll(&p, 1, timeoutMs);
 }
 
-Status<LocalChannelHandle> BufferHubBuffer::Promote() {
-    ATRACE_CALL();
-
-    // TODO(b/112338294) remove after migrate producer buffer to binder
-    ALOGW("BufferHubBuffer::Promote: not supported operation during migration");
-    return {};
-
-    ALOGD("BufferHubBuffer::Promote: id=%d.", mId);
-
-    auto statusOrHandle = mClient.InvokeRemoteMethod<DetachedBufferRPC::Promote>();
-    if (statusOrHandle.ok()) {
-        // Invalidate the buffer.
-        mBufferHandle = {};
-    } else {
-        ALOGE("BufferHubBuffer::Promote: Failed to promote buffer (id=%d): %s.", mId,
-              statusOrHandle.GetErrorMessage().c_str());
-    }
-    return statusOrHandle;
-}
-
 Status<LocalChannelHandle> BufferHubBuffer::Duplicate() {
     ATRACE_CALL();
     ALOGD("BufferHubBuffer::Duplicate: id=%d.", mId);
diff --git a/libs/ui/BufferHubMetadata.cpp b/libs/ui/BufferHubMetadata.cpp
index b0c4510..1e08ed1 100644
--- a/libs/ui/BufferHubMetadata.cpp
+++ b/libs/ui/BufferHubMetadata.cpp
@@ -22,7 +22,6 @@
 #include <ui/BufferHubMetadata.h>
 
 namespace android {
-namespace dvr {
 
 namespace {
 
@@ -30,8 +29,8 @@
 
 } // namespace
 
-using BufferHubDefs::kMetadataHeaderSize;
-using BufferHubDefs::MetadataHeader;
+using dvr::BufferHubDefs::kMetadataHeaderSize;
+using dvr::BufferHubDefs::MetadataHeader;
 
 /* static */
 BufferHubMetadata BufferHubMetadata::Create(size_t userMetadataSize) {
@@ -101,5 +100,4 @@
     }
 }
 
-} // namespace dvr
 } // namespace android
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 61df02d..ee06d93 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -234,6 +234,9 @@
         case ColorMode::BT2020:
             return std::string("ColorMode::BT2020");
 
+        case ColorMode::DISPLAY_BT2020:
+            return std::string("ColorMode::DISPLAY_BT2020");
+
         case ColorMode::BT2100_PQ:
             return std::string("ColorMode::BT2100_PQ");
 
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
index e8ae244..cefe5b3 100644
--- a/libs/ui/include/ui/BufferHubBuffer.h
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -88,7 +88,7 @@
 
     // A state mask which is unique to a buffer hub client among all its siblings sharing the same
     // concrete graphic buffer.
-    uint64_t buffer_state_bit() const { return mBfferStateBit; }
+    uint64_t buffer_state_bit() const { return mBufferStateBit; }
 
     size_t user_metadata_size() const { return mMetadata.user_metadata_size(); }
 
@@ -112,11 +112,6 @@
     // Polls the fd for |timeoutMs| milliseconds (-1 for infinity).
     int Poll(int timeoutMs);
 
-    // Promotes a BufferHubBuffer to become a ProducerBuffer. Once promoted the BufferHubBuffer
-    // channel will be closed automatically on successful IPC return. Further IPCs towards this
-    // channel will return error.
-    pdx::Status<pdx::LocalChannelHandle> Promote();
-
     // Creates a BufferHubBuffer client from an existing one. The new client will
     // share the same underlying gralloc buffer and ashmem region for metadata.
     pdx::Status<pdx::LocalChannelHandle> Duplicate();
@@ -130,15 +125,15 @@
     int ImportGraphicBuffer();
 
     // Global id for the buffer that is consistent across processes.
-    int mId;
-    uint64_t mBfferStateBit;
+    int mId = -1;
+    uint64_t mBufferStateBit = 0;
 
     // Wrapps the gralloc buffer handle of this buffer.
     dvr::NativeHandleWrapper<pdx::LocalHandle> mBufferHandle;
 
     // An ashmem-based metadata object. The same shared memory are mapped to the
     // bufferhubd daemon and all buffer clients.
-    dvr::BufferHubMetadata mMetadata;
+    BufferHubMetadata mMetadata;
 
     // PDX backend.
     BufferHubClient mClient;
diff --git a/libs/ui/include/ui/BufferHubMetadata.h b/libs/ui/include/ui/BufferHubMetadata.h
index 46adc6a..94a9000 100644
--- a/libs/ui/include/ui/BufferHubMetadata.h
+++ b/libs/ui/include/ui/BufferHubMetadata.h
@@ -38,7 +38,6 @@
 #pragma clang diagnostic pop
 
 namespace android {
-namespace dvr {
 
 class BufferHubMetadata {
 public:
@@ -83,24 +82,25 @@
     bool IsValid() const { return mAshmemHandle.IsValid() && mMetadataHeader != nullptr; }
 
     size_t user_metadata_size() const { return mUserMetadataSize; }
-    size_t metadata_size() const { return mUserMetadataSize + BufferHubDefs::kMetadataHeaderSize; }
+    size_t metadata_size() const {
+        return mUserMetadataSize + dvr::BufferHubDefs::kMetadataHeaderSize;
+    }
 
     const pdx::LocalHandle& ashmem_handle() const { return mAshmemHandle; }
-    BufferHubDefs::MetadataHeader* metadata_header() { return mMetadataHeader; }
+    dvr::BufferHubDefs::MetadataHeader* metadata_header() { return mMetadataHeader; }
 
 private:
     BufferHubMetadata(size_t userMetadataSize, pdx::LocalHandle ashmemHandle,
-                      BufferHubDefs::MetadataHeader* metadataHeader);
+                      dvr::BufferHubDefs::MetadataHeader* metadataHeader);
 
     BufferHubMetadata(const BufferHubMetadata&) = delete;
     void operator=(const BufferHubMetadata&) = delete;
 
     size_t mUserMetadataSize = 0;
     pdx::LocalHandle mAshmemHandle;
-    BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr;
+    dvr::BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr;
 };
 
-} // namespace dvr
 } // namespace android
 
 #endif // ANDROID_BUFFER_HUB_METADATA_H_
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 6a7479a..4cd9a0b 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -28,7 +28,7 @@
     float getHeight() const { return bottom - top; }
 
     FloatRect intersect(const FloatRect& other) const {
-        return {
+        FloatRect intersection = {
             // Inline to avoid tromping on other min/max defines or adding a
             // dependency on STL
             (left > other.left) ? left : other.left,
@@ -36,6 +36,10 @@
             (right < other.right) ? right : other.right,
             (bottom < other.bottom) ? bottom : other.bottom
         };
+        if (intersection.getWidth() < 0 || intersection.getHeight() < 0) {
+            return {0, 0, 0, 0};
+        }
+        return intersection;
     }
 
     float left = 0.0f;
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 0fa819d..1d53ac8 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/common/1.2/types.h>
 #include <system/graphics.h>
 
 // android::ui::* in this header file will alias different types as
@@ -25,10 +26,10 @@
 namespace ui {
 
 using android::hardware::graphics::common::V1_0::Hdr;
-using android::hardware::graphics::common::V1_1::ColorMode;
-using android::hardware::graphics::common::V1_1::Dataspace;
 using android::hardware::graphics::common::V1_1::PixelFormat;
 using android::hardware::graphics::common::V1_1::RenderIntent;
+using android::hardware::graphics::common::V1_2::ColorMode;
+using android::hardware::graphics::common::V1_2::Dataspace;
 
 }  // namespace ui
 }  // namespace android
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 6e73960..228d202 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -36,6 +36,14 @@
 }
 
 cc_test {
+    name: "BufferHubBuffer_test",
+    header_libs: ["libbufferhub_headers", "libdvr_headers"],
+    shared_libs: ["libpdx_default_transport", "libui", "libutils"],
+    srcs: ["BufferHubBuffer_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
     name: "BufferHubMetadata_test",
     header_libs: ["libbufferhub_headers", "libdvr_headers"],
     shared_libs: ["libpdx_default_transport", "libui", "libutils"],
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
new file mode 100644
index 0000000..be06ad2
--- /dev/null
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BufferHubBufferTest"
+
+#include <gtest/gtest.h>
+#include <ui/BufferHubBuffer.h>
+
+namespace android {
+
+namespace {
+
+const int kWidth = 640;
+const int kHeight = 480;
+const int kLayerCount = 1;
+const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+const int kUsage = 0;
+const size_t kUserMetadataSize = 0;
+
+} // namespace
+
+using BufferHubBufferTest = ::testing::Test;
+
+using dvr::BufferHubDefs::IsBufferGained;
+using dvr::BufferHubDefs::kMetadataHeaderSize;
+using dvr::BufferHubDefs::kProducerStateBit;
+using pdx::LocalChannelHandle;
+
+TEST_F(BufferHubBufferTest, CreateBufferHubBufferFails) {
+    // Buffer Creation will fail: BLOB format requires height to be 1.
+    auto b1 = BufferHubBuffer::Create(kWidth, /*height=*/2, kLayerCount,
+                                      /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, kUserMetadataSize);
+
+    EXPECT_FALSE(b1->IsConnected());
+    EXPECT_FALSE(b1->IsValid());
+
+    // Buffer Creation will fail: user metadata size too large.
+    auto b2 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                      /*userMetadataSize=*/std::numeric_limits<size_t>::max());
+
+    EXPECT_FALSE(b2->IsConnected());
+    EXPECT_FALSE(b2->IsValid());
+
+    // Buffer Creation will fail: user metadata size too large.
+    const size_t userMetadataSize = std::numeric_limits<size_t>::max() - kMetadataHeaderSize;
+    auto b3 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                      userMetadataSize);
+
+    EXPECT_FALSE(b3->IsConnected());
+    EXPECT_FALSE(b3->IsValid());
+}
+
+TEST_F(BufferHubBufferTest, CreateBufferHubBuffer) {
+    auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                      kUserMetadataSize);
+    EXPECT_TRUE(b1->IsConnected());
+    EXPECT_TRUE(b1->IsValid());
+    EXPECT_NE(b1->id(), 0);
+}
+
+TEST_F(BufferHubBufferTest, DuplicateBufferHubBuffer) {
+    auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                      kUserMetadataSize);
+    int id1 = b1->id();
+    EXPECT_TRUE(b1->IsValid());
+    EXPECT_EQ(b1->user_metadata_size(), kUserMetadataSize);
+
+    auto statusOrHandle = b1->Duplicate();
+    EXPECT_TRUE(statusOrHandle);
+
+    // The detached buffer should still be valid.
+    EXPECT_TRUE(b1->IsConnected());
+    EXPECT_TRUE(b1->IsValid());
+
+    // Gets the channel handle for the duplicated buffer.
+    LocalChannelHandle h2 = statusOrHandle.take();
+    EXPECT_TRUE(h2.valid());
+
+    std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(std::move(h2));
+    EXPECT_FALSE(h2.valid());
+    ASSERT_TRUE(b2 != nullptr);
+    EXPECT_TRUE(b2->IsValid());
+    EXPECT_EQ(b2->user_metadata_size(), kUserMetadataSize);
+
+    int id2 = b2->id();
+
+    // These two buffer instances are based on the same physical buffer under the
+    // hood, so they should share the same id.
+    EXPECT_EQ(id1, id2);
+    // We use buffer_state_bit() to tell those two instances apart.
+    EXPECT_NE(b1->buffer_state_bit(), b2->buffer_state_bit());
+    EXPECT_NE(b1->buffer_state_bit(), 0ULL);
+    EXPECT_NE(b2->buffer_state_bit(), 0ULL);
+    EXPECT_NE(b1->buffer_state_bit(), kProducerStateBit);
+    EXPECT_NE(b2->buffer_state_bit(), kProducerStateBit);
+
+    // Both buffer instances should be in gained state.
+    EXPECT_TRUE(IsBufferGained(b1->buffer_state()));
+    EXPECT_TRUE(IsBufferGained(b2->buffer_state()));
+
+    // TODO(b/112338294): rewrite test after migration
+    return;
+}
+
+} // namespace android
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index b477f1a..8c6e7e2 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -829,43 +829,7 @@
   EXPECT_NE(b1->id(), 0);
 }
 
-TEST_F(LibBufferHubTest, TestPromoteBufferHubBuffer) {
-  // TODO(b/112338294) rewrite test after migration
-  return;
-
-  auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat,
-                                    kUsage, kUserMetadataSize);
-  int b1_id = b1->id();
-  EXPECT_TRUE(b1->IsValid());
-
-  auto status_or_handle = b1->Promote();
-  EXPECT_TRUE(status_or_handle);
-
-  // The detached buffer should have hangup.
-  EXPECT_GT(RETRY_EINTR(b1->Poll(kPollTimeoutMs)), 0);
-  auto status_or_int = b1->GetEventMask(POLLHUP);
-  EXPECT_TRUE(status_or_int.ok());
-  EXPECT_EQ(status_or_int.get(), POLLHUP);
-
-  // The buffer client is still considered as connected but invalid.
-  EXPECT_TRUE(b1->IsConnected());
-  EXPECT_FALSE(b1->IsValid());
-
-  // Gets the channel handle for the producer.
-  LocalChannelHandle h1 = status_or_handle.take();
-  EXPECT_TRUE(h1.valid());
-
-  std::unique_ptr<ProducerBuffer> p1 = ProducerBuffer::Import(std::move(h1));
-  EXPECT_FALSE(h1.valid());
-  ASSERT_TRUE(p1 != nullptr);
-  int p1_id = p1->id();
-
-  // A newly promoted ProducerBuffer should inherit the same buffer id.
-  EXPECT_EQ(b1_id, p1_id);
-  EXPECT_TRUE(IsBufferGained(p1->buffer_state()));
-}
-
-TEST_F(LibBufferHubTest, TestDetachThenPromote) {
+TEST_F(LibBufferHubTest, TestDetach) {
   // TODO(b/112338294) rewrite test after migration
   return;
 
@@ -887,26 +851,6 @@
   EXPECT_TRUE(b1->IsValid());
   int b1_id = b1->id();
   EXPECT_EQ(b1_id, p1_id);
-
-  // Promote the detached buffer.
-  status_or_handle = b1->Promote();
-  // The buffer client is still considered as connected but invalid.
-  EXPECT_TRUE(b1->IsConnected());
-  EXPECT_FALSE(b1->IsValid());
-  EXPECT_TRUE(status_or_handle.ok());
-
-  // Gets the channel handle for the producer.
-  LocalChannelHandle h2 = status_or_handle.take();
-  EXPECT_TRUE(h2.valid());
-
-  std::unique_ptr<ProducerBuffer> p2 = ProducerBuffer::Import(std::move(h2));
-  EXPECT_FALSE(h2.valid());
-  ASSERT_TRUE(p2 != nullptr);
-  int p2_id = p2->id();
-
-  // A newly promoted ProducerBuffer should inherit the same buffer id.
-  EXPECT_EQ(b1_id, p2_id);
-  EXPECT_TRUE(IsBufferGained(p2->buffer_state()));
 }
 
 TEST_F(LibBufferHubTest, TestDuplicateBufferHubBuffer) {
@@ -951,10 +895,4 @@
 
   // TODO(b/112338294) rewrite test after migration
   return;
-
-  // Promote the detached buffer should fail as b1 is no longer the exclusive
-  // owner of the buffer..
-  status_or_handle = b1->Promote();
-  EXPECT_FALSE(status_or_handle.ok());
-  EXPECT_EQ(status_or_handle.error(), EINVAL);
 }
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
index 6d28d41..49f9c3e 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
@@ -191,11 +191,6 @@
     // Imports the given channel handle to a DetachedBuffer, taking ownership.
     kOpImport,
 
-    // Promotes a DetachedBuffer to become a ProducerBuffer. Once promoted the
-    // DetachedBuffer channel will be closed automatically on successful IPC
-    // return. Further IPCs towards this channel will return error.
-    kOpPromote,
-
     // Creates a DetachedBuffer client from an existing one. The new client will
     // share the same underlying gralloc buffer and ashmem region for metadata.
     kOpDuplicate,
@@ -212,10 +207,9 @@
                          uint32_t format, uint64_t usage,
                          size_t user_metadata_size));
   PDX_REMOTE_METHOD(Import, kOpImport, BufferTraits<LocalHandle>(Void));
-  PDX_REMOTE_METHOD(Promote, kOpPromote, LocalChannelHandle(Void));
   PDX_REMOTE_METHOD(Duplicate, kOpDuplicate, LocalChannelHandle(Void));
 
-  PDX_REMOTE_API(API, Create, Import, Promote, Duplicate);
+  PDX_REMOTE_API(API, Create, Import, Duplicate);
 };
 
 }  // namespace dvr
diff --git a/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h
index 3086982..ae70b77 100644
--- a/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h
+++ b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h
@@ -40,7 +40,7 @@
   bool IsValid() const { return ints_.size() != 0 || fds_.size() != 0; }
 
   // Duplicate a native handle from the wrapper.
-  const native_handle_t* DuplicateHandle() const {
+  native_handle_t* DuplicateHandle() const {
     if (!IsValid()) {
       return nullptr;
     }
@@ -58,7 +58,7 @@
   }
 
   // Takes the native handle out of the wrapper.
-  const native_handle_t* TakeHandle() {
+  native_handle_t* TakeHandle() {
     if (!IsValid()) {
       return nullptr;
     }
@@ -70,8 +70,8 @@
   NativeHandleWrapper(const NativeHandleWrapper&) = delete;
   void operator=(const NativeHandleWrapper&) = delete;
 
-  static const native_handle_t* FromFdsAndInts(std::vector<FileHandleType> fds,
-                                               std::vector<int> ints) {
+  static native_handle_t* FromFdsAndInts(std::vector<FileHandleType> fds,
+                                         std::vector<int> ints) {
     native_handle_t* handle = native_handle_create(fds.size(), ints.size());
     if (!handle) {
       ALOGE("NativeHandleWrapper::TakeHandle: Failed to create new handle.");
diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h
index 93a3965..c9e8b7c 100644
--- a/opengl/include/EGL/egl.h
+++ b/opengl/include/EGL/egl.h
@@ -33,12 +33,12 @@
 ** used to make the header, and the header can be found at
 **   http://www.khronos.org/registry/egl
 **
-** Khronos $Git commit SHA1: a732b061e7 $ on $Git commit date: 2017-06-17 23:27:53 +0100 $
+** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $
 */
 
 #include <EGL/eglplatform.h>
 
-/* Generated on date 20170627 */
+/* Generated on date 20180517 */
 
 /* Generated C header for:
  * API: egl
@@ -235,10 +235,66 @@
 EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext (void);
 #endif /* EGL_VERSION_1_4 */
 
-/* This version of Android does not yet support EGL 1.5, but the following
- * portion of EGL 1.5 is included in order to support portions of "eglext.h".
- */
+#ifndef EGL_VERSION_1_5
+#define EGL_VERSION_1_5 1
+typedef void *EGLSync;
 typedef intptr_t EGLAttrib;
+typedef khronos_utime_nanoseconds_t EGLTime;
+typedef void *EGLImage;
+#define EGL_CONTEXT_MAJOR_VERSION         0x3098
+#define EGL_CONTEXT_MINOR_VERSION         0x30FB
+#define EGL_CONTEXT_OPENGL_PROFILE_MASK   0x30FD
+#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY 0x31BD
+#define EGL_NO_RESET_NOTIFICATION         0x31BE
+#define EGL_LOSE_CONTEXT_ON_RESET         0x31BF
+#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001
+#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT 0x00000002
+#define EGL_CONTEXT_OPENGL_DEBUG          0x31B0
+#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE 0x31B1
+#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS  0x31B2
+#define EGL_OPENGL_ES3_BIT                0x00000040
+#define EGL_CL_EVENT_HANDLE               0x309C
+#define EGL_SYNC_CL_EVENT                 0x30FE
+#define EGL_SYNC_CL_EVENT_COMPLETE        0x30FF
+#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE  0x30F0
+#define EGL_SYNC_TYPE                     0x30F7
+#define EGL_SYNC_STATUS                   0x30F1
+#define EGL_SYNC_CONDITION                0x30F8
+#define EGL_SIGNALED                      0x30F2
+#define EGL_UNSIGNALED                    0x30F3
+#define EGL_SYNC_FLUSH_COMMANDS_BIT       0x0001
+#define EGL_FOREVER                       0xFFFFFFFFFFFFFFFFull
+#define EGL_TIMEOUT_EXPIRED               0x30F5
+#define EGL_CONDITION_SATISFIED           0x30F6
+#define EGL_NO_SYNC                       EGL_CAST(EGLSync,0)
+#define EGL_SYNC_FENCE                    0x30F9
+#define EGL_GL_COLORSPACE                 0x309D
+#define EGL_GL_COLORSPACE_SRGB            0x3089
+#define EGL_GL_COLORSPACE_LINEAR          0x308A
+#define EGL_GL_RENDERBUFFER               0x30B9
+#define EGL_GL_TEXTURE_2D                 0x30B1
+#define EGL_GL_TEXTURE_LEVEL              0x30BC
+#define EGL_GL_TEXTURE_3D                 0x30B2
+#define EGL_GL_TEXTURE_ZOFFSET            0x30BD
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x30B3
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x30B4
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x30B5
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x30B6
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x30B7
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8
+#define EGL_IMAGE_PRESERVED               0x30D2
+#define EGL_NO_IMAGE                      EGL_CAST(EGLImage,0)
+EGLAPI EGLSync EGLAPIENTRY eglCreateSync (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroySync (EGLDisplay dpy, EGLSync sync);
+EGLAPI EGLint EGLAPIENTRY eglClientWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
+EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttrib (EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value);
+EGLAPI EGLImage EGLAPIENTRY eglCreateImage (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImage (EGLDisplay dpy, EGLImage image);
+EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplay (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurface (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags);
+#endif /* EGL_VERSION_1_5 */
 
 #ifdef __cplusplus
 }
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 0fd91eb..ad4ffd7 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -33,12 +33,12 @@
 ** used to make the header, and the header can be found at
 **   http://www.khronos.org/registry/egl
 **
-** Khronos $Git commit SHA1: feaaeb19e1 $ on $Git commit date: 2018-02-26 20:49:02 -0800 $
+** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $
 */
 
 #include <EGL/eglplatform.h>
 
-#define EGL_EGLEXT_VERSION 20180228
+#define EGL_EGLEXT_VERSION 20180517
 
 /* Generated C header for:
  * API: egl
@@ -618,6 +618,16 @@
 #define EGL_EXT_client_extensions 1
 #endif /* EGL_EXT_client_extensions */
 
+#ifndef EGL_EXT_client_sync
+#define EGL_EXT_client_sync 1
+#define EGL_SYNC_CLIENT_EXT               0x3364
+#define EGL_SYNC_CLIENT_SIGNAL_EXT        0x3365
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLCLIENTSIGNALSYNCEXTPROC) (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglClientSignalSyncEXT (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#endif
+#endif /* EGL_EXT_client_sync */
+
 #ifndef EGL_EXT_compositor
 #define EGL_EXT_compositor 1
 #define EGL_PRIMARY_COMPOSITOR_CONTEXT_EXT 0x3460
@@ -903,6 +913,14 @@
 #endif
 #endif /* EGL_EXT_swap_buffers_with_damage */
 
+#ifndef EGL_EXT_sync_reuse
+#define EGL_EXT_sync_reuse 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNSIGNALSYNCEXTPROC) (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglUnsignalSyncEXT (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#endif
+#endif /* EGL_EXT_sync_reuse */
+
 #ifndef EGL_EXT_yuv_surface
 #define EGL_EXT_yuv_surface 1
 #define EGL_YUV_ORDER_EXT                 0x3301
@@ -1147,6 +1165,14 @@
 #define EGL_STREAM_FIFO_SYNCHRONOUS_NV    0x3336
 #endif /* EGL_NV_stream_fifo_synchronous */
 
+#ifndef EGL_NV_stream_flush
+#define EGL_NV_stream_flush 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMFLUSHNVPROC) (EGLDisplay dpy, EGLStreamKHR stream);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamFlushNV (EGLDisplay dpy, EGLStreamKHR stream);
+#endif
+#endif /* EGL_NV_stream_flush */
+
 #ifndef EGL_NV_stream_frame_limits
 #define EGL_NV_stream_frame_limits 1
 #define EGL_PRODUCER_MAX_FRAME_HINT_NV    0x3337
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index c77c333..0bc2cb9 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -77,12 +77,24 @@
 typedef HBITMAP EGLNativePixmapType;
 typedef HWND    EGLNativeWindowType;
 
-#elif defined(__APPLE__) || defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian */
+#elif defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian */
 
 typedef int   EGLNativeDisplayType;
 typedef void *EGLNativeWindowType;
 typedef void *EGLNativePixmapType;
 
+#elif defined(WL_EGL_PLATFORM)
+
+typedef struct wl_display     *EGLNativeDisplayType;
+typedef struct wl_egl_pixmap  *EGLNativePixmapType;
+typedef struct wl_egl_window  *EGLNativeWindowType;
+
+#elif defined(__GBM__)
+
+typedef struct gbm_device  *EGLNativeDisplayType;
+typedef struct gbm_bo      *EGLNativePixmapType;
+typedef void               *EGLNativeWindowType;
+
 #elif defined(__ANDROID__) || defined(ANDROID)
 
 struct ANativeWindow;
@@ -92,7 +104,13 @@
 typedef struct egl_native_pixmap_t*     EGLNativePixmapType;
 typedef void*                           EGLNativeDisplayType;
 
-#elif defined(__unix__)
+#elif defined(USE_OZONE)
+
+typedef intptr_t EGLNativeDisplayType;
+typedef intptr_t EGLNativeWindowType;
+typedef intptr_t EGLNativePixmapType;
+
+#elif defined(__unix__) || defined(USE_X11)
 
 /* X11 (tentative)  */
 #include <X11/Xlib.h>
@@ -102,6 +120,20 @@
 typedef Pixmap   EGLNativePixmapType;
 typedef Window   EGLNativeWindowType;
 
+#elif defined(__APPLE__)
+
+typedef int   EGLNativeDisplayType;
+typedef void *EGLNativeWindowType;
+typedef void *EGLNativePixmapType;
+
+#elif defined(__HAIKU__)
+
+#include <kernel/image.h>
+
+typedef void              *EGLNativeDisplayType;
+typedef khronos_uintptr_t  EGLNativePixmapType;
+typedef khronos_uintptr_t  EGLNativeWindowType;
+
 #else
 #error "Platform not recognized"
 #endif
diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp
index 1d5def5..8bb7e83 100644
--- a/opengl/libagl/state.cpp
+++ b/opengl/libagl/state.cpp
@@ -181,7 +181,7 @@
     case GL_FOG:
     case GL_DEPTH_TEST:
         ogles_invalidate_perspective(c);
-        // fall-through...
+        [[fallthrough]];
     case GL_BLEND:
     case GL_SCISSOR_TEST:
     case GL_ALPHA_TEST:
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index aae8e05..4c5f3e9 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -401,14 +401,14 @@
     switch (format) {
     case GL_PALETTE4_RGB8_OES:
         indexBits = 4;
-        /* FALLTHROUGH */
+        [[fallthrough]];
     case GL_PALETTE8_RGB8_OES:
         entrySize = 3;
         break;
 
     case GL_PALETTE4_RGBA8_OES:
         indexBits = 4;
-        /* FALLTHROUGH */
+        [[fallthrough]];
     case GL_PALETTE8_RGBA8_OES:
         entrySize = 4;
         break;
@@ -417,7 +417,7 @@
     case GL_PALETTE4_RGBA4_OES:
     case GL_PALETTE4_RGB5_A1_OES:
         indexBits = 4;
-        /* FALLTHROUGH */
+        [[fallthrough]];
     case GL_PALETTE8_R5_G6_B5_OES:
     case GL_PALETTE8_RGBA4_OES:
     case GL_PALETTE8_RGB5_A1_OES:
@@ -446,14 +446,14 @@
     switch (format) {
     case GL_PALETTE4_RGB8_OES:
         indexBits = 4;
-        /* FALLTHROUGH */
+        [[fallthrough]];
     case GL_PALETTE8_RGB8_OES:
         entrySize = 3;
         break;
 
     case GL_PALETTE4_RGBA8_OES:
         indexBits = 4;
-        /* FALLTHROUGH */
+        [[fallthrough]];
     case GL_PALETTE8_RGBA8_OES:
         entrySize = 4;
         break;
@@ -462,7 +462,7 @@
     case GL_PALETTE4_RGBA4_OES:
     case GL_PALETTE4_RGB5_A1_OES:
         indexBits = 4;
-        /* FALLTHROUGH */
+        [[fallthrough]];
     case GL_PALETTE8_R5_G6_B5_OES:
     case GL_PALETTE8_RGBA4_OES:
     case GL_PALETTE8_RGB5_A1_OES:
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 87f0fe1..6f792ec 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"
 
@@ -50,6 +51,14 @@
   typedef bool (*fpANGLEUseForApplication)(const char* appName, const char* deviceMfr,
                                            const char* deviceModel, ANGLEPreference developerOption,
                                            ANGLEPreference appPreference);
+
+  // TODO(ianelliott@): Get this from an ANGLE header:
+  typedef bool (*fpANGLEGetUtilityAPI)(unsigned int* versionToUse);
+
+  // TODO(ianelliott@): Get this from an ANGLE header:
+  typedef bool (*fpAndroidUseANGLEForApplication)(int fd, long offset, long length,
+                                                  const char* appName, const char* deviceMfr,
+                                                  const char* deviceModel);
 }
 
 // ----------------------------------------------------------------------------
@@ -510,7 +519,7 @@
     return ANGLE_NO_PREFERENCE;
 }
 
-static void* load_angle(const char* kind, egl_connection_t* cnx) {
+static void* load_angle(const char* kind, android_namespace_t* ns, egl_connection_t* cnx) {
     // Only attempt to load ANGLE libs
     if (strcmp(kind, "EGL") != 0 && strcmp(kind, "GLESv2") != 0 && strcmp(kind, "GLESv1_CM") != 0)
         return nullptr;
@@ -519,10 +528,12 @@
     std::string name;
     char prop[PROPERTY_VALUE_MAX];
 
-    android_namespace_t* ns = android_getAngleNamespace();
     const char* app_name = android_getAngleAppName();
     const char* app_pref = android_getAngleAppPref();
     bool developer_opt_in = android_getAngleDeveloperOptIn();
+    const int rules_fd = android_getAngleRulesFd();
+    const long rules_offset = android_getAngleRulesOffset();
+    const long rules_length = android_getAngleRulesLength();
 
     // Determine whether or not to use ANGLE:
     ANGLEPreference developer_option = developer_opt_in ? ANGLE_PREFER_ANGLE : ANGLE_NO_PREFERENCE;
@@ -538,21 +549,51 @@
         char model[PROPERTY_VALUE_MAX];
         property_get("ro.product.manufacturer", manufacturer, "UNSET");
         property_get("ro.product.model", model, "UNSET");
-        ANGLEPreference app_preference = getAnglePref(android_getAngleAppPref());
-
         so = load_angle_from_namespace("feature_support", ns);
         if (so) {
             ALOGV("Temporarily loaded ANGLE's opt-in/out logic from namespace");
-            fpANGLEUseForApplication fp =
-                    (fpANGLEUseForApplication)dlsym(so, "ANGLEUseForApplication");
-            if (fp) {
-                use_angle = (fp)(app_name_str.c_str(), manufacturer, model, developer_option,
-                                 app_preference);
-                ALOGV("Result of opt-in/out logic is %s", use_angle ? "true" : "false");
+            bool use_version0_API = false;
+            bool use_version1_API = false;
+            fpANGLEGetUtilityAPI ANGLEGetUtilityAPI =
+                    (fpANGLEGetUtilityAPI)dlsym(so, "ANGLEGetUtilityAPI");
+            if (ANGLEGetUtilityAPI) {
+                unsigned int versionToUse = 1;
+                if ((ANGLEGetUtilityAPI)(&versionToUse)) {
+                    if (versionToUse == 1) {
+                        use_version1_API = true;
+                    } else {
+                        use_version0_API = true;
+                    }
+                }
             } else {
-                ALOGW("Cannot find ANGLEUseForApplication in library");
+                use_version0_API = true;
             }
-
+            if (use_version1_API) {
+                // Use the new version 1 API to determine if the
+                // application should use the ANGLE or the native driver.
+                fpAndroidUseANGLEForApplication AndroidUseANGLEForApplication =
+                        (fpAndroidUseANGLEForApplication)dlsym(so, "AndroidUseANGLEForApplication");
+                if (AndroidUseANGLEForApplication) {
+                    use_angle = (AndroidUseANGLEForApplication)(rules_fd, rules_offset,
+                                                                rules_length, app_name_str.c_str(),
+                                                                manufacturer, model);
+                } else {
+                    ALOGW("Cannot find AndroidUseANGLEForApplication in library");
+                }
+            } else if (use_version0_API) {
+                // Use the old version 0 API to determine if the
+                // application should use the ANGLE or the native driver.
+                fpANGLEUseForApplication ANGLEUseForApplication =
+                        (fpANGLEUseForApplication)dlsym(so, "ANGLEUseForApplication");
+                if (ANGLEUseForApplication) {
+                    ANGLEPreference app_preference = getAnglePref(android_getAngleAppPref());
+                    use_angle = (ANGLEUseForApplication)(app_name_str.c_str(), manufacturer, model,
+                                                         developer_option, app_preference);
+                    ALOGV("Result of opt-in/out logic is %s", use_angle ? "true" : "false");
+                } else {
+                    ALOGW("Cannot find ANGLEUseForApplication in library");
+                }
+            }
             ALOGV("Close temporarily-loaded ANGLE opt-in/out logic");
             dlclose(so);
             so = nullptr;
@@ -619,7 +660,10 @@
     ATRACE_CALL();
 
     void* dso = nullptr;
-    dso = load_angle(kind, cnx);
+    android_namespace_t* ns = android_getAngleNamespace();
+    if (ns) {
+        dso = load_angle(kind, ns, cnx);
+    }
 #ifndef __ANDROID_VNDK__
     if (!dso) {
         android_namespace_t* ns = android_getDriverNamespace();
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index e88d1a2..9cc73f3 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 7089860..8870d5f 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;
 }
 
@@ -262,6 +273,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..29a966d 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,225 @@
 
 #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)
-{
+EGLDisplay eglGetPlatformDisplay(EGLenum platform, EGLNativeDisplayType display,
+                                 const EGLAttrib* attrib_list) {
+    ATRACE_CALL();
     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);
+    if (egl_init_drivers() == EGL_FALSE) {
+        return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
     }
 
-    EGLBoolean res = EGL_FALSE;
-    *num_config = 0;
+    // 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.eglGetPlatformDisplay(platform, display, attrib_list);
+}
+
+EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) {
+    clearError();
 
     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 eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void* native_window,
+                                          const EGLAttrib* 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.eglCreatePlatformWindowSurface(dpy, config, native_window, attrib_list);
 }
 
-EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface,
-                            EGLint attribute, EGLint *value)
-{
+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;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreatePixmapSurface(dpy, config, pixmap, attrib_list);
+}
 
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+EGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap,
+                                          const EGLAttrib* attrib_list) {
+    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.eglCreatePlatformPixmapSurface(dpy, config, native_pixmap, attrib_list);
+}
+
+EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreatePbufferSurface(dpy, config, attrib_list);
+}
+
+EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) {
+    clearError();
+
+    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)
-{
-    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;
-}
-
-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;
-}
-
-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);
-
-    // 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 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)
-{
+EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_list,
+                            const EGLint* attrib_list) {
     clearError();
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    return cnx->egl.eglWaitGL();
+    return cnx->platform.eglCreateContext(dpy, config, share_list, attrib_list);
 }
 
-EGLBoolean eglWaitNative(EGLint engine)
-{
+EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) {
     clearError();
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    return cnx->egl.eglWaitNative(engine);
+    return cnx->platform.eglDestroyContext(dpy, ctx);
 }
 
-EGLint eglGetError(void)
-{
-    EGLint err = EGL_SUCCESS;
+EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
+    clearError();
+
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso) {
-        err = cnx->egl.eglGetError();
-    }
-    if (err == EGL_SUCCESS) {
-        err = egl_tls_t::getError();
-    }
-    return err;
+    return cnx->platform.eglMakeCurrent(dpy, draw, read, ctx);
 }
 
-static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
-        const char* procname) {
-    const egl_connection_t* cnx = &gEGLImpl;
-    void* proc = nullptr;
+EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) {
+    clearError();
 
-    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;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglQueryContext(dpy, ctx, attribute, value);
 }
 
-__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
-{
+EGLContext eglGetCurrentContext(void) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCurrentContext();
+}
+
+EGLSurface eglGetCurrentSurface(EGLint readdraw) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCurrentSurface(readdraw);
+}
+
+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;
+    return cnx->platform.eglGetError();
+}
+
+__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 +243,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 +331,335 @@
     }
 
     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)
-{
+EGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
+                        const EGLAttrib* attrib_list) {
     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.eglCreateImage(dpy, ctx, target, buffer, attrib_list);
+}
+
+EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglDestroyImageKHR(dpy, img);
+}
+
+EGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImageKHR img) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglDestroyImage(dpy, img);
 }
 
 // ----------------------------------------------------------------------------
 // EGL_EGLEXT_VERSION 5
 // ----------------------------------------------------------------------------
 
-
-EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
-{
+EGLSyncKHR eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib* 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.eglCreateSync(dpy, type, attrib_list);
 }
 
-EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
-{
+EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) {
     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.eglCreateSyncKHR(dpy, type, attrib_list);
+}
+
+EGLBoolean eglDestroySync(EGLDisplay dpy, EGLSyncKHR sync) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglDestroySync(dpy, sync);
+}
+
+EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    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 eglClientWaitSync(EGLDisplay dpy, EGLSync 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)
-{
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) {
     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.eglClientWaitSyncKHR(dpy, sync, flags, timeout);
 }
 
-EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint *attrib_list)
-{
+EGLBoolean eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) {
     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.eglGetSyncAttrib(dpy, sync, attribute, value);
 }
 
-EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream)
-{
+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.eglDestroyStreamKHR) {
-        result = cnx->egl.eglDestroyStreamKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglGetSyncAttribKHR(dpy, sync, attribute, value);
 }
 
-EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLint value)
-{
+EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint* attrib_list) {
     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.eglCreateStreamKHR(dpy, attrib_list);
 }
 
-EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLint *value)
-{
+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.eglQueryStreamKHR) {
-        result = cnx->egl.eglQueryStreamKHR(
-                dp->disp.dpy, stream, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglDestroyStreamKHR(dpy, stream);
 }
 
-EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLuint64KHR *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.eglQueryStreamu64KHR) {
-        result = cnx->egl.eglQueryStreamu64KHR(
-                dp->disp.dpy, stream, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglStreamAttribKHR(dpy, stream, attribute, value);
 }
 
-EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLTimeKHR *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.eglQueryStreamTimeKHR) {
-        result = cnx->egl.eglQueryStreamTimeKHR(
-                dp->disp.dpy, stream, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglQueryStreamKHR(dpy, stream, attribute, value);
 }
 
-EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config,
-        EGLStreamKHR stream, const EGLint *attrib_list)
-{
+EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                                EGLuint64KHR* value) {
     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.eglQueryStreamu64KHR(dpy, stream, attribute, value);
 }
 
-EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy,
-        EGLStreamKHR stream)
-{
+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.eglStreamConsumerGLTextureExternalKHR) {
-        result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglQueryStreamTimeKHR(dpy, stream, attribute, value);
 }
 
-EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy,
-        EGLStreamKHR stream)
-{
+EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream,
+                                             const EGLint* attrib_list) {
     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.eglCreateStreamProducerSurfaceKHR(dpy, config, stream, attrib_list);
 }
 
-EGLBoolean eglStreamConsumerReleaseKHR(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.eglStreamConsumerReleaseKHR) {
-        result = cnx->egl.eglStreamConsumerReleaseKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglStreamConsumerGLTextureExternalKHR(dpy, stream);
 }
 
-EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(
-        EGLDisplay dpy, EGLStreamKHR stream)
-{
+EGLBoolean eglStreamConsumerAcquireKHR(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.eglStreamConsumerAcquireKHR(dpy, stream);
 }
 
-EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(
-        EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
-{
+EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy, EGLStreamKHR stream) {
     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.eglStreamConsumerReleaseKHR(dpy, stream);
 }
 
-// ----------------------------------------------------------------------------
-// EGL_EGLEXT_VERSION 15
-// ----------------------------------------------------------------------------
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(EGLDisplay dpy, EGLStreamKHR stream) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetStreamFileDescriptorKHR(dpy, stream);
+}
+
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(EGLDisplay dpy,
+                                                  EGLNativeFileDescriptorKHR file_descriptor) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreateStreamFromFileDescriptorKHR(dpy, file_descriptor);
+}
 
 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)
-{
+EGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags) {
     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.eglWaitSync(dpy, sync, flags);
 }
 
-EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface,
-        EGLnsecsANDROID time)
-{
+EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) {
     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.eglDupNativeFenceFDANDROID(dpy, sync);
 }
 
-EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) {
+EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface, EGLnsecsANDROID time) {
     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.eglPresentationTimeANDROID(dpy, surface, time);
 }
 
-// ----------------------------------------------------------------------------
-// NVIDIA extensions
-// ----------------------------------------------------------------------------
-EGLuint64NV eglGetSystemTimeFrequencyNV()
-{
+EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer* buffer) {
+    clearError();
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetNativeClientBufferANDROID(buffer);
+}
+
+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_display.cpp b/opengl/libs/EGL/egl_display.cpp
index d452a6c..476b304 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -46,7 +46,8 @@
 // ----------------------------------------------------------------------------
 
 static char const * const sVendorString     = "Android";
-static char const * const sVersionString    = "1.4 Android META-EGL";
+static char const* const sVersionString14 = "1.4 Android META-EGL";
+static char const* const sVersionString15 = "1.5 Android META-EGL";
 static char const * const sClientApiString  = "OpenGL_ES";
 
 extern char const * const gBuiltinExtensionString;
@@ -119,14 +120,15 @@
     return false;
 }
 
-EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp) {
+EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp,
+                                               const EGLAttrib* attrib_list) {
     if (uintptr_t(disp) >= NUM_DISPLAYS)
         return nullptr;
 
-    return sDisplay[uintptr_t(disp)].getDisplay(disp);
+    return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list);
 }
 
-static void addAnglePlatformAttributes(egl_connection_t* const cnx, const EGLAttrib* attrib_list,
+static bool addAnglePlatformAttributes(egl_connection_t* const cnx, const EGLAttrib* attrib_list,
                                        std::vector<EGLAttrib>& attrs) {
     intptr_t vendorEGL = (intptr_t)cnx->vendorEGL;
 
@@ -153,24 +155,29 @@
         }
     }
 
-    cnx->angleBackend = angleBackendDefault;
-
     // Allow debug property to override application's
     char prop[PROPERTY_VALUE_MAX];
     property_get("debug.angle.backend", prop, "0");
     switch (atoi(prop)) {
         case 1:
             ALOGV("addAnglePlatformAttributes: Requesting OpenGLES back-end");
-            cnx->angleBackend = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
+            angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
             break;
         case 2:
             ALOGV("addAnglePlatformAttributes: Requesting Vulkan back-end");
-            cnx->angleBackend = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
+            angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
             break;
         default:
             break;
     }
 
+    if (cnx->angleBackend == 0) {
+        // Haven't been initialized yet, so set it.
+        cnx->angleBackend = angleBackendDefault;
+    } else if (cnx->angleBackend != angleBackendDefault) {
+        return false;
+    }
+
     attrs.reserve(4 * 2);
 
     attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
@@ -198,6 +205,8 @@
     }
     attrs.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE);
     attrs.push_back(EGL_FALSE);
+
+    return true;
 }
 
 // Initialize function ptrs for ANGLE PlatformMethods struct, used for systrace
@@ -237,24 +246,30 @@
     return true;
 }
 
-static EGLDisplay getDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx) {
+static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx,
+                                          const EGLAttrib* attrib_list, EGLint* error) {
     EGLDisplay dpy = EGL_NO_DISPLAY;
+    *error = EGL_NONE;
 
-    // Locally define this until EGL 1.5 is supported
-    typedef EGLDisplay (*PFNEGLGETPLATFORMDISPLAYPROC)(EGLenum platform, void* native_display,
-                                                       const EGLAttrib* attrib_list);
-
-    PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplay =
-            reinterpret_cast<PFNEGLGETPLATFORMDISPLAYPROC>(
-                    cnx->egl.eglGetProcAddress("eglGetPlatformDisplay"));
-
-    if (eglGetPlatformDisplay) {
+    if (cnx->egl.eglGetPlatformDisplay) {
         std::vector<EGLAttrib> attrs;
-        addAnglePlatformAttributes(cnx, nullptr, attrs);
+        if (attrib_list) {
+            for (const EGLAttrib* attr = attrib_list; *attr != EGL_NONE; attr += 2) {
+                attrs.push_back(attr[0]);
+                attrs.push_back(attr[1]);
+            }
+        }
+
+        if (!addAnglePlatformAttributes(cnx, attrib_list, attrs)) {
+            ALOGE("eglGetDisplay(%p) failed: Mismatch display request", display);
+            *error = EGL_BAD_PARAMETER;
+            return EGL_NO_DISPLAY;
+        }
         attrs.push_back(EGL_NONE);
 
-        dpy = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
-                                    reinterpret_cast<void*>(EGL_DEFAULT_DISPLAY), attrs.data());
+        dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
+                                             reinterpret_cast<void*>(EGL_DEFAULT_DISPLAY),
+                                             attrs.data());
         if (dpy == EGL_NO_DISPLAY) {
             ALOGE("eglGetPlatformDisplay failed!");
         } else {
@@ -270,8 +285,8 @@
     return dpy;
 }
 
-EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) {
-
+EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display,
+                                             const EGLAttrib* attrib_list) {
     std::lock_guard<std::mutex> _l(lock);
     ATRACE_CALL();
 
@@ -283,10 +298,24 @@
         EGLDisplay dpy = EGL_NO_DISPLAY;
 
         if (cnx->useAngle) {
-            dpy = getDisplayAngle(display, cnx);
+            EGLint error;
+            dpy = getPlatformDisplayAngle(display, cnx, attrib_list, &error);
+            if (error != EGL_NONE) {
+                return setError(error, dpy);
+            }
         }
         if (dpy == EGL_NO_DISPLAY) {
-            dpy = cnx->egl.eglGetDisplay(display);
+            // NOTE: eglGetPlatformDisplay with a empty attribute list
+            // behaves the same as eglGetDisplay
+            if (cnx->egl.eglGetPlatformDisplay) {
+                dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANDROID_KHR, display,
+                                                     attrib_list);
+            } else {
+                if (attrib_list) {
+                    ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored");
+                }
+                dpy = cnx->egl.eglGetDisplay(display);
+            }
         }
 
         disp.dpy = dpy;
@@ -305,13 +334,20 @@
         std::unique_lock<std::mutex> _l(refLock);
         refs++;
         if (refs > 1) {
-            if (major != nullptr)
-                *major = VERSION_MAJOR;
-            if (minor != nullptr)
-                *minor = VERSION_MINOR;
+            // We don't know what to report until we know what the
+            // driver supports. Make sure we are initialized before
+            // returning the version info.
             while(!eglIsInitialized) {
                 refCond.wait(_l);
             }
+            egl_connection_t* const cnx = &gEGLImpl;
+
+            // TODO: If device doesn't provide 1.4 or 1.5 then we'll be
+            // changing the behavior from the past where we always advertise
+            // version 1.4. May need to check that revision is valid
+            // before using cnx->major & cnx->minor
+            if (major != nullptr) *major = cnx->major;
+            if (minor != nullptr) *minor = cnx->minor;
             return EGL_TRUE;
         }
         while(eglIsInitialized) {
@@ -358,7 +394,52 @@
 
         // the query strings are per-display
         mVendorString = sVendorString;
-        mVersionString = sVersionString;
+        mVersionString.clear();
+        cnx->driverVersion = EGL_MAKE_VERSION(1, 4, 0);
+        if ((cnx->major == 1) && (cnx->minor == 5)) {
+            mVersionString = sVersionString15;
+            cnx->driverVersion = EGL_MAKE_VERSION(1, 5, 0);
+        } else if ((cnx->major == 1) && (cnx->minor == 4)) {
+            mVersionString = sVersionString14;
+            // Extensions needed for an EGL 1.4 implementation to be
+            // able to support EGL 1.5 functionality
+            std::vector<const char*> egl15extensions = {
+                    "EGL_EXT_client_extensions",
+                    // "EGL_EXT_platform_base",  // implemented by EGL runtime
+                    "EGL_KHR_image_base",
+                    "EGL_KHR_fence_sync",
+                    "EGL_KHR_wait_sync",
+                    "EGL_KHR_create_context",
+                    "EGL_EXT_create_context_robustness",
+                    "EGL_KHR_gl_colorspace",
+                    "EGL_ANDROID_native_fence_sync",
+            };
+            bool extensionsFound = true;
+            for (const auto& name : egl15extensions) {
+                extensionsFound &= findExtension(disp.queryString.extensions, name);
+                ALOGV("Extension %s: %s", name,
+                      findExtension(disp.queryString.extensions, name) ? "Found" : "Missing");
+            }
+            // NOTE: From the spec:
+            // Creation of fence sync objects requires support from the bound
+            // client API, and will not succeed unless the client API satisfies:
+            // client API is OpenGL ES, and either the OpenGL ES version is 3.0
+            // or greater, or the GL_OES_EGL_sync extension is supported.
+            // We don't have a way to check the GL_EXTENSIONS string at this
+            // point in the code, assume that GL_OES_EGL_sync is supported
+            // because EGL_KHR_fence_sync is supported (as verified above).
+            if (extensionsFound) {
+                // Have everything needed to emulate EGL 1.5 so report EGL 1.5
+                // to the application.
+                mVersionString = sVersionString15;
+                cnx->major = 1;
+                cnx->minor = 5;
+            }
+        }
+        if (mVersionString.empty()) {
+            ALOGW("Unexpected driver version: %d.%d, want 1.4 or 1.5", cnx->major, cnx->minor);
+            mVersionString = sVersionString14;
+        }
         mClientApiString = sClientApiString;
 
         mExtensionString = gBuiltinExtensionString;
@@ -419,10 +500,12 @@
             traceGpuCompletion = true;
         }
 
-        if (major != nullptr)
-            *major = VERSION_MAJOR;
-        if (minor != nullptr)
-            *minor = VERSION_MINOR;
+        // TODO: If device doesn't provide 1.4 or 1.5 then we'll be
+        // changing the behavior from the past where we always advertise
+        // version 1.4. May need to check that revision is valid
+        // before using cnx->major & cnx->minor
+        if (major != nullptr) *major = cnx->major;
+        if (minor != nullptr) *minor = cnx->minor;
     }
 
     { // scope for refLock
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index f764028..36856b7 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -49,6 +49,7 @@
 class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
     static egl_display_t sDisplay[NUM_DISPLAYS];
     EGLDisplay getDisplay(EGLNativeDisplayType display);
+    EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list);
     void loseCurrentImpl(egl_context_t * cur_c);
 
 public:
@@ -72,7 +73,7 @@
     bool getObject(egl_object_t* object) const;
 
     static egl_display_t* get(EGLDisplay dpy);
-    static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp);
+    static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list);
 
     EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c,
             EGLSurface draw, EGLSurface read, EGLContext ctx,
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index b587a16..2921d51 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -44,6 +44,18 @@
 
 /* EGL 1.4 */
 
+/* EGL 1.5 */
+EGL_ENTRY(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage)
+EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSyncKHR, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync)
+EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSync, EGLint, EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglWaitSync, EGLDisplay, EGLSync, EGLint)
+
 /* EGL_EGLEXT_VERSION 3 */
 
 EGL_ENTRY(EGLBoolean,  eglLockSurfaceKHR,   EGLDisplay, EGLSurface, const EGLint *)
@@ -75,6 +87,10 @@
 EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR,   EGLDisplay, EGLNativeFileDescriptorKHR)
 EGL_ENTRY(EGLint,       eglWaitSyncKHR,         EGLDisplay, EGLSyncKHR, EGLint)
 
+/* EGL_EGLEXT_VERSION 20170627 */
+EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *)
+
 /* ANDROID extensions */
 
 EGL_ENTRY(EGLBoolean, eglSetSwapRectangleANDROID, EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint)
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..1daa4d2
--- /dev/null
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -0,0 +1,2717 @@
+/*
+ ** 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 "
+        ;
+
+char const * const gClientExtensionString =
+        "EGL_EXT_client_extensions "
+        "EGL_KHR_platform_android "
+        "EGL_ANGLE_platform_angle";
+// 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(); }
+
+// ----------------------------------------------------------------------------
+
+static EGLDisplay eglGetPlatformDisplayTmpl(EGLenum platform, EGLNativeDisplayType display,
+                                            const EGLAttrib* attrib_list) {
+    if (platform != EGL_PLATFORM_ANDROID_KHR) {
+        return setError(EGL_BAD_PARAMETER, EGL_NO_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, attrib_list);
+    return dpy;
+}
+
+EGLDisplay eglGetDisplayImpl(EGLNativeDisplayType display) {
+    return eglGetPlatformDisplayTmpl(EGL_PLATFORM_ANDROID_KHR, display, nullptr);
+}
+
+EGLDisplay eglGetPlatformDisplayImpl(EGLenum platform, void* native_display,
+                                     const EGLAttrib* attrib_list) {
+    return eglGetPlatformDisplayTmpl(platform, static_cast<EGLNativeDisplayType>(native_display),
+                                     attrib_list);
+}
+
+// ----------------------------------------------------------------------------
+// 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.
+template <typename AttrType>
+static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window,
+                                    android_pixel_format format, const AttrType* attrib_list,
+                                    EGLint* colorSpace, std::vector<AttrType>* strippedAttribList) {
+    for (const AttrType* 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 = static_cast<EGLint>(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 (static_cast<EGLint>(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;
+}
+
+// Note: This only works for existing GLenum's that are all 32bits.
+// If you have 64bit attributes (e.g. pointers) you shouldn't be calling this.
+void convertAttribs(const EGLAttrib* attribList, std::vector<EGLint>& newList) {
+    for (const EGLAttrib* attr = attribList; attr && attr[0] != EGL_NONE; attr += 2) {
+        newList.push_back(static_cast<EGLint>(attr[0]));
+        newList.push_back(static_cast<EGLint>(attr[1]));
+    }
+    newList.push_back(EGL_NONE);
+}
+
+// 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;
+}
+
+template <typename AttrType, typename CreateFuncType>
+EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config,
+                                      ANativeWindow* window, const AttrType* attrib_list,
+                                      CreateFuncType createWindowSurfaceFunc) {
+    const AttrType* origAttribList = attrib_list;
+
+    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<AttrType> strippedAttribList;
+    if (!processAttributes<AttrType>(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.
+    window->setSwapInterval(window, 1);
+
+    EGLSurface surface = createWindowSurfaceFunc(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;
+}
+
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay dpy, EGLConfig config,
+                                                               NativeWindowType window,
+                                                               const EGLint* attrib_list);
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC)(
+        EGLDisplay dpy, EGLConfig config, void* native_window, const EGLAttrib* attrib_list);
+
+EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
+                                      const EGLint* attrib_list) {
+    egl_connection_t* cnx = NULL;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        return eglCreateWindowSurfaceTmpl<
+                EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list,
+                                                       cnx->egl.eglCreateWindowSurface);
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window,
+                                              const EGLAttrib* attrib_list) {
+    egl_connection_t* cnx = NULL;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+            if (cnx->egl.eglCreatePlatformWindowSurface) {
+                return eglCreateWindowSurfaceTmpl<EGLAttrib, PFNEGLCREATEPLATFORMWINDOWSURFACEPROC>(
+                        dp, cnx, config, static_cast<ANativeWindow*>(native_window), attrib_list,
+                        cnx->egl.eglCreatePlatformWindowSurface);
+            }
+            // driver doesn't support native function, return EGL_BAD_DISPLAY
+            ALOGE("Driver indicates EGL 1.5 support, but does not have "
+                  "eglCreatePlatformWindowSurface");
+            return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
+        }
+
+        std::vector<EGLint> convertedAttribs;
+        convertAttribs(attrib_list, convertedAttribs);
+        if (cnx->egl.eglCreatePlatformWindowSurfaceEXT) {
+            return eglCreateWindowSurfaceTmpl<EGLint, PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(
+                    dp, cnx, config, static_cast<ANativeWindow*>(native_window),
+                    convertedAttribs.data(), cnx->egl.eglCreatePlatformWindowSurfaceEXT);
+        } else {
+            return eglCreateWindowSurfaceTmpl<
+                    EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config,
+                                                           static_cast<ANativeWindow*>(
+                                                                   native_window),
+                                                           convertedAttribs.data(),
+                                                           cnx->egl.eglCreateWindowSurface);
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePlatformPixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/,
+                                              void* /*native_pixmap*/,
+                                              const EGLAttrib* /*attrib_list*/) {
+    // Per EGL_KHR_platform_android:
+    // It is not valid to call eglCreatePlatformPixmapSurface with a <dpy> that
+    // belongs to the Android platform. Any such call fails and generates
+    // an EGL_BAD_PARAMETER error.
+
+    egl_connection_t* cnx = NULL;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+    }
+    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) {
+        return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+    }
+    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)
+{
+    if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) {
+        // Return list of client extensions
+        return gClientExtensionString;
+    }
+
+    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);
+}
+
+// Note: EGLImageKHR and EGLImage are the same thing so no need
+// to templatize that.
+template <typename AttrType, typename FuncType>
+EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+                               EGLClientBuffer buffer, const AttrType* attrib_list,
+                               FuncType eglCreateImageFunc) {
+    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 && eglCreateImageFunc) {
+        result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer,
+                                    attrib_list);
+    }
+    return result;
+}
+
+typedef EGLImage(EGLAPIENTRYP PFNEGLCREATEIMAGE)(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+                                                 EGLClientBuffer buffer,
+                                                 const EGLAttrib* attrib_list);
+
+EGLImageKHR eglCreateImageKHRImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+                                  EGLClientBuffer buffer, const EGLint* attrib_list) {
+    return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer,
+                                                                attrib_list,
+                                                                gEGLImpl.egl.eglCreateImageKHR);
+}
+
+EGLImage eglCreateImageImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
+                            const EGLAttrib* attrib_list) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglCreateImage) {
+            return eglCreateImageTmpl<EGLAttrib, PFNEGLCREATEIMAGE>(dpy, ctx, target, buffer,
+                                                                    attrib_list,
+                                                                    cnx->egl.eglCreateImage);
+        }
+        // driver doesn't support native function, return EGL_BAD_DISPLAY
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateImage");
+        return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE);
+    }
+
+    std::vector<EGLint> convertedAttribs;
+    convertAttribs(attrib_list, convertedAttribs);
+    return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer,
+                                                                convertedAttribs.data(),
+                                                                gEGLImpl.egl.eglCreateImageKHR);
+}
+
+EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img,
+                               PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) {
+    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 && destroyImageFunc) {
+        result = destroyImageFunc(dp->disp.dpy, img);
+    }
+    return result;
+}
+
+EGLBoolean eglDestroyImageKHRImpl(EGLDisplay dpy, EGLImageKHR img) {
+    return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR);
+}
+
+EGLBoolean eglDestroyImageImpl(EGLDisplay dpy, EGLImageKHR img) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglDestroyImage) {
+            return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImage);
+        }
+        // driver doesn't support native function, return EGL_BAD_DISPLAY
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroyImage");
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    }
+
+    return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR);
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 5
+// ----------------------------------------------------------------------------
+
+// NOTE: EGLSyncKHR and EGLSync are identical, no need to templatize
+template <typename AttrType, typename FuncType>
+EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list,
+                             FuncType eglCreateSyncFunc) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_SYNC_KHR;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    EGLSyncKHR result = EGL_NO_SYNC_KHR;
+    if (cnx->dso && eglCreateSyncFunc) {
+        result = eglCreateSyncFunc(dp->disp.dpy, type, attrib_list);
+    }
+    return result;
+}
+
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATESYNC)(EGLDisplay dpy, EGLenum type,
+                                                  const EGLAttrib* attrib_list);
+
+EGLSyncKHR eglCreateSyncKHRImpl(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) {
+    return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, attrib_list,
+                                                              gEGLImpl.egl.eglCreateSyncKHR);
+}
+
+EGLSync eglCreateSyncImpl(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglCreateSync) {
+            return eglCreateSyncTmpl<EGLAttrib, PFNEGLCREATESYNC>(dpy, type, attrib_list,
+                                                                  cnx->egl.eglCreateSync);
+        }
+        // driver doesn't support native function, return EGL_BAD_DISPLAY
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateSync");
+        return setError(EGL_BAD_DISPLAY, EGL_NO_SYNC);
+    }
+
+    std::vector<EGLint> convertedAttribs;
+    convertAttribs(attrib_list, convertedAttribs);
+    return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, convertedAttribs.data(),
+                                                              cnx->egl.eglCreateSyncKHR);
+}
+
+EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync,
+                              PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) {
+    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 && eglDestroySyncFunc) {
+        result = eglDestroySyncFunc(dp->disp.dpy, sync);
+    }
+    return result;
+}
+
+EGLBoolean eglDestroySyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+    return eglDestroySyncTmpl(dpy, sync, gEGLImpl.egl.eglDestroySyncKHR);
+}
+
+EGLBoolean eglDestroySyncImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglDestroySync) {
+            return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySync);
+        }
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroySync");
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    }
+
+    return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySyncKHR);
+}
+
+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 && gEGLImpl.egl.eglSignalSyncKHR) {
+        result = gEGLImpl.egl.eglSignalSyncKHR(dp->disp.dpy, sync, mode);
+    }
+    return result;
+}
+
+EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout,
+                             PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) {
+    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 && eglClientWaitSyncFunc) {
+        result = eglClientWaitSyncFunc(dp->disp.dpy, sync, flags, timeout);
+    }
+    return result;
+}
+
+EGLint eglClientWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR);
+}
+
+EGLint eglClientWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglClientWaitSync) {
+            return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSync);
+        }
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglClientWaitSync");
+        return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+    }
+
+    return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR);
+}
+
+template <typename AttrType, typename FuncType>
+EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value,
+                                FuncType eglGetSyncAttribFunc) {
+    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 && eglGetSyncAttribFunc) {
+        result = eglGetSyncAttribFunc(dp->disp.dpy, sync, attribute, value);
+    }
+    return result;
+}
+
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLGETSYNCATTRIB)(EGLDisplay dpy, EGLSync sync, EGLint attribute,
+                                                     EGLAttrib* value);
+
+EGLBoolean eglGetSyncAttribImpl(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglGetSyncAttrib) {
+            return eglGetSyncAttribTmpl<EGLAttrib, PFNEGLGETSYNCATTRIB>(dpy, sync, attribute, value,
+                                                                        cnx->egl.eglGetSyncAttrib);
+        }
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglGetSyncAttrib");
+        return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+    }
+
+    // Fallback to KHR, ask for EGLint attribute and cast back to EGLAttrib
+    EGLint attribValue;
+    EGLBoolean ret =
+            eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute,
+                                                                     &attribValue,
+                                                                     gEGLImpl.egl
+                                                                             .eglGetSyncAttribKHR);
+    if (ret) {
+        *value = static_cast<EGLAttrib>(attribValue);
+    }
+    return ret;
+}
+
+EGLBoolean eglGetSyncAttribKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute,
+                                   EGLint* value) {
+    return eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute, value,
+                                                                    gEGLImpl.egl
+                                                                            .eglGetSyncAttribKHR);
+}
+
+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
+// ----------------------------------------------------------------------------
+
+// Need to template function type because return type is different
+template <typename ReturnType, typename FuncType>
+ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags,
+                           FuncType eglWaitSyncFunc) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+    ReturnType result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && eglWaitSyncFunc) {
+        result = eglWaitSyncFunc(dp->disp.dpy, sync, flags);
+    }
+    return result;
+}
+
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLWAITSYNC)(EGLDisplay dpy, EGLSync sync, EGLint flags);
+
+EGLint eglWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    return eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags,
+                                                          cnx->egl.eglWaitSyncKHR);
+}
+
+EGLBoolean eglWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglWaitSync) {
+            return eglWaitSyncTmpl<EGLBoolean, PFNEGLWAITSYNC>(dpy, sync, flags,
+                                                               cnx->egl.eglWaitSync);
+        }
+        return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+    }
+
+    return static_cast<EGLBoolean>(
+            eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags,
+                                                           cnx->egl.eglWaitSyncKHR));
+}
+
+// ----------------------------------------------------------------------------
+// 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[] = {
+        // clang-format off
+    { "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl },
+    { "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl },
+    { "eglInitialize", (EGLFuncPointer)&eglInitializeImpl },
+    { "eglTerminate", (EGLFuncPointer)&eglTerminateImpl },
+    { "eglGetConfigs", (EGLFuncPointer)&eglGetConfigsImpl },
+    { "eglChooseConfig", (EGLFuncPointer)&eglChooseConfigImpl },
+    { "eglGetConfigAttrib", (EGLFuncPointer)&eglGetConfigAttribImpl },
+    { "eglCreateWindowSurface", (EGLFuncPointer)&eglCreateWindowSurfaceImpl },
+    { "eglCreatePixmapSurface", (EGLFuncPointer)&eglCreatePixmapSurfaceImpl },
+    { "eglCreatePlatformWindowSurface", (EGLFuncPointer)&eglCreatePlatformWindowSurfaceImpl },
+    { "eglCreatePlatformPixmapSurface", (EGLFuncPointer)&eglCreatePlatformPixmapSurfaceImpl },
+    { "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 },
+    { "eglCreateImage", (EGLFuncPointer)&eglCreateImageImpl },
+    { "eglDestroyImage", (EGLFuncPointer)&eglDestroyImageImpl },
+    { "eglCreateSync", (EGLFuncPointer)&eglCreateSyncImpl },
+    { "eglDestroySync", (EGLFuncPointer)&eglDestroySyncImpl },
+    { "eglClientWaitSync", (EGLFuncPointer)&eglClientWaitSyncImpl },
+    { "eglGetSyncAttrib", (EGLFuncPointer)&eglGetSyncAttribImpl },
+    { "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 },
+    { "eglWaitSync", (EGLFuncPointer)&eglWaitSyncImpl },
+    { "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 },
+        // clang-format on
+};
+
+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..85b1db3
--- /dev/null
+++ b/opengl/libs/EGL/egl_platform_entries.h
@@ -0,0 +1,32 @@
+/*
+ * 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 {
+
+EGLint eglGetErrorImpl();
+EGLFuncPointer FindPlatformImplAddr(const char* name);
+
+}; // namespace android
+
+#endif // ANDROID_EGLAPI_H
+
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index b57c357..aaecb62 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -21,6 +21,7 @@
 #include <cutils/properties.h>
 #include <log/log.h>
 #include "CallStack.h"
+#include "egl_platform_entries.h"
 
 namespace android {
 
@@ -137,7 +138,7 @@
 void egl_tls_t::clearError() {
     // This must clear the error from all the underlying EGL implementations as
     // well as the EGL wrapper layer.
-    eglGetError();
+    android::eglGetErrorImpl();
 }
 
 EGLint egl_tls_t::getError() {
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 449ffc7..7c710d5 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -18,9 +18,13 @@
 #define ANDROID_EGLDEFS_H
 
 #include "../hooks.h"
+#include "egl_platform_entries.h"
+
+#include <log/log.h>
 
 #define VERSION_MAJOR 1
 #define VERSION_MINOR 4
+#define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -31,19 +35,43 @@
 
 // ----------------------------------------------------------------------------
 
+extern char const * const platform_names[];
+
+// clang-format off
 struct egl_connection_t {
     enum {
         GLESv1_INDEX = 0,
         GLESv2_INDEX = 1
     };
 
-    inline egl_connection_t() : dso(nullptr) { }
+    inline egl_connection_t() : dso(nullptr) {
+
+        char const* const* entries = platform_names;
+        EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&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++;
+        }
+    }
+
     void *              dso;
     gl_hooks_t *        hooks[2];
     EGLint              major;
     EGLint              minor;
+    EGLint              driverVersion;
     egl_t               egl;
 
+    // Functions implemented or redirected by platform libraries
+    platform_impl_t     platform;
+
     void*               libEgl;
     void*               libGles1;
     void*               libGles2;
@@ -52,6 +80,7 @@
     EGLint              angleBackend;
     void*               vendorEGL;
 };
+// clang-format on
 
 // ----------------------------------------------------------------------------
 
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..4673411
--- /dev/null
+++ b/opengl/libs/platform_entries.in
@@ -0,0 +1,86 @@
+EGL_ENTRY(EGLDisplay, eglGetDisplay, EGLNativeDisplayType)
+EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, EGLNativeDisplayType, const EGLAttrib*)
+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, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*)
+EGL_ENTRY(EGLSurface, eglCreatePixmapSurface, EGLDisplay, EGLConfig, NativePixmapType, const EGLint*)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*)
+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(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib*)
+EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage)
+EGL_ENTRY(EGLImageKHR, eglCreateImageKHR, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyImageKHR, EGLDisplay, EGLImageKHR)
+EGL_ENTRY(EGLSync, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib*)
+EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync)
+EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSyncKHR, EGLint, EGLAttrib*)
+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, eglWaitSync, EGLDisplay, EGLSync, EGLint)
+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*)
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index f9e96ea..9fa58e2 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -93,6 +93,7 @@
 pushd out > /dev/null
 mkdir classes
 javac -d classes    android/opengl/EGL14.java \
+                    android/opengl/EGL15.java \
                     android/opengl/EGLExt.java \
                     com/google/android/gles_jni/GLImpl.java \
                     javax/microedition/khronos/opengles/GL10.java \
@@ -155,13 +156,13 @@
     compareGenerated ../../../../base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x
 done
 
-for x in EGL14 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32
+for x in EGL14 EGL15 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32
 do
     compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
     compareGenerated ../../../../base/core/jni generated/C android_opengl_${x}.cpp
 done
 
-for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface
+for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface EGLImage EGLSync
 do
     compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
 done
diff --git a/opengl/tools/glgen/specs/egl/EGL15.spec b/opengl/tools/glgen/specs/egl/EGL15.spec
new file mode 100644
index 0000000..e0aad30
--- /dev/null
+++ b/opengl/tools/glgen/specs/egl/EGL15.spec
@@ -0,0 +1,14 @@
+EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list )
+EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync )
+EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout )
+EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value )
+// NOTE: native_display isn't actually an EGLAttrib. Using EGLAttrib
+// so that the generate creates mostly correct code (do not want a buffer)
+// have to manually change cast to (void *) in generated code that calls
+// the native function.
+EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list )
+EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list )
+EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags )
+EGLImage eglCreateImage ( EGLDisplay dpy, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list )
+EGLBoolean eglDestroyImage ( EGLDisplay dpy, EGLImage image )
diff --git a/opengl/tools/glgen/specs/egl/checks.spec b/opengl/tools/glgen/specs/egl/checks.spec
index ae531ee..e2bae48 100644
--- a/opengl/tools/glgen/specs/egl/checks.spec
+++ b/opengl/tools/glgen/specs/egl/checks.spec
@@ -11,3 +11,5 @@
 //STUB function: eglCreatePbufferFromClientBuffer nullAllowed attrib_list sentinel attrib_list EGL_NONE
 eglCreateContext sentinel attrib_list EGL_NONE
 eglQueryContext check value 1
+//unsupported: eglCreatePlatformPixmapSurface nullAllowed attrib_list sentinel attrib_list EGL_NONE
+eglCreatePlatformPixmapSurface unsupported
diff --git a/opengl/tools/glgen/src/CType.java b/opengl/tools/glgen/src/CType.java
index aba98af..b1f8e05 100644
--- a/opengl/tools/glgen/src/CType.java
+++ b/opengl/tools/glgen/src/CType.java
@@ -57,7 +57,9 @@
         if(baseType.equals("EGLContext")
            || baseType.equals("EGLConfig")
            || baseType.equals("EGLSurface")
-           || baseType.equals("EGLDisplay")) {
+           || baseType.equals("EGLDisplay")
+           || baseType.equals("EGLImage")
+           || baseType.equals("EGLSync")) {
                return true;
         }
         return false;
diff --git a/opengl/tools/glgen/src/GenerateEGL.java b/opengl/tools/glgen/src/GenerateEGL.java
index 2ef3970..57958c6 100644
--- a/opengl/tools/glgen/src/GenerateEGL.java
+++ b/opengl/tools/glgen/src/GenerateEGL.java
@@ -84,7 +84,7 @@
         ParameterChecker checker = new ParameterChecker(checksReader);
 
 
-        for(String suffix: new String[] {"EGL14", "EGLExt"}) {
+        for(String suffix: new String[] {"EGL14", "EGL15", "EGLExt"}) {
             BufferedReader specReader = new BufferedReader(new FileReader(
                     "specs/egl/" + suffix + ".spec"));
             String egljFilename = "android/opengl/" + suffix + ".java";
diff --git a/opengl/tools/glgen/src/JType.java b/opengl/tools/glgen/src/JType.java
index 7f08503..0b4401a 100644
--- a/opengl/tools/glgen/src/JType.java
+++ b/opengl/tools/glgen/src/JType.java
@@ -60,12 +60,16 @@
     typeMapping.put(new CType("EGLNativeDisplayType"), new JType("long"));
     typeMapping.put(new CType("EGLClientBuffer"), new JType("long"));
     typeMapping.put(new CType("EGLnsecsANDROID"), new JType("long"));
+    typeMapping.put(new CType("EGLAttrib"), new JType("long"));
+    typeMapping.put(new CType("EGLTime"), new JType("long"));
 
     // EGL nonprimitive types
     typeMapping.put(new CType("EGLConfig"), new JType("EGLConfig", true, false));
     typeMapping.put(new CType("EGLContext"), new JType("EGLContext", true, false));
     typeMapping.put(new CType("EGLDisplay"), new JType("EGLDisplay", true, false));
     typeMapping.put(new CType("EGLSurface"), new JType("EGLSurface", true, false));
+    typeMapping.put(new CType("EGLImage"), new JType("EGLImage", true, false));
+    typeMapping.put(new CType("EGLSync"), new JType("EGLSync", true, false));
 
 
     // Untyped pointers map to untyped Buffers
@@ -139,6 +143,8 @@
     arrayTypeMapping.put(new CType("EGLint", true, true), new JType("int", false, true));
     arrayTypeMapping.put(new CType("EGLConfig", false, true), new JType("EGLConfig", true, true));
     arrayTypeMapping.put(new CType("EGLConfig", true, true), new JType("EGLConfig", true, true));
+    arrayTypeMapping.put(new CType("EGLAttrib", false, true), new JType("long", false, true));
+    arrayTypeMapping.put(new CType("EGLAttrib", true, true), new JType("long", false, true));
 
     }
 
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index e8691bb..6697189 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -103,6 +103,12 @@
             if (cfunc.hasEGLHandleArg()) {
                 return;
             }
+            // eglGetPlatformDisplay does not have any EGLHandleArgs
+            // but we do not want to create IOBuffers of this, so
+            // exit
+            if (cfunc.getName().equals("eglGetPlatformDisplay")) {
+                return;
+            }
         }
 
         jfunc = JFunc.convert(cfunc, false);
diff --git a/opengl/tools/glgen/static/egl/EGLImage.java b/opengl/tools/glgen/static/egl/EGLImage.java
new file mode 100644
index 0000000..731ce72
--- /dev/null
+++ b/opengl/tools/glgen/static/egl/EGLImage.java
@@ -0,0 +1,37 @@
+/*
+**
+** 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.
+*/
+
+package android.opengl;
+
+/**
+ * Wrapper class for native EGLImage objects.
+ *
+ */
+public class EGLImage extends EGLObjectHandle {
+    private EGLImage(long handle) {
+        super(handle);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EGLImage)) return false;
+
+        EGLImage that = (EGLImage) o;
+        return getNativeHandle() == that.getNativeHandle();
+    }
+}
diff --git a/opengl/tools/glgen/static/egl/EGLSync.java b/opengl/tools/glgen/static/egl/EGLSync.java
new file mode 100644
index 0000000..472f9e7
--- /dev/null
+++ b/opengl/tools/glgen/static/egl/EGLSync.java
@@ -0,0 +1,37 @@
+/*
+**
+** 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.
+*/
+
+package android.opengl;
+
+/**
+ * Wrapper class for native EGLSync objects.
+ *
+ */
+public class EGLSync extends EGLObjectHandle {
+    private EGLSync(long handle) {
+        super(handle);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EGLSync)) return false;
+
+        EGLSync that = (EGLSync) o;
+        return getNativeHandle() == that.getNativeHandle();
+    }
+}
diff --git a/opengl/tools/glgen/stubs/egl/EGL15Header.java-if b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if
new file mode 100644
index 0000000..7409d93
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if
@@ -0,0 +1,76 @@
+/*
+** 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.
+*/
+
+package android.opengl;
+
+/**
+ * EGL 1.5
+ *
+ */
+public class EGL15 {
+
+    public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT            = 0x00000001;
+    public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT   = 0x00000002;
+    public static final int EGL_OPENGL_ES3_BIT                             = 0x00000040;
+    public static final int EGL_SYNC_FLUSH_COMMANDS_BIT                    = 0x0001;
+    public static final int EGL_GL_COLORSPACE_SRGB                         = 0x3089;
+    public static final int EGL_GL_COLORSPACE_LINEAR                       = 0x308A;
+    public static final int EGL_CONTEXT_MAJOR_VERSION                      = 0x3098;
+    public static final int EGL_CL_EVENT_HANDLE                            = 0x309C;
+    public static final int EGL_GL_COLORSPACE                              = 0x309D;
+    public static final int EGL_GL_TEXTURE_2D                              = 0x30B1;
+    public static final int EGL_GL_TEXTURE_3D                              = 0x30B2;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X             = 0x30B3;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X             = 0x30B4;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y             = 0x30B5;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y             = 0x30B6;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z             = 0x30B7;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z             = 0x30B8;
+    public static final int EGL_GL_RENDERBUFFER                            = 0x30B9;
+    public static final int EGL_GL_TEXTURE_LEVEL                           = 0x30BC;
+    public static final int EGL_GL_TEXTURE_ZOFFSET                         = 0x30BD;
+    public static final int EGL_IMAGE_PRESERVED                            = 0x30D2;
+    public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE               = 0x30F0;
+    public static final int EGL_SYNC_STATUS                                = 0x30F1;
+    public static final int EGL_SIGNALED                                   = 0x30F2;
+    public static final int EGL_UNSIGNALED                                 = 0x30F3;
+    public static final int EGL_TIMEOUT_EXPIRED                            = 0x30F5;
+    public static final int EGL_CONDITION_SATISFIED                        = 0x30F6;
+    public static final int EGL_SYNC_TYPE                                  = 0x30F7;
+    public static final int EGL_SYNC_CONDITION                             = 0x30F8;
+    public static final int EGL_SYNC_FENCE                                 = 0x30F9;
+    public static final int EGL_CONTEXT_MINOR_VERSION                      = 0x30FB;
+    public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK                = 0x30FD;
+    public static final int EGL_SYNC_CL_EVENT                              = 0x30FE;
+    public static final int EGL_SYNC_CL_EVENT_COMPLETE                     = 0x30FF;
+    public static final int EGL_CONTEXT_OPENGL_DEBUG                       = 0x31B0;
+    public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE          = 0x31B1;
+    public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS               = 0x31B2;
+    public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 0x31BD;
+    public static final int EGL_NO_RESET_NOTIFICATION                      = 0x31BE;
+    public static final int EGL_LOSE_CONTEXT_ON_RESET                      = 0x31BF;
+    public static final int EGL_PLATFORM_ANDROID_KHR                       = 0x3141;
+    public static final long EGL_FOREVER                                   = 0xFFFFFFFFFFFFFFFFL;
+    public static final EGLImage EGL_NO_IMAGE                              = null;
+    public static final EGLSync EGL_NO_SYNC                                = null;
+    public static final EGLContext EGL_NO_CONTEXT                          = null;
+    public static final EGLDisplay EGL_NO_DISPLAY                          = null;
+    public static final EGLSurface EGL_NO_SURFACE                          = null;
+
+    native private static void _nativeClassInit();
+    static {
+        _nativeClassInit();
+    }
diff --git a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
new file mode 100644
index 0000000..70b46f7
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
@@ -0,0 +1,205 @@
+/*
+** 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.
+*/
+
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/misc.h>
+#include "jni.h"
+
+#include <EGL/egl.h>
+#include <assert.h>
+
+#include <ui/ANativeObjectBase.h>
+
+static int initialized = 0;
+
+// classes from EGL 1.4
+static jclass egldisplayClass;
+static jclass eglsurfaceClass;
+static jclass eglconfigClass;
+static jclass eglcontextClass;
+static jclass bufferClass;
+static jclass nioAccessClass;
+
+static jfieldID positionID;
+static jfieldID limitID;
+static jfieldID elementSizeShiftID;
+
+static jmethodID getBasePointerID;
+static jmethodID getBaseArrayID;
+static jmethodID getBaseArrayOffsetID;
+
+static jmethodID egldisplayGetHandleID;
+static jmethodID eglconfigGetHandleID;
+static jmethodID eglcontextGetHandleID;
+static jmethodID eglsurfaceGetHandleID;
+
+static jmethodID egldisplayConstructor;
+static jmethodID eglcontextConstructor;
+static jmethodID eglsurfaceConstructor;
+static jmethodID eglconfigConstructor;
+
+static jobject eglNoContextObject;
+static jobject eglNoDisplayObject;
+static jobject eglNoSurfaceObject;
+
+// classes from EGL 1.5
+static jclass eglimageClass;
+static jclass eglsyncClass;
+
+static jmethodID eglimageGetHandleID;
+static jmethodID eglsyncGetHandleID;
+
+static jmethodID eglimageConstructor;
+static jmethodID eglsyncConstructor;
+
+static jobject eglNoImageObject;
+static jobject eglNoSyncObject;
+
+/* Cache method IDs each time the class is loaded. */
+
+static void nativeClassInit(JNIEnv *_env, jclass glImplClass) {
+    // EGL 1.4 Init
+    jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig");
+    eglconfigClass = (jclass)_env->NewGlobalRef(eglconfigClassLocal);
+    jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext");
+    eglcontextClass = (jclass)_env->NewGlobalRef(eglcontextClassLocal);
+    jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay");
+    egldisplayClass = (jclass)_env->NewGlobalRef(egldisplayClassLocal);
+    jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface");
+    eglsurfaceClass = (jclass)_env->NewGlobalRef(eglsurfaceClassLocal);
+
+    eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J");
+    eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J");
+    egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
+    eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
+
+    eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V");
+    eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V");
+    egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V");
+    eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V");
+
+    jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor,
+                                                      reinterpret_cast<jlong>(EGL_NO_CONTEXT));
+    eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject);
+    jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor,
+                                                      reinterpret_cast<jlong>(EGL_NO_DISPLAY));
+    eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject);
+    jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor,
+                                                      reinterpret_cast<jlong>(EGL_NO_SURFACE));
+    eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject);
+
+    jclass eglClass = _env->FindClass("android/opengl/EGL15");
+    jfieldID noContextFieldID =
+            _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;");
+    _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject);
+
+    jfieldID noDisplayFieldID =
+            _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;");
+    _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject);
+
+    jfieldID noSurfaceFieldID =
+            _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;");
+    _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject);
+
+    // EGL 1.5 init
+    jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
+    nioAccessClass = (jclass)_env->NewGlobalRef(nioAccessClassLocal);
+
+    jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
+    bufferClass = (jclass)_env->NewGlobalRef(bufferClassLocal);
+
+    getBasePointerID =
+            _env->GetStaticMethodID(nioAccessClass, "getBasePointer", "(Ljava/nio/Buffer;)J");
+    getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, "getBaseArray",
+                                             "(Ljava/nio/Buffer;)Ljava/lang/Object;");
+    getBaseArrayOffsetID =
+            _env->GetStaticMethodID(nioAccessClass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
+
+    positionID = _env->GetFieldID(bufferClass, "position", "I");
+    limitID = _env->GetFieldID(bufferClass, "limit", "I");
+    elementSizeShiftID = _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
+
+    jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage");
+    eglimageClass = (jclass)_env->NewGlobalRef(eglimageClassLocal);
+    jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync");
+    eglsyncClass = (jclass)_env->NewGlobalRef(eglsyncClassLocal);
+
+    eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+    eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+    eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V");
+    eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V");
+
+    jfieldID noImageFieldID =
+            _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;");
+    _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject);
+
+    jfieldID noSyncFieldID =
+            _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;");
+    _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject);
+}
+
+static void *getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining,
+                        jint *offset) {
+    jint position;
+    jint limit;
+    jint elementSizeShift;
+    jlong pointer;
+
+    position = _env->GetIntField(buffer, positionID);
+    limit = _env->GetIntField(buffer, limitID);
+    elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+    *remaining = (limit - position) << elementSizeShift;
+    pointer = _env->CallStaticLongMethod(nioAccessClass, getBasePointerID, buffer);
+    if (pointer != 0L) {
+        *array = NULL;
+        return reinterpret_cast<void *>(pointer);
+    }
+    eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+    eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+    *array = (jarray)_env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer);
+    *offset = _env->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetID, buffer);
+
+    return NULL;
+}
+
+static void releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) {
+    _env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT);
+}
+
+static void *fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) {
+    if (obj == NULL) {
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null.");
+    }
+
+    jlong handle = _env->CallLongMethod(obj, mid);
+    return reinterpret_cast<void *>(handle);
+}
+
+static jobject toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void *handle) {
+    if (cls == eglimageClass && (EGLImage)handle == EGL_NO_IMAGE) {
+        return eglNoImageObject;
+    }
+
+    return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle));
+}
+
+// --------------------------------------------------------------------------
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp
new file mode 100644
index 0000000..fd44498
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp
@@ -0,0 +1,46 @@
+/* EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglGetPlatformDisplay
+  (JNIEnv *_env, jobject _this, jint platform, jlong native_display, jlongArray attrib_list_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    EGLDisplay _returnValue = (EGLDisplay) 0;
+    EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+    jint _remaining;
+    EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+    if (!attrib_list_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "attrib_list == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+    attrib_list_base = (EGLAttrib *)
+        _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+    attrib_list = attrib_list_base + offset;
+
+    _returnValue = eglGetPlatformDisplay(
+        (EGLenum)platform,
+        (void *)native_display,
+        (EGLAttrib *)attrib_list
+    );
+
+exit:
+    if (attrib_list_base) {
+        _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+            JNI_ABORT);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue);
+}
+
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java
new file mode 100644
index 0000000..28945e8
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java
@@ -0,0 +1,9 @@
+    // C function EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+
+    public static native EGLDisplay eglGetPlatformDisplay(
+        int platform,
+        long native_display,
+        long[] attrib_list,
+        int offset
+    );
+
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg
new file mode 100644
index 0000000..8a309bf
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg
@@ -0,0 +1 @@
+{"eglGetPlatformDisplay", "(IJ[JI)Landroid/opengl/EGLDisplay;", (void *) android_eglGetPlatformDisplay },
diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml
index e422e96..26771e1 100644
--- a/opengl/tools/glgen2/registry/egl.xml
+++ b/opengl/tools/glgen2/registry/egl.xml
@@ -801,7 +801,9 @@
         <enum value="0x3361" name="EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT"/>
         <enum value="0x3362" name="EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT"/>
         <enum value="0x3363" name="EGL_GL_COLORSPACE_DISPLAY_P3_EXT"/>
-            <unused start="0x3364" end="0x339F"/>
+        <enum value="0x3364" name="EGL_SYNC_CLIENT_EXT"/>
+        <enum value="0x3365" name="EGL_SYNC_CLIENT_SIGNAL_EXT"/>
+            <unused start="0x3366" end="0x339F"/>
     </enums>
 
     <enums namespace="EGL" start="0x33A0" end="0x33AF" vendor="ANGLE" comment="Reserved for Shannon Woods (Bug 13175)">
@@ -887,6 +889,10 @@
         <enum value="0x3471" name="EGL_IMPORT_IMPLICIT_SYNC_EXT"/>
         <enum value="0x3472" name="EGL_IMPORT_EXPLICIT_SYNC_EXT"/>
     </enums>
+    <enums namespace="EGL" start="0x3480" end="0x348F" vendor="ANGLE" comment="Reserved for Courtney Goeltzenleuchter - ANGLE (gitlab EGL bug 7)">
+        <enum value="0x3480" name="EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE"/>
+            <unused start="0x3481" end="0x348F"/>
+    </enums>
 
 <!-- Please remember that new enumerant allocations must be obtained by
      request to the Khronos API registrar (see comments at the top of this
@@ -897,8 +903,8 @@
 
 <!-- Reservable for future use. To generate a new range, allocate multiples
      of 16 starting at the lowest available point in this block. -->
-    <enums namespace="EGL" start="0x3480" end="0x3FFF" vendor="KHR" comment="Reserved for future use">
-            <unused start="0x3480" end="0x3FFF"/>
+    <enums namespace="EGL" start="0x3490" end="0x3FFF" vendor="KHR" comment="Reserved for future use">
+            <unused start="0x3490" end="0x3FFF"/>
     </enums>
 
     <enums namespace="EGL" start="0x8F70" end="0x8F7F" vendor="HI" comment="For Mark Callow, Khronos bug 4055. Shared with GL.">
@@ -930,6 +936,12 @@
             <param><ptype>EGLint</ptype> *<name>num_config</name></param>
         </command>
         <command>
+            <proto><ptype>EGLBoolean</ptype> <name>eglClientSignalSyncEXT</name></proto>
+            <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+            <param><ptype>EGLSync</ptype> <name>sync</name></param>
+            <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
+        </command>
+        <command>
             <proto><ptype>EGLint</ptype> <name>eglClientWaitSync</name></proto>
             <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
             <param><ptype>EGLSync</ptype> <name>sync</name></param>
@@ -1647,6 +1659,11 @@
             <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
         </command>
         <command>
+            <proto><ptype>EGLBoolean</ptype> <name>eglStreamFlushNV</name></proto>
+            <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+            <param><ptype>EGLStreamKHR</ptype> <name>stream</name></param>
+        </command>
+        <command>
             <proto><ptype>EGLBoolean</ptype> <name>eglSurfaceAttrib</name></proto>
             <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
             <param><ptype>EGLSurface</ptype> <name>surface</name></param>
@@ -1701,6 +1718,12 @@
             <param><ptype>EGLSurface</ptype> <name>surface</name></param>
         </command>
         <command>
+            <proto><ptype>EGLBoolean</ptype> <name>eglUnsignalSyncEXT</name></proto>
+            <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+            <param><ptype>EGLSync</ptype> <name>sync</name></param>
+            <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
+        </command>
+        <command>
             <proto><ptype>EGLBoolean</ptype> <name>eglWaitClient</name></proto>
         </command>
         <command>
@@ -2146,6 +2169,13 @@
             </require>
         </extension>
         <extension name="EGL_EXT_client_extensions" supported="egl"/>
+        <extension name="EGL_EXT_client_sync" supported="egl">
+            <require>
+                <enum name="EGL_SYNC_CLIENT_EXT"/>
+                <enum name="EGL_SYNC_CLIENT_SIGNAL_EXT"/>
+                <command name="eglClientSignalSyncEXT"/>
+            </require>
+        </extension>
         <extension name="EGL_EXT_create_context_robustness" supported="egl">
             <require>
                 <enum name="EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT"/>
@@ -2371,6 +2401,11 @@
                 <command name="eglSwapBuffersWithDamageEXT"/>
             </require>
         </extension>
+        <extension name="EGL_EXT_sync_reuse" supported="egl">
+            <require>
+                <command name="eglUnsignalSyncEXT"/>
+            </require>
+        </extension>
         <extension name="EGL_EXT_yuv_surface" supported="egl">
             <require>
                 <enum name="EGL_YUV_ORDER_EXT"/>
@@ -2932,6 +2967,11 @@
                 <enum name="EGL_STREAM_FIFO_SYNCHRONOUS_NV"/>
             </require>
         </extension>
+        <extension name="EGL_NV_stream_flush" supported="egl">
+            <require>
+                <command name="eglStreamFlushNV"/>
+            </require>
+        </extension>
         <extension name="EGL_NV_stream_frame_limits" supported="egl">
             <require>
                 <enum name="EGL_PRODUCER_MAX_FRAME_HINT_NV"/>
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 9a65452..622a623 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -41,6 +41,8 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        // Allow implicit fallthroughs in InputReader.cpp until they are fixed.
+        "-Wno-error=implicit-fallthrough",
         "-Wno-unused-parameter",
         // TODO: Move inputflinger to its own process and mark it hidden
         //-fvisibility=hidden
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 38104c4..2cad986 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -852,7 +852,8 @@
         return true;
     }
 
-    addMonitoringTargetsLocked(inputTargets);
+    // Add monitor channels from event's or focused display.
+    addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
 
     // Dispatch the key.
     dispatchEventLocked(currentTime, entry, inputTargets);
@@ -919,7 +920,8 @@
         return true;
     }
 
-    addMonitoringTargetsLocked(inputTargets);
+    // Add monitor channels from event's or focused display.
+    addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
 
     // Dispatch the motion.
     if (conflictingPointerActions) {
@@ -1665,17 +1667,29 @@
     target.pointerIds = pointerIds;
 }
 
-void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets) {
-    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-        inputTargets.push();
+void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets,
+        int32_t displayId) {
+    std::unordered_map<int32_t, Vector<sp<InputChannel>>>::const_iterator it =
+            mMonitoringChannelsByDisplay.find(displayId);
 
-        InputTarget& target = inputTargets.editTop();
-        target.inputChannel = mMonitoringChannels[i];
-        target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-        target.xOffset = 0;
-        target.yOffset = 0;
-        target.pointerIds.clear();
-        target.scaleFactor = 1.0f;
+    if (it != mMonitoringChannelsByDisplay.end()) {
+        const Vector<sp<InputChannel>>& monitoringChannels = it->second;
+        const size_t numChannels = monitoringChannels.size();
+        for (size_t i = 0; i < numChannels; i++) {
+            inputTargets.push();
+
+            InputTarget& target = inputTargets.editTop();
+            target.inputChannel = monitoringChannels[i];
+            target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+            target.xOffset = 0;
+            target.yOffset = 0;
+            target.pointerIds.clear();
+            target.scaleFactor = 1.0f;
+        }
+    } else {
+        // If there is no monitor channel registered or all monitor channel unregistered,
+        // the display can't detect the extra system gesture by a copy of input events.
+        ALOGW("There is no monitor channel found in display=%" PRId32, displayId);
     }
 }
 
@@ -2294,8 +2308,12 @@
 
 void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
         const CancelationOptions& options) {
-    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-        synthesizeCancelationEventsForInputChannelLocked(mMonitoringChannels[i], options);
+    for (auto& it : mMonitoringChannelsByDisplay) {
+        const Vector<sp<InputChannel>>& monitoringChannels = it.second;
+        const size_t numChannels = monitoringChannels.size();
+        for (size_t i = 0; i < numChannels; i++) {
+            synthesizeCancelationEventsForInputChannelLocked(monitoringChannels[i], options);
+        }
     }
 }
 
@@ -3486,12 +3504,16 @@
         dump += INDENT "Displays: <none>\n";
     }
 
-    if (!mMonitoringChannels.isEmpty()) {
-        dump += INDENT "MonitoringChannels:\n";
-        for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-            const sp<InputChannel>& channel = mMonitoringChannels[i];
-            dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str());
-        }
+    if (!mMonitoringChannelsByDisplay.empty()) {
+       for (auto& it : mMonitoringChannelsByDisplay) {
+            const Vector<sp<InputChannel>>& monitoringChannels = it.second;
+            dump += INDENT "MonitoringChannels in Display %d:\n";
+            const size_t numChannels = monitoringChannels.size();
+            for (size_t i = 0; i < numChannels; i++) {
+                const sp<InputChannel>& channel = monitoringChannels[i];
+                dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str());
+            }
+       }
     } else {
         dump += INDENT "MonitoringChannels: <none>\n";
     }
@@ -3609,10 +3631,10 @@
 }
 
 status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
-        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
+        const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId) {
 #if DEBUG_REGISTRATION
-    ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().c_str(),
-            toString(monitor));
+    ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32,
+            inputChannel->getName().c_str(), displayId);
 #endif
 
     { // acquire lock
@@ -3624,13 +3646,20 @@
             return BAD_VALUE;
         }
 
+        // If InputWindowHandle is null and displayId is not ADISPLAY_ID_NONE,
+        // treat inputChannel as monitor channel for displayId.
+        bool monitor = inputWindowHandle == nullptr && displayId != ADISPLAY_ID_NONE;
+
         sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
 
         int fd = inputChannel->getFd();
         mConnectionsByFd.add(fd, connection);
 
+        // Store monitor channel by displayId.
         if (monitor) {
-            mMonitoringChannels.push(inputChannel);
+            Vector<sp<InputChannel>>& monitoringChannels =
+                    mMonitoringChannelsByDisplay[displayId];
+            monitoringChannels.push(inputChannel);
         }
 
         mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
@@ -3687,11 +3716,21 @@
 }
 
 void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
-    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-         if (mMonitoringChannels[i] == inputChannel) {
-             mMonitoringChannels.removeAt(i);
-             break;
-         }
+    for (auto it = mMonitoringChannelsByDisplay.begin();
+            it != mMonitoringChannelsByDisplay.end(); ) {
+        Vector<sp<InputChannel>>& monitoringChannels = it->second;
+        const size_t numChannels = monitoringChannels.size();
+        for (size_t i = 0; i < numChannels; i++) {
+             if (monitoringChannels[i] == inputChannel) {
+                 monitoringChannels.removeAt(i);
+                 break;
+             }
+        }
+        if (monitoringChannels.empty()) {
+            it = mMonitoringChannelsByDisplay.erase(it);
+        } else {
+            ++it;
+        }
     }
 }
 
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index aedad2f..5efb2fa 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -346,13 +346,19 @@
     virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
             const sp<InputChannel>& toChannel) = 0;
 
-    /* Registers or unregister input channels that may be used as targets for input events.
-     * If monitor is true, the channel will receive a copy of all input events.
+    /* Registers input channels that may be used as targets for input events.
+     * If inputWindowHandle is null, and displayId is not ADISPLAY_ID_NONE,
+     * the channel will receive a copy of all input events form the specific displayId.
      *
-     * These methods may be called on any thread (usually by the input manager).
+     * This method may be called on any thread (usually by the input manager).
      */
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
-            const sp<InputWindowHandle>& inputWindowHandle, bool monitor) = 0;
+            const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId) = 0;
+
+    /* Unregister input channels that will no longer receive input events.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
 };
 
@@ -407,7 +413,7 @@
             const sp<InputChannel>& toChannel);
 
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
-            const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
+            const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
 private:
@@ -914,8 +920,8 @@
 
     ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
 
-    // Input channels that will receive a copy of all input events.
-    Vector<sp<InputChannel> > mMonitoringChannels;
+    // Input channels that will receive a copy of all input events sent to the provided display.
+    std::unordered_map<int32_t, Vector<sp<InputChannel>>> mMonitoringChannelsByDisplay;
 
     // Event injection and synchronization.
     Condition mInjectionResultAvailableCondition;
@@ -1070,7 +1076,7 @@
 
     void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
             int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
-    void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets);
+    void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId);
 
     void pokeUserActivityLocked(const EventEntry* eventEntry);
     bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 57fc17f..0b73be0 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -256,18 +256,17 @@
 
 // --- InputReaderConfiguration ---
 
-bool InputReaderConfiguration::getDisplayViewport(ViewportType viewportType,
-        const std::string& uniqueDisplayId, DisplayViewport* outViewport) const {
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewport(
+        ViewportType viewportType, const std::string& uniqueDisplayId) const {
     for (const DisplayViewport& currentViewport : mDisplays) {
         if (currentViewport.type == viewportType) {
             if (uniqueDisplayId.empty() ||
                     (!uniqueDisplayId.empty() && uniqueDisplayId == currentViewport.uniqueId)) {
-                *outViewport = currentViewport;
-                return true;
+                return std::make_optional(currentViewport);
             }
         }
     }
-    return false;
+    return std::nullopt;
 }
 
 void InputReaderConfiguration::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
@@ -2249,7 +2248,6 @@
     dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
 }
 
-
 void KeyboardInputMapper::configure(nsecs_t when,
         const InputReaderConfiguration* config, uint32_t changes) {
     InputMapper::configure(when, config, changes);
@@ -2261,9 +2259,7 @@
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         if (mParameters.orientationAware) {
-            DisplayViewport dvp;
-            config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "", &dvp);
-            mViewport = dvp;
+            mViewport = config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
         }
     }
 }
@@ -2672,9 +2668,10 @@
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         mOrientation = DISPLAY_ORIENTATION_0;
         if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
-            DisplayViewport v;
-            if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "", &v)) {
-                mOrientation = v.orientation;
+            std::optional<DisplayViewport> internalViewport =
+                    config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+            if (internalViewport) {
+                mOrientation = internalViewport->orientation;
             }
         }
         bumpGeneration();
@@ -2987,9 +2984,10 @@
         mRotaryEncoderScrollAccumulator.configure(getDevice());
     }
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-        DisplayViewport v;
-        if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "", &v)) {
-            mOrientation = v.orientation;
+        std::optional<DisplayViewport> internalViewport =
+                config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+        if (internalViewport) {
+            mOrientation = internalViewport->orientation;
         } else {
             mOrientation = DISPLAY_ORIENTATION_0;
         }
@@ -3502,7 +3500,9 @@
             viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
         }
 
-        if (!mConfig.getDisplayViewport(viewportTypeToUse, uniqueDisplayId, &newViewport)) {
+        std::optional<DisplayViewport> viewportToUse =
+                mConfig.getDisplayViewport(viewportTypeToUse, uniqueDisplayId);
+        if (!viewportToUse) {
             ALOGI(INDENT "Touch device '%s' could not query the properties of its associated "
                     "display.  The device will be inoperable until the display size "
                     "becomes available.",
@@ -3510,6 +3510,7 @@
             mDeviceMode = DEVICE_MODE_DISABLED;
             return;
         }
+        newViewport = *viewportToUse;
     } else {
         newViewport.setNonDisplayViewport(rawWidth, rawHeight);
     }
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 74668b7..3410bc9 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -202,8 +202,8 @@
             pointerGestureZoomSpeedRatio(0.3f),
             showTouches(false) { }
 
-    bool getDisplayViewport(ViewportType viewportType, const std::string& uniqueDisplayId,
-            DisplayViewport* outViewport) const;
+    std::optional<DisplayViewport> getDisplayViewport(ViewportType viewportType,
+            const std::string& uniqueDisplayId) const;
     void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
 
 
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 517e639..a1cd71c 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -10,6 +10,7 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wextra",
         "-Wno-unused-parameter",
     ],
     shared_libs: [
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f75b0b6..c6eaf9f 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -278,68 +278,8 @@
     }
 };
 
-class FakeWindowHandle : public InputWindowHandle {
+class FakeInputReceiver {
 public:
-    static const int32_t WIDTH = 600;
-    static const int32_t HEIGHT = 800;
-
-    FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
-        const sp<InputDispatcher>& dispatcher, const std::string name) :
-            InputWindowHandle(inputApplicationHandle), mDispatcher(dispatcher),
-            mName(name), mFocused(false), mDisplayId(ADISPLAY_ID_DEFAULT) {
-        InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
-        mConsumer = new InputConsumer(mClientChannel);
-        mDispatcher->registerInputChannel(mServerChannel, this, false);
-    }
-
-    virtual ~FakeWindowHandle() {
-        mDispatcher->unregisterInputChannel(mServerChannel);
-        mServerChannel.clear();
-        mClientChannel.clear();
-        mDispatcher.clear();
-
-        if (mConsumer != nullptr) {
-            delete mConsumer;
-        }
-    }
-
-    virtual bool updateInfo() {
-        if (!mInfo) {
-            mInfo = new InputWindowInfo();
-        }
-        mInfo->inputChannel = mServerChannel;
-        mInfo->name = mName;
-        mInfo->layoutParamsFlags = 0;
-        mInfo->layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
-        mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
-        mInfo->frameLeft = 0;
-        mInfo->frameTop = 0;
-        mInfo->frameRight = WIDTH;
-        mInfo->frameBottom = HEIGHT;
-        mInfo->scaleFactor = 1.0;
-        mInfo->addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
-        mInfo->visible = true;
-        mInfo->canReceiveKeys = true;
-        mInfo->hasFocus = mFocused;
-        mInfo->hasWallpaper = false;
-        mInfo->paused = false;
-        mInfo->layer = 0;
-        mInfo->ownerPid = INJECTOR_PID;
-        mInfo->ownerUid = INJECTOR_UID;
-        mInfo->inputFeatures = 0;
-        mInfo->displayId = mDisplayId;
-
-        return true;
-    }
-
-    void setFocus() {
-        mFocused = true;
-    }
-
-    void setDisplayId(int32_t displayId) {
-        mDisplayId = displayId;
-    }
-
     void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId,
             int32_t expectedFlags = 0) {
         uint32_t consumeSeq;
@@ -376,7 +316,7 @@
         ASSERT_EQ(expectedFlags, flags)
                 << mName.c_str() << ": event flags should be the same as expected.";
 
-        status = mConsumer->sendFinishedSignal(consumeSeq, true /*handled*/);
+        status = mConsumer->sendFinishedSignal(consumeSeq, handled());
         ASSERT_EQ(OK, status)
                 << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
     }
@@ -391,17 +331,94 @@
                 << ": should not have received any events, so consume(..) should not return OK.";
     }
 
-    private:
+protected:
+        explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher,
+            const std::string name, int32_t displayId) :
+                mDispatcher(dispatcher), mName(name), mDisplayId(displayId) {
+            InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+            mConsumer = new InputConsumer(mClientChannel);
+        }
+
+        virtual ~FakeInputReceiver() {
+        }
+
+        // return true if the event has been handled.
+        virtual bool handled() {
+            return false;
+        }
+
         sp<InputDispatcher> mDispatcher;
         sp<InputChannel> mServerChannel, mClientChannel;
         InputConsumer *mConsumer;
         PreallocatedInputEventFactory mEventFactory;
 
         std::string mName;
-        bool mFocused;
         int32_t mDisplayId;
 };
 
+class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
+public:
+    static const int32_t WIDTH = 600;
+    static const int32_t HEIGHT = 800;
+
+    FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+        const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
+            InputWindowHandle(inputApplicationHandle),
+            FakeInputReceiver(dispatcher, name, displayId),
+            mFocused(false) {
+            mDispatcher->registerInputChannel(mServerChannel, this, displayId);
+    }
+
+    virtual bool updateInfo() {
+        if (!mInfo) {
+            mInfo = new InputWindowInfo();
+        }
+        mInfo->inputChannel = mServerChannel;
+        mInfo->name = mName;
+        mInfo->layoutParamsFlags = 0;
+        mInfo->layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+        mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
+        mInfo->frameLeft = 0;
+        mInfo->frameTop = 0;
+        mInfo->frameRight = WIDTH;
+        mInfo->frameBottom = HEIGHT;
+        mInfo->scaleFactor = 1.0;
+        mInfo->addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+        mInfo->visible = true;
+        mInfo->canReceiveKeys = true;
+        mInfo->hasFocus = mFocused;
+        mInfo->hasWallpaper = false;
+        mInfo->paused = false;
+        mInfo->layer = 0;
+        mInfo->ownerPid = INJECTOR_PID;
+        mInfo->ownerUid = INJECTOR_UID;
+        mInfo->inputFeatures = 0;
+        mInfo->displayId = mDisplayId;
+
+        return true;
+    }
+
+    void setFocus() {
+        mFocused = true;
+    }
+
+    void assertNoEvents() {
+        uint32_t consumeSeq;
+        InputEvent* event;
+        status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
+            &consumeSeq, &event);
+        ASSERT_NE(OK, status)
+                << mName.c_str()
+                << ": should not have received any events, so consume(..) should not return OK.";
+    }
+protected:
+    virtual bool handled() {
+        return true;
+    }
+
+    bool mFocused;
+};
+
 static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
         int32_t displayId = ADISPLAY_ID_NONE) {
     KeyEvent event;
@@ -419,7 +436,8 @@
             INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
 }
 
-static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t displayId) {
+static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
+        int32_t displayId) {
     MotionEvent event;
     PointerProperties pointerProperties[1];
     PointerCoords pointerCoords[1];
@@ -434,7 +452,7 @@
 
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid motion down event.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, displayId,
+    event.initialize(DEVICE_ID, source, displayId,
             AMOTION_EVENT_ACTION_DOWN, /* actionButton */0, /* flags */ 0, /* edgeFlags */ 0,
             AMETA_NONE, /* buttonState */ 0, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
             /* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties,
@@ -449,13 +467,15 @@
 
 TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
     sp<FakeApplicationHandle> application = new FakeApplicationHandle();
-    sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window");
+    sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
+            ADISPLAY_ID_DEFAULT);
 
     Vector<sp<InputWindowHandle>> inputWindowHandles;
     inputWindowHandles.add(window);
 
     mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
     // Window should receive motion event.
@@ -465,15 +485,18 @@
 // The foreground window should receive the first touch down event.
 TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
     sp<FakeApplicationHandle> application = new FakeApplicationHandle();
-    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top");
-    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second");
+    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+            ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+            ADISPLAY_ID_DEFAULT);
 
     Vector<sp<InputWindowHandle>> inputWindowHandles;
     inputWindowHandles.add(windowTop);
     inputWindowHandles.add(windowSecond);
 
     mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
     // Top window should receive the touch down event. Second window should not receive anything.
@@ -483,8 +506,10 @@
 
 TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) {
     sp<FakeApplicationHandle> application = new FakeApplicationHandle();
-    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top");
-    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second");
+    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+            ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+            ADISPLAY_ID_DEFAULT);
 
     // Set focus application.
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -504,61 +529,70 @@
     windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
 }
 
-TEST_F(InputDispatcherTest, SetInputWindow_MultiDisplayTouch) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
-    sp<FakeWindowHandle> windowInPrimary = new FakeWindowHandle(application, mDispatcher, "D_1");
-    sp<FakeWindowHandle> windowInSecondary = new FakeWindowHandle(application, mDispatcher, "D_2");
+/* Test InputDispatcher for MultiDisplay */
+class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
+public:
+    static constexpr int32_t SECOND_DISPLAY_ID = 1;
+    virtual void SetUp() {
+        InputDispatcherTest::SetUp();
 
-    // Test the primary display touch down.
-    Vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push(windowInPrimary);
+        application1 = new FakeApplicationHandle();
+        windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
+                ADISPLAY_ID_DEFAULT);
+        Vector<sp<InputWindowHandle>> inputWindowHandles;
+        inputWindowHandles.push(windowInPrimary);
+        // Set focus window for primary display, but focused display would be second one.
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
+        windowInPrimary->setFocus();
+        mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
 
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+        application2 = new FakeApplicationHandle();
+        windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
+                SECOND_DISPLAY_ID);
+        // Set focus to second display window.
+        Vector<sp<InputWindowHandle>> inputWindowHandles_Second;
+        inputWindowHandles_Second.push(windowInSecondary);
+        // Set focus display to second one.
+        mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
+        // Set focus window for second display.
+        mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
+        windowInSecondary->setFocus();
+        mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+    }
+
+    virtual void TearDown() {
+        InputDispatcherTest::TearDown();
+
+        application1.clear();
+        windowInPrimary.clear();
+        application2.clear();
+        windowInSecondary.clear();
+    }
+
+protected:
+    sp<FakeApplicationHandle> application1;
+    sp<FakeWindowHandle> windowInPrimary;
+    sp<FakeApplicationHandle> application2;
+    sp<FakeWindowHandle> windowInSecondary;
+};
+
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) {
+    // Test touch down on primary display.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
 
-    // Test the second display touch down.
-    constexpr int32_t SECOND_DISPLAY_ID = 1;
-    windowInSecondary->setDisplayId(SECOND_DISPLAY_ID);
-    Vector<sp<InputWindowHandle>> inputWindowHandles_Second;
-    inputWindowHandles_Second.push(windowInSecondary);
-
-    mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, SECOND_DISPLAY_ID))
+    // Test touch down on second display.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
     windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
 }
 
-TEST_F(InputDispatcherTest, SetInputWindow_FocusedInMultiDisplay) {
-    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
-    sp<FakeWindowHandle> windowInPrimary = new FakeWindowHandle(application, mDispatcher, "D_1");
-    sp<FakeApplicationHandle> application2 = new FakeApplicationHandle();
-    sp<FakeWindowHandle> windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2");
-
-    constexpr int32_t SECOND_DISPLAY_ID = 1;
-
-    // Set focus to primary display window.
-    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-    windowInPrimary->setFocus();
-
-    // Set focus to second display window.
-    mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
-    mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
-    windowInSecondary->setFocus();
-
-    // Update all windows per displays.
-    Vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push(windowInPrimary);
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
-
-    windowInSecondary->setDisplayId(SECOND_DISPLAY_ID);
-    Vector<sp<InputWindowHandle>> inputWindowHandles_Second;
-    inputWindowHandles_Second.push(windowInSecondary);
-    mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
-
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
     // Test inject a key down with display id specified.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -572,8 +606,8 @@
     windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
 
     // Remove secondary display.
-    inputWindowHandles_Second.clear();
-    mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+    Vector<sp<InputWindowHandle>> noWindows;
+    mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID);
 
     // Expect old focus should receive a cancel event.
     windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE,
@@ -586,4 +620,66 @@
     windowInSecondary->assertNoEvents();
 }
 
+class FakeMonitorReceiver : public FakeInputReceiver, public RefBase {
+public:
+    FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
+            int32_t displayId) : FakeInputReceiver(dispatcher, name, displayId) {
+        mDispatcher->registerInputChannel(mServerChannel, nullptr, displayId);
+    }
+};
+
+// Test per-display input monitors for motion event.
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
+    sp<FakeMonitorReceiver> monitorInPrimary =
+            new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    sp<FakeMonitorReceiver> monitorInSecondary =
+            new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+    // Test touch down on primary display.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    monitorInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowInSecondary->assertNoEvents();
+    monitorInSecondary->assertNoEvents();
+
+    // Test touch down on second display.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    monitorInPrimary->assertNoEvents();
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+
+    // Test inject a non-pointer motion event.
+    // If specific a display, it will dispatch to the focused window of particular display,
+    // or it will dispatch to the focused window of focused display.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+        AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    monitorInPrimary->assertNoEvents();
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
+    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
+}
+
+// Test per-display input monitors for key event.
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
+    //Input monitor per display.
+    sp<FakeMonitorReceiver> monitorInPrimary =
+            new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    sp<FakeMonitorReceiver> monitorInSecondary =
+            new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+    // Test inject a key down.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    monitorInPrimary->assertNoEvents();
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index b70ee09..707f3c5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -33,7 +33,7 @@
 static const int32_t VIRTUAL_DISPLAY_ID = 1;
 static const int32_t VIRTUAL_DISPLAY_WIDTH = 400;
 static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
-static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "Vr-display-unique-ID";
+static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
 
 // Error tolerance for floating point assertions.
 static const float EPSILON = 0.001f;
@@ -142,21 +142,21 @@
     FakeInputReaderPolicy() {
     }
 
-    void setDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
-            const std::string& uniqueId) {
+    virtual void clearViewports() {
         mViewports.clear();
-        // Set the size of both the internal and external display at the same time.
-        mViewports.push_back(createDisplayViewport(displayId, width, height, orientation, uniqueId,
-                ViewportType::VIEWPORT_INTERNAL));
-        mViewports.push_back(createDisplayViewport(displayId, width, height, orientation, uniqueId,
-                ViewportType::VIEWPORT_EXTERNAL));
         mConfig.setDisplayViewports(mViewports);
     }
 
-    void setVirtualDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
+    std::optional<DisplayViewport> getDisplayViewport(ViewportType viewportType,
             const std::string& uniqueId) {
-        mViewports.push_back(createDisplayViewport(displayId, width, height, orientation, uniqueId,
-                ViewportType::VIEWPORT_VIRTUAL));
+        return mConfig.getDisplayViewport(viewportType, uniqueId);
+    }
+
+    void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
+            const std::string& uniqueId, ViewportType viewportType) {
+        const DisplayViewport viewport = createDisplayViewport(displayId, width, height,
+                orientation, uniqueId, viewportType);
+        mViewports.push_back(viewport);
         mConfig.setDisplayViewports(mViewports);
     }
 
@@ -1080,6 +1080,146 @@
     friend class InputReaderTest;
 };
 
+// --- InputReaderPolicyTest ---
+class InputReaderPolicyTest : public testing::Test {
+    protected:
+    sp<FakeInputReaderPolicy> mFakePolicy;
+
+    virtual void SetUp() {
+        mFakePolicy = new FakeInputReaderPolicy();
+    }
+    virtual void TearDown() {
+        mFakePolicy.clear();
+    }
+};
+
+/**
+ * Check that empty set of viewports is an acceptable configuration.
+ * Also try to get internal viewport two different ways - by type and by uniqueId.
+ *
+ * There will be confusion if two viewports with empty uniqueId and identical type are present.
+ * Such configuration is not currently allowed.
+ */
+TEST_F(InputReaderPolicyTest, Viewports_GetCleared) {
+    const std::string uniqueId = "local:0";
+
+    // We didn't add any viewports yet, so there shouldn't be any.
+    std::optional<DisplayViewport> internalViewport =
+            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+    ASSERT_FALSE(internalViewport);
+
+    // Add an internal viewport, then clear it
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId, ViewportType::VIEWPORT_INTERNAL);
+
+    // Check matching by uniqueId
+    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+    ASSERT_TRUE(internalViewport);
+
+    // Check matching by viewport type
+    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+    ASSERT_TRUE(internalViewport);
+
+    mFakePolicy->clearViewports();
+    // Make sure nothing is found after clear
+    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+    ASSERT_FALSE(internalViewport);
+    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+    ASSERT_FALSE(internalViewport);
+}
+
+TEST_F(InputReaderPolicyTest, Viewports_GetByType) {
+    const std::string internalUniqueId = "local:0";
+    const std::string externalUniqueId = "local:1";
+    const std::string virtualUniqueId1 = "virtual:2";
+    const std::string virtualUniqueId2 = "virtual:3";
+    constexpr int32_t virtualDisplayId1 = 2;
+    constexpr int32_t virtualDisplayId2 = 3;
+
+    // Add an internal viewport
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, internalUniqueId, ViewportType::VIEWPORT_INTERNAL);
+    // Add an external viewport
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, externalUniqueId, ViewportType::VIEWPORT_EXTERNAL);
+    // Add an virtual viewport
+    mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, virtualUniqueId1, ViewportType::VIEWPORT_VIRTUAL);
+    // Add another virtual viewport
+    mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, virtualUniqueId2, ViewportType::VIEWPORT_VIRTUAL);
+
+    // Check matching by type for internal
+    std::optional<DisplayViewport> internalViewport =
+            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+    ASSERT_TRUE(internalViewport);
+    ASSERT_EQ(internalUniqueId, internalViewport->uniqueId);
+
+    // Check matching by type for external
+    std::optional<DisplayViewport> externalViewport =
+            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_EXTERNAL, "");
+    ASSERT_TRUE(externalViewport);
+    ASSERT_EQ(externalUniqueId, externalViewport->uniqueId);
+
+    // Check matching by uniqueId for virtual viewport #1
+    std::optional<DisplayViewport> virtualViewport1 =
+            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_VIRTUAL, virtualUniqueId1);
+    ASSERT_TRUE(virtualViewport1);
+    ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId);
+    ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId);
+
+    // Check matching by uniqueId for virtual viewport #2
+    std::optional<DisplayViewport> virtualViewport2 =
+            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_VIRTUAL, virtualUniqueId2);
+    ASSERT_TRUE(virtualViewport2);
+    ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId);
+    ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId);
+}
+
+
+/**
+ * We can have 2 viewports of the same kind. We can distinguish them by uniqueId, and confirm
+ * that lookup works by checking display id.
+ * Check that 2 viewports of each kind is possible, for all existing viewport types.
+ */
+TEST_F(InputReaderPolicyTest, Viewports_TwoOfSameType) {
+    const std::string uniqueId1 = "uniqueId1";
+    const std::string uniqueId2 = "uniqueId2";
+    constexpr int32_t displayId1 = 2;
+    constexpr int32_t displayId2 = 3;
+
+    std::vector<ViewportType> types = {ViewportType::VIEWPORT_INTERNAL,
+            ViewportType::VIEWPORT_EXTERNAL, ViewportType::VIEWPORT_VIRTUAL};
+    for (const ViewportType& type : types) {
+        mFakePolicy->clearViewports();
+        // Add a viewport
+        mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId1, type);
+        // Add another viewport
+        mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId2, type);
+
+        // Check that correct display viewport was returned by comparing the display IDs.
+        std::optional<DisplayViewport> viewport1 = mFakePolicy->getDisplayViewport(type, uniqueId1);
+        ASSERT_TRUE(viewport1);
+        ASSERT_EQ(displayId1, viewport1->displayId);
+        ASSERT_EQ(type, viewport1->type);
+
+        std::optional<DisplayViewport> viewport2 = mFakePolicy->getDisplayViewport(type, uniqueId2);
+        ASSERT_TRUE(viewport2);
+        ASSERT_EQ(displayId2, viewport2->displayId);
+        ASSERT_EQ(type, viewport2->type);
+
+        // When there are multiple viewports of the same kind, and uniqueId is not specified
+        // in the call to getDisplayViewport, then that situation is not supported.
+        // The viewports can be stored in any order, so we cannot rely on the order, since that
+        // is just implementation detail.
+        // However, we can check that it still returns *a* viewport, we just cannot assert
+        // which one specifically is returned.
+        std::optional<DisplayViewport> someViewport = mFakePolicy->getDisplayViewport(type, "");
+        ASSERT_TRUE(someViewport);
+    }
+}
 
 // --- InputReaderTest ---
 
@@ -1603,15 +1743,14 @@
     }
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
-            int32_t orientation) {
-        mFakePolicy->setDisplayViewport(displayId, width, height, orientation, "");
+            int32_t orientation, const std::string& uniqueId, ViewportType viewportType) {
+        mFakePolicy->addDisplayViewport(
+                displayId, width, height, orientation, uniqueId, viewportType);
         configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     }
 
-    void setVirtualDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
-            int32_t orientation, const std::string& uniqueId) {
-        mFakePolicy->setVirtualDisplayViewport(displayId, width, height, orientation, uniqueId);
-        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    void clearViewports() {
+        mFakePolicy->clearViewports();
     }
 
     static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type,
@@ -1715,12 +1854,24 @@
 
 class KeyboardInputMapperTest : public InputMapperTest {
 protected:
+    const std::string UNIQUE_ID = "local:0";
+
+    void prepareDisplay(int32_t orientation);
+
     void testDPadKeyRotation(KeyboardInputMapper* mapper,
             int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode);
 };
 
+/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
+ * orientation.
+ */
+void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            orientation, UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+}
+
 void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
-        int32_t originalScanCode, int32_t, int32_t rotatedKeyCode) {
+        int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) {
     NotifyKeyArgs args;
 
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, 1);
@@ -1908,9 +2059,7 @@
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_90);
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1932,9 +2081,7 @@
     addConfigurationProperty("keyboard.orientationAware", "1");
     addMapperAndConfigure(mapper);
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1944,9 +2091,8 @@
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_90);
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1956,9 +2102,8 @@
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_180);
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1968,9 +2113,8 @@
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_270);
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_270);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1983,19 +2127,16 @@
     // Special case: if orientation changes while key is down, we still emit the same keycode
     // in the key up as we did in the key down.
     NotifyKeyArgs args;
-
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_270);
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_270);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
     ASSERT_EQ(KEY_UP, args.scanCode);
     ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_180);
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_180);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
@@ -2020,8 +2161,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0);
@@ -2043,8 +2183,8 @@
     // Display id should be ADISPLAY_ID_NONE without any display configuration.
     // ^--- already checked by the previous test
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0);
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+            UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0);
@@ -2052,8 +2192,9 @@
     ASSERT_EQ(DISPLAY_ID, args.displayId);
 
     constexpr int32_t newDisplayId = 2;
-    setDisplayInfoAndReconfigure(newDisplayId,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0);
+    clearViewports();
+    setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+            UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0);
@@ -2484,9 +2625,12 @@
     addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
+    const std::string uniqueId = "local:0";
+    const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
+
     setDisplayInfoAndReconfigure(DISPLAY_ID,
             DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_90);
+            DISPLAY_ORIENTATION_90, uniqueId, viewportType);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -2503,8 +2647,11 @@
     addConfigurationProperty("cursor.orientationAware", "1");
     addMapperAndConfigure(mapper);
 
+    const std::string uniqueId = "local:0";
+    const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
+
     setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0);
+            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, uniqueId, viewportType);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -2515,7 +2662,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 
     setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_90);
+            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_90, uniqueId, viewportType);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0, -1));
@@ -2526,7 +2673,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1,  1));
 
     setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_180);
+            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_180, uniqueId, viewportType);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0, -1,  0));
@@ -2537,7 +2684,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));
 
     setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_270);
+            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_270, uniqueId, viewportType);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0,  1));
@@ -2982,6 +3129,8 @@
 
     static const VirtualKeyDefinition VIRTUAL_KEYS[2];
 
+    const std::string UNIQUE_ID = "local:0";
+
     enum Axes {
         POSITION = 1 << 0,
         TOUCH = 1 << 1,
@@ -3050,12 +3199,14 @@
 };
 
 void TouchInputMapperTest::prepareDisplay(int32_t orientation) {
-    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation);
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
+            UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
 }
 
 void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
-    setVirtualDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
-        VIRTUAL_DISPLAY_HEIGHT, orientation, VIRTUAL_DISPLAY_UNIQUE_ID);
+    setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
+        VIRTUAL_DISPLAY_HEIGHT, orientation,
+        VIRTUAL_DISPLAY_UNIQUE_ID, ViewportType::VIEWPORT_VIRTUAL);
 }
 
 void TouchInputMapperTest::prepareVirtualKeys() {
@@ -3784,6 +3935,7 @@
     NotifyMotionArgs args;
 
     // Rotation 0.
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_0);
     processDown(mapper, toRawX(50), toRawY(75));
     processSync(mapper);
@@ -3797,6 +3949,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 
     // Rotation 90.
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_90);
     processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
     processSync(mapper);
@@ -3810,6 +3963,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 
     // Rotation 180.
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_180);
     processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
     processSync(mapper);
@@ -3823,6 +3977,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 
     // Rotation 270.
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
     processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
     processSync(mapper);
diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp
index cec2ae5..207b097 100644
--- a/services/sensorservice/RecentEventLogger.cpp
+++ b/services/sensorservice/RecentEventLogger.cpp
@@ -32,19 +32,26 @@
 
 RecentEventLogger::RecentEventLogger(int sensorType) :
         mSensorType(sensorType), mEventSize(eventSizeBySensorType(mSensorType)),
-        mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false) {
+        mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false),
+        mIsLastEventCurrent(false) {
     // blank
 }
 
 void RecentEventLogger::addEvent(const sensors_event_t& event) {
     std::lock_guard<std::mutex> lk(mLock);
     mRecentEvents.emplace(event);
+    mIsLastEventCurrent = true;
 }
 
 bool RecentEventLogger::isEmpty() const {
     return mRecentEvents.size() == 0;
 }
 
+void RecentEventLogger::setLastEventStale() {
+    std::lock_guard<std::mutex> lk(mLock);
+    mIsLastEventCurrent = false;
+}
+
 std::string RecentEventLogger::dump() const {
     std::lock_guard<std::mutex> lk(mLock);
 
@@ -85,10 +92,10 @@
     }
 }
 
-bool RecentEventLogger::populateLastEvent(sensors_event_t *event) const {
+bool RecentEventLogger::populateLastEventIfCurrent(sensors_event_t *event) const {
     std::lock_guard<std::mutex> lk(mLock);
 
-    if (mRecentEvents.size()) {
+    if (mIsLastEventCurrent && mRecentEvents.size()) {
         // Index 0 contains the latest event emplace()'ed
         *event = mRecentEvents[0].mEvent;
         return true;
diff --git a/services/sensorservice/RecentEventLogger.h b/services/sensorservice/RecentEventLogger.h
index bf1f655..67378b7 100644
--- a/services/sensorservice/RecentEventLogger.h
+++ b/services/sensorservice/RecentEventLogger.h
@@ -37,8 +37,13 @@
 public:
     explicit RecentEventLogger(int sensorType);
     void addEvent(const sensors_event_t& event);
-    bool populateLastEvent(sensors_event_t *event) const;
+
+    // Populate event with the last recorded sensor event if it is not stale. An event is
+    // considered stale if the sensor has become deactivated since the event was recorded.
+    // returns true on success, false if no recent event is available or the last event is stale
+    bool populateLastEventIfCurrent(sensors_event_t *event) const;
     bool isEmpty() const;
+    void setLastEventStale();
     virtual ~RecentEventLogger() {}
 
     // Dumpable interface
@@ -59,6 +64,7 @@
     RingBuffer<SensorEventLog> mRecentEvents;
 
     bool mMaskData;
+    bool mIsLastEventCurrent;
 
 private:
     static size_t logSizeBySensorType(int sensorType);
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 1b9b945..85450f8 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1290,6 +1290,15 @@
             ALOGD_IF(DEBUG_CONNECTIONS, "... and it was the last connection");
             mActiveSensors.removeItemsAt(i, 1);
             mActiveVirtualSensors.erase(handle);
+
+            // If this is the last connection, then mark the RecentEventLogger as stale. This is
+            // critical for on-change events since the previous event is sent to a client if the
+            // sensor is already active. If two clients request the sensor at the same time, one
+            // of the clients would receive a stale event.
+            auto logger = mRecentEvent.find(handle);
+            if (logger != mRecentEvent.end()) {
+                logger->second->setLastEventStale();
+            }
             delete rec;
             size--;
         } else {
@@ -1356,10 +1365,11 @@
                 auto logger = mRecentEvent.find(handle);
                 if (logger != mRecentEvent.end()) {
                     sensors_event_t event;
-                    // It is unlikely that this buffer is empty as the sensor is already active.
-                    // One possible corner case may be two applications activating an on-change
-                    // sensor at the same time.
-                    if(logger->second->populateLastEvent(&event)) {
+                    // Verify that the last sensor event was generated from the current activation
+                    // of the sensor. If not, it is possible for an on-change sensor to receive a
+                    // sensor event that is stale if two clients re-activate the sensor
+                    // simultaneously.
+                    if(logger->second->populateLastEventIfCurrent(&event)) {
                         event.sensor = handle;
                         if (event.version == sizeof(sensors_event_t)) {
                             if (isWakeUpSensorEvent(event) && !mWakeLockAcquired) {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 339f83b..16003a2 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -30,6 +30,7 @@
         "android.hardware.configstore@1.1",
         "android.hardware.configstore@1.2",
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
@@ -79,6 +80,7 @@
     ],
     export_shared_lib_headers: [
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index cafe26b..ab677e1 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -234,27 +234,71 @@
     const auto& viewport = display->getViewport();
     Region visible = tr.transform(visibleRegion.intersect(viewport));
     const auto displayId = display->getId();
+    if (!hasHwcLayer(displayId)) {
+        ALOGE("[%s] failed to setPerFrameData: no HWC layer found (%d)",
+              mName.string(), displayId);
+        return;
+    }
+    auto& hwcInfo = getBE().mHwcLayers[displayId];
+    auto& hwcLayer = hwcInfo.layer;
+    auto error = hwcLayer->setVisibleRegion(visible);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        visible.dump(LOG_TAG);
+    }
     getBE().compositionInfo.hwc.visibleRegion = visible;
+
+    error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        surfaceDamageRegion.dump(LOG_TAG);
+    }
     getBE().compositionInfo.hwc.surfaceDamage = surfaceDamageRegion;
 
     // Sideband layers
     if (getBE().compositionInfo.hwc.sidebandStream.get()) {
         setCompositionType(displayId, HWC2::Composition::Sideband);
+        ALOGV("[%s] Requesting Sideband composition", mName.string());
+        error = hwcLayer->setSidebandStream(getBE().compositionInfo.hwc.sidebandStream->handle());
+        if (error != HWC2::Error::None) {
+            ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(),
+                  getBE().compositionInfo.hwc.sidebandStream->handle(), to_string(error).c_str(),
+                  static_cast<int32_t>(error));
+        }
         getBE().compositionInfo.compositionType = HWC2::Composition::Sideband;
         return;
     }
 
-    if (getBE().compositionInfo.hwc.skipGeometry) {
-        // Device or Cursor layers
-        if (mPotentialCursor) {
-            ALOGV("[%s] Requesting Cursor composition", mName.string());
-            setCompositionType(displayId, HWC2::Composition::Cursor);
-        } else {
-            ALOGV("[%s] Requesting Device composition", mName.string());
-            setCompositionType(displayId, HWC2::Composition::Device);
-        }
+    // Device or Cursor layers
+    if (mPotentialCursor) {
+        ALOGV("[%s] Requesting Cursor composition", mName.string());
+        setCompositionType(displayId, HWC2::Composition::Cursor);
+    } else {
+        ALOGV("[%s] Requesting Device composition", mName.string());
+        setCompositionType(displayId, HWC2::Composition::Device);
     }
 
+    ALOGV("setPerFrameData: dataspace = %d", mCurrentDataSpace);
+    error = hwcLayer->setDataspace(mCurrentDataSpace);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    const HdrMetadata& metadata = getDrawingHdrMetadata();
+    error = hwcLayer->setPerFrameMetadata(display->getSupportedPerFrameMetadata(), metadata);
+    if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) {
+        ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    error = hwcLayer->setColorTransform(getColorTransform());
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
+                to_string(error).c_str(), static_cast<int32_t>(error));
+    }
     getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace;
     getBE().compositionInfo.hwc.hdrMetadata = getDrawingHdrMetadata();
     getBE().compositionInfo.hwc.supportedPerFrameMetadata = display->getSupportedPerFrameMetadata();
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index a294793..d000d85 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -138,7 +138,7 @@
 
     virtual void setFilteringEnabled(bool enabled) = 0;
 
-    virtual status_t bindTextureImage() const = 0;
+    virtual status_t bindTextureImage() = 0;
     virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                                     const sp<Fence>& flushFence) = 0;
 
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 8c8915c..6826050 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -111,12 +111,6 @@
         return NO_INIT;
     }
 
-    // Make sure RenderEngine is current
-    if (!mRE.isCurrent()) {
-        BLC_LOGE("updateTexImage: RenderEngine is not current");
-        return INVALID_OPERATION;
-    }
-
     BufferItem item;
 
     // Acquire the next buffer.
@@ -185,8 +179,7 @@
         return;
     }
 
-    auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
-                                            : mCurrentTextureImage->graphicBuffer();
+    auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer : mCurrentTextureBuffer;
     auto err = addReleaseFence(slot, buffer, fence);
     if (err != OK) {
         BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
@@ -222,10 +215,9 @@
     }
 
     // If item->mGraphicBuffer is not null, this buffer has not been acquired
-    // before, so any prior EglImage created is using a stale buffer. This
-    // replaces any old EglImage with a new one (using the new buffer).
+    // before.
     if (item->mGraphicBuffer != nullptr) {
-        mImages[item->mSlot] = new Image(item->mGraphicBuffer, mRE);
+        mImages[item->mSlot] = nullptr;
     }
 
     return NO_ERROR;
@@ -252,19 +244,18 @@
     }
 
     BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
-             mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : 0,
-             slot, mSlots[slot].mGraphicBuffer->handle);
+             mCurrentTextureBuffer != nullptr ? mCurrentTextureBuffer->handle : 0, slot,
+             mSlots[slot].mGraphicBuffer->handle);
 
     // Hang onto the pointer so that it isn't freed in the call to
     // releaseBufferLocked() if we're in shared buffer mode and both buffers are
     // the same.
-    sp<Image> nextTextureImage = mImages[slot];
+    sp<GraphicBuffer> nextTextureBuffer = mSlots[slot].mGraphicBuffer;
 
     // release old buffer
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         if (pendingRelease == nullptr) {
-            status_t status =
-                    releaseBufferLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer());
+            status_t status = releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer);
             if (status < NO_ERROR) {
                 BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
                          status);
@@ -273,14 +264,15 @@
             }
         } else {
             pendingRelease->currentTexture = mCurrentTexture;
-            pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer();
+            pendingRelease->graphicBuffer = mCurrentTextureBuffer;
             pendingRelease->isPending = true;
         }
     }
 
     // Update the BufferLayerConsumer state.
     mCurrentTexture = slot;
-    mCurrentTextureImage = nextTextureImage;
+    mCurrentTextureBuffer = nextTextureBuffer;
+    mCurrentTextureImageFreed = nullptr;
     mCurrentCrop = item.mCrop;
     mCurrentTransform = item.mTransform;
     mCurrentScalingMode = item.mScalingMode;
@@ -301,22 +293,46 @@
 
 status_t BufferLayerConsumer::bindTextureImageLocked() {
     ATRACE_CALL();
+
     mRE.checkErrors();
 
-    if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
+    // It is possible for the current slot's buffer to be freed before a new one
+    // is bound. In that scenario we still want to bind the image.
+    if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureBuffer == nullptr) {
         BLC_LOGE("bindTextureImage: no currently-bound texture");
         mRE.bindExternalTextureImage(mTexName, *mRE.createImage());
         return NO_INIT;
     }
 
-    status_t err = mCurrentTextureImage->createIfNeeded();
-    if (err != NO_ERROR) {
-        BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture);
-        mRE.bindExternalTextureImage(mTexName, *mRE.createImage());
-        return UNKNOWN_ERROR;
+    renderengine::Image* imageToRender;
+
+    // mCurrentTextureImageFreed is non-null iff mCurrentTexture ==
+    // BufferQueue::INVALID_BUFFER_SLOT, so we can omit that check.
+    if (mCurrentTextureImageFreed) {
+        imageToRender = mCurrentTextureImageFreed.get();
+    } else if (mImages[mCurrentTexture]) {
+        imageToRender = mImages[mCurrentTexture].get();
+    } else {
+        std::unique_ptr<renderengine::Image> image = mRE.createImage();
+        bool success = image->setNativeWindowBuffer(mCurrentTextureBuffer->getNativeBuffer(),
+                                                    mCurrentTextureBuffer->getUsage() &
+                                                            GRALLOC_USAGE_PROTECTED);
+        if (!success) {
+            BLC_LOGE("bindTextureImage: Failed to create image. size=%ux%u st=%u usage=%#" PRIx64
+                     " fmt=%d",
+                     mCurrentTextureBuffer->getWidth(), mCurrentTextureBuffer->getHeight(),
+                     mCurrentTextureBuffer->getStride(), mCurrentTextureBuffer->getUsage(),
+                     mCurrentTextureBuffer->getPixelFormat());
+            BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture);
+            mRE.bindExternalTextureImage(mTexName, *image);
+            return UNKNOWN_ERROR;
+        }
+        imageToRender = image.get();
+        // Cache the image here so that we can reuse it.
+        mImages[mCurrentTexture] = std::move(image);
     }
 
-    mRE.bindExternalTextureImage(mTexName, mCurrentTextureImage->image());
+    mRE.bindExternalTextureImage(mTexName, *imageToRender);
 
     // Wait for the new buffer to be ready.
     return doFenceWaitLocked();
@@ -333,8 +349,7 @@
                 return UNKNOWN_ERROR;
             }
             status_t err =
-                    addReleaseFenceLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer(),
-                                          releaseFence);
+                    addReleaseFenceLocked(mCurrentTexture, mCurrentTextureBuffer, releaseFence);
             if (err != OK) {
                 BLC_LOGE("syncForReleaseLocked: error adding release fence: "
                          "%s (%d)",
@@ -361,24 +376,22 @@
     bool needsRecompute = mFilteringEnabled != enabled;
     mFilteringEnabled = enabled;
 
-    if (needsRecompute && mCurrentTextureImage == nullptr) {
-        BLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == nullptr");
+    if (needsRecompute && mCurrentTextureBuffer == nullptr) {
+        BLC_LOGD("setFilteringEnabled called with mCurrentTextureBuffer == nullptr");
     }
 
-    if (needsRecompute && mCurrentTextureImage != nullptr) {
+    if (needsRecompute && mCurrentTextureBuffer != nullptr) {
         computeCurrentTransformMatrixLocked();
     }
 }
 
 void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
     BLC_LOGV("computeCurrentTransformMatrixLocked");
-    sp<GraphicBuffer> buf =
-            (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer();
-    if (buf == nullptr) {
+    if (mCurrentTextureBuffer == nullptr) {
         BLC_LOGD("computeCurrentTransformMatrixLocked: "
-                 "mCurrentTextureImage is nullptr");
+                 "mCurrentTextureBuffer is nullptr");
     }
-    GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop,
+    GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, mCurrentTextureBuffer, mCurrentCrop,
                                        mCurrentTransform, mFilteringEnabled);
 }
 
@@ -427,7 +440,7 @@
         *outSlot = mCurrentTexture;
     }
 
-    return (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer();
+    return mCurrentTextureBuffer;
 }
 
 Rect BufferLayerConsumer::getCurrentCrop() const {
@@ -458,11 +471,6 @@
 }
 
 status_t BufferLayerConsumer::doFenceWaitLocked() const {
-    if (!mRE.isCurrent()) {
-        BLC_LOGE("doFenceWait: RenderEngine is not current");
-        return INVALID_OPERATION;
-    }
-
     if (mCurrentFence->isValid()) {
         if (mRE.useWaitSync()) {
             base::unique_fd fenceFd(mCurrentFence->dup());
@@ -490,8 +498,10 @@
     BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
     if (slotIndex == mCurrentTexture) {
         mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+        mCurrentTextureImageFreed = std::move(mImages[slotIndex]);
+    } else {
+        mImages[slotIndex] = nullptr;
     }
-    mImages[slotIndex].clear();
     ConsumerBase::freeBufferLocked(slotIndex);
 }
 
@@ -530,7 +540,7 @@
 
 void BufferLayerConsumer::abandonLocked() {
     BLC_LOGV("abandonLocked");
-    mCurrentTextureImage.clear();
+    mCurrentTextureBuffer.clear();
     ConsumerBase::abandonLocked();
 }
 
@@ -548,29 +558,4 @@
     ConsumerBase::dumpLocked(result, prefix);
 }
 
-BufferLayerConsumer::Image::Image(sp<GraphicBuffer> graphicBuffer,
-                                  renderengine::RenderEngine& engine)
-      : mGraphicBuffer(graphicBuffer),
-        mImage{engine.createImage()},
-        mCreated(false),
-        mCropWidth(0),
-        mCropHeight(0) {}
-
-BufferLayerConsumer::Image::~Image() = default;
-
-status_t BufferLayerConsumer::Image::createIfNeeded() {
-    if (mCreated) return OK;
-
-    mCreated = mImage->setNativeWindowBuffer(mGraphicBuffer->getNativeBuffer(),
-                                             mGraphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
-    if (!mCreated) {
-        const sp<GraphicBuffer>& buffer = mGraphicBuffer;
-        ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
-              buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
-              buffer->getPixelFormat());
-    }
-
-    return mCreated ? OK : UNKNOWN_ERROR;
-}
-
 }; // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index e174680..ea46245 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -185,8 +185,7 @@
     // specific info in addition to the ConsumerBase behavior.
     virtual void dumpLocked(String8& result, const char* prefix) const;
 
-    // acquireBufferLocked overrides the ConsumerBase method to update the
-    // mImages array in addition to the ConsumerBase behavior.
+    // See ConsumerBase::acquireBufferLocked
     virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
                                          uint64_t maxFrameNumber = 0) override;
 
@@ -210,53 +209,14 @@
                                     PendingRelease* pendingRelease = nullptr,
                                     const sp<Fence>& releaseFence = Fence::NO_FENCE);
 
-    // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.  Uses
-    // mCurrentTexture if it's set, mCurrentTextureImage if not.  If the
-    // bind succeeds, this calls doFenceWait.
+    // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.
+    // If the bind succeeds, this calls doFenceWait.
     status_t bindTextureImageLocked();
 
 private:
-    // Image is a utility class for tracking and creating renderengine::Images. There
-    // is primarily just one image per slot, but there is also special cases:
-    //  - After freeBuffer, we must still keep the current image/buffer
-    // Reference counting renderengine::Images lets us handle all these cases easily while
-    // also only creating new renderengine::Images from buffers when required.
-    class Image : public LightRefBase<Image> {
-    public:
-        Image(sp<GraphicBuffer> graphicBuffer, renderengine::RenderEngine& engine);
-
-        Image(const Image& rhs) = delete;
-        Image& operator=(const Image& rhs) = delete;
-
-        // createIfNeeded creates an renderengine::Image if we haven't created one yet.
-        status_t createIfNeeded();
-
-        const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
-        const native_handle* graphicBufferHandle() {
-            return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
-        }
-
-        const renderengine::Image& image() const { return *mImage; }
-
-    private:
-        // Only allow instantiation using ref counting.
-        friend class LightRefBase<Image>;
-        virtual ~Image();
-
-        // mGraphicBuffer is the buffer that was used to create this image.
-        sp<GraphicBuffer> mGraphicBuffer;
-
-        // mImage is the image created from mGraphicBuffer.
-        std::unique_ptr<renderengine::Image> mImage;
-        bool mCreated;
-        int32_t mCropWidth;
-        int32_t mCropHeight;
-    };
-
     // freeBufferLocked frees up the given buffer slot. If the slot has been
     // initialized this will release the reference to the GraphicBuffer in
-    // that slot and destroy the renderengine::Image in that slot.  Otherwise it has no
-    // effect.
+    // that slot.  Otherwise it has no effect.
     //
     // This method must be called with mMutex locked.
     virtual void freeBufferLocked(int slotIndex);
@@ -290,10 +250,10 @@
     // consume buffers as hardware textures.
     static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
 
-    // mCurrentTextureImage is the Image/buffer of the current texture. It's
+    // mCurrentTextureImage is the buffer containing the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
     // must track it separately in order to support the getCurrentBuffer method.
-    sp<Image> mCurrentTextureImage;
+    sp<GraphicBuffer> mCurrentTextureBuffer;
 
     // mCurrentCrop is the crop rectangle that applies to the current texture.
     // It gets set each time updateTexImage is called.
@@ -363,15 +323,6 @@
 
     wp<ContentsChangedListener> mContentsChangedListener;
 
-    // mImages stores the buffers that have been allocated by the BufferQueue
-    // for each buffer slot.  It is initialized to null pointers, and gets
-    // filled in with the result of BufferQueue::acquire when the
-    // client dequeues a buffer from a
-    // slot that has not yet been used. The buffer allocated to a slot will also
-    // be replaced if the requested buffer usage or geometry differs from that
-    // of the buffer allocated to a slot.
-    sp<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS];
-
     // mCurrentTexture is the buffer slot index of the buffer that is currently
     // bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT,
     // indicating that no buffer slot is currently bound to the texture. Note,
@@ -380,6 +331,17 @@
     // reset mCurrentTexture to INVALID_BUFFER_SLOT.
     int mCurrentTexture;
 
+    // Cached image used for rendering the current texture through GPU
+    // composition, which contains the cached image after freeBufferLocked is
+    // called on the current buffer. Whenever latchBuffer is called, this is
+    // expected to be cleared. Then, if bindTexImage is called before the next
+    // buffer is acquired, then this image is bound.
+    std::unique_ptr<renderengine::Image> mCurrentTextureImageFreed;
+
+    // Cached images used for rendering the current texture through GPU
+    // composition.
+    std::unique_ptr<renderengine::Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
     // A release that is pending on the receipt of a new release fence from
     // presentDisplay
     PendingRelease mPendingRelease;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 06e8a98..6cbc4c4 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -216,7 +216,7 @@
     return mConsumer->setFilteringEnabled(enabled);
 }
 
-status_t BufferQueueLayer::bindTextureImage() const {
+status_t BufferQueueLayer::bindTextureImage() {
     return mConsumer->bindTextureImage();
 }
 
@@ -327,8 +327,22 @@
     return NO_ERROR;
 }
 
-void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>&) {
+void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
+    const auto displayId = display->getId();
+    auto& hwcInfo = getBE().mHwcLayers[displayId];
+    auto& hwcLayer = hwcInfo.layer;
+
+    uint32_t hwcSlot = 0;
+    sp<GraphicBuffer> hwcBuffer;
+    hwcInfo.bufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer, &hwcSlot, &hwcBuffer);
+
     auto acquireFence = mConsumer->getCurrentFence();
+    auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
+              getBE().compositionInfo.mBuffer->handle, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
     getBE().compositionInfo.mBufferSlot = mActiveBufferSlot;
     getBE().compositionInfo.mBuffer = mActiveBuffer;
     getBE().compositionInfo.hwc.fence = acquireFence;
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index c239a00..abe0bc7 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -89,7 +89,7 @@
 
     void setFilteringEnabled(bool enabled) override;
 
-    status_t bindTextureImage() const override;
+    status_t bindTextureImage() override;
     status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                             const sp<Fence>& releaseFence) override;
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index c817fb0..e52b35a 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -323,22 +323,14 @@
                                        mCurrentTransform, enabled);
 }
 
-status_t BufferStateLayer::bindTextureImage() const {
+status_t BufferStateLayer::bindTextureImage() {
     const State& s(getDrawingState());
     auto& engine(mFlinger->getRenderEngine());
 
-    if (!engine.isCurrent()) {
-        ALOGE("RenderEngine is not current");
-        return INVALID_OPERATION;
-    }
-
     engine.checkErrors();
 
-    if (!mTextureImage) {
-        ALOGE("no currently-bound texture");
-        engine.bindExternalTextureImage(mTextureName, *engine.createImage());
-        return NO_INIT;
-    }
+    // TODO(marissaw): once buffers are cached, don't create a new image everytime
+    mTextureImage = engine.createImage();
 
     bool created =
             mTextureImage->setNativeWindowBuffer(s.buffer->getNativeBuffer(),
@@ -385,16 +377,6 @@
         return NO_ERROR;
     }
 
-    auto& engine(mFlinger->getRenderEngine());
-    if (!engine.isCurrent()) {
-        ALOGE("RenderEngine is not current");
-        return INVALID_OPERATION;
-    }
-    engine.checkErrors();
-
-    // TODO(marissaw): once buffers are cached, don't create a new image everytime
-    mTextureImage = engine.createImage();
-
     // Reject if the layer is invalid
     uint32_t bufferWidth = s.buffer->width;
     uint32_t bufferHeight = s.buffer->height;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index b134fc5..0c6eaf5 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -114,7 +114,7 @@
 
     void setFilteringEnabled(bool enabled) override;
 
-    status_t bindTextureImage() const override;
+    status_t bindTextureImage() override;
     status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                             const sp<Fence>& releaseFence) override;
 
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 3a554c9..263f872 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -62,20 +62,64 @@
     const auto& viewport = display->getViewport();
     Region visible = tr.transform(visibleRegion.intersect(viewport));
     const auto displayId = display->getId();
+    if (!hasHwcLayer(displayId)) {
+        ALOGE("[%s] failed to setPerFrameData: no HWC layer found (%d)",
+              mName.string(), displayId);
+        return;
+    }
+    auto& hwcInfo = getBE().mHwcLayers[displayId];
+    auto& hwcLayer = hwcInfo.layer;
+    auto error = hwcLayer->setVisibleRegion(visible);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        visible.dump(LOG_TAG);
+    }
     getBE().compositionInfo.hwc.visibleRegion = visible;
-    getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace;
 
     setCompositionType(displayId, HWC2::Composition::SolidColor);
+
+    error = hwcLayer->setDataspace(mCurrentDataSpace);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
     getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace;
 
     half4 color = getColor();
-    getBE().compositionInfo.hwc.color = {static_cast<uint8_t>(std::round(255.0f * color.r)),
-                                         static_cast<uint8_t>(std::round(255.0f * color.g)),
-                                         static_cast<uint8_t>(std::round(255.0f * color.b)), 255};
+    error = hwcLayer->setColor({static_cast<uint8_t>(std::round(255.0f * color.r)),
+                                static_cast<uint8_t>(std::round(255.0f * color.g)),
+                                static_cast<uint8_t>(std::round(255.0f * color.b)), 255});
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set color: %s (%d)", mName.string(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+    getBE().compositionInfo.hwc.color = { static_cast<uint8_t>(std::round(255.0f * color.r)),
+                                      static_cast<uint8_t>(std::round(255.0f * color.g)),
+                                      static_cast<uint8_t>(std::round(255.0f * color.b)), 255 };
 
     // Clear out the transform, because it doesn't make sense absent a source buffer
+    error = hwcLayer->setTransform(HWC2::Transform::None);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to clear transform: %s (%d)", mName.string(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
     getBE().compositionInfo.hwc.transform = HWC2::Transform::None;
+
+    error = hwcLayer->setColorTransform(getColorTransform());
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
+                to_string(error).c_str(), static_cast<int32_t>(error));
+    }
     getBE().compositionInfo.hwc.colorTransform = getColorTransform();
+
+    error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        surfaceDamageRegion.dump(LOG_TAG);
+    }
+    getBE().compositionInfo.hwc.surfaceDamage = surfaceDamageRegion;
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 341dfd5..6f645df 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -37,8 +37,8 @@
 #include <ui/DebugUtils.h>
 #include <ui/DisplayInfo.h>
 #include <ui/PixelFormat.h>
-#include <utils/RefBase.h>
 #include <utils/Log.h>
+#include <utils/RefBase.h>
 
 #include "DisplayHardware/DisplaySurface.h"
 #include "DisplayHardware/HWComposer.h"
@@ -66,7 +66,8 @@
 namespace {
 
 // ordered list of known SDR color modes
-const std::array<ColorMode, 2> sSdrColorModes = {
+const std::array<ColorMode, 3> sSdrColorModes = {
+        ColorMode::DISPLAY_BT2020,
         ColorMode::DISPLAY_P3,
         ColorMode::SRGB,
 };
@@ -96,6 +97,8 @@
             return Dataspace::SRGB;
         case ColorMode::DISPLAY_P3:
             return Dataspace::DISPLAY_P3;
+        case ColorMode::DISPLAY_BT2020:
+            return Dataspace::DISPLAY_BT2020;
         case ColorMode::BT2100_HLG:
             return Dataspace::BT2020_HLG;
         case ColorMode::BT2100_PQ:
@@ -767,9 +770,12 @@
         *outMode = iter->second.colorMode;
         *outIntent = iter->second.renderIntent;
     } else {
-        ALOGE("map unknown (%s)/(%s) to default color mode",
-              dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
-              decodeRenderIntent(intent).c_str());
+        // this is unexpected on a WCG display
+        if (hasWideColorGamut()) {
+            ALOGE("map unknown (%s)/(%s) to default color mode",
+                  dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+                  decodeRenderIntent(intent).c_str());
+        }
 
         *outDataspace = Dataspace::UNKNOWN;
         *outMode = ColorMode::NATIVE;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 163b26c..f0bccaa 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -354,16 +354,26 @@
 {
     Error error = kDefaultError;
 
-    if (mClient_2_2) {
-        mClient_2_2->getColorModes_2_2(display,
-                [&](const auto& tmpError, const auto& tmpModes) {
-                    error = tmpError;
-                    if (error != Error::NONE) {
-                        return;
-                    }
+    if (mClient_2_3) {
+        mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
+            error = tmpError;
+            if (error != Error::NONE) {
+                return;
+            }
 
-                    *outModes = tmpModes;
-                });
+            *outModes = tmpModes;
+        });
+    } else if (mClient_2_2) {
+        mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
+            error = tmpError;
+            if (error != Error::NONE) {
+                return;
+            }
+
+            for (types::V1_1::ColorMode colorMode : tmpModes) {
+                outModes->push_back(static_cast<ColorMode>(colorMode));
+            }
+        });
     } else {
         mClient->getColorModes(display,
                 [&](const auto& tmpError, const auto& tmpModes) {
@@ -555,8 +565,11 @@
         RenderIntent renderIntent)
 {
     hardware::Return<Error> ret(kDefaultError);
-    if (mClient_2_2) {
-        ret = mClient_2_2->setColorMode_2_2(display, mode, renderIntent);
+    if (mClient_2_3) {
+        ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
+    } else if (mClient_2_2) {
+        ret = mClient_2_2->setColorMode_2_2(display, static_cast<types::V1_1::ColorMode>(mode),
+                                            renderIntent);
     } else {
         ret = mClient->setColorMode(display,
                 static_cast<types::V1_0::ColorMode>(mode));
@@ -934,15 +947,22 @@
     }
 
     Error error = kDefaultError;
-    mClient_2_2->getRenderIntents(display, colorMode,
-            [&](const auto& tmpError, const auto& tmpKeys) {
+
+    auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
         error = tmpError;
         if (error != Error::NONE) {
             return;
         }
 
         *outRenderIntents = tmpKeys;
-    });
+    };
+
+    if (mClient_2_3) {
+        mClient_2_3->getRenderIntents_2_3(display, colorMode, getRenderIntentsLambda);
+    } else {
+        mClient_2_2->getRenderIntents(display, static_cast<types::V1_1::ColorMode>(colorMode),
+                                      getRenderIntentsLambda);
+    }
 
     return error;
 }
@@ -955,14 +975,14 @@
     }
 
     Error error = kDefaultError;
-    mClient_2_2->getDataspaceSaturationMatrix(dataspace, [&](const auto& tmpError, const auto& tmpMatrix) {
-        error = tmpError;
-        if (error != Error::NONE) {
-            return;
-        }
-
-        *outMatrix = mat4(tmpMatrix.data());
-    });
+    mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
+                                              [&](const auto& tmpError, const auto& tmpMatrix) {
+                                                  error = tmpError;
+                                                  if (error != Error::NONE) {
+                                                      return;
+                                                  }
+                                                  *outMatrix = mat4(tmpMatrix.data());
+                                              });
 
     return error;
 }
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 94be6e9..4188352 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -49,10 +49,10 @@
 using types::V1_0::Hdr;
 using types::V1_0::Transform;
 
-using types::V1_1::ColorMode;
-using types::V1_1::Dataspace;
 using types::V1_1::PixelFormat;
 using types::V1_1::RenderIntent;
+using types::V1_2::ColorMode;
+using types::V1_2::Dataspace;
 
 using V2_1::Config;
 using V2_1::Display;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8afd3b3..3a3f5eb 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -102,6 +102,7 @@
     mCurrentState.hdrMetadata.validTypes = 0;
     mCurrentState.surfaceDamageRegion.clear();
     mCurrentState.api = -1;
+    mCurrentState.hasColorTransform = false;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
@@ -457,18 +458,6 @@
     }
     auto& hwcInfo = getBE().mHwcLayers[displayId];
 
-    // Device or Cursor layers
-    if (mPotentialCursor) {
-        ALOGV("[%s] Requesting Cursor composition", mName.string());
-        setCompositionType(displayId, HWC2::Composition::Cursor);
-    } else {
-        ALOGV("[%s] Requesting Device composition", mName.string());
-        setCompositionType(displayId, HWC2::Composition::Device);
-    }
-
-    // Need to program geometry parts
-    getBE().compositionInfo.hwc.skipGeometry = false;
-
     // enable this layer
     hwcInfo.forceClientComposition = false;
 
@@ -476,6 +465,8 @@
         hwcInfo.forceClientComposition = true;
     }
 
+    auto& hwcLayer = hwcInfo.layer;
+
     // this gives us only the "orientation" component of the transform
     const State& s(getDrawingState());
     auto blendMode = HWC2::BlendMode::None;
@@ -483,6 +474,12 @@
         blendMode =
                 mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage;
     }
+    auto error = hwcLayer->setBlendMode(blendMode);
+    ALOGE_IF(error != HWC2::Error::None,
+             "[%s] Failed to set blend mode %s:"
+             " %s (%d)",
+             mName.string(), to_string(blendMode).c_str(), to_string(error).c_str(),
+             static_cast<int32_t>(error));
     getBE().compositionInfo.hwc.blendMode = blendMode;
 
     // apply the layer's transform, followed by the display's global transform
@@ -522,14 +519,39 @@
     }
     const ui::Transform& tr = display->getTransform();
     Rect transformedFrame = tr.transform(frame);
+    error = hwcLayer->setDisplayFrame(transformedFrame);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", mName.string(),
+              transformedFrame.left, transformedFrame.top, transformedFrame.right,
+              transformedFrame.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
+    } else {
+        hwcInfo.displayFrame = transformedFrame;
+    }
     getBE().compositionInfo.hwc.displayFrame = transformedFrame;
 
     FloatRect sourceCrop = computeCrop(display);
+    error = hwcLayer->setSourceCrop(sourceCrop);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
+              "%s (%d)",
+              mName.string(), sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom,
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    } else {
+        hwcInfo.sourceCrop = sourceCrop;
+    }
     getBE().compositionInfo.hwc.sourceCrop = sourceCrop;
 
     float alpha = static_cast<float>(getAlpha());
+    error = hwcLayer->setPlaneAlpha(alpha);
+    ALOGE_IF(error != HWC2::Error::None,
+             "[%s] Failed to set plane alpha %.3f: "
+             "%s (%d)",
+             mName.string(), alpha, to_string(error).c_str(), static_cast<int32_t>(error));
     getBE().compositionInfo.hwc.alpha = alpha;
 
+    error = hwcLayer->setZOrder(z);
+    ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)", mName.string(), z,
+             to_string(error).c_str(), static_cast<int32_t>(error));
     getBE().compositionInfo.hwc.z = z;
 
     int type = s.type;
@@ -543,6 +565,10 @@
         }
     }
 
+    error = hwcLayer->setInfo(type, appId);
+    ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set info (%d)", mName.string(),
+             static_cast<int32_t>(error));
+
     getBE().compositionInfo.hwc.type = type;
     getBE().compositionInfo.hwc.appId = appId;
 
@@ -582,9 +608,16 @@
     if (orientation & ui::Transform::ROT_INVALID) {
         // we can only handle simple transformation
         hwcInfo.forceClientComposition = true;
+        getBE().mHwcLayers[displayId].compositionType = HWC2::Composition::Client;
     } else {
         auto transform = static_cast<HWC2::Transform>(orientation);
         hwcInfo.transform = transform;
+        auto error = hwcLayer->setTransform(transform);
+        ALOGE_IF(error != HWC2::Error::None,
+                 "[%s] Failed to set transform %s: "
+                 "%s (%d)",
+                 mName.string(), to_string(transform).c_str(), to_string(error).c_str(),
+                 static_cast<int32_t>(error));
         getBE().compositionInfo.hwc.transform = transform;
     }
 }
@@ -665,16 +698,25 @@
     clearWithOpenGL(renderArea, 0, 0, 0, 0);
 }
 
-void Layer::setCompositionType(int32_t displayId, HWC2::Composition type, bool /*callIntoHwc*/) {
+void Layer::setCompositionType(int32_t displayId, HWC2::Composition type, bool callIntoHwc) {
     if (getBE().mHwcLayers.count(displayId) == 0) {
         ALOGE("setCompositionType called without a valid HWC layer");
         return;
-    } else {
-        if (getBE().mHwcLayers[displayId].compositionType != type) {
-            ALOGV("setCompositionType: Changing compositionType from %s to %s",
-                    to_string(getBE().mHwcLayers[displayId].compositionType).c_str(),
-                    to_string(type).c_str());
-            getBE().mHwcLayers[displayId].compositionType = type;
+    }
+    auto& hwcInfo = getBE().mHwcLayers[displayId];
+    auto& hwcLayer = hwcInfo.layer;
+    ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", (hwcLayer)->getId(), to_string(type).c_str(),
+          static_cast<int>(callIntoHwc));
+    if (hwcInfo.compositionType != type) {
+        ALOGV("    actually setting");
+        hwcInfo.compositionType = type;
+        if (callIntoHwc) {
+            auto error = (hwcLayer)->setCompositionType(type);
+            ALOGE_IF(error != HWC2::Error::None,
+                     "[%s] Failed to set "
+                     "composition type %s: %s (%d)",
+                     mName.string(), to_string(type).c_str(), to_string(error).c_str(),
+                     static_cast<int32_t>(error));
         }
     }
 }
@@ -1547,11 +1589,15 @@
 }
 
 bool Layer::setColorTransform(const mat4& matrix) {
+    static const mat4 identityMatrix = mat4();
+
     if (mCurrentState.colorTransform == matrix) {
         return false;
     }
     ++mCurrentState.sequence;
     mCurrentState.colorTransform = matrix;
+    mCurrentState.hasColorTransform = matrix != identityMatrix;
+    mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
@@ -1561,8 +1607,7 @@
 }
 
 bool Layer::hasColorTransform() const {
-    static const mat4 identityMatrix = mat4();
-    return getDrawingState().colorTransform != identityMatrix;
+    return getDrawingState().hasColorTransform;
 }
 
 bool Layer::isLegacyDataSpace() const {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index e2d1178..5d05f05 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -182,6 +182,7 @@
 
         sp<NativeHandle> sidebandStream;
         mat4 colorTransform;
+        bool hasColorTransform;
     };
 
     explicit Layer(const LayerCreationArgs& args);
diff --git a/services/surfaceflinger/LayerBE.cpp b/services/surfaceflinger/LayerBE.cpp
index c9b7933..70b00dd 100644
--- a/services/surfaceflinger/LayerBE.cpp
+++ b/services/surfaceflinger/LayerBE.cpp
@@ -65,9 +65,7 @@
 }
 
 void LayerBE::onLayerDisplayed(const sp<Fence>& releaseFence) {
-    if (mLayer) {
-        mLayer->onLayerDisplayed(releaseFence);
-    }
+    mLayer->onLayerDisplayed(releaseFence);
 }
 
 void LayerBE::clear(renderengine::RenderEngine& engine) {
diff --git a/services/surfaceflinger/LayerBE.h b/services/surfaceflinger/LayerBE.h
index d63d16f..463c46c 100644
--- a/services/surfaceflinger/LayerBE.h
+++ b/services/surfaceflinger/LayerBE.h
@@ -34,14 +34,13 @@
 
 struct CompositionInfo {
     std::string layerName;
-    HWC2::Composition compositionType = HWC2::Composition::Invalid;
+    HWC2::Composition compositionType;
     bool firstClear = false;
     sp<GraphicBuffer> mBuffer = nullptr;
     int mBufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
     std::shared_ptr<LayerBE> layer;
     struct {
         std::shared_ptr<HWC2::Layer> hwcLayer;
-        bool skipGeometry = true;
         int32_t displayId = -1;
         sp<Fence> fence;
         HWC2::BlendMode blendMode = HWC2::BlendMode::Invalid;
diff --git a/services/surfaceflinger/RenderEngine/TEST_MAPPING b/services/surfaceflinger/RenderEngine/TEST_MAPPING
new file mode 100644
index 0000000..995dba1
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "librenderengine_test"
+    }
+  ]
+}
diff --git a/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.cpp
index 5c4c3d5..dbf50c5 100644
--- a/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.cpp
@@ -650,6 +650,13 @@
     } while (true);
 }
 
+status_t GLES20RenderEngine::drawLayers(const DisplaySettings& /*settings*/,
+                                        const std::vector<LayerSettings>& /*layers*/,
+                                        ANativeWindowBuffer* const /*buffer*/,
+                                        base::unique_fd* /*displayFence*/) const {
+    return NO_ERROR;
+}
+
 void GLES20RenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
                                                   ui::Transform::orientation_flags rotation) {
     int32_t l = sourceCrop.left;
diff --git a/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.h
index 4f03a90..b08e096 100644
--- a/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.h
@@ -73,6 +73,10 @@
     void unbindFrameBuffer(Framebuffer* framebuffer) override;
     void checkErrors() const override;
 
+    status_t drawLayers(const DisplaySettings& settings, const std::vector<LayerSettings>& layers,
+                        ANativeWindowBuffer* const buffer,
+                        base::unique_fd* displayFence) const override;
+
     // internal to RenderEngine
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
     EGLConfig getEGLConfig() const { return mEGLConfig; }
diff --git a/services/surfaceflinger/RenderEngine/include/renderengine/DisplaySettings.h b/services/surfaceflinger/RenderEngine/include/renderengine/DisplaySettings.h
new file mode 100644
index 0000000..5941cdf
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/include/renderengine/DisplaySettings.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math/mat4.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+namespace android {
+namespace renderengine {
+
+// DisplaySettings contains the settings that are applicable when drawing all
+// layers for a given display.
+struct DisplaySettings {
+    // Rectangle describing the physical display. We will project from the
+    // logical clip onto this rectangle.
+    Rect physicalDisplay;
+
+    // Rectangle bounded by the x,y- clipping planes in the logical display, so
+    // that the orthographic projection matrix can be computed. When
+    // constructing this matrix, z-coordinate bound are assumed to be at z=0 and
+    // z=1.
+    Rect clip;
+
+    // Global transform to apply to all layers.
+    mat4 globalTransform;
+
+    // Maximum luminance pulled from the display's HDR capabilities.
+    float maxLuminence;
+
+    // Output dataspace that will be populated if wide color gamut is used, or
+    // DataSpace::UNKNOWN otherwise.
+    ui::Dataspace outputDataspace;
+
+    // Additional color transform to apply in linear space after transforming
+    // to the output dataspace.
+    mat4 colorTransform;
+
+    // Region that will be cleared to (0, 0, 0, 0) prior to rendering.
+    // clearRegion will first be transformed by globalTransform so that it will
+    // be in the same coordinate space as the rendered layers.
+    Region clearRegion;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/include/renderengine/LayerSettings.h b/services/surfaceflinger/RenderEngine/include/renderengine/LayerSettings.h
new file mode 100644
index 0000000..facea21
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/include/renderengine/LayerSettings.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math/mat4.h>
+#include <math/vec3.h>
+#include <renderengine/Texture.h>
+#include <ui/FloatRect.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android {
+namespace renderengine {
+
+// Metadata describing the input buffer to render from.
+struct Buffer {
+    // Buffer containing the image that we will render.
+    // If buffer == nullptr, then the rest of the fields in this struct will be
+    // ignored.
+    sp<GraphicBuffer> buffer;
+
+    // Texture identifier to bind the external texture to.
+    // TODO(alecmouri): This is GL-specific...make the type backend-agnostic.
+    uint32_t textureName;
+
+    // Whether to use filtering when rendering the texture.
+    bool useTextureFiltering;
+
+    // Transform matrix to apply to texture coordinates.
+    mat4 textureTransform;
+
+    // Wheteher to use pre-multiplied alpha
+    bool usePremultipliedAlpha;
+
+    // HDR color-space setting for Y410.
+    bool isY410BT2020;
+};
+
+// Metadata describing the layer geometry.
+struct Geometry {
+    // Boundaries of the layer.
+    FloatRect boundaries;
+
+    // Transform matrix to apply to mesh coordinates.
+    mat4 positionTransform;
+};
+
+// Descriptor of the source pixels for this layer.
+struct PixelSource {
+    // Source buffer
+    Buffer buffer;
+
+    // The solid color with which to fill the layer.
+    // This should only be populated if we don't render from an application
+    // buffer.
+    half3 solidColor;
+};
+
+// The settings that RenderEngine requires for correctly rendering a Layer.
+struct LayerSettings {
+    // Geometry information
+    Geometry geometry;
+
+    // Source pixels for this layer.
+    PixelSource source;
+
+    // Alpha option to apply to the source pixels
+    half alpha;
+
+    // Color space describing how the source pixels should be interpreted.
+    ui::Dataspace sourceDataspace;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/include/renderengine/RenderEngine.h b/services/surfaceflinger/RenderEngine/include/renderengine/RenderEngine.h
index 122271f..f5d3d6b 100644
--- a/services/surfaceflinger/RenderEngine/include/renderengine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/include/renderengine/RenderEngine.h
@@ -23,8 +23,10 @@
 
 #include <android-base/unique_fd.h>
 #include <math/mat4.h>
+#include <renderengine/DisplaySettings.h>
 #include <renderengine/Framebuffer.h>
 #include <renderengine/Image.h>
+#include <renderengine/LayerSettings.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Transform.h>
 
@@ -64,6 +66,11 @@
 
     virtual ~RenderEngine() = 0;
 
+    // ----- BEGIN DEPRECATED INTERFACE -----
+    // This interface, while still in use until a suitable replacement is built,
+    // should be considered deprecated, minus some methods which still may be
+    // used to support legacy behavior.
+
     virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0;
     virtual std::unique_ptr<Surface> createSurface() = 0;
     virtual std::unique_ptr<Image> createImage() = 0;
@@ -133,6 +140,37 @@
     // queries
     virtual size_t getMaxTextureSize() const = 0;
     virtual size_t getMaxViewportDims() const = 0;
+
+    // ----- END DEPRECATED INTERFACE -----
+
+    // ----- BEGIN NEW INTERFACE -----
+
+    // Renders layers for a particular display via GPU composition. This method
+    // should be called for every display that needs to be rendered via the GPU.
+    // @param settings The display-wide settings that should be applied prior to
+    // drawing any layers.
+    // @param layers The layers to draw onto the display, in Z-order.
+    // @param buffer The buffer which will be drawn to. This buffer will be
+    // ready once displayFence fires.
+    // @param displayFence A pointer to a fence, which will fire when the buffer
+    // has been drawn to and is ready to be examined. The fence will be
+    // initialized by this method. The caller will be responsible for owning the
+    // fence.
+    // @return An error code indicating whether drawing was successful. For
+    // now, this always returns NO_ERROR.
+    // TODO(alecmouri): Consider making this a multi-display API, so that the
+    // caller deoes not need to handle multiple fences.
+    virtual status_t drawLayers(const DisplaySettings& settings,
+                                const std::vector<LayerSettings>& layers,
+                                ANativeWindowBuffer* const buffer,
+                                base::unique_fd* displayFence) const = 0;
+
+    // TODO(alecmouri): Expose something like bindTexImage() so that devices
+    // that don't support native sync fences can get rid of code duplicated
+    // between BufferStateLayer and BufferQueueLayer for binding an external
+    // texture.
+
+    // TODO(alecmouri): Add API to help with managing a texture pool.
 };
 
 class BindNativeBufferAsFramebuffer {
diff --git a/services/surfaceflinger/RenderEngine/tests/Android.bp b/services/surfaceflinger/RenderEngine/tests/Android.bp
new file mode 100644
index 0000000..65b7c82
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/tests/Android.bp
@@ -0,0 +1,36 @@
+// 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.
+
+cc_test {
+    name: "librenderengine_test",
+    defaults: ["surfaceflinger_defaults"],
+    test_suites: ["device-tests"],
+    srcs: [
+        "RenderEngineTest.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "librenderengine",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libEGL",
+        "libGLESv2",
+        "libgui",
+        "liblog",
+        "libnativewindow",
+        "libui",
+        "libutils",
+    ],
+}
diff --git a/services/surfaceflinger/RenderEngine/tests/RenderEngineTest.cpp b/services/surfaceflinger/RenderEngine/tests/RenderEngineTest.cpp
new file mode 100644
index 0000000..345c7ea
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/tests/RenderEngineTest.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <renderengine/RenderEngine.h>
+#include <ui/PixelFormat.h>
+
+namespace android {
+
+class RenderEngineTest : public ::testing::Test {
+public:
+    RenderEngineTest() {
+        // Initialize with some sane defaults.
+        // TODO(alecmouri): This should probably be the same instance used by
+        // SurfaceFlinger eventually.
+        mRE = renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888),
+                                                 0);
+    }
+
+    status_t drawEmptyLayers() {
+        renderengine::DisplaySettings settings;
+        std::vector<renderengine::LayerSettings> layers;
+        // Meaningless buffer since we don't do any drawing
+        sp<GraphicBuffer> buffer = new GraphicBuffer();
+        base::unique_fd fence;
+        return mRE->drawLayers(settings, layers, buffer->getNativeBuffer(), &fence);
+    }
+
+private:
+    std::unique_ptr<renderengine::RenderEngine> mRE;
+};
+
+TEST_F(RenderEngineTest, drawLayers_noLayersToDraw_works) {
+    status_t result = drawEmptyLayers();
+    ASSERT_EQ(NO_ERROR, result);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f0723e8..1a5a389 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -124,6 +124,7 @@
         case ColorMode::ADOBE_RGB:
         case ColorMode::DCI_P3:
         case ColorMode::BT2020:
+        case ColorMode::DISPLAY_BT2020:
         case ColorMode::BT2100_PQ:
         case ColorMode::BT2100_HLG:
             return true;
@@ -1522,13 +1523,8 @@
     preComposition();
     rebuildLayerStacks();
     calculateWorkingSet();
-
     for (const auto& [token, display] : mDisplays) {
-        const auto displayId = display->getId();
         beginFrame(display);
-        for (auto& compositionInfo : getBE().mCompositionInfo[displayId]) {
-            setUpHWComposer(compositionInfo);
-        }
         prepareFrame(display);
         doDebugFlashRegions(display, repaintEverything);
         doComposition(display, repaintEverything);
@@ -2008,6 +2004,7 @@
 // can only be one of
 //  - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced)
 //  - Dataspace::DISPLAY_P3
+//  - Dataspace::DISPLAY_BT2020
 // The returned HDR data space is one of
 //  - Dataspace::UNKNOWN
 //  - Dataspace::BT2020_HLG
@@ -2021,6 +2018,12 @@
         switch (layer->getDataSpace()) {
             case Dataspace::V0_SCRGB:
             case Dataspace::V0_SCRGB_LINEAR:
+            case Dataspace::BT2020:
+            case Dataspace::BT2020_ITU:
+            case Dataspace::BT2020_LINEAR:
+            case Dataspace::DISPLAY_BT2020:
+                bestDataSpace = Dataspace::DISPLAY_BT2020;
+                break;
             case Dataspace::DISPLAY_P3:
                 bestDataSpace = Dataspace::DISPLAY_P3;
                 break;
@@ -2081,127 +2084,6 @@
     display->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
 }
 
-void SurfaceFlinger::configureSidebandComposition(const CompositionInfo& compositionInfo) const
-{
-    HWC2::Error error;
-    LOG_ALWAYS_FATAL_IF(compositionInfo.hwc.sidebandStream == nullptr,
-                        "CompositionType is sideband, but sideband stream is nullptr");
-    error = (compositionInfo.hwc.hwcLayer)
-                    ->setSidebandStream(compositionInfo.hwc.sidebandStream->handle());
-    if (error != HWC2::Error::None) {
-        ALOGE("[SF] Failed to set sideband stream %p: %s (%d)",
-                compositionInfo.hwc.sidebandStream->handle(), to_string(error).c_str(),
-                static_cast<int32_t>(error));
-    }
-}
-
-void SurfaceFlinger::configureHwcCommonData(const CompositionInfo& compositionInfo) const
-{
-    HWC2::Error error;
-
-    if (!compositionInfo.hwc.skipGeometry) {
-        error = (compositionInfo.hwc.hwcLayer)->setBlendMode(compositionInfo.hwc.blendMode);
-        ALOGE_IF(error != HWC2::Error::None,
-                 "[SF] Failed to set blend mode %s:"
-                 " %s (%d)",
-                 to_string(compositionInfo.hwc.blendMode).c_str(), to_string(error).c_str(),
-                 static_cast<int32_t>(error));
-
-        error = (compositionInfo.hwc.hwcLayer)->setDisplayFrame(compositionInfo.hwc.displayFrame);
-        ALOGE_IF(error != HWC2::Error::None,
-                "[SF] Failed to set the display frame [%d, %d, %d, %d] %s (%d)",
-                compositionInfo.hwc.displayFrame.left,
-                compositionInfo.hwc.displayFrame.right,
-                compositionInfo.hwc.displayFrame.top,
-                compositionInfo.hwc.displayFrame.bottom,
-                to_string(error).c_str(), static_cast<int32_t>(error));
-
-        error = (compositionInfo.hwc.hwcLayer)->setSourceCrop(compositionInfo.hwc.sourceCrop);
-        ALOGE_IF(error != HWC2::Error::None,
-                "[SF] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: %s (%d)",
-                compositionInfo.hwc.sourceCrop.left,
-                compositionInfo.hwc.sourceCrop.right,
-                compositionInfo.hwc.sourceCrop.top,
-                compositionInfo.hwc.sourceCrop.bottom,
-                to_string(error).c_str(), static_cast<int32_t>(error));
-
-        error = (compositionInfo.hwc.hwcLayer)->setPlaneAlpha(compositionInfo.hwc.alpha);
-        ALOGE_IF(error != HWC2::Error::None,
-                 "[SF] Failed to set plane alpha %.3f: "
-                 "%s (%d)",
-                 compositionInfo.hwc.alpha,
-                 to_string(error).c_str(), static_cast<int32_t>(error));
-
-
-        error = (compositionInfo.hwc.hwcLayer)->setZOrder(compositionInfo.hwc.z);
-        ALOGE_IF(error != HWC2::Error::None,
-                "[SF] Failed to set Z %u: %s (%d)",
-                compositionInfo.hwc.z,
-                to_string(error).c_str(), static_cast<int32_t>(error));
-
-        error = (compositionInfo.hwc.hwcLayer)
-                        ->setInfo(compositionInfo.hwc.type, compositionInfo.hwc.appId);
-        ALOGE_IF(error != HWC2::Error::None,
-                "[SF] Failed to set info (%d)",
-                static_cast<int32_t>(error));
-
-        error = (compositionInfo.hwc.hwcLayer)->setTransform(compositionInfo.hwc.transform);
-        ALOGE_IF(error != HWC2::Error::None,
-                 "[SF] Failed to set transform %s: "
-                 "%s (%d)",
-                 to_string(compositionInfo.hwc.transform).c_str(), to_string(error).c_str(),
-                 static_cast<int32_t>(error));
-    }
-
-    error = (compositionInfo.hwc.hwcLayer)->setCompositionType(compositionInfo.compositionType);
-    ALOGE_IF(error != HWC2::Error::None,
-            "[SF] Failed to set composition type: %s (%d)",
-                to_string(error).c_str(), static_cast<int32_t>(error));
-
-    error = (compositionInfo.hwc.hwcLayer)->setDataspace(compositionInfo.hwc.dataspace);
-    ALOGE_IF(error != HWC2::Error::None,
-            "[SF] Failed to set dataspace: %s (%d)",
-            to_string(error).c_str(), static_cast<int32_t>(error));
-
-    error = (compositionInfo.hwc.hwcLayer)->setPerFrameMetadata(
-            compositionInfo.hwc.supportedPerFrameMetadata, compositionInfo.hwc.hdrMetadata);
-    ALOGE_IF(error != HWC2::Error::None && error != HWC2::Error::Unsupported,
-            "[SF] Failed to set hdrMetadata: %s (%d)",
-            to_string(error).c_str(), static_cast<int32_t>(error));
-
-    if (compositionInfo.compositionType == HWC2::Composition::SolidColor) {
-        error = (compositionInfo.hwc.hwcLayer)->setColor(compositionInfo.hwc.color);
-        ALOGE_IF(error != HWC2::Error::None,
-                "[SF] Failed to set color: %s (%d)",
-                to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    error = (compositionInfo.hwc.hwcLayer)->setVisibleRegion(compositionInfo.hwc.visibleRegion);
-    ALOGE_IF(error != HWC2::Error::None,
-            "[SF] Failed to set visible region: %s (%d)",
-            to_string(error).c_str(), static_cast<int32_t>(error));
-
-    error = (compositionInfo.hwc.hwcLayer)->setSurfaceDamage(compositionInfo.hwc.surfaceDamage);
-    ALOGE_IF(error != HWC2::Error::None,
-            "[SF] Failed to set surface damage: %s (%d)",
-            to_string(error).c_str(), static_cast<int32_t>(error));
-
-    error = (compositionInfo.hwc.hwcLayer)->setColorTransform(compositionInfo.hwc.colorTransform);
-}
-
-void SurfaceFlinger::configureDeviceComposition(const CompositionInfo& compositionInfo) const
-{
-    HWC2::Error error;
-
-    if (compositionInfo.hwc.fence) {
-        error = (compositionInfo.hwc.hwcLayer)->setBuffer(compositionInfo.mBufferSlot,
-                compositionInfo.mBuffer, compositionInfo.hwc.fence);
-        ALOGE_IF(error != HWC2::Error::None,
-                "[SF] Failed to set buffer: %s (%d)",
-                to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-}
-
 void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& display)
 {
     bool dirty = !display->getDirtyRegion(false).isEmpty();
@@ -2247,42 +2129,6 @@
              display->getId(), result, strerror(-result));
 }
 
-void SurfaceFlinger::setUpHWComposer(const CompositionInfo& compositionInfo) {
-    ATRACE_CALL();
-    ALOGV("setUpHWComposer");
-
-    switch (compositionInfo.compositionType)
-    {
-        case HWC2::Composition::Invalid:
-            break;
-
-        case HWC2::Composition::Client:
-            if (compositionInfo.hwc.hwcLayer) {
-                auto error = (compositionInfo.hwc.hwcLayer)->
-                    setCompositionType(compositionInfo.compositionType);
-                ALOGE_IF(error != HWC2::Error::None,
-                        "[SF] Failed to set composition type: %s (%d)",
-                            to_string(error).c_str(), static_cast<int32_t>(error));
-            }
-            break;
-
-        case HWC2::Composition::Sideband:
-            configureHwcCommonData(compositionInfo);
-            configureSidebandComposition(compositionInfo);
-            break;
-
-        case HWC2::Composition::SolidColor:
-            configureHwcCommonData(compositionInfo);
-            break;
-
-        case HWC2::Composition::Device:
-        case HWC2::Composition::Cursor:
-            configureHwcCommonData(compositionInfo);
-            configureDeviceComposition(compositionInfo);
-            break;
-    }
-}
-
 void SurfaceFlinger::doComposition(const sp<DisplayDevice>& display, bool repaintEverything) {
     ATRACE_CALL();
     ALOGV("doComposition");
@@ -2325,14 +2171,15 @@
         }
         display->onSwapBuffersCompleted();
         display->makeCurrent();
-        for (auto& compositionInfo : getBE().mCompositionInfo[displayId]) {
+        for (auto& layer : display->getVisibleLayersSortedByZ()) {
             sp<Fence> releaseFence = Fence::NO_FENCE;
+
             // The layer buffer from the previous frame (if any) is released
             // by HWC only when the release fence from this frame (if any) is
             // signaled.  Always get the release fence from HWC first.
-            auto hwcLayer = compositionInfo.hwc.hwcLayer;
-            if ((displayId >= 0) && hwcLayer) {
-                releaseFence = getBE().mHwc->getLayerReleaseFence(displayId, hwcLayer.get());
+            auto hwcLayer = layer->getHwcLayer(displayId);
+            if (displayId >= 0) {
+                releaseFence = getBE().mHwc->getLayerReleaseFence(displayId, hwcLayer);
             }
 
             // If the layer was client composited in the previous frame, we
@@ -2340,14 +2187,12 @@
             // Since we do not track that, always merge with the current
             // client target acquire fence when it is available, even though
             // this is suboptimal.
-            if (compositionInfo.compositionType == HWC2::Composition::Client) {
+            if (layer->getCompositionType(displayId) == HWC2::Composition::Client) {
                 releaseFence = Fence::merge("LayerRelease", releaseFence,
                                             display->getClientTargetAcquireFence());
             }
 
-            if (compositionInfo.layer) {
-                compositionInfo.layer->onLayerDisplayed(releaseFence);
-            }
+            layer->getBE().onLayerDisplayed(releaseFence);
         }
 
         // We've got a list of layers needing fences, that are disjoint with
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f535c4e..658f04e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -689,7 +689,6 @@
      * to prepare the hardware composer
      */
     void prepareFrame(const sp<DisplayDevice>& display);
-    void setUpHWComposer(const CompositionInfo& compositionInfo);
     void doComposition(const sp<DisplayDevice>& display, bool repainEverything);
     void doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything);
     void doTracing(const char* where);
@@ -806,11 +805,6 @@
 
     std::atomic<bool> mRepaintEverything{false};
 
-    // helper methods
-    void configureHwcCommonData(const CompositionInfo& compositionInfo) const;
-    void configureDeviceComposition(const CompositionInfo& compositionInfo) const;
-    void configureSidebandComposition(const CompositionInfo& compositionInfo) const;
-
     // constant members (no synchronization needed for access)
     nsecs_t mBootTime;
     bool mGpuToCpuSupported;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 0bc1c0a..2e1231b 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -145,14 +145,15 @@
     return static_cast<int32_t>(delta);
 }
 
+// This regular expression captures the following for instance:
+// StatusBar in StatusBar#0
+// com.appname in com.appname/com.appname.activity#0
+// com.appname in SurfaceView - com.appname/com.appname.activity#0
+static const std::regex packageNameRegex("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+");
+
 static std::string getPackageName(const std::string& layerName) {
-    // This regular expression captures the following for instance:
-    // StatusBar in StatusBar#0
-    // com.appname in com.appname/com.appname.activity#0
-    // com.appname in SurfaceView - com.appname/com.appname.activity#0
-    const std::regex re("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+");
     std::smatch match;
-    if (std::regex_match(layerName.begin(), layerName.end(), match, re)) {
+    if (std::regex_match(layerName.begin(), layerName.end(), match, packageNameRegex)) {
         // There must be a match for group 1 otherwise the whole string is not
         // matched and the above will return false
         return match[1];
@@ -223,17 +224,19 @@
     }
 }
 
+// This regular expression captures the following layer names for instance:
+// 1) StatusBat#0
+// 2) NavigationBar#1
+// 3) co(m).*#0
+// 4) SurfaceView - co(m).*#0
+// Using [-\\s\t]+ for the conjunction part between SurfaceView and co(m).*
+// is a bit more robust in case there's a slight change.
+// The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases.
+static const std::regex layerNameRegex(
+        "(((SurfaceView[-\\s\\t]+)?com?\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+");
+
 static bool layerNameIsValid(const std::string& layerName) {
-    // This regular expression captures the following layer names for instance:
-    // 1) StatusBat#0
-    // 2) NavigationBar#1
-    // 3) co(m).*#0
-    // 4) SurfaceView - co(m).*#0
-    // Using [-\\s\t]+ for the conjunction part between SurfaceView and co(m).*
-    // is a bit more robust in case there's a slight change.
-    // The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases.
-    std::regex re("(((SurfaceView[-\\s\\t]+)?com?\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+");
-    return std::regex_match(layerName.begin(), layerName.end(), re);
+    return std::regex_match(layerName.begin(), layerName.end(), layerNameRegex);
 }
 
 void TimeStats::setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime) {
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 7b79fbd..3719a3d 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -42,6 +42,7 @@
 namespace {
 
 using testing::_;
+using testing::AtLeast;
 using testing::ByMove;
 using testing::DoAll;
 using testing::IsNull;
@@ -474,10 +475,7 @@
         enqueueBuffer(test, layer);
         Mock::VerifyAndClear(test->mMessageQueue);
 
-        EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true));
         EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-        EXPECT_CALL(*test->mRenderEngine, createImage())
-                .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage))));
         bool ignoredRecomputeVisibleRegions;
         layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, Fence::NO_FENCE);
         Mock::VerifyAndClear(test->mRenderEngine);
@@ -517,7 +515,7 @@
         // expectations are for appears to make an extra call to them.
         // TODO: Investigate this extra call
         EXPECT_CALL(*test->mComposer, setLayerTransform(HWC_DISPLAY, HWC_LAYER, DEFAULT_TRANSFORM))
-                .Times(1)
+                .Times(AtLeast(1))
                 .RetiresOnSaturation();
     }
 
@@ -574,6 +572,9 @@
                                              LayerProperties::COLOR[2], LayerProperties::COLOR[3])))
                 .Times(1);
 
+        EXPECT_CALL(*test->mRenderEngine, createImage())
+                .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage))));
+        EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true));
         EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1);
         EXPECT_CALL(*test->mRenderEngine, setupLayerTexturing(_)).Times(1);
         EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
@@ -669,6 +670,9 @@
     static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
 
     static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mRenderEngine, createImage())
+                .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage))));
+        EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true));
         EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1);
         EXPECT_CALL(*test->mRenderEngine, setupLayerBlackedOut()).Times(1);
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index b474e42..d32627a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -22,6 +22,7 @@
 
 #include <log/log.h>
 
+#include <ui/DebugUtils.h>
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockDisplaySurface.h"
@@ -997,6 +998,93 @@
 }
 
 /* ------------------------------------------------------------------------
+ * DisplayDevice::GetBestColorMode
+ */
+class GetBestColorModeTest : public DisplayTransactionTest {
+public:
+    GetBestColorModeTest()
+          : DisplayTransactionTest(),
+            mInjector(FakeDisplayDeviceInjector(mFlinger, DisplayDevice::DISPLAY_PRIMARY, 0)) {}
+
+    void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
+
+    void addHwcColorModesMapping(ui::ColorMode colorMode,
+                                 std::vector<ui::RenderIntent> renderIntents) {
+        mHwcColorModes[colorMode] = renderIntents;
+    }
+
+    void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
+
+    void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
+
+    void getBestColorMode() {
+        mInjector.setHwcColorModes(mHwcColorModes);
+        mInjector.setHasWideColorGamut(mHasWideColorGamut);
+        auto displayDevice = mInjector.inject();
+
+        displayDevice->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
+                                        &mOutColorMode, &mOutRenderIntent);
+    }
+
+    ui::Dataspace mOutDataspace;
+    ui::ColorMode mOutColorMode;
+    ui::RenderIntent mOutRenderIntent;
+
+private:
+    ui::Dataspace mInputDataspace;
+    ui::RenderIntent mInputRenderIntent;
+    bool mHasWideColorGamut = false;
+    std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
+    FakeDisplayDeviceInjector mInjector;
+};
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
+    addHwcColorModesMapping(ui::ColorMode::SRGB,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    setInputDataspace(ui::Dataspace::DISPLAY_P3);
+    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+    setHasWideColorGamut(true);
+
+    getBestColorMode();
+
+    ASSERT_EQ(ui::Dataspace::SRGB, mOutDataspace);
+    ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
+    addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    addHwcColorModesMapping(ui::ColorMode::SRGB,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    setInputDataspace(ui::Dataspace::DISPLAY_P3);
+    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+    setHasWideColorGamut(true);
+
+    getBestColorMode();
+
+    ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
+    ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
+    addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    setInputDataspace(ui::Dataspace::DISPLAY_P3);
+    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+    setHasWideColorGamut(true);
+
+    getBestColorMode();
+
+    ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
+    ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+/* ------------------------------------------------------------------------
  * SurfaceFlinger::setupNewDisplayDeviceInternal
  */
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 2046439..62afde9 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -369,6 +369,18 @@
             return *this;
         }
 
+        auto& setHwcColorModes(
+                const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>
+                        hwcColorModes) {
+            mCreationArgs.hwcColorModes = hwcColorModes;
+            return *this;
+        }
+
+        auto& setHasWideColorGamut(bool hasWideColorGamut) {
+            mCreationArgs.hasWideColorGamut = hasWideColorGamut;
+            return *this;
+        }
+
         sp<DisplayDevice> inject() {
             DisplayDeviceState state;
             state.type = mCreationArgs.type;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index ecf3181..c0395c0 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -30,10 +30,10 @@
 using android::hardware::graphics::common::V1_0::ColorTransform;
 using android::hardware::graphics::common::V1_0::Hdr;
 using android::hardware::graphics::common::V1_0::Transform;
-using android::hardware::graphics::common::V1_1::ColorMode;
-using android::hardware::graphics::common::V1_1::Dataspace;
 using android::hardware::graphics::common::V1_1::PixelFormat;
 using android::hardware::graphics::common::V1_1::RenderIntent;
+using android::hardware::graphics::common::V1_2::ColorMode;
+using android::hardware::graphics::common::V1_2::Dataspace;
 
 using android::hardware::graphics::composer::V2_1::Config;
 using android::hardware::graphics::composer::V2_1::Display;
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
index 6813cda..afca63a 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
@@ -17,13 +17,15 @@
 #pragma once
 
 #include <gmock/gmock.h>
-
+#include <renderengine/DisplaySettings.h>
 #include <renderengine/Framebuffer.h>
 #include <renderengine/Image.h>
+#include <renderengine/LayerSettings.h>
 #include <renderengine/Mesh.h>
 #include <renderengine/RenderEngine.h>
 #include <renderengine/Surface.h>
 #include <renderengine/Texture.h>
+#include <ui/GraphicBuffer.h>
 
 namespace android {
 namespace renderengine {
@@ -75,6 +77,9 @@
     MOCK_METHOD1(drawMesh, void(const Mesh&));
     MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
     MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
+    MOCK_CONST_METHOD4(drawLayers,
+                       status_t(const DisplaySettings&, const std::vector<LayerSettings>&,
+                                ANativeWindowBuffer* const, base::unique_fd*));
 };
 
 class Surface : public renderengine::Surface {
diff --git a/services/vr/bufferhubd/buffer_channel.cpp b/services/vr/bufferhubd/buffer_channel.cpp
index dcc6ea4..ee85746 100644
--- a/services/vr/bufferhubd/buffer_channel.cpp
+++ b/services/vr/bufferhubd/buffer_channel.cpp
@@ -1,3 +1,4 @@
+#include <errno.h>
 #include <private/dvr/buffer_channel.h>
 #include <private/dvr/producer_channel.h>
 
@@ -16,9 +17,8 @@
                              size_t user_metadata_size)
     : BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType),
       buffer_node_(
-          std::make_shared<BufferNode>(std::move(buffer), user_metadata_size)),
-      buffer_state_bit_(BufferHubDefs::FindFirstClearedBit()) {
-  buffer_node_->set_buffer_state_bit(buffer_state_bit_);
+          std::make_shared<BufferNode>(std::move(buffer), user_metadata_size)) {
+  buffer_state_bit_ = buffer_node_->AddNewActiveClientsBitToMask();
 }
 
 BufferChannel::BufferChannel(BufferHubService* service, int buffer_id,
@@ -27,24 +27,28 @@
                              uint64_t usage, size_t user_metadata_size)
     : BufferHubChannel(service, buffer_id, buffer_id, kDetachedBufferType),
       buffer_node_(std::make_shared<BufferNode>(
-          width, height, layer_count, format, usage, user_metadata_size)),
-      buffer_state_bit_(BufferHubDefs::FindFirstClearedBit()) {
-  buffer_node_->set_buffer_state_bit(buffer_state_bit_);
+          width, height, layer_count, format, usage, user_metadata_size)) {
+  buffer_state_bit_ = buffer_node_->AddNewActiveClientsBitToMask();
 }
 
 BufferChannel::BufferChannel(BufferHubService* service, int buffer_id,
                              int channel_id,
-                             std::shared_ptr<BufferNode> buffer_node,
-                             uint64_t buffer_state_bit)
+                             std::shared_ptr<BufferNode> buffer_node)
     : BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType),
-      buffer_node_(buffer_node),
-      buffer_state_bit_(buffer_state_bit) {
-  buffer_node_->set_buffer_state_bit(buffer_state_bit_);
+      buffer_node_(buffer_node) {
+  buffer_state_bit_ = buffer_node_->AddNewActiveClientsBitToMask();
+  if (buffer_state_bit_ == 0ULL) {
+    ALOGE("BufferChannel::BufferChannel: %s", strerror(errno));
+    buffer_node_ = nullptr;
+  }
 }
 
 BufferChannel::~BufferChannel() {
   ALOGD_IF(TRACE, "BufferChannel::~BufferChannel: channel_id=%d buffer_id=%d.",
            channel_id(), buffer_id());
+  if (buffer_state_bit_ != 0ULL) {
+    buffer_node_->RemoveClientsBitFromMask(buffer_state_bit_);
+  }
   Hangup();
 }
 
@@ -74,11 +78,6 @@
           *this, &BufferChannel::OnDuplicate, message);
       return true;
 
-    case DetachedBufferRPC::Promote::Opcode:
-      DispatchRemoteMethod<DetachedBufferRPC::Promote>(
-          *this, &BufferChannel::OnPromote, message);
-      return true;
-
     default:
       return false;
   }
@@ -106,38 +105,22 @@
       /*released_fence_fd=*/BorrowedHandle{}};
 }
 
-Status<RemoteChannelHandle> BufferChannel::OnDuplicate(
-    Message& message) {
+Status<RemoteChannelHandle> BufferChannel::OnDuplicate(Message& message) {
   ATRACE_NAME("BufferChannel::OnDuplicate");
-  ALOGD_IF(TRACE, "BufferChannel::OnDuplicate: buffer=%d.",
-           buffer_id());
+  ALOGD_IF(TRACE, "BufferChannel::OnDuplicate: buffer=%d.", buffer_id());
 
   int channel_id;
   auto status = message.PushChannel(0, nullptr, &channel_id);
-  if (!status) {
-    ALOGE(
-        "BufferChannel::OnDuplicate: Failed to push buffer channel: %s",
-        status.GetErrorMessage().c_str());
+  if (!status.ok()) {
+    ALOGE("BufferChannel::OnDuplicate: Failed to push buffer channel: %s",
+          status.GetErrorMessage().c_str());
     return ErrorStatus(ENOMEM);
   }
 
-  // Try find the next buffer state bit which has not been claimed by any
-  // other buffers yet.
-  uint64_t buffer_state_bit =
-      BufferHubDefs::FindNextClearedBit(buffer_node_->active_buffer_bit_mask() |
-                                        BufferHubDefs::kProducerStateBit);
-  if (buffer_state_bit == 0ULL) {
-    ALOGE(
-        "BufferChannel::OnDuplicate: reached the maximum mumber of channels "
-        "per buffer node: 63.");
-    return ErrorStatus(E2BIG);
-  }
-
-  auto channel =
-      std::shared_ptr<BufferChannel>(new BufferChannel(
-          service(), buffer_id(), channel_id, buffer_node_, buffer_state_bit));
-  if (!channel) {
-    ALOGE("BufferChannel::OnDuplicate: Invalid buffer.");
+  auto channel = std::shared_ptr<BufferChannel>(
+      new BufferChannel(service(), buffer_id(), channel_id, buffer_node_));
+  if (!channel->IsValid()) {
+    ALOGE("BufferChannel::OnDuplicate: Invalid buffer. %s", strerror(errno));
     return ErrorStatus(EINVAL);
   }
 
@@ -154,69 +137,5 @@
   return status;
 }
 
-Status<RemoteChannelHandle> BufferChannel::OnPromote(
-    Message& message) {
-  ATRACE_NAME("BufferChannel::OnPromote");
-  ALOGD_IF(TRACE, "BufferChannel::OnPromote: buffer_id=%d", buffer_id());
-
-  // Check whether this is the channel exclusive owner of the buffer_node_.
-  if (buffer_state_bit_ != buffer_node_->active_buffer_bit_mask()) {
-    ALOGE(
-        "BufferChannel::OnPromote: Cannot promote this BufferChannel as its "
-        "BufferNode is shared between multiple channels. This channel's  state "
-        "bit=0x%" PRIx64 ", acitve_buffer_bit_mask=0x%" PRIx64 ".",
-        buffer_state_bit_, buffer_node_->active_buffer_bit_mask());
-    return ErrorStatus(EINVAL);
-  }
-
-  // Note that the new ProducerChannel will have different channel_id, but
-  // inherits the buffer_id from the DetachedBuffer.
-  int channel_id;
-  auto status = message.PushChannel(0, nullptr, &channel_id);
-  if (!status) {
-    ALOGE(
-        "BufferChannel::OnPromote: Failed to push ProducerChannel: %s.",
-        status.GetErrorMessage().c_str());
-    return ErrorStatus(ENOMEM);
-  }
-
-  IonBuffer buffer = std::move(buffer_node_->buffer());
-  IonBuffer metadata_buffer;
-  if (int ret = metadata_buffer.Alloc(buffer_node_->metadata().metadata_size(),
-                                      /*height=*/1,
-                                      /*layer_count=*/1,
-                                      BufferHubDefs::kMetadataFormat,
-                                      BufferHubDefs::kMetadataUsage)) {
-    ALOGE("BufferChannel::OnPromote: Failed to allocate metadata: %s",
-          strerror(-ret));
-    return ErrorStatus(EINVAL);
-  }
-
-  size_t user_metadata_size = buffer_node_->user_metadata_size();
-
-  std::unique_ptr<ProducerChannel> channel = ProducerChannel::Create(
-      service(), buffer_id(), channel_id, std::move(buffer),
-      std::move(metadata_buffer), user_metadata_size);
-  if (!channel) {
-    ALOGE(
-        "BufferChannel::OnPromote: Failed to create ProducerChannel from a "
-        "BufferChannel, buffer_id=%d.",
-        buffer_id());
-  }
-
-  const auto channel_status =
-      service()->SetChannel(channel_id, std::move(channel));
-  if (!channel_status) {
-    // Technically, this should never fail, as we just pushed the channel. Note
-    // that LOG_FATAL will be stripped out in non-debug build.
-    LOG_FATAL(
-        "BufferChannel::OnPromote: Failed to set new producer buffer channel: "
-        "%s.",
-        channel_status.GetErrorMessage().c_str());
-  }
-
-  return status;
-}
-
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index 15391da..6421a0b 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -265,14 +265,6 @@
       SetChannel(channel->channel_id(), nullptr);
       return {};
 
-    case DetachedBufferRPC::Promote::Opcode:
-      // In addition to the message handler in the BufferChannel's
-      // HandleMessage method, we also need to invalid the channel. Note that
-      // this has to be done after HandleMessage returns to make sure the IPC
-      // request has went back to the client first.
-      SetChannel(channel->channel_id(), nullptr);
-      return {};
-
     default:
       return DefaultHandleMessage(message);
   }
diff --git a/services/vr/bufferhubd/buffer_hub_binder.cpp b/services/vr/bufferhubd/buffer_hub_binder.cpp
index b507717..de82c09 100644
--- a/services/vr/bufferhubd/buffer_hub_binder.cpp
+++ b/services/vr/bufferhubd/buffer_hub_binder.cpp
@@ -13,7 +13,7 @@
     const std::shared_ptr<BufferHubService>& pdx_service) {
   IPCThreadState::self()->disableBackgroundScheduling(true);
 
-  BufferHubBinderService* service = new BufferHubBinderService();
+  sp<BufferHubBinderService> service = new BufferHubBinderService();
   service->pdx_service_ = pdx_service;
 
   // Not using BinderService::publish because need to get an instance of this
diff --git a/services/vr/bufferhubd/buffer_node.cpp b/services/vr/bufferhubd/buffer_node.cpp
index 782b9c2..bedec6f 100644
--- a/services/vr/bufferhubd/buffer_node.cpp
+++ b/services/vr/bufferhubd/buffer_node.cpp
@@ -1,12 +1,24 @@
+#include <errno.h>
 #include <private/dvr/buffer_hub_defs.h>
 #include <private/dvr/buffer_node.h>
 
 namespace android {
 namespace dvr {
 
+void BufferNode::InitializeMetadata() {
+  // Using placement new here to reuse shared memory instead of new allocation
+  // Initialize the atomic variables to zero.
+  BufferHubDefs::MetadataHeader* metadata_header = metadata_.metadata_header();
+  buffer_state_ = new (&metadata_header->buffer_state) std::atomic<uint64_t>(0);
+  fence_state_ = new (&metadata_header->fence_state) std::atomic<uint64_t>(0);
+  active_clients_bit_mask_ =
+      new (&metadata_header->active_clients_bit_mask) std::atomic<uint64_t>(0);
+}
+
 BufferNode::BufferNode(IonBuffer buffer, size_t user_metadata_size)
     : buffer_(std::move(buffer)) {
   metadata_ = BufferHubMetadata::Create(user_metadata_size);
+  InitializeMetadata();
 }
 
 // Allocates a new BufferNode.
@@ -22,6 +34,37 @@
   }
 
   metadata_ = BufferHubMetadata::Create(user_metadata_size);
+  InitializeMetadata();
+}
+
+uint64_t BufferNode::GetActiveClientsBitMask() const {
+  return active_clients_bit_mask_->load(std::memory_order_acquire);
+}
+
+uint64_t BufferNode::AddNewActiveClientsBitToMask() {
+  uint64_t current_active_clients_bit_mask = GetActiveClientsBitMask();
+  uint64_t buffer_state_bit = 0ULL;
+  uint64_t updated_active_clients_bit_mask = 0ULL;
+  do {
+    buffer_state_bit =
+        BufferHubDefs::FindNextClearedBit(current_active_clients_bit_mask);
+    if (buffer_state_bit == 0ULL) {
+      ALOGE(
+          "BufferNode::AddNewActiveClientsBitToMask: reached the maximum "
+          "mumber of channels per buffer node: 32.");
+      errno = E2BIG;
+      return 0ULL;
+    }
+    updated_active_clients_bit_mask =
+        current_active_clients_bit_mask | buffer_state_bit;
+  } while (!(active_clients_bit_mask_->compare_exchange_weak(
+      current_active_clients_bit_mask, updated_active_clients_bit_mask,
+      std::memory_order_acq_rel, std::memory_order_acquire)));
+  return buffer_state_bit;
+}
+
+void BufferNode::RemoveClientsBitFromMask(const uint64_t& value) {
+  active_clients_bit_mask_->fetch_and(~value);
 }
 
 }  // namespace dvr
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_channel.h b/services/vr/bufferhubd/include/private/dvr/buffer_channel.h
index 1697251..e9bdb37 100644
--- a/services/vr/bufferhubd/include/private/dvr/buffer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_channel.h
@@ -42,21 +42,20 @@
                 uint32_t height, uint32_t layer_count, uint32_t format,
                 uint64_t usage, size_t user_metadata_size);
 
-  // Creates a detached buffer from an existing BufferNode.
+  // Creates a detached buffer from an existing BufferNode. This method is used
+  // in OnDuplicate method.
   BufferChannel(BufferHubService* service, int buffer_id, int channel_id,
-                std::shared_ptr<BufferNode> buffer_node,
-                uint64_t buffer_state_bit);
+                std::shared_ptr<BufferNode> buffer_node);
 
   pdx::Status<BufferTraits<pdx::BorrowedHandle>> OnImport(
       pdx::Message& message);
   pdx::Status<pdx::RemoteChannelHandle> OnDuplicate(pdx::Message& message);
-  pdx::Status<pdx::RemoteChannelHandle> OnPromote(pdx::Message& message);
 
   // The concrete implementation of the Buffer object.
-  std::shared_ptr<BufferNode> buffer_node_;
+  std::shared_ptr<BufferNode> buffer_node_ = nullptr;
 
   // The state bit of this buffer. Must be one the lower 63 bits.
-  uint64_t buffer_state_bit_;
+  uint64_t buffer_state_bit_ = 0ULL;
 };
 
 }  // namespace dvr
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_node.h b/services/vr/bufferhubd/include/private/dvr/buffer_node.h
index d6c6105..e1e8057 100644
--- a/services/vr/bufferhubd/include/private/dvr/buffer_node.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_node.h
@@ -10,7 +10,7 @@
 class BufferNode {
  public:
   // Creates a BufferNode from existing IonBuffers, i.e. creating from an
-  // existing ProducerChannel.
+  // existing ProducerChannel. Allocate a new BufferHubMetadata.
   BufferNode(IonBuffer buffer, size_t user_metadata_size);
 
   // Allocates a new BufferNode.
@@ -21,26 +21,56 @@
   bool IsValid() const { return buffer_.IsValid() && metadata_.IsValid(); }
 
   size_t user_metadata_size() const { return metadata_.user_metadata_size(); }
-  uint64_t active_buffer_bit_mask() const { return active_buffer_bit_mask_; }
-  void set_buffer_state_bit(uint64_t buffer_state_bit) {
-    active_buffer_bit_mask_ |= buffer_state_bit;
-  }
 
-  // Accessor of the IonBuffer.
+  // Accessors of the IonBuffer.
   IonBuffer& buffer() { return buffer_; }
   const IonBuffer& buffer() const { return buffer_; }
 
-  // Accessor of the metadata.
+  // Accessors of metadata.
   const BufferHubMetadata& metadata() const { return metadata_; }
 
+  // Gets the current value of active_clients_bit_mask in metadata_ with
+  // std::memory_order_acquire, so that all previous releases of
+  // active_clients_bit_mask from all threads will be returned here.
+  uint64_t GetActiveClientsBitMask() const;
+
+  // Find and add a new buffer_state_bit to active_clients_bit_mask in
+  // metadata_.
+  // Return the new buffer_state_bit that is added to active_clients_bit_mask.
+  // Return 0ULL if there are already 32 bp clients of the buffer.
+  uint64_t AddNewActiveClientsBitToMask();
+
+  // Removes the value from active_clients_bit_mask in metadata_ with
+  // std::memory_order_release, so that the change will be visible to any
+  // acquire of active_clients_bit_mask_ in any threads after the succeed of
+  // this operation.
+  void RemoveClientsBitFromMask(const uint64_t& value);
+
  private:
+  // Helper method for constructors to initialize atomic metadata header
+  // variables in shared memory.
+  void InitializeMetadata();
+
   // Gralloc buffer handles.
   IonBuffer buffer_;
+
+  // Metadata in shared memory.
   BufferHubMetadata metadata_;
 
-  // All active buffer bits. Valid bits are the lower 63 bits, while the
-  // highest bit is reserved for the exclusive writing and should not be set.
-  uint64_t active_buffer_bit_mask_ = 0ULL;
+  // The following variables are atomic variables in metadata_ that are visible
+  // to Bn object and Bp objects. Please find more info in
+  // BufferHubDefs::MetadataHeader.
+
+  // buffer_state_ tracks the state of the buffer. Buffer can be in one of these
+  // four states: gained, posted, acquired, released.
+  std::atomic<uint64_t>* buffer_state_ = nullptr;
+
+  // TODO(b/112012161): add comments to fence_state_.
+  std::atomic<uint64_t>* fence_state_ = nullptr;
+
+  // active_clients_bit_mask_ tracks all the bp clients of the buffer. It is the
+  // union of all buffer_state_bit of all bp clients.
+  std::atomic<uint64_t>* active_clients_bit_mask_ = nullptr;
 };
 
 }  // namespace dvr
diff --git a/services/vr/bufferhubd/tests/Android.bp b/services/vr/bufferhubd/tests/Android.bp
index bf8ea5b..a80691f 100644
--- a/services/vr/bufferhubd/tests/Android.bp
+++ b/services/vr/bufferhubd/tests/Android.bp
@@ -23,4 +23,31 @@
 
     // TODO(b/117568153): Temporarily opt out using libcrt.
     no_libcrt: true,
-}
\ No newline at end of file
+}
+
+cc_test {
+    name: "buffer_node-test",
+    srcs: ["buffer_node-test.cpp"],
+    cflags: [
+        "-DLOG_TAG=\"buffer_node-test\"",
+        "-DTRACE=0",
+        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+    ],
+    header_libs: ["libdvr_headers"],
+    static_libs: [
+        "libbufferhub",
+        "libbufferhubd",
+        "libgmock",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libpdx_default_transport",
+        "libui",
+        "libutils",
+    ],
+    // TODO(b/117568153): Temporarily opt out using libcrt.
+    no_libcrt: true,
+}
+
diff --git a/services/vr/bufferhubd/tests/buffer_node-test.cpp b/services/vr/bufferhubd/tests/buffer_node-test.cpp
new file mode 100644
index 0000000..c2526fe
--- /dev/null
+++ b/services/vr/bufferhubd/tests/buffer_node-test.cpp
@@ -0,0 +1,89 @@
+#include <errno.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <private/dvr/buffer_node.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+const uint32_t kWidth = 640;
+const uint32_t kHeight = 480;
+const uint32_t kLayerCount = 1;
+const uint32_t kFormat = 1;
+const uint64_t kUsage = 0;
+const size_t kUserMetadataSize = 0;
+
+class BufferNodeTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    buffer_node = new BufferNode(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                 kUserMetadataSize);
+    ASSERT_TRUE(buffer_node->IsValid());
+  }
+
+  void TearDown() override {
+    if (buffer_node != nullptr) {
+      delete buffer_node;
+    }
+  }
+
+  BufferNode* buffer_node = nullptr;
+};
+
+TEST_F(BufferNodeTest, TestCreateBufferNode) {
+  EXPECT_EQ(buffer_node->user_metadata_size(), kUserMetadataSize);
+}
+
+TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_twoNewClients) {
+  uint64_t new_buffer_state_bit_1 = buffer_node->AddNewActiveClientsBitToMask();
+  EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), new_buffer_state_bit_1);
+
+  // Request and add a new buffer_state_bit again.
+  // Active clients bit mask should be the union of the two new
+  // buffer_state_bits.
+  uint64_t new_buffer_state_bit_2 = buffer_node->AddNewActiveClientsBitToMask();
+  EXPECT_EQ(buffer_node->GetActiveClientsBitMask(),
+            new_buffer_state_bit_1 | new_buffer_state_bit_2);
+}
+
+TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_32NewClients) {
+  uint64_t new_buffer_state_bit = 0ULL;
+  uint64_t current_mask = 0ULL;
+  uint64_t expected_mask = 0ULL;
+
+  for (int i = 0; i < 64; ++i) {
+    new_buffer_state_bit = buffer_node->AddNewActiveClientsBitToMask();
+    EXPECT_NE(new_buffer_state_bit, 0);
+    EXPECT_FALSE(new_buffer_state_bit & current_mask);
+    expected_mask = current_mask | new_buffer_state_bit;
+    current_mask = buffer_node->GetActiveClientsBitMask();
+    EXPECT_EQ(current_mask, expected_mask);
+  }
+
+  // Method should fail upon requesting for more than maximum allowable clients.
+  new_buffer_state_bit = buffer_node->AddNewActiveClientsBitToMask();
+  EXPECT_EQ(new_buffer_state_bit, 0ULL);
+  EXPECT_EQ(errno, E2BIG);
+}
+
+TEST_F(BufferNodeTest, TestRemoveActiveClientsBitFromMask) {
+  buffer_node->AddNewActiveClientsBitToMask();
+  uint64_t current_mask = buffer_node->GetActiveClientsBitMask();
+  uint64_t new_buffer_state_bit = buffer_node->AddNewActiveClientsBitToMask();
+  EXPECT_NE(buffer_node->GetActiveClientsBitMask(), current_mask);
+
+  buffer_node->RemoveClientsBitFromMask(new_buffer_state_bit);
+  EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask);
+
+  // Remove the test_mask again to the active client bit mask should not modify
+  // the value of active clients bit mask.
+  buffer_node->RemoveClientsBitFromMask(new_buffer_state_bit);
+  EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask);
+}
+
+}  // namespace
+
+}  // namespace dvr
+}  // namespace android
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index 7fbe315..78d6694 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -7,6 +7,7 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wimplicit-fallthrough",
     ],
     cppflags: [
         "-Wno-sign-compare",
@@ -32,6 +33,7 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wimplicit-fallthrough",
     ],
     cppflags: [
         "-Wno-sign-compare",
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index f841862..57668a8 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -791,6 +791,7 @@
                          &device->external_fence_properties) &&
           visitor->Visit("externalSemaphoreProperties",
                          &device->external_semaphore_properties);
+      FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_0:
       ret &= visitor->Visit("properties", &device->properties) &&
              visitor->Visit("features", &device->features) &&
@@ -817,6 +818,7 @@
   switch (instance->api_version ^ VK_VERSION_PATCH(instance->api_version)) {
     case VK_API_VERSION_1_1:
       ret &= visitor->Visit("deviceGroups", &instance->device_groups);
+      FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_0:
       ret &= visitor->Visit("layers", &instance->layers) &&
              visitor->Visit("extensions", &instance->extensions) &&
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index 3373f19..450fb24 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -41,6 +41,12 @@
 #define VK_API_VERSION_1_1 VK_MAKE_VERSION(1, 1, 0)
 #endif
 
+/*
+ * Annotation to tell clang that we intend to fall through from one case to
+ * another in a switch. Sourced from android-base/macros.h.
+ */
+#define FALLTHROUGH_INTENDED [[clang::fallthrough]]
+
 struct VkJsonLayer {
   VkLayerProperties properties;
   std::vector<VkExtensionProperties> extensions;
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 313d095..05d4dfe 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -353,7 +353,7 @@
                                       1,
                                       "",
                                       0,
-                                      VK_API_VERSION_1_0};
+                                      VK_API_VERSION_1_1};
   VkInstanceCreateInfo instance_info = {
       VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
       nullptr,