Merge "Fix binder memory handling for 64 bit devices"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index cd0fbd4..16abc1e 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -4,6 +4,7 @@
 [Builtin Hooks Options]
 # Only turn on clang-format check for the following subfolders.
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+               libs/graphicsenv/
                libs/gui/
                libs/ui/
                libs/vr/
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..86ec8f8 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -77,7 +77,6 @@
 
 static constexpr const char* kCpPath = "/system/bin/cp";
 static constexpr const char* kXattrDefault = "user.default";
-static constexpr const char* kPropHasReserved = "vold.has_reserved";
 
 static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
 
@@ -343,55 +342,6 @@
     return 0;
 }
 
-/**
- * Ensure that we have a hard-limit quota to protect against abusive apps;
- * they should never use more than 90% of blocks or 50% of inodes.
- */
-static int prepare_app_quota(const std::unique_ptr<std::string>& uuid ATTRIBUTE_UNUSED,
-        const std::string& device, uid_t uid) {
-    // Skip when reserved blocks are protecting us against abusive apps
-    if (android::base::GetBoolProperty(kPropHasReserved, false)) return 0;
-    // Skip when device no quotas present
-    if (device.empty()) return 0;
-
-    struct dqblk dq;
-    if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
-            reinterpret_cast<char*>(&dq)) != 0) {
-        PLOG(WARNING) << "Failed to find quota for " << uid;
-        return -1;
-    }
-
-#if APPLY_HARD_QUOTAS
-    if ((dq.dqb_bhardlimit == 0) || (dq.dqb_ihardlimit == 0)) {
-        auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
-        struct statvfs stat;
-        if (statvfs(path.c_str(), &stat) != 0) {
-            PLOG(WARNING) << "Failed to statvfs " << path;
-            return -1;
-        }
-
-        dq.dqb_valid = QIF_LIMITS;
-        dq.dqb_bhardlimit =
-            (((static_cast<uint64_t>(stat.f_blocks) * stat.f_frsize) / 10) * 9) / QIF_DQBLKSIZE;
-        dq.dqb_ihardlimit = (stat.f_files / 2);
-        if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), device.c_str(), uid,
-                reinterpret_cast<char*>(&dq)) != 0) {
-            PLOG(WARNING) << "Failed to set hard quota for " << uid;
-            return -1;
-        } else {
-            LOG(DEBUG) << "Applied hard quotas for " << uid;
-            return 0;
-        }
-    } else {
-        // Hard quota already set; assume it's reasonable
-        return 0;
-    }
-#else
-    // Hard quotas disabled
-    return 0;
-#endif
-}
-
 static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) {
     if (!property_get_bool("dalvik.vm.usejitprofiles", false)) {
         return true;
@@ -506,10 +456,6 @@
             return error("Failed to restorecon " + path);
         }
 
-        if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), uid)) {
-            return error("Failed to set hard quota " + path);
-        }
-
         if (!prepare_app_profile_dir(packageName, appId, userId)) {
             return error("Failed to prepare profiles for " + packageName);
         }
@@ -805,7 +751,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;
@@ -988,13 +934,6 @@
         }
     }
 
-    // Data under /data/media doesn't have an app, but we still want
-    // to limit it to prevent abuse.
-    if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid),
-            multiuser_get_uid(userId, AID_MEDIA_RW))) {
-        return error("Failed to set hard quota for media_rw");
-    }
-
     return ok();
 }
 
@@ -1442,7 +1381,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 +1796,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:
@@ -2643,21 +2583,6 @@
                     reinterpret_cast<char*>(&dq)) == 0) {
                 LOG(DEBUG) << "Found quota mount " << source << " at " << target;
                 mQuotaReverseMounts[target] = source;
-
-                // ext4 only enables DQUOT_USAGE_ENABLED by default, so we
-                // need to kick it again to enable DQUOT_LIMITS_ENABLED. We
-                // only need hard limits enabled when we're not being protected
-                // by reserved blocks.
-                if (!android::base::GetBoolProperty(kPropHasReserved, false)) {
-                    if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1,
-                            nullptr) != 0 && errno != EBUSY) {
-                        PLOG(ERROR) << "Failed to enable USRQUOTA on " << source;
-                    }
-                    if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1,
-                            nullptr) != 0 && errno != EBUSY) {
-                        PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source;
-                    }
-                }
             }
         }
 #endif
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 28c7658..b2e7047 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -322,21 +322,8 @@
             return false;
         }
         const char* isa = parameters_.instruction_set;
-
-        // Check whether the file exists where expected.
         std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE;
         std::string isa_path = dalvik_cache + "/" + isa;
-        std::string art_path = isa_path + "/system@framework@boot.art";
-        std::string oat_path = isa_path + "/system@framework@boot.oat";
-        bool cleared = false;
-        if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) {
-            // Files exist, assume everything is alright if not forced. Otherwise clean up.
-            if (!force) {
-                return true;
-            }
-            ClearDirectory(isa_path);
-            cleared = true;
-        }
 
         // Reset umask in otapreopt, so that we control the the access for the files we create.
         umask(0);
@@ -355,17 +342,34 @@
             }
         }
 
-        // Prepare to create.
+        // Check whether we have files in /data.
+        // TODO: check that the files are correct wrt/ jars.
+        std::string art_path = isa_path + "/system@framework@boot.art";
+        std::string oat_path = isa_path + "/system@framework@boot.oat";
+        bool cleared = false;
+        if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) {
+            // Files exist, assume everything is alright if not forced. Otherwise clean up.
+            if (!force) {
+                return true;
+            }
+            ClearDirectory(isa_path);
+            cleared = true;
+        }
+
+        // Check whether we have an image in /system.
+        // TODO: check that the files are correct wrt/ jars.
+        std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa);
+        if (access(preopted_boot_art_path.c_str(), F_OK) == 0) {
+            // Note: we ignore |force| here.
+            return true;
+        }
+
+
         if (!cleared) {
             ClearDirectory(isa_path);
         }
 
-        std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa);
-        if (access(preopted_boot_art_path.c_str(), F_OK) != 0) {
-          // No preopted boot image. Try to compile.
-          return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa);
-        }
-        return true;
+        return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa);
     }
 
     static bool CreatePath(const std::string& path) {
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/cmds/installd/utils.h b/cmds/installd/utils.h
index 5829c4f..d05724a 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -36,8 +36,6 @@
 #define BYPASS_QUOTA 0
 #define BYPASS_SDCARDFS 0
 
-#define APPLY_HARD_QUOTAS 0
-
 namespace android {
 namespace installd {
 
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index d776682..79cd6b5 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -274,6 +274,7 @@
     // Note that we ignore the strict_policy and don't propagate it
     // further (since we do no outbound RPCs anyway).
     strict_policy = bio_get_uint32(msg);
+    bio_get_uint32(msg);  // Ignore worksource header.
     s = bio_get_string16(msg, &len);
     if (s == NULL) {
         return -1;
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/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index f052bcb..7a47724 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -109,6 +109,10 @@
     "BC_DEAD_BINDER_DONE"
 };
 
+// The work source represents the UID of the process we should attribute the transaction to.
+// We use -1 to specify that the work source was not set using #setWorkSource.
+static const int kUnsetWorkSource = -1;
+
 static const char* getReturnString(uint32_t cmd)
 {
     size_t idx = cmd & 0xff;
@@ -383,6 +387,25 @@
     return mStrictModePolicy;
 }
 
+uid_t IPCThreadState::setWorkSource(uid_t uid)
+{
+    uid_t returnValue = mWorkSource;
+    mWorkSource = uid;
+    return returnValue;
+}
+
+uid_t IPCThreadState::getWorkSource() const
+{
+    return mWorkSource;
+}
+
+uid_t IPCThreadState::clearWorkSource()
+{
+    uid_t returnValue = mWorkSource;
+    mWorkSource = kUnsetWorkSource;
+    return returnValue;
+}
+
 void IPCThreadState::setLastTransactionBinderFlags(int32_t flags)
 {
     mLastTransactionBinderFlags = flags;
@@ -736,6 +759,7 @@
 
 IPCThreadState::IPCThreadState()
     : mProcess(ProcessState::self()),
+      mWorkSource(kUnsetWorkSource),
       mStrictModePolicy(0),
       mLastTransactionBinderFlags(0)
 {
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 87c9842..bc1a71c 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -601,6 +601,7 @@
 {
     writeInt32(IPCThreadState::self()->getStrictModePolicy() |
                STRICT_MODE_PENALTY_GATHER);
+    writeInt32(IPCThreadState::self()->getWorkSource());
     // currently the interface identification token is just its name as a string
     return writeString16(interface);
 }
@@ -613,6 +614,7 @@
 bool Parcel::enforceInterface(const String16& interface,
                               IPCThreadState* threadState) const
 {
+    // StrictModePolicy.
     int32_t strictPolicy = readInt32();
     if (threadState == nullptr) {
         threadState = IPCThreadState::self();
@@ -627,6 +629,10 @@
     } else {
       threadState->setStrictModePolicy(strictPolicy);
     }
+    // WorkSource.
+    int32_t workSource = readInt32();
+    threadState->setWorkSource(workSource);
+    // Interface descriptor.
     const String16 str(readString16());
     if (str == interface) {
         return true;
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 40b51ad..5888e14 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -47,6 +47,13 @@
             void                setStrictModePolicy(int32_t policy);
             int32_t             getStrictModePolicy() const;
 
+            // See Binder#setThreadWorkSource in Binder.java.
+            uid_t               setWorkSource(uid_t uid);
+            // See Binder#getThreadWorkSource in Binder.java.
+            uid_t               getWorkSource() const;
+            // See Binder#clearThreadWorkSource in Binder.java.
+            uid_t               clearWorkSource();
+
             void                setLastTransactionBinderFlags(int32_t flags);
             int32_t             getLastTransactionBinderFlags() const;
 
@@ -155,6 +162,9 @@
             status_t            mLastError;
             pid_t               mCallingPid;
             uid_t               mCallingUid;
+            // The UID of the process who is responsible for this transaction.
+            // This is used for resource attribution.
+            int32_t             mWorkSource;
             int32_t             mStrictModePolicy;
             int32_t             mLastTransactionBinderFlags;
             IPCThreadStateBase  *mIPCThreadStateBase;
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..bf0c956 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,204 @@
 #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;
+}
+
+// @START
+/**
+ * Writes a vector of int32_t to the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_writeVector(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.
+ */
+inline binder_status_t AParcel_readVector(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.
+ */
+inline binder_status_t AParcel_writeVector(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.
+ */
+inline binder_status_t AParcel_readVector(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.
+ */
+inline binder_status_t AParcel_writeVector(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.
+ */
+inline binder_status_t AParcel_readVector(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.
+ */
+inline binder_status_t AParcel_writeVector(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.
+ */
+inline binder_status_t AParcel_readVector(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.
+ */
+inline binder_status_t AParcel_writeVector(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.
+ */
+inline binder_status_t AParcel_readVector(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.
+ */
+inline binder_status_t AParcel_writeVector(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.
+ */
+inline binder_status_t AParcel_readVector(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.
+ */
+inline binder_status_t AParcel_writeVector(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.
+ */
+inline binder_status_t AParcel_readVector(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.
+ */
+inline binder_status_t AParcel_writeVector(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.
+ */
+inline binder_status_t AParcel_readVector(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.
+ */
+inline binder_status_t AParcel_writeVector(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.
+ */
+inline binder_status_t AParcel_readVector(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 +237,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 +254,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..2cf10d3 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,95 @@
         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 += "inline binder_status_t AParcel_writeVector(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 += "inline binder_status_t AParcel_readVector(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/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 73c2eba..7bcfffd 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -71,6 +71,7 @@
     BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
     BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
     BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
+    BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION,
 };
 
 pid_t start_server_process(int arg2, bool usePoll = false)
@@ -938,6 +939,43 @@
     EXPECT_EQ(NO_ERROR, ret);
 }
 
+TEST_F(BinderLibTest, WorkSourceUnsetByDefault)
+{
+    status_t ret;
+    Parcel data, reply;
+    data.writeInterfaceToken(binderLibTestServiceName);
+    ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+    EXPECT_EQ(-1, reply.readInt32());
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceSet)
+{
+    status_t ret;
+    Parcel data, reply;
+    uid_t previousWorkSource = IPCThreadState::self()->setWorkSource(100);
+    data.writeInterfaceToken(binderLibTestServiceName);
+    ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+    EXPECT_EQ(100, reply.readInt32());
+    EXPECT_EQ(-1, previousWorkSource);
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceCleared)
+{
+    status_t ret;
+    Parcel data, reply;
+
+    IPCThreadState::self()->setWorkSource(100);
+    uid_t previousWorkSource = IPCThreadState::self()->clearWorkSource();
+    data.writeInterfaceToken(binderLibTestServiceName);
+    ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+
+    EXPECT_EQ(-1, reply.readInt32());
+    EXPECT_EQ(100, previousWorkSource);
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
 class BinderLibTestService : public BBinder
 {
     public:
@@ -1236,6 +1274,11 @@
                 }
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION: {
+                data.enforceInterface(binderLibTestServiceName);
+                reply->writeInt32(IPCThreadState::self()->getWorkSource());
+                return NO_ERROR;
+            }
             default:
                 return UNKNOWN_TRANSACTION;
             };
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..024d72b 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;
 }
@@ -135,7 +167,7 @@
         auto sphalNamespace = android_get_exported_namespace("sphal");
         if (!sphalNamespace) return;
         mDriverNamespace = android_create_namespace("gfx driver",
-                                                    nullptr,             // ld_library_path
+                                                    mDriverPath.c_str(), // ld_library_path
                                                     mDriverPath.c_str(), // default_library_path
                                                     ANDROID_NAMESPACE_TYPE_SHARED |
                                                             ANDROID_NAMESPACE_TYPE_ISOLATED,
@@ -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/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index f99bc53..cf9d4c5 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -103,11 +103,15 @@
     }
 
     virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
-                                   Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-                                   bool useIdentityTransform, ISurfaceComposer::Rotation rotation) {
+                                   const ui::Dataspace reqDataspace,
+                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+                                   ISurfaceComposer::Rotation rotation) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeStrongBinder(display);
+        data.writeInt32(static_cast<int32_t>(reqDataspace));
+        data.writeInt32(static_cast<int32_t>(reqPixelFormat));
         data.write(sourceCrop);
         data.writeUint32(reqWidth);
         data.writeUint32(reqHeight);
@@ -126,15 +130,19 @@
 
         *outBuffer = new GraphicBuffer();
         reply.read(**outBuffer);
+
         return result;
     }
 
     virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder,
-                                   sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
+                                   sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace,
+                                   const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                    float frameScale, bool childrenOnly) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeStrongBinder(layerHandleBinder);
+        data.writeInt32(static_cast<int32_t>(reqDataspace));
+        data.writeInt32(static_cast<int32_t>(reqPixelFormat));
         data.write(sourceCrop);
         data.writeFloat(frameScale);
         data.writeBool(childrenOnly);
@@ -148,6 +156,7 @@
             ALOGE("captureLayers failed to readInt32: %d", result);
             return result;
         }
+
         *outBuffer = new GraphicBuffer();
         reply.read(**outBuffer);
 
@@ -645,6 +654,8 @@
         case CAPTURE_SCREEN: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> display = data.readStrongBinder();
+            ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
+            ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
             sp<GraphicBuffer> outBuffer;
             Rect sourceCrop(Rect::EMPTY_RECT);
             data.read(sourceCrop);
@@ -653,8 +664,8 @@
             bool useIdentityTransform = static_cast<bool>(data.readInt32());
             int32_t rotation = data.readInt32();
 
-            status_t res = captureScreen(display, &outBuffer, sourceCrop, reqWidth, reqHeight,
-                                         useIdentityTransform,
+            status_t res = captureScreen(display, &outBuffer, reqDataspace, reqPixelFormat,
+                                         sourceCrop, reqWidth, reqHeight, useIdentityTransform,
                                          static_cast<ISurfaceComposer::Rotation>(rotation));
             reply->writeInt32(res);
             if (res == NO_ERROR) {
@@ -665,14 +676,16 @@
         case CAPTURE_LAYERS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> layerHandleBinder = data.readStrongBinder();
+            ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
+            ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
             sp<GraphicBuffer> outBuffer;
             Rect sourceCrop(Rect::EMPTY_RECT);
             data.read(sourceCrop);
             float frameScale = data.readFloat();
             bool childrenOnly = data.readBool();
 
-            status_t res = captureLayers(layerHandleBinder, &outBuffer, sourceCrop, frameScale,
-                                         childrenOnly);
+            status_t res = captureLayers(layerHandleBinder, &outBuffer, reqDataspace,
+                                         reqPixelFormat, sourceCrop, frameScale, childrenOnly);
             reply->writeInt32(res);
             if (res == NO_ERROR) {
                 reply->write(*outBuffer);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1ac9609..7498f36 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -887,13 +887,14 @@
 
 // ----------------------------------------------------------------------------
 
-status_t ScreenshotClient::capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth,
-                                   uint32_t reqHeight, bool useIdentityTransform, uint32_t rotation,
-                                   sp<GraphicBuffer>* outBuffer) {
+status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
+                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+                                   uint32_t rotation, sp<GraphicBuffer>* outBuffer) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == nullptr) return NO_INIT;
-    status_t ret = s->captureScreen(display, outBuffer, sourceCrop, reqWidth, reqHeight,
-                                    useIdentityTransform,
+    status_t ret = s->captureScreen(display, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop,
+                                    reqWidth, reqHeight, useIdentityTransform,
                                     static_cast<ISurfaceComposer::Rotation>(rotation));
     if (ret != NO_ERROR) {
         return ret;
@@ -901,21 +902,25 @@
     return ret;
 }
 
-status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop,
+status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle,
+                                         const ui::Dataspace reqDataSpace,
+                                         const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
                                          float frameScale, sp<GraphicBuffer>* outBuffer) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == nullptr) return NO_INIT;
-    status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale,
-                                    false /* childrenOnly */);
+    status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat,
+                                    sourceCrop, frameScale, false /* childrenOnly */);
     return ret;
 }
 
-status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop,
+status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle,
+                                              const ui::Dataspace reqDataSpace,
+                                              const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
                                               float frameScale, sp<GraphicBuffer>* outBuffer) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == nullptr) return NO_INIT;
-    status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale,
-                                    true /* childrenOnly */);
+    status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat,
+                                    sourceCrop, frameScale, true /* childrenOnly */);
     return ret;
 }
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index a55cfe0..781e062 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -180,6 +180,36 @@
      * The subregion can be optionally rotated.  It will also be scaled to
      * match the size of the output buffer.
      *
+     * reqDataspace and reqPixelFormat specify the data space and pixel format
+     * of the buffer. The caller should pick the data space and pixel format
+     * that it can consume.
+     *
+     * At the moment, sourceCrop is ignored and is always set to the visible
+     * region (projected display viewport) of the screen.
+     *
+     * reqWidth and reqHeight specifies the size of the buffer.  When either
+     * of them is 0, they are set to the size of the logical display viewport.
+     *
+     * When useIdentityTransform is true, layer transformations are disabled.
+     *
+     * rotation specifies the rotation of the source crop (and the pixels in
+     * it) around its center.
+     */
+    virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
+                                   const ui::Dataspace reqDataspace,
+                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+                                   Rotation rotation = eRotateNone) = 0;
+    /**
+     * Capture the specified screen. This requires READ_FRAME_BUFFER
+     * permission.  This function will fail if there is a secure window on
+     * screen.
+     *
+     * This function can capture a subregion (the source crop) of the screen
+     * into an sRGB buffer with RGBA_8888 pixel format.
+     * The subregion can be optionally rotated.  It will also be scaled to
+     * match the size of the output buffer.
+     *
      * At the moment, sourceCrop is ignored and is always set to the visible
      * region (projected display viewport) of the screen.
      *
@@ -193,15 +223,34 @@
      */
     virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
                                    Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-                                   bool useIdentityTransform, Rotation rotation = eRotateNone) = 0;
+                                   bool useIdentityTransform, Rotation rotation = eRotateNone) {
+        return captureScreen(display, outBuffer, ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+                             sourceCrop, reqWidth, reqHeight, useIdentityTransform, rotation);
+    }
 
     /**
      * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
+     *
+     * reqDataspace and reqPixelFormat specify the data space and pixel format
+     * of the buffer. The caller should pick the data space and pixel format
+     * that it can consume.
      */
     virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder,
-                                   sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
+                                   sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace,
+                                   const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                    float frameScale = 1.0, bool childrenOnly = false) = 0;
 
+    /**
+     * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format,
+     * potentially ignoring the root node.
+     */
+    status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
+                           const Rect& sourceCrop, float frameScale = 1.0,
+                           bool childrenOnly = false) {
+        return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB,
+                             ui::PixelFormat::RGBA_8888, sourceCrop, frameScale, childrenOnly);
+    }
+
     /* Clears the frame statistics for animations.
      *
      * Requires the ACCESS_SURFACE_FLINGER permission.
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 69a759f..1b4eda7 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -329,12 +329,16 @@
 public:
     // if cropping isn't required, callers may pass in a default Rect, e.g.:
     //   capture(display, producer, Rect(), reqWidth, ...);
-    static status_t capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth,
-                            uint32_t reqHeight, bool useIdentityTransform, uint32_t rotation,
-                            sp<GraphicBuffer>* outBuffer);
-    static status_t captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, float frameScale,
-                                  sp<GraphicBuffer>* outBuffer);
-    static status_t captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop,
+    static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
+                            const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                            uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+                            uint32_t rotation, sp<GraphicBuffer>* outBuffer);
+    static status_t captureLayers(const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace,
+                                  const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                  float frameScale, sp<GraphicBuffer>* outBuffer);
+    static status_t captureChildLayers(const sp<IBinder>& layerHandle,
+                                       const ui::Dataspace reqDataSpace,
+                                       const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
                                        float frameScale, sp<GraphicBuffer>* outBuffer);
 };
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index bc80899..500df05 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -134,8 +134,9 @@
     sp<IBinder> display(sf->getBuiltInDisplay(
             ISurfaceComposer::eDisplayIdMain));
     sp<GraphicBuffer> outBuffer;
-    ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(),
-            64, 64, false));
+    ASSERT_EQ(NO_ERROR,
+              sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB,
+                                ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
 
     ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
             NATIVE_WINDOW_API_CPU));
@@ -165,8 +166,9 @@
                 &buf));
         ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
     }
-    ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(),
-            64, 64, false));
+    ASSERT_EQ(NO_ERROR,
+              sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB,
+                                ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
 }
 
 TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
@@ -596,14 +598,19 @@
     }
     status_t setActiveColorMode(const sp<IBinder>& /*display*/,
         ColorMode /*colorMode*/) override { return NO_ERROR; }
-    status_t captureScreen(const sp<IBinder>& /*display*/,
-            sp<GraphicBuffer>* /*outBuffer*/,
-            Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
-            bool /*useIdentityTransform*/,
-            Rotation /*rotation*/) override { return NO_ERROR; }
+    status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/,
+                           const ui::Dataspace /*reqDataspace*/,
+                           const ui::PixelFormat /*reqPixelFormat*/, Rect /*sourceCrop*/,
+                           uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
+                           bool /*useIdentityTransform*/, Rotation /*rotation*/) override {
+        return NO_ERROR;
+    }
     virtual status_t captureLayers(const sp<IBinder>& /*parentHandle*/,
-                                   sp<GraphicBuffer>* /*outBuffer*/, const Rect& /*sourceCrop*/,
-                                   float /*frameScale*/, bool /*childrenOnly*/) override {
+                                   sp<GraphicBuffer>* /*outBuffer*/,
+                                   const ui::Dataspace /*reqDataspace*/,
+                                   const ui::PixelFormat /*reqPixelFormat*/,
+                                   const Rect& /*sourceCrop*/, float /*frameScale*/,
+                                   bool /*childrenOnly*/) override {
         return NO_ERROR;
     }
     status_t clearAnimationFrameStats() override { return NO_ERROR; }
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 7b35409..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: [
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/Android.bp b/libs/vr/libbufferhub/Android.bp
index ba5cfcd..43ac79e 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -22,6 +22,7 @@
     "buffer_hub_base.cpp",
     "buffer_hub_rpc.cpp",
     "consumer_buffer.cpp",
+    "buffer_client_impl.cpp",
     "ion_buffer.cpp",
     "producer_buffer.cpp",
 ]
diff --git a/libs/vr/libbufferhub/buffer_client_impl.cpp b/libs/vr/libbufferhub/buffer_client_impl.cpp
new file mode 100644
index 0000000..6bef98a
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_client_impl.cpp
@@ -0,0 +1,55 @@
+#include <log/log.h>
+#include <private/dvr/IBufferClient.h>
+
+namespace android {
+namespace dvr {
+
+class BpBufferClient : public BpInterface<IBufferClient> {
+ public:
+  explicit BpBufferClient(const sp<IBinder>& impl)
+      : BpInterface<IBufferClient>(impl) {}
+
+  bool isValid() override;
+};
+
+IMPLEMENT_META_INTERFACE(BufferClient, "android.dvr.IBufferClient");
+
+// Transaction code
+enum {
+  IS_VALID = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+bool BpBufferClient::isValid() {
+  Parcel data, reply;
+  status_t ret =
+      data.writeInterfaceToken(IBufferClient::getInterfaceDescriptor());
+  if (ret != NO_ERROR) {
+    ALOGE("BpBufferClient::isValid: failed to write into parcel; errno=%d",
+          ret);
+    return false;
+  }
+
+  ret = remote()->transact(IS_VALID, data, &reply);
+  if (ret == NO_ERROR) {
+    return reply.readBool();
+  } else {
+    ALOGE("BpBufferClient::isValid: failed to transact; errno=%d", ret);
+    return false;
+  }
+}
+
+status_t BnBufferClient::onTransact(uint32_t code, const Parcel& data,
+                                    Parcel* reply, uint32_t flags) {
+  switch (code) {
+    case IS_VALID: {
+      CHECK_INTERFACE(IBufferClient, data, reply);
+      return reply->writeBool(isValid());
+    } break;
+    default:
+      // Should not reach except binder defined transactions such as dumpsys
+      return BBinder::onTransact(code, data, reply, flags);
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
\ No newline at end of file
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index b477f1a..4d98dfc 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -214,13 +214,12 @@
       ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
 
-  uint64_t context;
   LocalHandle fence;
 
   // The producer buffer starts in gained state.
 
   // Acquire, release, and gain in gained state should fail.
-  EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
+  EXPECT_EQ(-EBUSY, c->Acquire(&fence));
   EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
   EXPECT_EQ(-EALREADY, p->Gain(&fence));
 
@@ -233,10 +232,10 @@
   EXPECT_EQ(-EBUSY, p->Gain(&fence));
 
   // Acquire in posted state should succeed.
-  EXPECT_LE(0, c->Acquire(&fence, &context));
+  EXPECT_LE(0, c->Acquire(&fence));
 
   // Acquire, post, and gain in acquired state should fail.
-  EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
+  EXPECT_EQ(-EBUSY, c->Acquire(&fence));
   EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
   EXPECT_EQ(-EBUSY, p->Gain(&fence));
 
@@ -246,14 +245,14 @@
 
   // Release, acquire, and post in released state should fail.
   EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
-  EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
+  EXPECT_EQ(-EBUSY, c->Acquire(&fence));
   EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
 
   // Gain in released state should succeed.
   EXPECT_EQ(0, p->Gain(&fence));
 
   // Acquire, release, and gain in gained state should fail.
-  EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
+  EXPECT_EQ(-EBUSY, c->Acquire(&fence));
   EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
   EXPECT_EQ(-EALREADY, p->Gain(&fence));
 }
@@ -491,7 +490,7 @@
   EXPECT_LE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
   LocalHandle fence;
   Metadata m2 = {};
-  EXPECT_EQ(0, c->Acquire(&fence, &m2));
+  EXPECT_EQ(0, c->Acquire(&fence, &m2, sizeof(m2)));
   EXPECT_EQ(m.field1, m2.field1);
   EXPECT_EQ(m.field2, m2.field2);
   EXPECT_EQ(0, c->Release(LocalHandle()));
@@ -552,11 +551,11 @@
 
   // It is illegal to acquire metadata larger than originally requested during
   // buffer allocation.
-  EXPECT_NE(0, c->Acquire(&fence, &e));
+  EXPECT_NE(0, c->Acquire(&fence, &e, sizeof(e)));
 
   // It is ok to acquire metadata smaller than originally requested during
   // buffer allocation.
-  EXPECT_EQ(0, c->Acquire(&fence, &sequence));
+  EXPECT_EQ(0, c->Acquire(&fence, &sequence, sizeof(sequence)));
   EXPECT_EQ(m.field1, sequence);
 }
 
@@ -829,43 +828,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 +850,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 +894,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/IBufferClient.h b/libs/vr/libbufferhub/include/private/dvr/IBufferClient.h
new file mode 100644
index 0000000..03f2d95
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/IBufferClient.h
@@ -0,0 +1,29 @@
+#ifndef ANDROID_DVR_IBUFFERCLIENT_H
+#define ANDROID_DVR_IBUFFERCLIENT_H
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+namespace dvr {
+
+// Interface for acessing BufferHubBuffer remotely.
+class IBufferClient : public IInterface {
+ public:
+  DECLARE_META_INTERFACE(BufferClient);
+
+  // Checks if the buffer node is valid.
+  virtual bool isValid() = 0;
+};
+
+// BnInterface for IBufferClient. Should only be created in bufferhub service.
+class BnBufferClient : public BnInterface<IBufferClient> {
+ public:
+  virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                              uint32_t flags = 0);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_IBUFFERCLIENT_H
\ No newline at end of file
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/consumer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
index 2044c53..7349779 100644
--- a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
+++ b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
@@ -45,15 +45,6 @@
   // Returns zero on success, or a negative errno code otherwise.
   int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size);
 
-  // Attempt to retrieve a post event from buffer hub. If successful,
-  // |ready_fence| is set to a fence to wait on until the buffer is ready. This
-  // call will only succeed after the fd is signaled. This returns zero or a
-  // negative unix error code.
-  template <typename Meta>
-  int Acquire(LocalHandle* ready_fence, Meta* meta) {
-    return Acquire(ready_fence, meta, sizeof(*meta));
-  }
-
   // Asynchronously acquires a bufer.
   int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
 
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..a7fea33 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,58 @@
         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);
+        // Check if ANGLE is enabled. Workaround for b/118375731
+        // We suspect that loading & unloading a library somehow corrupts
+        // the process.
+        property_get("debug.angle.enable", prop, "0");
+        if (atoi(prop)) {
+            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 +667,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..859380f
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if
@@ -0,0 +1,78 @@
+/*
+** 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 final class EGL15 {
+
+    private 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/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/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..440f1e2 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -70,6 +70,8 @@
               mName.string());
         destroyAllHwcLayers();
     }
+
+    mTimeStats.onDestroy(getSequence());
 }
 
 void BufferLayer::useSurfaceDamage() {
@@ -234,27 +236,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();
@@ -291,8 +337,8 @@
     nsecs_t desiredPresentTime = getDesiredPresentTime();
     mFrameTracker.setDesiredPresentTime(desiredPresentTime);
 
-    const std::string layerName(getName().c_str());
-    mTimeStats.setDesiredTime(layerName, mCurrentFrameNumber, desiredPresentTime);
+    const int32_t layerID = getSequence();
+    mTimeStats.setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
 
     std::shared_ptr<FenceTime> frameReadyFence = getCurrentFenceTime();
     if (frameReadyFence->isValid()) {
@@ -304,14 +350,14 @@
     }
 
     if (presentFence->isValid()) {
-        mTimeStats.setPresentFence(layerName, mCurrentFrameNumber, presentFence);
+        mTimeStats.setPresentFence(layerID, mCurrentFrameNumber, presentFence);
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
     } else if (mFlinger->getHwComposer().isConnected(HWC_DISPLAY_PRIMARY)) {
         // The HWC doesn't support present fences, so use the refresh
         // timestamp instead.
         const nsecs_t actualPresentTime =
                 mFlinger->getHwComposer().getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
-        mTimeStats.setPresentTime(layerName, mCurrentFrameNumber, actualPresentTime);
+        mTimeStats.setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
         mFrameTracker.setActualPresentTime(actualPresentTime);
     }
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e75fbdf..e592a8b 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -227,6 +227,7 @@
     // BufferItem's that weren't actually queued. This can happen in shared
     // buffer mode.
     bool queuedBuffer = false;
+    const int32_t layerID = getSequence();
     LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
                     getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode,
                     getTransformToDisplayInverse(), mFreezeGeometryUpdates);
@@ -247,7 +248,7 @@
         // and return early
         if (queuedBuffer) {
             Mutex::Autolock lock(mQueueItemLock);
-            mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber);
+            mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
             mQueueItems.removeAt(0);
             mQueuedFrames--;
         }
@@ -261,7 +262,7 @@
             Mutex::Autolock lock(mQueueItemLock);
             mQueueItems.clear();
             mQueuedFrames = 0;
-            mTimeStats.clearLayerRecord(getName().c_str());
+            mTimeStats.clearLayerRecord(layerID);
         }
 
         // Once we have hit this state, the shadow queue may no longer
@@ -282,14 +283,13 @@
         // Remove any stale buffers that have been dropped during
         // updateTexImage
         while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
-            mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber);
+            mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
             mQueueItems.removeAt(0);
             mQueuedFrames--;
         }
 
-        const std::string layerName(getName().c_str());
-        mTimeStats.setAcquireFence(layerName, currentFrameNumber, mQueueItems[0].mFenceTime);
-        mTimeStats.setLatchTime(layerName, currentFrameNumber, latchTime);
+        mTimeStats.setAcquireFence(layerID, currentFrameNumber, mQueueItems[0].mFenceTime);
+        mTimeStats.setLatchTime(layerID, currentFrameNumber, latchTime);
 
         mQueueItems.removeAt(0);
     }
@@ -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/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index e52b35a..5df8ade 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -377,6 +377,8 @@
         return NO_ERROR;
     }
 
+    const int32_t layerID = getSequence();
+
     // Reject if the layer is invalid
     uint32_t bufferWidth = s.buffer->width;
     uint32_t bufferHeight = s.buffer->height;
@@ -397,7 +399,7 @@
         ALOGE("[%s] rejecting buffer: "
               "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
               mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h);
-        mTimeStats.removeTimeRecord(getName().c_str(), getFrameNumber());
+        mTimeStats.removeTimeRecord(layerID, getFrameNumber());
         return BAD_VALUE;
     }
 
@@ -405,7 +407,7 @@
     if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) {
         // TODO(alecmouri): Fail somewhere upstream if the fence is invalid.
         if (!releaseFence->isValid()) {
-            mTimeStats.clearLayerRecord(getName().c_str());
+            mTimeStats.clearLayerRecord(layerID);
             return UNKNOWN_ERROR;
         }
 
@@ -415,7 +417,7 @@
         auto currentStatus = s.acquireFence->getStatus();
         if (currentStatus == Fence::Status::Invalid) {
             ALOGE("Existing fence has invalid state");
-            mTimeStats.clearLayerRecord(getName().c_str());
+            mTimeStats.clearLayerRecord(layerID);
             return BAD_VALUE;
         }
 
@@ -423,7 +425,7 @@
         if (incomingStatus == Fence::Status::Invalid) {
             ALOGE("New fence has invalid state");
             mDrawingState.acquireFence = releaseFence;
-            mTimeStats.clearLayerRecord(getName().c_str());
+            mTimeStats.clearLayerRecord(layerID);
             return BAD_VALUE;
         }
 
@@ -439,7 +441,7 @@
                 // synchronization is broken, the best we can do is hope fences
                 // signal in order so the new fence will act like a union
                 mDrawingState.acquireFence = releaseFence;
-                mTimeStats.clearLayerRecord(getName().c_str());
+                mTimeStats.clearLayerRecord(layerID);
                 return BAD_VALUE;
             }
             mDrawingState.acquireFence = mergedFence;
@@ -462,16 +464,16 @@
         // a GL-composited layer) not at all.
         status_t err = bindTextureImage();
         if (err != NO_ERROR) {
-            mTimeStats.clearLayerRecord(getName().c_str());
+            mTimeStats.clearLayerRecord(layerID);
             return BAD_VALUE;
         }
     }
 
     // TODO(marissaw): properly support mTimeStats
-    const std::string layerName(getName().c_str());
-    mTimeStats.setPostTime(getName().c_str(), getFrameNumber(), latchTime);
-    mTimeStats.setAcquireFence(layerName, getFrameNumber(), getCurrentFenceTime());
-    mTimeStats.setLatchTime(layerName, getFrameNumber(), latchTime);
+    mTimeStats.setLayerName(layerID, getName().c_str());
+    mTimeStats.setPostTime(layerID, getFrameNumber(), latchTime);
+    mTimeStats.setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
+    mTimeStats.setLatchTime(layerID, getFrameNumber(), latchTime);
 
     return NO_ERROR;
 }
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/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 38482c9..918f7de 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -349,10 +349,11 @@
     DisplayRenderArea(const sp<const DisplayDevice> device,
                       ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
           : DisplayRenderArea(device, device->getBounds(), device->getWidth(), device->getHeight(),
-                              rotation) {}
+                              device->getCompositionDataSpace(), rotation) {}
     DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqWidth,
-                      uint32_t reqHeight, ui::Transform::orientation_flags rotation)
-          : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE,
+                      uint32_t reqHeight, ui::Dataspace reqDataSpace,
+                      ui::Transform::orientation_flags rotation)
+          : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
                        getDisplayRotation(rotation, device->getInstallOrientation())),
             mDevice(device),
             mSourceCrop(sourceCrop) {}
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/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 3a40648..393041d 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -184,7 +184,6 @@
 
         auto newDisplay = std::make_unique<Display>(
                 *mComposer.get(), mPowerAdvisor, mCapabilities, displayId, displayType);
-        newDisplay->setFrequencyScaleParameters(mFrequencyScaler);
         newDisplay->setConnected(true);
         mDisplays.emplace(displayId, std::move(newDisplay));
     } else if (connection == Connection::Disconnected) {
@@ -224,14 +223,6 @@
     return static_cast<Error>(mComposer->executeCommands());
 }
 
-void Device::setDisplayFrequencyScaleParameters(Device::FrequencyScaler frequencyScaler) {
-    mFrequencyScaler = frequencyScaler;
-}
-
-Device::FrequencyScaler Device::getDisplayFrequencyScaleParameters() {
-    return mFrequencyScaler;
-}
-
 // Display methods
 
 Display::Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
@@ -271,7 +262,6 @@
     mWidth(-1),
     mHeight(-1),
     mVsyncPeriod(-1),
-    mFrequencyScaler(display.mFrequencyScaler),
     mDpiX(-1),
     mDpiY(-1) {}
 
@@ -711,10 +701,6 @@
     mIsConnected = connected;
 }
 
-void Display::setFrequencyScaleParameters(Device::FrequencyScaler frequencyScaler) {
-    mFrequencyScaler = frequencyScaler;
-}
-
 int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute)
 {
     int32_t value = 0;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 363adb5..d274631 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -23,7 +23,6 @@
 #undef HWC2_INCLUDE_STRINGIFICATION
 #undef HWC2_USE_CPP11
 
-#include <cutils/properties.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/GraphicTypes.h>
@@ -85,11 +84,6 @@
 public:
     explicit Device(std::unique_ptr<android::Hwc2::Composer> composer);
 
-    struct FrequencyScaler {
-        int32_t multiplier = 1;
-        int32_t divisor = 1;
-    };
-
     void registerCallback(ComposerCallback* callback, int32_t sequenceId);
 
     // Required by HWC2
@@ -121,9 +115,6 @@
     // This method provides an explicit way to flush state changes to HWC.
     Error flushCommands();
 
-    void setDisplayFrequencyScaleParameters(FrequencyScaler frequecyScaler);
-    FrequencyScaler getDisplayFrequencyScaleParameters();
-
 private:
     // Initialization methods
 
@@ -134,7 +125,6 @@
     std::unordered_set<Capability> mCapabilities;
     std::unordered_map<hwc2_display_t, std::unique_ptr<Display>> mDisplays;
     android::Hwc2::impl::PowerAdvisor mPowerAdvisor;
-    FrequencyScaler mFrequencyScaler;
     bool mRegisteredCallback = false;
 };
 
@@ -199,8 +189,7 @@
 
         int32_t getWidth() const { return mWidth; }
         int32_t getHeight() const { return mHeight; }
-        nsecs_t getVsyncPeriod() const {
-            return mVsyncPeriod * mFrequencyScaler.multiplier / mFrequencyScaler.divisor; }
+        nsecs_t getVsyncPeriod() const { return mVsyncPeriod; }
         float getDpiX() const { return mDpiX; }
         float getDpiY() const { return mDpiY; }
 
@@ -213,7 +202,6 @@
         int32_t mWidth;
         int32_t mHeight;
         nsecs_t mVsyncPeriod;
-        Device::FrequencyScaler mFrequencyScaler;
         float mDpiX;
         float mDpiY;
     };
@@ -281,7 +269,6 @@
     hwc2_display_t getId() const { return mId; }
     bool isConnected() const { return mIsConnected; }
     void setConnected(bool connected);  // For use by Device only
-    void setFrequencyScaleParameters(Device::FrequencyScaler frequencyScaler);
 
 private:
     int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
@@ -306,7 +293,6 @@
     hwc2_display_t mId;
     bool mIsConnected;
     DisplayType mType;
-    Device::FrequencyScaler mFrequencyScaler;
     std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
     std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
 };
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 6b56ca4..d827fd2 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -814,15 +814,4 @@
     return mDisplayData[displayId].hwcDisplay->getId();
 }
 
-void HWComposer::setDisplayFrequencyScaleParameters(
-        HWC2::Device::FrequencyScaler frequencyScaler)
-{
-    mHwcDevice->setDisplayFrequencyScaleParameters(frequencyScaler);
-}
-
-HWC2::Device::FrequencyScaler HWComposer::getDisplayFrequencyScaleParameters()
-{
-    return mHwcDevice->getDisplayFrequencyScaleParameters();
-}
-
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index cca1f3b..4777ca9 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -186,14 +186,6 @@
     android::Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); }
 
     std::optional<hwc2_display_t> getHwcDisplayId(int32_t displayId) const;
-
-    // ------------------------------------------------------------------------
-    // These functions set and get the frequencyScaler.  The frequencyScaler holds
-    // a multiplier and divisor for virtually scaling the panel frequency in
-    // software.  This is used to simulate different panel frequencies when
-    // panel hardware is not available.
-    void setDisplayFrequencyScaleParameters(HWC2::Device::FrequencyScaler frequencyScaler);
-    HWC2::Device::FrequencyScaler getDisplayFrequencyScaleParameters();
 private:
     // For unit tests
     friend TestableSurfaceFlinger;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8afd3b3..88c3c8a 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));
         }
     }
 }
@@ -1428,14 +1470,15 @@
 void Layer::onDisconnect() {
     Mutex::Autolock lock(mFrameEventHistoryMutex);
     mFrameEventHistory.onDisconnect();
-    mTimeStats.onDisconnect(getName().c_str());
+    mTimeStats.onDisconnect(getSequence());
 }
 
 void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
                                      FrameEventHistoryDelta* outDelta) {
     if (newTimestamps) {
-        mTimeStats.setPostTime(getName().c_str(), newTimestamps->frameNumber,
-                               newTimestamps->postedTime);
+        const int32_t layerID = getSequence();
+        mTimeStats.setLayerName(layerID, getName().c_str());
+        mTimeStats.setPostTime(layerID, newTimestamps->frameNumber, newTimestamps->postedTime);
     }
 
     Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -1547,11 +1590,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 +1608,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/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 3c11e73..9bad6de 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <ui/GraphicTypes.h>
 #include <ui/Transform.h>
 
 #include <functional>
@@ -19,9 +20,11 @@
     static float getCaptureFillValue(CaptureFill captureFill);
 
     RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
+               ui::Dataspace reqDataSpace,
                ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
           : mReqWidth(reqWidth),
             mReqHeight(reqHeight),
+            mReqDataSpace(reqDataSpace),
             mCaptureFill(captureFill),
             mRotationFlags(rotation) {}
 
@@ -66,6 +69,9 @@
     int getReqWidth() const { return mReqWidth; };
     int getReqHeight() const { return mReqHeight; };
 
+    // Returns the composition data space of the render area.
+    ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
+
     // Returns the fill color of the physical render area.  Regions not
     // covered by any rendered layer should be filled with this color.
     CaptureFill getCaptureFill() const { return mCaptureFill; };
@@ -73,6 +79,7 @@
 private:
     const uint32_t mReqWidth;
     const uint32_t mReqHeight;
+    const ui::Dataspace mReqDataSpace;
     const CaptureFill mCaptureFill;
     const ui::Transform::orientation_flags mRotationFlags;
 };
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/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index cba9181..172c418 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -25,6 +25,7 @@
 #include <algorithm>
 
 #include <log/log.h>
+#include <cutils/properties.h>
 #include <utils/String8.h>
 #include <utils/Thread.h>
 #include <utils/Trace.h>
@@ -520,27 +521,12 @@
 
 void DispSync::setPeriod(nsecs_t period) {
     Mutex::Autolock lock(mMutex);
-    mPeriodBase = mPeriod = period;
+    mPeriod = period;
     mPhase = 0;
     mReferenceTime = 0;
     mThread->updateModel(mPeriod, mPhase, mReferenceTime);
 }
 
-void DispSync::scalePeriod(HWC2::Device::FrequencyScaler frequencyScaler) {
-    Mutex::Autolock lock(mMutex);
-
-    // if only 1 of the properties is updated, we will get to this
-    // point "attempting" to set the scale to 1 when it is already
-    // 1.  Check that special case so that we don't do a useless
-    // update of the model.
-    if ((frequencyScaler.multiplier == 1) &&
-            (frequencyScaler.divisor == 1) &&
-            (mPeriod == mPeriodBase)) return;
-
-    mPeriod = mPeriodBase * frequencyScaler.multiplier / frequencyScaler.divisor;
-    mThread->updateModel(mPeriod, mPhase, mReferenceTime);
-}
-
 nsecs_t DispSync::getPeriod() {
     // lock mutex as mPeriod changes multiple times in updateModelLocked
     Mutex::Autolock lock(mMutex);
@@ -565,7 +551,7 @@
 
         // Exclude the min and max from the average
         durationSum -= minDuration + maxDuration;
-        mPeriodBase = mPeriod = durationSum / (mNumResyncSamples - 3);
+        mPeriod = durationSum / (mNumResyncSamples - 3);
 
         ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod));
 
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index 5b511f4..5d19093 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -24,7 +24,6 @@
 #include <utils/Timers.h>
 
 #include <ui/FenceTime.h>
-#include <DisplayHardware/HWC2.h>
 
 #include <memory>
 
@@ -49,7 +48,6 @@
     virtual bool addResyncSample(nsecs_t timestamp) = 0;
     virtual void endResync() = 0;
     virtual void setPeriod(nsecs_t period) = 0;
-    virtual void scalePeriod(HWC2::Device::FrequencyScaler) = 0;
     virtual nsecs_t getPeriod() = 0;
     virtual void setRefreshSkipCount(int count) = 0;
     virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) = 0;
@@ -120,12 +118,6 @@
     // turned on.  It should NOT be used after that.
     void setPeriod(nsecs_t period) override;
 
-    // The scalePeriod method applies the multiplier and divisor to
-    // scale the vsync event model's period.   The function is added
-    // for an experimental test mode and should not be used outside
-    // of that purpose.
-    void scalePeriod(HWC2::Device::FrequencyScaler frequencyScaler);
-
     // The getPeriod method returns the current vsync period.
     nsecs_t getPeriod() override;
 
@@ -188,7 +180,6 @@
     // mPeriod is the computed period of the modeled vsync events in
     // nanoseconds.
     nsecs_t mPeriod;
-    nsecs_t mPeriodBase;
 
     // mPhase is the phase offset of the modeled vsync events.  It is the
     // number of nanoseconds from time 0 to the first vsync event.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f0723e8..c5a4cac 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);
@@ -1909,6 +1905,8 @@
         mTimeStats.incrementClientCompositionFrames();
     }
 
+    mTimeStats.setPresentFenceGlobal(presentFenceTime);
+
     if (display && getHwComposer().isConnected(display->getId()) &&
         display->getPowerMode() == HWC_POWER_MODE_OFF) {
         return;
@@ -2008,6 +2006,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 +2020,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 +2086,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 +2131,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 +2173,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 +2189,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
@@ -4135,6 +3982,10 @@
         getHwComposer().setPowerMode(type, mode);
     }
 
+    if (display->isPrimary()) {
+        mTimeStats.setPowerMode(mode);
+    }
+
     ALOGD("Finished setting power mode %d on display %d", mode, displayId);
 }
 
@@ -4927,9 +4778,9 @@
         code == IBinder::SYSPROPS_TRANSACTION) {
         return OK;
     }
-    // Numbers from 1000 to 1029 are currently use for backdoors. The code
+    // Numbers from 1000 to 1030 are currently use for backdoors. The code
     // in onTransact verifies that the user is root, and has access to use SF.
-    if (code >= 1000 && code <= 1029) {
+    if (code >= 1000 && code <= 1030) {
         ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
         return OK;
     }
@@ -5125,11 +4976,9 @@
                 repaintEverything();
                 return NO_ERROR;
             }
-            // TODO(b/111505327): Find out whether the usage of 1024 can switch to 1030,
-            // deprecate 1024 if they can.
-            case 1024: { // Does device have wide color gamut display?
-                reply->writeBool(hasWideColorDisplay);
-                return NO_ERROR;
+            // Deprecate, use 1030 to check whether the device is color managed.
+            case 1024: {
+                return NAME_NOT_FOUND;
             }
             case 1025: { // Set layer tracing
                 n = data.readInt32();
@@ -5180,54 +5029,6 @@
                 reply->writeBool(getBE().mHwc->isUsingVrComposer());
                 return NO_ERROR;
             }
-            case 1029: {
-                // Code 1029 is an experimental feature that allows applications to
-                // simulate a high frequency panel by setting a multiplier and divisor
-                // on the VSYNC-sf clock.  If either the multiplier or divisor are
-                // 0, then the code simply return the current multiplier and divisor.
-                HWC2::Device::FrequencyScaler frequencyScaler;
-                frequencyScaler.multiplier = data.readInt32();
-                frequencyScaler.divisor = data.readInt32();
-
-                if ((frequencyScaler.multiplier == 0) || (frequencyScaler.divisor == 0)) {
-                    frequencyScaler = getBE().mHwc->getDisplayFrequencyScaleParameters();
-                    reply->writeInt32(frequencyScaler.multiplier);
-                    reply->writeInt32(frequencyScaler.divisor);
-                    return NO_ERROR;
-                }
-
-                if ((frequencyScaler.multiplier == 1) && (frequencyScaler.divisor == 1)) {
-                    if (mUseScheduler) {
-                        mScheduler->enableHardwareVsync();
-                    } else {
-                        enableHardwareVsync();
-                    }
-                } else {
-                    if (mUseScheduler) {
-                        mScheduler->disableHardwareVsync(true);
-                    } else {
-                        disableHardwareVsync(true);
-                    }
-                }
-                mPrimaryDispSync->scalePeriod(frequencyScaler);
-                getBE().mHwc->setDisplayFrequencyScaleParameters(frequencyScaler);
-
-                ATRACE_INT("PeriodMultiplier", frequencyScaler.multiplier);
-                ATRACE_INT("PeriodDivisor", frequencyScaler.divisor);
-
-                const hwc2_display_t hwcDisplayId = getBE().mHwc->getActiveConfig(
-                        DisplayDevice::DISPLAY_PRIMARY)->getDisplayId();
-
-                onHotplugReceived(getBE().mComposerSequenceId,
-                        hwcDisplayId, HWC2::Connection::Disconnected);
-                onHotplugReceived(getBE().mComposerSequenceId,
-                        hwcDisplayId, HWC2::Connection::Connected);
-                frequencyScaler = getBE().mHwc->getDisplayFrequencyScaleParameters();
-                reply->writeInt32(frequencyScaler.multiplier);
-                reply->writeInt32(frequencyScaler.divisor);
-
-                return NO_ERROR;
-            }
             // Is device color managed?
             case 1030: {
                 reply->writeBool(useColorManagement);
@@ -5257,7 +5058,8 @@
 };
 
 status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken,
-                                       sp<GraphicBuffer>* outBuffer, Rect sourceCrop,
+                                       sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace,
+                                       const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
                                        uint32_t reqWidth, uint32_t reqHeight,
                                        bool useIdentityTransform,
                                        ISurfaceComposer::Rotation rotation) {
@@ -5286,23 +5088,27 @@
         }
     }
 
-    DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, renderAreaRotation);
+    DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
+                                 renderAreaRotation);
 
     auto traverseLayers = std::bind(std::mem_fn(&SurfaceFlinger::traverseLayersInDisplay), this,
                                     display, std::placeholders::_1);
-    return captureScreenCommon(renderArea, traverseLayers, outBuffer, useIdentityTransform);
+    return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
+                               useIdentityTransform);
 }
 
 status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder,
-                                       sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
+                                       sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace,
+                                       const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                        float frameScale, bool childrenOnly) {
     ATRACE_CALL();
 
     class LayerRenderArea : public RenderArea {
     public:
         LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
-                        int32_t reqWidth, int32_t reqHeight, bool childrenOnly)
-              : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR),
+                        int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
+                        bool childrenOnly)
+              : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace),
                 mLayer(layer),
                 mCrop(crop),
                 mNeedsFiltering(false),
@@ -5409,7 +5215,7 @@
         reqHeight = 1;
     }
 
-    LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, childrenOnly);
+    LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly);
 
     auto traverseLayers = [parent, childrenOnly](const LayerVector::Visitor& visitor) {
         parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -5421,19 +5227,22 @@
             visitor(layer);
         });
     };
-    return captureScreenCommon(renderArea, traverseLayers, outBuffer, false);
+    return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false);
 }
 
 status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
                                              TraverseLayersFunction traverseLayers,
                                              sp<GraphicBuffer>* outBuffer,
+                                             const ui::PixelFormat reqPixelFormat,
                                              bool useIdentityTransform) {
     ATRACE_CALL();
 
+    // TODO(b/116112787) Make buffer usage a parameter.
     const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
             GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
     *outBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
-                                   HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
+                                   static_cast<android_pixel_format>(reqPixelFormat), 1, usage,
+                                   "screenshot");
 
     // This mutex protects syncFd and captureResult for communication of the return values from the
     // main thread back to this Binder thread
@@ -5509,8 +5318,7 @@
     const auto sourceCrop = renderArea.getSourceCrop();
     const auto rotation = renderArea.getRotationFlags();
 
-    // assume ColorMode::SRGB / RenderIntent::COLORIMETRIC
-    engine.setOutputDataSpace(Dataspace::SRGB);
+    engine.setOutputDataSpace(renderArea.getReqDataSpace());
     engine.setDisplayMaxLuminance(DisplayDevice::sDefaultMaxLumiance);
 
     // make sure to clear all GL error flags
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f535c4e..8045246 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -433,10 +433,14 @@
     virtual sp<IDisplayEventConnection> createDisplayEventConnection(
             ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp);
     virtual status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
-                                   Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-                                   bool useIdentityTransform, ISurfaceComposer::Rotation rotation);
+                                   const ui::Dataspace reqDataspace,
+                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+                                   ISurfaceComposer::Rotation rotation);
     virtual status_t captureLayers(const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
-                                   const Rect& sourceCrop, float frameScale, bool childrenOnly);
+                                   const ui::Dataspace reqDataspace,
+                                   const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
+                                   float frameScale, bool childrenOnly);
     virtual status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
     virtual status_t getDisplayConfigs(const sp<IBinder>& displayToken,
                                        Vector<DisplayInfo>* configs);
@@ -586,7 +590,7 @@
     void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
                                 bool useIdentityTransform);
     status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
-                                 sp<GraphicBuffer>* outBuffer,
+                                 sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
                                  bool useIdentityTransform);
     status_t captureScreenImplLocked(const RenderArea& renderArea,
                                      TraverseLayersFunction traverseLayers,
@@ -689,7 +693,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 +809,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..43fa262 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -24,6 +24,7 @@
 #include <log/log.h>
 
 #include <utils/String8.h>
+#include <utils/Timers.h>
 #include <utils/Trace.h>
 
 #include <algorithm>
@@ -82,7 +83,7 @@
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mMutex);
-    timeStats.totalFrames++;
+    mTimeStats.totalFrames++;
 }
 
 void TimeStats::incrementMissedFrames() {
@@ -91,7 +92,7 @@
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mMutex);
-    timeStats.missedFrames++;
+    mTimeStats.missedFrames++;
 }
 
 void TimeStats::incrementClientCompositionFrames() {
@@ -100,12 +101,12 @@
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mMutex);
-    timeStats.clientCompositionFrames++;
+    mTimeStats.clientCompositionFrames++;
 }
 
-bool TimeStats::recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord) {
+bool TimeStats::recordReadyLocked(int32_t layerID, TimeRecord* timeRecord) {
     if (!timeRecord->ready) {
-        ALOGV("[%s]-[%" PRIu64 "]-presentFence is still not received", layerName.c_str(),
+        ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerID,
               timeRecord->frameTime.frameNumber);
         return false;
     }
@@ -118,7 +119,7 @@
             timeRecord->frameTime.acquireTime = timeRecord->acquireFence->getSignalTime();
             timeRecord->acquireFence = nullptr;
         } else {
-            ALOGV("[%s]-[%" PRIu64 "]-acquireFence signal time is invalid", layerName.c_str(),
+            ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerID,
                   timeRecord->frameTime.frameNumber);
         }
     }
@@ -131,7 +132,7 @@
             timeRecord->frameTime.presentTime = timeRecord->presentFence->getSignalTime();
             timeRecord->presentFence = nullptr;
         } else {
-            ALOGV("[%s]-[%" PRIu64 "]-presentFence signal time invalid", layerName.c_str(),
+            ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerID,
                   timeRecord->frameTime.frameNumber);
         }
     }
@@ -145,14 +146,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];
@@ -160,60 +162,61 @@
     return "";
 }
 
-void TimeStats::flushAvailableRecordsToStatsLocked(const std::string& layerName) {
+void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) {
     ATRACE_CALL();
 
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
     std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
     while (!timeRecords.empty()) {
-        if (!recordReadyLocked(layerName, &timeRecords[0])) break;
-        ALOGV("[%s]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerName.c_str(),
+        if (!recordReadyLocked(layerID, &timeRecords[0])) break;
+        ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID,
               timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
 
         if (prevTimeRecord.ready) {
-            if (!timeStats.stats.count(layerName)) {
-                timeStats.stats[layerName].layerName = layerName;
-                timeStats.stats[layerName].packageName = getPackageName(layerName);
+            const std::string& layerName = layerRecord.layerName;
+            if (!mTimeStats.stats.count(layerName)) {
+                mTimeStats.stats[layerName].layerName = layerName;
+                mTimeStats.stats[layerName].packageName = getPackageName(layerName);
             }
-            TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timeStats.stats[layerName];
+            TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
             timeStatsLayer.totalFrames++;
             timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
             layerRecord.droppedFrames = 0;
 
             const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime,
                                                       timeRecords[0].frameTime.acquireTime);
-            ALOGV("[%s]-[%" PRIu64 "]-post2acquire[%d]", layerName.c_str(),
+            ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerID,
                   timeRecords[0].frameTime.frameNumber, postToAcquireMs);
             timeStatsLayer.deltas["post2acquire"].insert(postToAcquireMs);
 
             const int32_t postToPresentMs = msBetween(timeRecords[0].frameTime.postTime,
                                                       timeRecords[0].frameTime.presentTime);
-            ALOGV("[%s]-[%" PRIu64 "]-post2present[%d]", layerName.c_str(),
+            ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerID,
                   timeRecords[0].frameTime.frameNumber, postToPresentMs);
             timeStatsLayer.deltas["post2present"].insert(postToPresentMs);
 
             const int32_t acquireToPresentMs = msBetween(timeRecords[0].frameTime.acquireTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%s]-[%" PRIu64 "]-acquire2present[%d]", layerName.c_str(),
+            ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerID,
                   timeRecords[0].frameTime.frameNumber, acquireToPresentMs);
             timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs);
 
             const int32_t latchToPresentMs = msBetween(timeRecords[0].frameTime.latchTime,
                                                        timeRecords[0].frameTime.presentTime);
-            ALOGV("[%s]-[%" PRIu64 "]-latch2present[%d]", layerName.c_str(),
+            ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerID,
                   timeRecords[0].frameTime.frameNumber, latchToPresentMs);
             timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs);
 
             const int32_t desiredToPresentMs = msBetween(timeRecords[0].frameTime.desiredTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%s]-[%" PRIu64 "]-desired2present[%d]", layerName.c_str(),
+            ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerID,
                   timeRecords[0].frameTime.frameNumber, desiredToPresentMs);
             timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs);
 
             const int32_t presentToPresentMs = msBetween(prevTimeRecord.frameTime.presentTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%s]-[%" PRIu64 "]-present2present[%d]", layerName.c_str(),
+            ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerID,
                   timeRecords[0].frameTime.frameNumber, presentToPresentMs);
             timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
         }
@@ -223,36 +226,48 @@
     }
 }
 
+// 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) {
+void TimeStats::setLayerName(int32_t layerID, const std::string& layerName) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-PostTime[%" PRId64 "]", layerName.c_str(), frameNumber, postTime);
+    ALOGV("[%d]-[%s]", layerID, layerName.c_str());
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName) && !layerNameIsValid(layerName)) {
-        return;
+    if (!mTimeStatsTracker.count(layerID) && layerNameIsValid(layerName)) {
+        mTimeStatsTracker[layerID].layerName = layerName;
     }
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+}
+
+void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, nsecs_t postTime) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    ALOGV("[%d]-[%" PRIu64 "]-PostTime[%" PRId64 "]", layerID, frameNumber, postTime);
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
-        ALOGV("[%s]-timeRecords is already at its maximum size[%zu]", layerName.c_str(),
-              MAX_NUM_TIME_RECORDS);
-        // TODO(zzyiwei): if this happens, there must be a present fence missing
-        // or waitData is not in the correct position. Need to think out a
-        // reasonable way to recover from this state.
+        ALOGE("[%d]-[%s]-timeRecords is already at its maximum size[%zu]. Please file a bug.",
+              layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
+        layerRecord.timeRecords.clear();
+        layerRecord.prevTimeRecord.ready = false;
+        layerRecord.waitData = -1;
         return;
     }
     // For most media content, the acquireFence is invalid because the buffer is
@@ -272,84 +287,77 @@
         layerRecord.waitData = layerRecord.timeRecords.size() - 1;
 }
 
-void TimeStats::setLatchTime(const std::string& layerName, uint64_t frameNumber,
-                             nsecs_t latchTime) {
+void TimeStats::setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerName.c_str(), frameNumber, latchTime);
+    ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerID, frameNumber, latchTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
     if (timeRecord.frameTime.frameNumber == frameNumber) {
         timeRecord.frameTime.latchTime = latchTime;
     }
 }
 
-void TimeStats::setDesiredTime(const std::string& layerName, uint64_t frameNumber,
-                               nsecs_t desiredTime) {
+void TimeStats::setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerName.c_str(), frameNumber,
-          desiredTime);
+    ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerID, frameNumber, desiredTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
     if (timeRecord.frameTime.frameNumber == frameNumber) {
         timeRecord.frameTime.desiredTime = desiredTime;
     }
 }
 
-void TimeStats::setAcquireTime(const std::string& layerName, uint64_t frameNumber,
-                               nsecs_t acquireTime) {
+void TimeStats::setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerName.c_str(), frameNumber,
-          acquireTime);
+    ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerID, frameNumber, acquireTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
     if (timeRecord.frameTime.frameNumber == frameNumber) {
         timeRecord.frameTime.acquireTime = acquireTime;
     }
 }
 
-void TimeStats::setAcquireFence(const std::string& layerName, uint64_t frameNumber,
+void TimeStats::setAcquireFence(int32_t layerID, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& acquireFence) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+    ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerID, frameNumber,
           acquireFence->getSignalTime());
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
     if (timeRecord.frameTime.frameNumber == frameNumber) {
         timeRecord.acquireFence = acquireFence;
     }
 }
 
-void TimeStats::setPresentTime(const std::string& layerName, uint64_t frameNumber,
-                               nsecs_t presentTime) {
+void TimeStats::setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerName.c_str(), frameNumber,
-          presentTime);
+    ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerID, frameNumber, presentTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
     if (timeRecord.frameTime.frameNumber == frameNumber) {
         timeRecord.frameTime.presentTime = presentTime;
@@ -357,20 +365,20 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerName);
+    flushAvailableRecordsToStatsLocked(layerID);
 }
 
-void TimeStats::setPresentFence(const std::string& layerName, uint64_t frameNumber,
+void TimeStats::setPresentFence(int32_t layerID, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& presentFence) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+    ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerID, frameNumber,
           presentFence->getSignalTime());
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
     if (timeRecord.frameTime.frameNumber == frameNumber) {
         timeRecord.presentFence = presentFence;
@@ -378,45 +386,57 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerName);
+    flushAvailableRecordsToStatsLocked(layerID);
 }
 
-void TimeStats::onDisconnect(const std::string& layerName) {
+void TimeStats::onDisconnect(int32_t layerID) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-onDisconnect", layerName.c_str());
+    ALOGV("[%d]-onDisconnect", layerID);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    flushAvailableRecordsToStatsLocked(layerName);
-    timeStatsTracker.erase(layerName);
+    if (!mTimeStatsTracker.count(layerID)) return;
+    flushAvailableRecordsToStatsLocked(layerID);
+    mTimeStatsTracker.erase(layerID);
 }
 
-void TimeStats::clearLayerRecord(const std::string& layerName) {
+void TimeStats::onDestroy(int32_t layerID) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-clearLayerRecord", layerName.c_str());
+    ALOGV("[%d]-onDestroy", layerID);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    flushAvailableRecordsToStatsLocked(layerID);
+    mTimeStatsTracker.erase(layerID);
+}
+
+void TimeStats::clearLayerRecord(int32_t layerID) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    ALOGV("[%d]-clearLayerRecord", layerID);
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     layerRecord.timeRecords.clear();
     layerRecord.prevTimeRecord.ready = false;
     layerRecord.waitData = -1;
     layerRecord.droppedFrames = 0;
 }
 
-void TimeStats::removeTimeRecord(const std::string& layerName, uint64_t frameNumber) {
+void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-removeTimeRecord", layerName.c_str(), frameNumber);
+    ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerID, frameNumber);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     size_t removeAt = 0;
     for (const TimeRecord& record : layerRecord.timeRecords) {
         if (record.frameTime.frameNumber == frameNumber) break;
@@ -430,6 +450,87 @@
     layerRecord.droppedFrames++;
 }
 
+void TimeStats::flushPowerTimeLocked() {
+    nsecs_t curTime = systemTime();
+    // elapsedTime is in milliseconds.
+    int64_t elapsedTime = (curTime - mPowerTime.prevTime) / 1000000;
+
+    switch (mPowerTime.powerMode) {
+        case HWC_POWER_MODE_NORMAL:
+            mTimeStats.displayOnTime += elapsedTime;
+            break;
+        case HWC_POWER_MODE_OFF:
+        case HWC_POWER_MODE_DOZE:
+        case HWC_POWER_MODE_DOZE_SUSPEND:
+        default:
+            break;
+    }
+
+    mPowerTime.prevTime = curTime;
+}
+
+void TimeStats::setPowerMode(int32_t powerMode) {
+    if (!mEnabled.load()) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mPowerTime.powerMode = powerMode;
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (powerMode == mPowerTime.powerMode) return;
+
+    flushPowerTimeLocked();
+    mPowerTime.powerMode = powerMode;
+}
+
+void TimeStats::flushAvailableGlobalRecordsToStatsLocked() {
+    ATRACE_CALL();
+
+    while (!mGlobalRecord.presentFences.empty()) {
+        const nsecs_t curPresentTime = mGlobalRecord.presentFences.front()->getSignalTime();
+        if (curPresentTime == Fence::SIGNAL_TIME_PENDING) break;
+
+        if (curPresentTime == Fence::SIGNAL_TIME_INVALID) {
+            ALOGE("GlobalPresentFence is invalid!");
+            mGlobalRecord.prevPresentTime = 0;
+            mGlobalRecord.presentFences.pop_front();
+            continue;
+        }
+
+        ALOGV("GlobalPresentFenceTime[%" PRId64 "]",
+              mGlobalRecord.presentFences.front()->getSignalTime());
+
+        const int32_t presentToPresentMs = msBetween(mGlobalRecord.prevPresentTime, curPresentTime);
+        ALOGV("Global present2present[%d]", presentToPresentMs);
+
+        mTimeStats.presentToPresent.insert(presentToPresentMs);
+        mGlobalRecord.prevPresentTime = curPresentTime;
+        mGlobalRecord.presentFences.pop_front();
+    }
+}
+
+void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (presentFence == nullptr) {
+        mGlobalRecord.prevPresentTime = 0;
+        return;
+    }
+
+    if (mGlobalRecord.presentFences.size() == MAX_NUM_TIME_RECORDS) {
+        // The front presentFence must be trapped in pending status in this
+        // case. Try dequeuing the front one to recover.
+        ALOGE("GlobalPresentFences is already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+        mGlobalRecord.prevPresentTime = 0;
+        mGlobalRecord.presentFences.pop_front();
+    }
+
+    mGlobalRecord.presentFences.emplace_back(presentFence);
+    flushAvailableGlobalRecordsToStatsLocked();
+}
+
 void TimeStats::enable() {
     if (mEnabled.load()) return;
 
@@ -438,7 +539,8 @@
     std::lock_guard<std::mutex> lock(mMutex);
     ALOGD("Enabled");
     mEnabled.store(true);
-    timeStats.statsStart = static_cast<int64_t>(std::time(0));
+    mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
+    mPowerTime.prevTime = systemTime();
 }
 
 void TimeStats::disable() {
@@ -449,7 +551,7 @@
     std::lock_guard<std::mutex> lock(mMutex);
     ALOGD("Disabled");
     mEnabled.store(false);
-    timeStats.statsEnd = static_cast<int64_t>(std::time(0));
+    mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
 }
 
 void TimeStats::clear() {
@@ -457,12 +559,15 @@
 
     std::lock_guard<std::mutex> lock(mMutex);
     ALOGD("Cleared");
-    timeStats.stats.clear();
-    timeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
-    timeStats.statsEnd = 0;
-    timeStats.totalFrames = 0;
-    timeStats.missedFrames = 0;
-    timeStats.clientCompositionFrames = 0;
+    mTimeStats.stats.clear();
+    mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
+    mTimeStats.statsEnd = 0;
+    mTimeStats.totalFrames = 0;
+    mTimeStats.missedFrames = 0;
+    mTimeStats.clientCompositionFrames = 0;
+    mTimeStats.displayOnTime = 0;
+    mTimeStats.presentToPresent.hist.clear();
+    mPowerTime.prevTime = systemTime();
 }
 
 bool TimeStats::isEnabled() {
@@ -473,19 +578,21 @@
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (timeStats.statsStart == 0) {
+    if (mTimeStats.statsStart == 0) {
         return;
     }
 
-    timeStats.statsEnd = static_cast<int64_t>(std::time(0));
+    mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+
+    flushPowerTimeLocked();
 
     if (asProto) {
         ALOGD("Dumping TimeStats as proto");
-        SFTimeStatsGlobalProto timeStatsProto = timeStats.toProto(maxLayers);
+        SFTimeStatsGlobalProto timeStatsProto = mTimeStats.toProto(maxLayers);
         result.append(timeStatsProto.SerializeAsString().c_str(), timeStatsProto.ByteSize());
     } else {
         ALOGD("Dumping TimeStats as text");
-        result.append(timeStats.toString(maxLayers).c_str());
+        result.append(mTimeStats.toString(maxLayers).c_str());
         result.append("\n");
     }
 }
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 5ab3934..d1e554c 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -19,6 +19,8 @@
 #include <timestatsproto/TimeStatsHelper.h>
 #include <timestatsproto/TimeStatsProtoHeader.h>
 
+#include <hardware/hwcomposer_defs.h>
+
 #include <ui/FenceTime.h>
 
 #include <utils/String16.h>
@@ -57,6 +59,7 @@
     };
 
     struct LayerRecord {
+        std::string layerName;
         // This is the index in timeRecords, at which the timestamps for that
         // specific frame are still not fully received. This is not waiting for
         // fences to signal, but rather waiting to receive those fences/timestamps.
@@ -66,6 +69,16 @@
         std::deque<TimeRecord> timeRecords;
     };
 
+    struct PowerTime {
+        int32_t powerMode = HWC_POWER_MODE_OFF;
+        nsecs_t prevTime = 0;
+    };
+
+    struct GlobalRecord {
+        nsecs_t prevPresentTime = 0;
+        std::deque<std::shared_ptr<FenceTime>> presentFences;
+    };
+
 public:
     static TimeStats& getInstance();
     void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, String8& result);
@@ -73,27 +86,35 @@
     void incrementMissedFrames();
     void incrementClientCompositionFrames();
 
-    void setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime);
-    void setLatchTime(const std::string& layerName, uint64_t frameNumber, nsecs_t latchTime);
-    void setDesiredTime(const std::string& layerName, uint64_t frameNumber, nsecs_t desiredTime);
-    void setAcquireTime(const std::string& layerName, uint64_t frameNumber, nsecs_t acquireTime);
-    void setAcquireFence(const std::string& layerName, uint64_t frameNumber,
+    void setLayerName(int32_t layerID, const std::string& layerName);
+    void setPostTime(int32_t layerID, uint64_t frameNumber, nsecs_t postTime);
+    void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime);
+    void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime);
+    void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime);
+    void setAcquireFence(int32_t layerID, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& acquireFence);
-    void setPresentTime(const std::string& layerName, uint64_t frameNumber, nsecs_t presentTime);
-    void setPresentFence(const std::string& layerName, uint64_t frameNumber,
+    void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime);
+    void setPresentFence(int32_t layerID, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence);
     // On producer disconnect with BufferQueue.
-    void onDisconnect(const std::string& layerName);
+    void onDisconnect(int32_t layerID);
+    // On layer tear down.
+    void onDestroy(int32_t layerID);
     // When SF is cleaning up the queue, clear the LayerRecord as well.
-    void clearLayerRecord(const std::string& layerName);
+    void clearLayerRecord(int32_t layerID);
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
-    void removeTimeRecord(const std::string& layerName, uint64_t frameNumber);
+    void removeTimeRecord(int32_t layerID, uint64_t frameNumber);
+
+    void setPowerMode(int32_t powerMode);
+    void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence);
 
 private:
     TimeStats() = default;
 
-    bool recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord);
-    void flushAvailableRecordsToStatsLocked(const std::string& layerName);
+    bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord);
+    void flushAvailableRecordsToStatsLocked(int32_t layerID);
+    void flushPowerTimeLocked();
+    void flushAvailableGlobalRecordsToStatsLocked();
 
     void enable();
     void disable();
@@ -103,8 +124,11 @@
 
     std::atomic<bool> mEnabled = false;
     std::mutex mMutex;
-    TimeStatsHelper::TimeStatsGlobal timeStats;
-    std::unordered_map<std::string, LayerRecord> timeStatsTracker;
+    TimeStatsHelper::TimeStatsGlobal mTimeStats;
+    // Hashmap for LayerRecord with layerID as the hash key
+    std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker;
+    PowerTime mPowerTime;
+    GlobalRecord mGlobalRecord;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index b7b2778..c701224 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -49,7 +49,7 @@
 float TimeStatsHelper::Histogram::averageTime() const {
     int64_t ret = 0;
     int64_t count = 0;
-    for (auto& ele : hist) {
+    for (const auto& ele : hist) {
         count += ele.second;
         ret += ele.first * ele.second;
     }
@@ -73,13 +73,13 @@
     StringAppendF(&result, "packageName = %s\n", packageName.c_str());
     StringAppendF(&result, "totalFrames = %d\n", totalFrames);
     StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
-    auto iter = deltas.find("present2present");
+    const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
         StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
     }
-    for (auto& ele : deltas) {
+    for (const auto& ele : deltas) {
         StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str());
-        StringAppendF(&result, "%s", ele.second.toString().c_str());
+        result.append(ele.second.toString());
     }
 
     return result;
@@ -92,9 +92,12 @@
     StringAppendF(&result, "totalFrames = %d\n", totalFrames);
     StringAppendF(&result, "missedFrames = %d\n", missedFrames);
     StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
+    StringAppendF(&result, "displayOnTime = %lld ms\n", static_cast<long long int>(displayOnTime));
+    StringAppendF(&result, "presentToPresent histogram is as below:\n");
+    result.append(presentToPresent.toString());
     const auto dumpStats = generateDumpStats(maxLayers);
-    for (auto& ele : dumpStats) {
-        StringAppendF(&result, "%s", ele->toString().c_str());
+    for (const auto& ele : dumpStats) {
+        result.append(ele->toString());
     }
 
     return result;
@@ -106,10 +109,10 @@
     layerProto.set_package_name(packageName);
     layerProto.set_total_frames(totalFrames);
     layerProto.set_dropped_frames(droppedFrames);
-    for (auto& ele : deltas) {
+    for (const auto& ele : deltas) {
         SFTimeStatsDeltaProto* deltaProto = layerProto.add_deltas();
         deltaProto->set_delta_name(ele.first);
-        for (auto& histEle : ele.second.hist) {
+        for (const auto& histEle : ele.second.hist) {
             SFTimeStatsHistogramBucketProto* histProto = deltaProto->add_histograms();
             histProto->set_time_millis(histEle.first);
             histProto->set_frame_count(histEle.second);
@@ -126,8 +129,14 @@
     globalProto.set_total_frames(totalFrames);
     globalProto.set_missed_frames(missedFrames);
     globalProto.set_client_composition_frames(clientCompositionFrames);
+    globalProto.set_display_on_time(displayOnTime);
+    for (const auto& histEle : presentToPresent.hist) {
+        SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present();
+        histProto->set_time_millis(histEle.first);
+        histProto->set_frame_count(histEle.second);
+    }
     const auto dumpStats = generateDumpStats(maxLayers);
-    for (auto& ele : dumpStats) {
+    for (const auto& ele : dumpStats) {
         SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
         layerProto->CopyFrom(ele->toProto());
     }
@@ -137,7 +146,7 @@
 std::vector<TimeStatsHelper::TimeStatsLayer const*>
 TimeStatsHelper::TimeStatsGlobal::generateDumpStats(std::optional<uint32_t> maxLayers) const {
     std::vector<TimeStatsLayer const*> dumpStats;
-    for (auto& ele : stats) {
+    for (const auto& ele : stats) {
         dumpStats.push_back(&ele.second);
     }
 
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 99c891b..7d0fe79 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -57,6 +57,8 @@
         int32_t totalFrames = 0;
         int32_t missedFrames = 0;
         int32_t clientCompositionFrames = 0;
+        int64_t displayOnTime = 0;
+        Histogram presentToPresent;
         std::unordered_map<std::string, TimeStatsLayer> stats;
 
         std::string toString(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index b8f38c2..377612a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -20,13 +20,12 @@
 
 option optimize_for = LITE_RUNTIME;
 
-// //depot/google3/java/com/google/android/apps/graphics/stats/proto/
+// //depot/google3/wireless/android/graphics/surfaceflingerstats/proto/
 // timestats.proto is based on this proto. Please only make valid protobuf
-// changes to these messages, and keep the other file in sync per Android
-// release. Please also do not include "option optimize_for = LITE_RUNTIME;" at
-// google3 side.
+// changes to these messages, and keep google3 side proto messages in sync if
+// the end to end pipeline needs to be updated.
 
-// Next tag: 7
+// Next tag: 9
 message SFTimeStatsGlobalProto {
   // The stats start time in UTC as seconds since January 1, 1970
   optional int64 stats_start = 1;
@@ -38,6 +37,10 @@
   optional int32 missed_frames = 4;
   // Total frames fallback to client composition.
   optional int32 client_composition_frames = 5;
+  // Primary display on time in milliseconds.
+  optional int64 display_on_time = 7;
+  // Present to present histogram.
+  repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
   // Stats per layer. Apps could have multiple layers.
   repeated SFTimeStatsLayerProto stats = 6;
 }
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 9e762f0..a73ec6c 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -267,8 +267,9 @@
     sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
     std::function<status_t()> condition = [=]() {
         sp<GraphicBuffer> outBuffer;
-        return ScreenshotClient::capture(display, Rect(), 0 /*reqWidth*/, 0 /*reqHeight*/, false,
-                                         ROTATION, &outBuffer);
+        return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB,
+                                         ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/,
+                                         0 /*reqHeight*/, false, ROTATION, &outBuffer);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
@@ -278,8 +279,9 @@
     sp<GraphicBuffer> outBuffer;
     std::function<status_t()> condition = [=]() {
         sp<GraphicBuffer> outBuffer;
-        return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(), Rect(), FRAME_SCALE,
-                                               &outBuffer);
+        return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
+                                               ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+                                               Rect(), FRAME_SCALE, &outBuffer);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 4e403b7..1352df5 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;
@@ -189,7 +190,8 @@
     constexpr bool forSystem = true;
 
     DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH,
-                                 DEFAULT_DISPLAY_HEIGHT, ui::Transform::ROT_0);
+                                 DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB,
+                                 ui::Transform::ROT_0);
 
     auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
         return mFlinger.traverseLayersInDisplay(mDisplay, visitor);
@@ -287,7 +289,7 @@
         EXPECT_CALL(*test->mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd())));
         EXPECT_CALL(*test->mRenderEngine, finish()).WillOnce(Return(true));
 
-        EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(ui::Dataspace::SRGB)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(_)).Times(1);
         EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE))
                 .Times(1);
         // This expectation retires on saturation as setViewportAndProjection is
@@ -514,7 +516,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();
     }
 
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/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
index 495b3f2..34e71cb 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
@@ -35,7 +35,6 @@
     MOCK_METHOD1(addResyncSample, bool(nsecs_t));
     MOCK_METHOD0(endResync, void());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
-    MOCK_METHOD1(scalePeriod, void(HWC2::Device::FrequencyScaler));
     MOCK_METHOD0(getPeriod, nsecs_t());
     MOCK_METHOD1(setRefreshSkipCount, void(int));
     MOCK_METHOD3(addEventListener, status_t(const char*, nsecs_t, Callback*));
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/IBufferHub.cpp b/services/vr/bufferhubd/IBufferHub.cpp
index 9d5b91a..2f39e41 100644
--- a/services/vr/bufferhubd/IBufferHub.cpp
+++ b/services/vr/bufferhubd/IBufferHub.cpp
@@ -4,14 +4,69 @@
 namespace android {
 namespace dvr {
 
+class BpBufferHub : public BpInterface<IBufferHub> {
+ public:
+  explicit BpBufferHub(const sp<IBinder>& impl)
+      : BpInterface<IBufferHub>(impl) {}
+
+  sp<IBufferClient> createBuffer(uint32_t width, uint32_t height,
+                                 uint32_t layer_count, uint32_t format,
+                                 uint64_t usage,
+                                 uint64_t user_metadata_size) override;
+};
+
 IMPLEMENT_META_INTERFACE(BufferHub, "android.dvr.IBufferHub");
 
+// Transaction code
+enum {
+  CREATE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+sp<IBufferClient> BpBufferHub::createBuffer(uint32_t width, uint32_t height,
+                                            uint32_t layer_count,
+                                            uint32_t format, uint64_t usage,
+                                            uint64_t user_metadata_size) {
+  Parcel data, reply;
+  status_t ret = NO_ERROR;
+  ret |= data.writeInterfaceToken(IBufferHub::getInterfaceDescriptor());
+  ret |= data.writeUint32(width);
+  ret |= data.writeUint32(height);
+  ret |= data.writeUint32(layer_count);
+  ret |= data.writeUint32(format);
+  ret |= data.writeUint64(usage);
+  ret |= data.writeUint64(user_metadata_size);
+
+  if (ret != NO_ERROR) {
+    ALOGE("BpBufferHub::createBuffer: failed to write into parcel");
+    return nullptr;
+  }
+
+  ret = remote()->transact(CREATE_BUFFER, data, &reply);
+  if (ret == NO_ERROR) {
+    return interface_cast<IBufferClient>(reply.readStrongBinder());
+  } else {
+    ALOGE("BpBufferHub::createBuffer: failed to transact; errno=%d", ret);
+    return nullptr;
+  }
+}
+
 status_t BnBufferHub::onTransact(uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags) {
   switch (code) {
+    case CREATE_BUFFER: {
+      CHECK_INTERFACE(IBufferHub, data, reply);
+      uint32_t width = data.readUint32();
+      uint32_t height = data.readUint32();
+      uint32_t layer_count = data.readUint32();
+      uint32_t format = data.readUint32();
+      uint64_t usage = data.readUint64();
+      uint64_t user_metadata_size = data.readUint64();
+      sp<IBufferClient> ret = createBuffer(width, height, layer_count, format,
+                                           usage, user_metadata_size);
+      return reply->writeStrongBinder(IInterface::asBinder(ret));
+    } break;
     default:
-      // Should not reach
-      ALOGE("onTransact(): unknown code %u received!", code);
+      // Should not reach except binder defined transactions such as dumpsys
       return BBinder::onTransact(code, data, reply, flags);
   }
 }
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..f8a9758 100644
--- a/services/vr/bufferhubd/buffer_hub_binder.cpp
+++ b/services/vr/bufferhubd/buffer_hub_binder.cpp
@@ -5,6 +5,7 @@
 #include <binder/ProcessState.h>
 #include <log/log.h>
 #include <private/dvr/buffer_hub_binder.h>
+#include <private/dvr/buffer_node.h>
 
 namespace android {
 namespace dvr {
@@ -13,7 +14,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
@@ -45,8 +46,14 @@
             "Input arguments are ignored.\n");
   }
 
-  // TODO(b/116526156): output real data in this class once we have it
+  fprintf(out, "Binder service:\n");
+  // Active buffers
+  fprintf(out, "Active BufferClients: %zu\n", client_list_.size());
+  // TODO(b/117790952): print buffer information after BufferNode has it
+  // TODO(b/116526156): print more information once we have them
+
   if (pdx_service_) {
+    fprintf(out, "\nPDX service:\n");
     // BufferHubService::Dumpstate(size_t) is not actually using the param
     // So just using 0 as the length
     fprintf(out, "%s", pdx_service_->DumpState(0).c_str());
@@ -58,23 +65,17 @@
   return NO_ERROR;
 }
 
-sp<IBufferHub> BufferHubBinderService::getServiceProxy() {
-  sp<IServiceManager> sm = defaultServiceManager();
-  sp<IBinder> service = sm->checkService(String16(getServiceName()));
+sp<IBufferClient> BufferHubBinderService::createBuffer(
+    uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
+    uint64_t usage, uint64_t user_metadata_size) {
+  std::shared_ptr<BufferNode> node = std::make_shared<BufferNode>(
+      width, height, layer_count, format, usage, user_metadata_size);
 
-  if (service == nullptr) {
-    ALOGE("getServiceProxy(): %s binder service not found!", getServiceName());
-    return nullptr;
-  }
+  sp<BufferClient> client = new BufferClient(node);
+  // Add it to list for bookkeeping and dumpsys.
+  client_list_.push_back(client);
 
-  sp<IBufferHub> ret = interface_cast<IBufferHub>(service);
-  if (ret == nullptr) {
-    ALOGE("getServiceProxy(): %s binder service type casting error!",
-          getServiceName());
-    return nullptr;
-  }
-
-  return ret;
+  return client;
 }
 
 }  // namespace dvr
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/IBufferHub.h b/services/vr/bufferhubd/include/private/dvr/IBufferHub.h
index 266ae88..bd5f9cf 100644
--- a/services/vr/bufferhubd/include/private/dvr/IBufferHub.h
+++ b/services/vr/bufferhubd/include/private/dvr/IBufferHub.h
@@ -3,6 +3,7 @@
 
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
+#include <private/dvr/IBufferClient.h>
 
 namespace android {
 namespace dvr {
@@ -10,6 +11,12 @@
 class IBufferHub : public IInterface {
  public:
   DECLARE_META_INTERFACE(BufferHub);
+
+  static const char* getServiceName() { return "bufferhubd"; }
+  virtual sp<IBufferClient> createBuffer(uint32_t width, uint32_t height,
+                                         uint32_t layer_count, uint32_t format,
+                                         uint64_t usage,
+                                         uint64_t user_metadata_size) = 0;
 };
 
 class BnBufferHub : public BnInterface<IBufferHub> {
@@ -18,13 +25,7 @@
                               uint32_t flags = 0);
 };
 
-class BpBufferHub : public BpInterface<IBufferHub> {
- public:
-  explicit BpBufferHub(const sp<IBinder>& impl)
-      : BpInterface<IBufferHub>(impl) {}
-};
-
 }  // namespace dvr
 }  // namespace android
 
-#endif
\ No newline at end of file
+#endif  // ANDROID_DVR_IBUFFERHUB_H
\ No newline at end of file
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_client.h b/services/vr/bufferhubd/include/private/dvr/buffer_client.h
new file mode 100644
index 0000000..20d51ee
--- /dev/null
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_client.h
@@ -0,0 +1,29 @@
+#ifndef ANDROID_DVR_BUFFERCLIENT_H
+#define ANDROID_DVR_BUFFERCLIENT_H
+
+#include <private/dvr/IBufferClient.h>
+#include <private/dvr/buffer_node.h>
+
+namespace android {
+namespace dvr {
+
+class BufferClient : public BnBufferClient {
+ public:
+  // Creates a server-side buffer client from an existing BufferNode. Note that
+  // this funciton takes ownership of the shared_ptr.
+  explicit BufferClient(std::shared_ptr<BufferNode> node)
+      : buffer_node_(std::move(node)){};
+
+  // Binder IPC functions
+  bool isValid() override {
+    return buffer_node_ ? buffer_node_->IsValid() : false;
+  };
+
+ private:
+  std::shared_ptr<BufferNode> buffer_node_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_IBUFFERCLIENT_H
\ No newline at end of file
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h
index e266ff8..9064d87 100644
--- a/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h
@@ -1,8 +1,11 @@
 #ifndef ANDROID_DVR_BUFFER_HUB_BINDER_H
 #define ANDROID_DVR_BUFFER_HUB_BINDER_H
 
+#include <vector>
+
 #include <binder/BinderService.h>
 #include <private/dvr/IBufferHub.h>
+#include <private/dvr/buffer_client.h>
 #include <private/dvr/buffer_hub.h>
 
 namespace android {
@@ -12,16 +15,20 @@
                                public BnBufferHub {
  public:
   static status_t start(const std::shared_ptr<BufferHubService>& pdx_service);
-  static const char* getServiceName() { return "bufferhubd"; }
   // Dump bufferhub related information to given fd (usually stdout)
   // usage: adb shell dumpsys bufferhubd
   virtual status_t dump(int fd, const Vector<String16>& args) override;
 
-  // Helper function to get the BpReference to this service
-  static sp<IBufferHub> getServiceProxy();
+  // Binder IPC functions
+  sp<IBufferClient> createBuffer(uint32_t width, uint32_t height,
+                                 uint32_t layer_count, uint32_t format,
+                                 uint64_t usage,
+                                 uint64_t user_metadata_size) override;
 
  private:
   std::shared_ptr<BufferHubService> pdx_service_;
+
+  std::vector<sp<BufferClient>> client_list_;
 };
 
 }  // 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_hub_binder_service-test.cpp b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
index 587e6db..7fa2226 100644
--- a/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
+++ b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
@@ -1,23 +1,41 @@
+#include <binder/IServiceManager.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <private/dvr/buffer_hub_binder.h>
+#include <private/dvr/IBufferClient.h>
+#include <private/dvr/IBufferHub.h>
+#include <ui/PixelFormat.h>
 
 namespace android {
 namespace dvr {
 
 namespace {
 
-using testing::Ne;
+using testing::NotNull;
+
+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;
 
 class BufferHubBinderServiceTest : public ::testing::Test {
-  // Add setup and teardown if necessary
+ protected:
+  void SetUp() override {
+    status_t ret = getService<IBufferHub>(
+        String16(IBufferHub::getServiceName()), &service);
+    ASSERT_EQ(ret, OK);
+    ASSERT_THAT(service, NotNull());
+  }
+
+  sp<IBufferHub> service;
 };
 
-TEST_F(BufferHubBinderServiceTest, TestInitialize) {
-  // Create a new service will kill the current one.
-  // So just check if Binder service is running
-  sp<IBufferHub> service = BufferHubBinderService::getServiceProxy();
-  EXPECT_THAT(service, Ne(nullptr));
+TEST_F(BufferHubBinderServiceTest, TestCreateBuffer) {
+  sp<IBufferClient> bufferClient = service->createBuffer(
+      kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
+  ASSERT_THAT(bufferClient, NotNull());
+  EXPECT_TRUE(bufferClient->isValid());
 }
 
 }  // namespace
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/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,