Merge "Add uid and pid information into recent sensor registeration log"
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index a61cb00..66beb6d 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -23,6 +23,7 @@
 
 #include <android-base/file.h>
 #include <utils/String16.h>
+#include <utils/String8.h>
 #include <utils/Vector.h>
 
 using namespace android;
@@ -95,7 +96,7 @@
     int i = 0;
     std::ostringstream actual_stream, expected_stream;
     for (String16 actual : arg) {
-        std::string actual_str = String16::std_string(actual);
+        std::string actual_str = String8(actual).c_str();
         std::string expected_str = expected[i];
         actual_stream << "'" << actual_str << "' ";
         expected_stream << "'" << expected_str << "' ";
diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp
index 6caf149..0349b0f 100644
--- a/cmds/installd/CacheItem.cpp
+++ b/cmds/installd/CacheItem.cpp
@@ -72,7 +72,7 @@
         FTS *fts;
         FTSENT *p;
         char *argv[] = { (char*) path.c_str(), nullptr };
-        if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+        if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
             PLOG(WARNING) << "Failed to fts_open " << path;
             return -1;
         }
diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp
index 9377836..fada699 100644
--- a/cmds/installd/CacheTracker.cpp
+++ b/cmds/installd/CacheTracker.cpp
@@ -83,7 +83,7 @@
     FTS *fts;
     FTSENT *p;
     char *argv[] = { (char*) path.c_str(), nullptr };
-    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
         PLOG(WARNING) << "Failed to fts_open " << path;
         return;
     }
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index fbb30db..754ba4c 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -203,14 +203,20 @@
 
     out << "installd is happy!" << endl;
 
-    out << endl << "Devices with quota support:" << endl;
-    for (const auto& n : mQuotaDevices) {
-        out << "    " << n.first << " = " << n.second << endl;
+    {
+        std::lock_guard<std::recursive_mutex> lock(mQuotaDevicesLock);
+        out << endl << "Devices with quota support:" << endl;
+        for (const auto& n : mQuotaDevices) {
+            out << "    " << n.first << " = " << n.second << endl;
+        }
     }
 
-    out << endl << "Per-UID cache quotas:" << endl;
-    for (const auto& n : mCacheQuotas) {
-        out << "    " << n.first << " = " << n.second << endl;
+    {
+        std::lock_guard<std::recursive_mutex> lock(mCacheQuotasLock);
+        out << endl << "Per-UID cache quotas:" << endl;
+        for (const auto& n : mCacheQuotas) {
+            out << "    " << n.first << " = " << n.second << endl;
+        }
     }
 
     out << endl;
@@ -426,12 +432,11 @@
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
-    const char* pkgname = packageName.c_str();
     binder::Status res = ok();
-    if (!clear_reference_profile(pkgname)) {
+    if (!clear_reference_profile(packageName)) {
         res = error("Failed to clear reference profile for " + packageName);
     }
-    if (!clear_current_profiles(pkgname)) {
+    if (!clear_current_profiles(packageName)) {
         res = error("Failed to clear current profiles for " + packageName);
     }
     return res;
@@ -479,7 +484,7 @@
             }
         }
         if (!only_cache) {
-            if (!clear_current_profile(pkgname, userId)) {
+            if (!clear_current_profile(packageName, userId)) {
                 res = error("Failed to clear current profile for " + packageName);
             }
         }
@@ -487,13 +492,13 @@
     return res;
 }
 
-static int destroy_app_reference_profile(const char *pkgname) {
+static int destroy_app_reference_profile(const std::string& pkgname) {
     return delete_dir_contents_and_dir(
         create_data_ref_profile_package_path(pkgname),
         /*ignore_if_missing*/ true);
 }
 
-static int destroy_app_current_profiles(const char *pkgname, userid_t userid) {
+static int destroy_app_current_profiles(const std::string& pkgname, userid_t userid) {
     return delete_dir_contents_and_dir(
         create_data_user_profile_package_path(userid, pkgname),
         /*ignore_if_missing*/ true);
@@ -504,15 +509,14 @@
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
-    const char* pkgname = packageName.c_str();
     binder::Status res = ok();
     std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
     for (auto user : users) {
-        if (destroy_app_current_profiles(pkgname, user) != 0) {
+        if (destroy_app_current_profiles(packageName, user) != 0) {
             res = error("Failed to destroy current profiles for " + packageName);
         }
     }
-    if (destroy_app_reference_profile(pkgname) != 0) {
+    if (destroy_app_reference_profile(packageName) != 0) {
         res = error("Failed to destroy reference profile for " + packageName);
     }
     return res;
@@ -540,11 +544,11 @@
         if (delete_dir_contents_and_dir(path) != 0) {
             res = error("Failed to delete " + path);
         }
-        destroy_app_current_profiles(pkgname, userId);
+        destroy_app_current_profiles(packageName, userId);
         // TODO(calin): If the package is still installed by other users it's probably
         // beneficial to keep the reference profile around.
         // Verify if it's ok to do that.
-        destroy_app_reference_profile(pkgname);
+        destroy_app_reference_profile(packageName);
     }
     return res;
 }
@@ -755,9 +759,6 @@
     CHECK_ARGUMENT_UUID(uuid);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
-    // TODO: remove this once framework is more robust
-    invalidateMounts();
-
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     auto data_path = create_data_path(uuid_);
     auto device = findQuotaDeviceForUuid(uuid);
@@ -789,7 +790,7 @@
                     (char*) create_data_user_de_path(uuid_, user).c_str(),
                     nullptr
             };
-            if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+            if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
                 return error("Failed to fts_open");
             }
             while ((p = fts_read(fts)) != NULL) {
@@ -802,7 +803,10 @@
                         auto tracker = std::shared_ptr<CacheTracker>(new CacheTracker(
                                 multiuser_get_user_id(uid), multiuser_get_app_id(uid), device));
                         tracker->addDataPath(p->fts_path);
-                        tracker->cacheQuota = mCacheQuotas[uid];
+                        {
+                            std::lock_guard<std::recursive_mutex> lock(mCacheQuotasLock);
+                            tracker->cacheQuota = mCacheQuotas[uid];
+                        }
                         if (tracker->cacheQuota == 0) {
                             LOG(WARNING) << "UID " << uid << " has no cache quota; assuming 64MB";
                             tracker->cacheQuota = 67108864;
@@ -1122,7 +1126,7 @@
     FTS *fts;
     FTSENT *p;
     char *argv[] = { (char*) path.c_str(), nullptr };
-    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
         PLOG(ERROR) << "Failed to fts_open " << path;
         return;
     }
@@ -1161,7 +1165,8 @@
     for (auto packageName : packageNames) {
         CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     }
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    // NOTE: Locking is relaxed on this method, since it's limited to
+    // read-only measurements without mutation.
 
     // When modifying this logic, always verify using tests:
     // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetAppSize
@@ -1276,7 +1281,8 @@
         std::vector<int64_t>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    // NOTE: Locking is relaxed on this method, since it's limited to
+    // read-only measurements without mutation.
 
     // When modifying this logic, always verify using tests:
     // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetUserSize
@@ -1422,7 +1428,8 @@
         int32_t userId, int32_t flags, std::vector<int64_t>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    // NOTE: Locking is relaxed on this method, since it's limited to
+    // read-only measurements without mutation.
 
     // When modifying this logic, always verify using tests:
     // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetExternalSize
@@ -1488,7 +1495,7 @@
         FTSENT *p;
         auto path = create_data_media_path(uuid_, userId);
         char *argv[] = { (char*) path.c_str(), nullptr };
-        if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+        if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
             return error("Failed to fts_open " + path);
         }
         while ((p = fts_read(fts)) != NULL) {
@@ -1497,7 +1504,7 @@
             switch (p->fts_info) {
             case FTS_F:
                 // Only categorize files not belonging to apps
-                if (p->fts_statp->st_gid < AID_APP_START) {
+                if (p->fts_parent->fts_number == 0) {
                     ext = strrchr(p->fts_name, '.');
                     if (ext != nullptr) {
                         switch (MatchExtension(++ext)) {
@@ -1509,6 +1516,11 @@
                 }
                 // Fall through to 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;
+                }
             case FTS_DEFAULT:
             case FTS_SL:
             case FTS_SLNONE:
@@ -1535,7 +1547,7 @@
         int32_t userId, int32_t appId, int64_t cacheQuota) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    std::lock_guard<std::recursive_mutex> lock(mCacheQuotasLock);
 
     int32_t uid = multiuser_get_uid(userId, appId);
     mCacheQuotas[uid] = cacheQuota;
@@ -1988,7 +2000,7 @@
 
 binder::Status InstalldNativeService::invalidateMounts() {
     ENFORCE_UID(AID_SYSTEM);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    std::lock_guard<std::recursive_mutex> lock(mQuotaDevicesLock);
 
     mQuotaDevices.clear();
 
@@ -2019,6 +2031,7 @@
 
 std::string InstalldNativeService::findQuotaDeviceForUuid(
         const std::unique_ptr<std::string>& uuid) {
+    std::lock_guard<std::recursive_mutex> lock(mQuotaDevicesLock);
     auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
     return mQuotaDevices[path];
 }
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index feb2219..7ad8687 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -117,6 +117,9 @@
 private:
     std::recursive_mutex mLock;
 
+    std::recursive_mutex mQuotaDevicesLock;
+    std::recursive_mutex mCacheQuotasLock;
+
     /* Map from mount point to underlying device node */
     std::unordered_map<std::string, std::string> mQuotaDevices;
     /* Map from UID to cache quota size */
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 0fb207b..0d5652f 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -45,10 +45,15 @@
 
 using android::base::StringPrintf;
 using android::base::EndsWith;
+using android::base::unique_fd;
 
 namespace android {
 namespace installd {
 
+static unique_fd invalid_unique_fd() {
+    return unique_fd(-1);
+}
+
 static const char* parse_null(const char* arg) {
     if (strcmp(arg, "!") == 0) {
         return nullptr;
@@ -58,7 +63,7 @@
 }
 
 static bool clear_profile(const std::string& profile) {
-    base::unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
+    unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
     if (ufd.get() < 0) {
         if (errno != ENOENT) {
             PLOG(WARNING) << "Could not open profile " << profile;
@@ -101,19 +106,19 @@
     return truncated;
 }
 
-bool clear_reference_profile(const char* pkgname) {
+bool clear_reference_profile(const std::string& pkgname) {
     std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
     std::string reference_profile = create_primary_profile(reference_profile_dir);
     return clear_profile(reference_profile);
 }
 
-bool clear_current_profile(const char* pkgname, userid_t user) {
+bool clear_current_profile(const std::string& pkgname, userid_t user) {
     std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
     std::string profile = create_primary_profile(profile_dir);
     return clear_profile(profile);
 }
 
-bool clear_current_profiles(const char* pkgname) {
+bool clear_current_profiles(const std::string& pkgname) {
     bool success = true;
     std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
     for (auto user : users) {
@@ -467,18 +472,10 @@
     }
 }
 
-static void close_all_fds(const std::vector<fd_t>& fds, const char* description) {
-    for (size_t i = 0; i < fds.size(); i++) {
-        if (close(fds[i]) != 0) {
-            PLOG(WARNING) << "Failed to close fd for " << description << " at index " << i;
-        }
-    }
-}
-
-static fd_t open_profile_dir(const std::string& profile_dir) {
-    fd_t profile_dir_fd = TEMP_FAILURE_RETRY(open(profile_dir.c_str(),
-            O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW));
-    if (profile_dir_fd < 0) {
+static unique_fd open_profile_dir(const std::string& profile_dir) {
+    unique_fd profile_dir_fd(TEMP_FAILURE_RETRY(open(profile_dir.c_str(),
+            O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW)));
+    if (profile_dir_fd.get() < 0) {
         // In a multi-user environment, these directories can be created at
         // different points and it's possible we'll attempt to open a profile
         // dir before it exists.
@@ -489,66 +486,61 @@
     return profile_dir_fd;
 }
 
-static fd_t open_primary_profile_file_from_dir(const std::string& profile_dir, mode_t open_mode) {
-    fd_t profile_dir_fd  = open_profile_dir(profile_dir);
-    if (profile_dir_fd < 0) {
-        return -1;
+static unique_fd open_primary_profile_file_from_dir(const std::string& profile_dir,
+        mode_t open_mode) {
+    unique_fd profile_dir_fd  = open_profile_dir(profile_dir);
+    if (profile_dir_fd.get() < 0) {
+        return invalid_unique_fd();
     }
 
-    fd_t profile_fd = -1;
     std::string profile_file = create_primary_profile(profile_dir);
-
-    profile_fd = TEMP_FAILURE_RETRY(open(profile_file.c_str(), open_mode | O_NOFOLLOW, 0600));
+    unique_fd profile_fd(TEMP_FAILURE_RETRY(open(profile_file.c_str(),
+            open_mode | O_NOFOLLOW, 0600)));
     if (profile_fd == -1) {
         // It's not an error if the profile file does not exist.
         if (errno != ENOENT) {
-            PLOG(ERROR) << "Failed to lstat profile_dir: " << profile_dir;
+            PLOG(ERROR) << "Failed to open profile : " << profile_file;
         }
     }
-    // TODO(calin): use AutoCloseFD instead of closing the fd manually.
-    if (close(profile_dir_fd) != 0) {
-        PLOG(WARNING) << "Could not close profile dir " << profile_dir;
-    }
     return profile_fd;
 }
 
-static fd_t open_primary_profile_file(userid_t user, const char* pkgname) {
+static unique_fd open_primary_profile_file(userid_t user, const std::string& pkgname) {
     std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
     return open_primary_profile_file_from_dir(profile_dir, O_RDONLY);
 }
 
-static fd_t open_reference_profile(uid_t uid, const char* pkgname, bool read_write) {
+static unique_fd open_reference_profile(uid_t uid, const std::string& pkgname, bool read_write) {
     std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
     int flags = read_write ? O_RDWR | O_CREAT : O_RDONLY;
-    fd_t fd = open_primary_profile_file_from_dir(reference_profile_dir, flags);
-    if (fd < 0) {
-        return -1;
+    unique_fd fd = open_primary_profile_file_from_dir(reference_profile_dir, flags);
+    if (fd.get() < 0) {
+        return invalid_unique_fd();
     }
     if (read_write) {
         // Fix the owner.
-        if (fchown(fd, uid, uid) < 0) {
-            close(fd);
-            return -1;
+        if (fchown(fd.get(), uid, uid) < 0) {
+            return invalid_unique_fd();
         }
     }
     return fd;
 }
 
-static void open_profile_files(uid_t uid, const char* pkgname,
-            /*out*/ std::vector<fd_t>* profiles_fd, /*out*/ fd_t* reference_profile_fd) {
+static void open_profile_files(uid_t uid, const std::string& pkgname,
+            /*out*/ std::vector<unique_fd>* profiles_fd, /*out*/ unique_fd* reference_profile_fd) {
     // Open the reference profile in read-write mode as profman might need to save the merge.
     *reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ true);
-    if (*reference_profile_fd < 0) {
+    if (reference_profile_fd->get() < 0) {
         // We can't access the reference profile file.
         return;
     }
 
     std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
     for (auto user : users) {
-        fd_t profile_fd = open_primary_profile_file(user, pkgname);
+        unique_fd profile_fd = open_primary_profile_file(user, pkgname);
         // Add to the lists only if both fds are valid.
-        if (profile_fd >= 0) {
-            profiles_fd->push_back(profile_fd);
+        if (profile_fd.get() >= 0) {
+            profiles_fd->push_back(std::move(profile_fd));
         }
     }
 }
@@ -580,18 +572,19 @@
 static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
 static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
 
-static void run_profman_merge(const std::vector<fd_t>& profiles_fd, fd_t reference_profile_fd) {
+static void run_profman_merge(const std::vector<unique_fd>& profiles_fd,
+        const unique_fd& reference_profile_fd) {
     static const size_t MAX_INT_LEN = 32;
     static const char* PROFMAN_BIN = "/system/bin/profman";
 
     std::vector<std::string> profile_args(profiles_fd.size());
     char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
     for (size_t k = 0; k < profiles_fd.size(); k++) {
-        sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k]);
+        sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k].get());
         profile_args[k].assign(profile_buf);
     }
     char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
-    sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd);
+    sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd.get());
 
     // program name, reference profile fd, the final NULL and the profile fds
     const char* argv[3 + profiles_fd.size()];
@@ -614,22 +607,16 @@
 // a re-compilation of the package.
 // If the return value is true all the current profiles would have been merged into
 // the reference profiles accessible with open_reference_profile().
-bool analyse_profiles(uid_t uid, const char* pkgname) {
-    std::vector<fd_t> profiles_fd;
-    fd_t reference_profile_fd = -1;
+bool analyse_profiles(uid_t uid, const std::string& pkgname) {
+    std::vector<unique_fd> profiles_fd;
+    unique_fd reference_profile_fd;
     open_profile_files(uid, pkgname, &profiles_fd, &reference_profile_fd);
-    if (profiles_fd.empty() || (reference_profile_fd == -1)) {
+    if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) {
         // Skip profile guided compilation because no profiles were found.
         // Or if the reference profile info couldn't be opened.
-        close_all_fds(profiles_fd, "profiles_fd");
-        if ((reference_profile_fd != - 1) && (close(reference_profile_fd) != 0)) {
-            PLOG(WARNING) << "Failed to close fd for reference profile";
-        }
         return false;
     }
 
-    ALOGV("PROFMAN (MERGE): --- BEGIN '%s' ---\n", pkgname);
-
     pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
@@ -681,10 +668,7 @@
                 break;
         }
     }
-    close_all_fds(profiles_fd, "profiles_fd");
-    if (close(reference_profile_fd) != 0) {
-        PLOG(WARNING) << "Failed to close fd for reference profile";
-    }
+
     if (should_clear_current_profiles) {
         clear_current_profiles(pkgname);
     }
@@ -694,28 +678,28 @@
     return need_to_compile;
 }
 
-static void run_profman_dump(const std::vector<fd_t>& profile_fds,
-                             fd_t reference_profile_fd,
+static void run_profman_dump(const std::vector<unique_fd>& profile_fds,
+                             const unique_fd& reference_profile_fd,
                              const std::vector<std::string>& dex_locations,
-                             const std::vector<fd_t>& apk_fds,
-                             fd_t output_fd) {
+                             const std::vector<unique_fd>& apk_fds,
+                             const unique_fd& output_fd) {
     std::vector<std::string> profman_args;
     static const char* PROFMAN_BIN = "/system/bin/profman";
     profman_args.push_back(PROFMAN_BIN);
     profman_args.push_back("--dump-only");
-    profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd));
+    profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd.get()));
     if (reference_profile_fd != -1) {
         profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d",
-                                            reference_profile_fd));
+                                            reference_profile_fd.get()));
     }
-    for (fd_t profile_fd : profile_fds) {
-        profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fd));
+    for (size_t i = 0; i < profile_fds.size(); i++) {
+        profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fds[i].get()));
     }
     for (const std::string& dex_location : dex_locations) {
         profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str()));
     }
-    for (fd_t apk_fd : apk_fds) {
-        profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fd));
+    for (size_t i = 0; i < apk_fds.size(); i++) {
+        profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fds[i].get()));
     }
     const char **argv = new const char*[profman_args.size() + 1];
     size_t i = 0;
@@ -740,40 +724,38 @@
     }
 }
 
-bool dump_profiles(int32_t uid, const char* pkgname, const char* code_paths) {
-    std::vector<fd_t> profile_fds;
-    fd_t reference_profile_fd = -1;
-    std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname);
-
-    ALOGV("PROFMAN (DUMP): --- BEGIN '%s' ---\n", pkgname);
+bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths) {
+    std::vector<unique_fd> profile_fds;
+    unique_fd reference_profile_fd;
+    std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname.c_str());
 
     open_profile_files(uid, pkgname, &profile_fds, &reference_profile_fd);
 
-    const bool has_reference_profile = (reference_profile_fd != -1);
+    const bool has_reference_profile = (reference_profile_fd.get() != -1);
     const bool has_profiles = !profile_fds.empty();
 
     if (!has_reference_profile && !has_profiles) {
-        ALOGE("profman dump: no profiles to dump for '%s'", pkgname);
+        LOG(ERROR)  << "profman dump: no profiles to dump for " << pkgname;
         return false;
     }
 
-    fd_t output_fd = open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644);
+    unique_fd output_fd(open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644));
     if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
         ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str());
         return false;
     }
     std::vector<std::string> code_full_paths = base::Split(code_paths, ";");
     std::vector<std::string> dex_locations;
-    std::vector<fd_t> apk_fds;
+    std::vector<unique_fd> apk_fds;
     for (const std::string& code_full_path : code_full_paths) {
         const char* full_path = code_full_path.c_str();
-        fd_t apk_fd = open(full_path, O_RDONLY | O_NOFOLLOW);
+        unique_fd apk_fd(open(full_path, O_RDONLY | O_NOFOLLOW));
         if (apk_fd == -1) {
             ALOGE("installd cannot open '%s'\n", full_path);
             return false;
         }
         dex_locations.push_back(get_location_from_path(full_path));
-        apk_fds.push_back(apk_fd);
+        apk_fds.push_back(std::move(apk_fd));
     }
 
     pid_t pid = fork();
@@ -785,11 +767,6 @@
         exit(68);   /* only get here on exec failure */
     }
     /* parent */
-    close_all_fds(apk_fds, "apk_fds");
-    close_all_fds(profile_fds, "profile_fds");
-    if (close(reference_profile_fd) != 0) {
-        PLOG(WARNING) << "Failed to close fd for reference profile";
-    }
     int return_code = wait_child(pid);
     if (!WIFEXITED(return_code)) {
         LOG(WARNING) << "profman failed for package " << pkgname << ": "
@@ -1057,17 +1034,17 @@
 
 // Creates the dexopt swap file if necessary and return its fd.
 // Returns -1 if there's no need for a swap or in case of errors.
-base::unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) {
+unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) {
     if (!ShouldUseSwapFileForDexopt()) {
-        return base::unique_fd();
+        return invalid_unique_fd();
     }
     // Make sure there really is enough space.
     char swap_file_name[PKG_PATH_MAX];
     strcpy(swap_file_name, out_oat_path);
     if (!add_extension_to_file_name(swap_file_name, ".swap")) {
-        return base::unique_fd();
+        return invalid_unique_fd();
     }
-    base::unique_fd swap_fd(open_output_file(
+    unique_fd swap_fd(open_output_file(
             swap_file_name, /*recreate*/true, /*permissions*/0600));
     if (swap_fd.get() < 0) {
         // Could not create swap file. Optimistically go on and hope that we can compile
@@ -1092,8 +1069,9 @@
     if (profile_guided && !is_secondary_dex && !is_public && (pkgname[0] != '*')) {
         // Open reference profile in read only mode as dex2oat does not get write permissions.
         const std::string pkgname_str(pkgname);
+        unique_fd profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ false);
         return Dex2oatFileWrapper(
-                open_reference_profile(uid, pkgname, /*read_write*/ false),
+                profile_fd.release(),
                 [pkgname_str]() {
                     clear_reference_profile(pkgname_str.c_str());
                 });
@@ -1105,8 +1083,8 @@
 // Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to
 // out_vdex_wrapper_fd. Returns true for success or false in case of errors.
 bool open_vdex_files(const char* apk_path, const char* out_oat_path, int dexopt_needed,
-        const char* instruction_set, bool is_public, int uid, bool is_secondary_dex,
-        Dex2oatFileWrapper* in_vdex_wrapper_fd,
+        const char* instruction_set, bool is_public, bool profile_guided,
+        int uid, bool is_secondary_dex, Dex2oatFileWrapper* in_vdex_wrapper_fd,
         Dex2oatFileWrapper* out_vdex_wrapper_fd) {
     CHECK(in_vdex_wrapper_fd != nullptr);
     CHECK(out_vdex_wrapper_fd != nullptr);
@@ -1116,7 +1094,9 @@
     int dexopt_action = abs(dexopt_needed);
     bool is_odex_location = dexopt_needed < 0;
     std::string in_vdex_path_str;
-    if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
+    // Disable passing an input vdex when the compilation is profile-guided. The dexlayout
+    // optimization in dex2oat is incompatible with it. b/35872504.
+    if (dexopt_action != DEX2OAT_FROM_SCRATCH && !profile_guided) {
         // Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
         const char* path = nullptr;
         if (is_odex_location) {
@@ -1135,7 +1115,7 @@
             return false;
         }
         if (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) {
-            // When we dex2oat because iof boot image change, we are going to update
+            // When we dex2oat because of boot image change, we are going to update
             // in-place the vdex file.
             in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
         } else {
@@ -1432,7 +1412,7 @@
     }
 
     // Open the input file.
-    base::unique_fd input_fd(open(dex_path, O_RDONLY, 0));
+    unique_fd input_fd(open(dex_path, O_RDONLY, 0));
     if (input_fd.get() < 0) {
         ALOGE("installd cannot open '%s' for input during dexopt\n", dex_path);
         return -1;
@@ -1449,13 +1429,13 @@
     // Open vdex files.
     Dex2oatFileWrapper in_vdex_fd;
     Dex2oatFileWrapper out_vdex_fd;
-    if (!open_vdex_files(dex_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid,
-            is_secondary_dex, &in_vdex_fd, &out_vdex_fd)) {
+    if (!open_vdex_files(dex_path, out_oat_path, dexopt_needed, instruction_set, is_public,
+            profile_guided, uid, is_secondary_dex, &in_vdex_fd, &out_vdex_fd)) {
         return -1;
     }
 
     // Create a swap file if necessary.
-    base::unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path);
+    unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path);
 
     // Create the app image file if needed.
     Dex2oatFileWrapper image_fd =
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 7bb6eee..df6d176 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -32,16 +32,14 @@
 static constexpr int DEX2OAT_FOR_RELOCATION      = 4;
 static constexpr int PATCHOAT_FOR_RELOCATION     = 5;
 
-typedef int fd_t;
-
-bool clear_reference_profile(const char* pkgname);
-bool clear_current_profile(const char* pkgname, userid_t user);
-bool clear_current_profiles(const char* pkgname);
+bool clear_reference_profile(const std::string& pkgname);
+bool clear_current_profile(const std::string& pkgname, userid_t user);
+bool clear_current_profiles(const std::string& pkgname);
 
 bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path);
 
-bool analyse_profiles(uid_t uid, const char* pkgname);
-bool dump_profiles(int32_t uid, const char* pkgname, const char* code_paths);
+bool analyse_profiles(uid_t uid, const std::string& pkgname);
+bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths);
 
 bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
 
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 93a1458..850d257 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -36,6 +36,8 @@
 #define TEST_SYSTEM_DIR1 "/system/app/"
 #define TEST_SYSTEM_DIR2 "/vendor/app/"
 
+#define TEST_PROFILE_DIR "/data/misc/profiles"
+
 #define REALLY_LONG_APP_NAME "com.example." \
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
@@ -78,6 +80,9 @@
 
         android_system_dirs.dirs[1].path = (char*) TEST_SYSTEM_DIR2;
         android_system_dirs.dirs[1].len = strlen(TEST_SYSTEM_DIR2);
+
+        android_profiles_dir.path = (char*) TEST_PROFILE_DIR;
+        android_profiles_dir.len = strlen(TEST_PROFILE_DIR);
     }
 
     virtual void TearDown() {
@@ -519,5 +524,31 @@
     EXPECT_EQ(false, is_valid_package_name("/com.evil"));
 }
 
+TEST_F(UtilsTest, CreateDataUserProfilePath) {
+    EXPECT_EQ("/data/misc/profiles/cur/0", create_data_user_profile_path(0));
+    EXPECT_EQ("/data/misc/profiles/cur/1", create_data_user_profile_path(1));
+}
+
+TEST_F(UtilsTest, CreateDataUserProfilePackagePath) {
+    EXPECT_EQ("/data/misc/profiles/cur/0/com.example",
+            create_data_user_profile_package_path(0, "com.example"));
+    EXPECT_EQ("/data/misc/profiles/cur/1/com.example",
+            create_data_user_profile_package_path(1, "com.example"));
+}
+
+TEST_F(UtilsTest, CreateDataRefProfilePath) {
+    EXPECT_EQ("/data/misc/profiles/ref", create_data_ref_profile_path());
+}
+
+TEST_F(UtilsTest, CreateDataRefProfilePackagePath) {
+    EXPECT_EQ("/data/misc/profiles/ref/com.example",
+        create_data_ref_profile_package_path("com.example"));
+}
+
+TEST_F(UtilsTest, CreatePrimaryProfile) {
+    EXPECT_EQ("/data/misc/profiles/ref/com.example/primary.prof",
+        create_primary_profile("/data/misc/profiles/ref/com.example"));
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index af7a7c6..5a08c32 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -221,18 +221,18 @@
     return StringPrintf("%s/cur/%u", android_profiles_dir.path, userid);
 }
 
-std::string create_data_user_profile_package_path(userid_t user, const char* package_name) {
-    check_package_name(package_name);
-    return StringPrintf("%s/%s",create_data_user_profile_path(user).c_str(), package_name);
+std::string create_data_user_profile_package_path(userid_t user, const std::string& package_name) {
+    check_package_name(package_name.c_str());
+    return StringPrintf("%s/%s",create_data_user_profile_path(user).c_str(), package_name.c_str());
 }
 
 std::string create_data_ref_profile_path() {
     return StringPrintf("%s/ref", android_profiles_dir.path);
 }
 
-std::string create_data_ref_profile_package_path(const char* package_name) {
-    check_package_name(package_name);
-    return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name);
+std::string create_data_ref_profile_package_path(const std::string& package_name) {
+    check_package_name(package_name.c_str());
+    return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name.c_str());
 }
 
 std::string create_data_dalvik_cache_path() {
@@ -284,7 +284,7 @@
     FTSENT *p;
     int64_t matchedSize = 0;
     char *argv[] = { (char*) path.c_str(), nullptr };
-    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
         if (errno != ENOENT) {
             PLOG(ERROR) << "Failed to fts_open " << path;
         }
@@ -1440,7 +1440,7 @@
     FTS *fts;
     FTSENT *p;
     char *argv[] = { (char*) path.c_str(), nullptr };
-    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
         PLOG(ERROR) << "Failed to fts_open " << path;
         return -1;
     }
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index d99b445..1c36a54 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -97,10 +97,10 @@
 std::string create_data_misc_legacy_path(userid_t userid);
 
 std::string create_data_user_profile_path(userid_t userid);
-std::string create_data_user_profile_package_path(userid_t user, const char* package_name);
+std::string create_data_user_profile_package_path(userid_t user, const std::string& package_name);
 
 std::string create_data_ref_profile_path();
-std::string create_data_ref_profile_package_path(const char* package_name);
+std::string create_data_ref_profile_package_path(const std::string& package_name);
 
 std::string create_data_dalvik_cache_path();
 
diff --git a/include/binder/HalToken.h b/include/binder/HalToken.h
deleted file mode 100644
index ce97c78..0000000
--- a/include/binder/HalToken.h
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2017 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_HALTOKEN_H
-#define ANDROID_HALTOKEN_H
-
-#include <binder/Parcel.h>
-#include <hidl/HidlSupport.h>
-
-/**
- * Hybrid Interfaces
- * =================
- *
- * A hybrid interface is a binder interface that
- * 1. is implemented both traditionally and as a wrapper around a hidl
- *    interface, and allows querying whether the underlying instance comes from
- *    a hidl interface or not; and
- * 2. allows efficient calls to a hidl interface (if the underlying instance
- *    comes from a hidl interface) by automatically creating the wrapper in the
- *    process that calls it.
- *
- * Terminology:
- * - `HalToken`: The type for a "token" of a hidl interface. This is defined to
- *   be compatible with `ITokenManager.hal`.
- * - `HInterface`: The base type for a hidl interface. Currently, it is defined
- *   as `::android::hidl::base::V1_0::IBase`.
- * - `HALINTERFACE`: The hidl interface that will be sent through binders.
- * - `INTERFACE`: The binder interface that will be the wrapper of
- *   `HALINTERFACE`. `INTERFACE` is supposed to be somewhat similar to
- *   `HALINTERFACE`.
- *
- * To demonstrate how this is done, here is an example. Suppose `INTERFACE` is
- * `IFoo` and `HALINTERFACE` is `HFoo`. The required steps are:
- * 1. Use DECLARE_HYBRID_META_INTERFACE instead of DECLARE_META_INTERFACE in the
- *    definition of `IFoo`. The usage is
- *        DECLARE_HYBRID_META_INTERFACE(IFoo, HFoo)
- *    inside the body of `IFoo`.
- * 2. Create a converter class that derives from
- *    `H2BConverter<HFoo, IFoo, BnFoo>`. Let us call this `H2BFoo`.
- * 3. Add the following constructor in `H2BFoo` that call the corresponding
- *    constructors in `H2BConverter`:
- *        H2BFoo(const sp<HalInterface>& base) : CBase(base) {}
- *    Note: `CBase = H2BConverter<HFoo, IFoo, BnFoo>` and `HalInterface = HFoo`
- *    are member typedefs of `H2BConverter<HFoo, IFoo, BnFoo>`, so the above
- *    line can be copied into `H2BFoo`.
- * 4. Implement `IFoo` in `H2BFoo` on top of `HFoo`. `H2BConverter` provides a
- *    protected `mBase` of type `sp<HFoo>` that can be used to access the `HFoo`
- *    instance. (There is also a public function named `getHalInterface()` that
- *    returns `mBase`.)
- * 5. Create a hardware proxy class that derives from
- *    `HpInterface<BpFoo, H2BFoo>`. Name this class `HpFoo`. (This name cannot
- *    deviate. See step 8 below.)
- * 6. Add the following constructor to `HpFoo`:
- *        HpFoo(const sp<IBinder>& base): PBase(base) {}
- *    Note: `PBase` a member typedef of `HpInterface<BpFoo, H2BFoo>` that is
- *    equal to `HpInterface<BpFoo, H2BFoo>` itself, so the above line can be
- *    copied verbatim into `HpFoo`.
- * 7. Delegate all functions in `HpFoo` that come from `IFoo` except
- *    `getHalInterface` to the protected member `mBase`,
- *    which is defined in `HpInterface<BpFoo, H2BFoo>` (hence in `HpFoo`) with
- *    type `IFoo`. (There is also a public function named `getBaseInterface()`
- *    that returns `mBase`.)
- * 8. Replace the existing `IMPLEMENT_META_INTERFACE` for INTERFACE by
- *    `IMPLEMENT_HYBRID_META_INTERFACE`. Note that this macro relies on the
- *    exact naming of `HpFoo`, where `Foo` comes from the interface name `IFoo`.
- *    An example usage is
- *        IMPLEMENT_HYBRID_META_INTERFACE(IFoo, HFoo, "example.interface.foo");
- *
- * `GETTOKEN` Template Argument
- * ============================
- *
- * Following the instructions above, `H2BConverter` and `HpInterface` would use
- * `transact()` to send over tokens, with `code` (the first argument of
- * `transact()`) equal to a 4-byte value of '_GTK'. If this value clashes with
- * other values already in use in the `Bp` class, it can be changed by supplying
- * the last optional template argument to `H2BConverter` and `HpInterface`.
- *
- */
-
-namespace android {
-
-typedef uint64_t HalToken;
-typedef ::android::hidl::base::V1_0::IBase HInterface;
-
-sp<HInterface> retrieveHalInterface(const HalToken& token);
-bool createHalToken(const sp<HInterface>& interface, HalToken* token);
-bool deleteHalToken(const HalToken& token);
-
-template <
-        typename HINTERFACE,
-        typename INTERFACE,
-        typename BNINTERFACE,
-        uint32_t GETTOKEN = '_GTK'>
-class H2BConverter : public BNINTERFACE {
-public:
-    typedef H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE, GETTOKEN> CBase; // Converter Base
-    typedef INTERFACE BaseInterface;
-    typedef HINTERFACE HalInterface;
-    static constexpr uint32_t GET_HAL_TOKEN = GETTOKEN;
-
-    H2BConverter(const sp<HalInterface>& base) : mBase(base) {}
-    virtual status_t onTransact(uint32_t code,
-            const Parcel& data, Parcel* reply, uint32_t flags = 0);
-    sp<HalInterface> getHalInterface() override { return mBase; }
-    HalInterface* getBaseInterface() { return mBase.get(); }
-
-protected:
-    sp<HalInterface> mBase;
-};
-
-template <
-        typename BPINTERFACE,
-        typename CONVERTER,
-        uint32_t GETTOKEN = '_GTK'>
-class HpInterface : public CONVERTER::BaseInterface {
-public:
-    typedef HpInterface<BPINTERFACE, CONVERTER, GETTOKEN> PBase; // Proxy Base
-    typedef typename CONVERTER::BaseInterface BaseInterface;
-    typedef typename CONVERTER::HalInterface HalInterface;
-    static constexpr uint32_t GET_HAL_TOKEN = GETTOKEN;
-
-    explicit HpInterface(const sp<IBinder>& impl);
-    sp<HalInterface> getHalInterface() override { return mHal; }
-    BaseInterface* getBaseInterface() { return mBase.get(); }
-
-protected:
-    sp<IBinder> mImpl;
-    sp<BaseInterface> mBase;
-    sp<HalInterface> mHal;
-    IBinder* onAsBinder() override { return mImpl.get(); }
-};
-
-// ----------------------------------------------------------------------
-
-#define DECLARE_HYBRID_META_INTERFACE(INTERFACE, HAL)                   \
-    static const ::android::String16 descriptor;                        \
-    static ::android::sp<I##INTERFACE> asInterface(                     \
-            const ::android::sp<::android::IBinder>& obj);              \
-    virtual const ::android::String16& getInterfaceDescriptor() const;  \
-    I##INTERFACE();                                                     \
-    virtual ~I##INTERFACE();                                            \
-    virtual sp<HAL> getHalInterface();                                  \
-
-
-#define IMPLEMENT_HYBRID_META_INTERFACE(INTERFACE, HAL, NAME)           \
-    const ::android::String16 I##INTERFACE::descriptor(NAME);           \
-    const ::android::String16&                                          \
-            I##INTERFACE::getInterfaceDescriptor() const {              \
-        return I##INTERFACE::descriptor;                                \
-    }                                                                   \
-    ::android::sp<I##INTERFACE> I##INTERFACE::asInterface(              \
-            const ::android::sp<::android::IBinder>& obj)               \
-    {                                                                   \
-        ::android::sp<I##INTERFACE> intr;                               \
-        if (obj != NULL) {                                              \
-            intr = static_cast<I##INTERFACE*>(                          \
-                obj->queryLocalInterface(                               \
-                        I##INTERFACE::descriptor).get());               \
-            if (intr == NULL) {                                         \
-                intr = new Hp##INTERFACE(obj);                          \
-            }                                                           \
-        }                                                               \
-        return intr;                                                    \
-    }                                                                   \
-    I##INTERFACE::I##INTERFACE() { }                                    \
-    I##INTERFACE::~I##INTERFACE() { }                                   \
-    sp<HAL> I##INTERFACE::getHalInterface() { return nullptr; }         \
-
-// ----------------------------------------------------------------------
-
-template <
-        typename HINTERFACE,
-        typename INTERFACE,
-        typename BNINTERFACE,
-        uint32_t GETTOKEN>
-status_t H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE, GETTOKEN>::
-        onTransact(
-        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-    if (code == GET_HAL_TOKEN) {
-        HalToken token;
-        bool result;
-        result = createHalToken(mBase, &token);
-        if (!result) {
-            ALOGE("H2BConverter: Failed to create HAL token.");
-        }
-        reply->writeBool(result);
-        reply->writeUint64(token);
-        return NO_ERROR;
-    }
-    return BNINTERFACE::onTransact(code, data, reply, flags);
-}
-
-template <typename BPINTERFACE, typename CONVERTER, uint32_t GETTOKEN>
-HpInterface<BPINTERFACE, CONVERTER, GETTOKEN>::HpInterface(
-        const sp<IBinder>& impl) : mImpl(impl) {
-    Parcel data, reply;
-    data.writeInterfaceToken(BaseInterface::getInterfaceDescriptor());
-    if (impl->transact(GET_HAL_TOKEN, data, &reply) == NO_ERROR) {
-        bool tokenCreated = reply.readBool();
-        HalToken token = reply.readUint64();
-        if (!tokenCreated) {
-            ALOGE("HpInterface: Sender failed to create HAL token.");
-            mBase = new BPINTERFACE(impl);
-        } else {
-            sp<HInterface> hInterface = retrieveHalInterface(token);
-            deleteHalToken(token);
-            if (hInterface != nullptr) {
-                mHal = static_cast<HalInterface*>(hInterface.get());
-                mBase = new CONVERTER(mHal);
-            } else {
-                ALOGE("HpInterface: Cannot retrieve HAL interface from token.");
-                mBase = new BPINTERFACE(impl);
-            }
-        }
-    } else {
-        mBase = new BPINTERFACE(impl);
-    }
-}
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_HALTOKEN_H
diff --git a/include/gui/BufferQueueDefs.h b/include/gui/BufferQueueDefs.h
index 83e9580..ffafb49 100644
--- a/include/gui/BufferQueueDefs.h
+++ b/include/gui/BufferQueueDefs.h
@@ -18,16 +18,12 @@
 #define ANDROID_GUI_BUFFERQUEUECOREDEFS_H
 
 #include <gui/BufferSlot.h>
+#include <ui/BufferQueueDefs.h>
 
 namespace android {
     class BufferQueueCore;
 
     namespace BufferQueueDefs {
-        // BufferQueue will keep track of at most this value of buffers.
-        // Attempts at runtime to increase the number of buffers past this
-        // will fail.
-        enum { NUM_BUFFER_SLOTS = 64 };
-
         typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
     } // namespace BufferQueueDefs
 } // namespace android
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 258cd2f..5810335 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -32,12 +32,17 @@
 
 #include <gui/FrameTimestamps.h>
 
+#include <hidl/HybridInterface.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
 class IProducerListener;
 class NativeHandle;
 class Surface;
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+        HGraphicBufferProducer;
 
 /*
  * This class defines the Binder IPC interface for the producer side of
@@ -56,7 +61,7 @@
 class IGraphicBufferProducer : public IInterface
 {
 public:
-    DECLARE_META_INTERFACE(GraphicBufferProducer)
+    DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer)
 
     enum {
         // A flag returned by dequeueBuffer when the client needs to call
diff --git a/include/gui/bufferqueue/1.0/B2HProducerListener.h b/include/gui/bufferqueue/1.0/B2HProducerListener.h
new file mode 100644
index 0000000..fa6c2d9
--- /dev/null
+++ b/include/gui/bufferqueue/1.0/B2HProducerListener.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016, 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_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/IBinder.h>
+#include <gui/IProducerListener.h>
+
+#include <android/hidl/base/1.0/IBase.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener
+        HProducerListener;
+
+typedef ::android::IProducerListener
+        BProducerListener;
+
+struct B2HProducerListener : public HProducerListener {
+    sp<BProducerListener> mBase;
+    B2HProducerListener(sp<BProducerListener> const& base);
+    Return<void> onBufferReleased() override;
+    Return<bool> needsReleaseNotify() override;
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H
+
diff --git a/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h b/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
new file mode 100644
index 0000000..93c452a
--- /dev/null
+++ b/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016, 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_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/Binder.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+
+#include <hidl/HybridInterface.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::media::V1_0::AnwBuffer;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+        HGraphicBufferProducer;
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener
+        HProducerListener;
+
+typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
+using ::android::BnGraphicBufferProducer;
+using ::android::IProducerListener;
+
+struct H2BGraphicBufferProducer : public ::android::H2BConverter<
+        HGraphicBufferProducer,
+        BGraphicBufferProducer,
+        BnGraphicBufferProducer> {
+    H2BGraphicBufferProducer(sp<HGraphicBufferProducer> const& base) : CBase(base) {}
+
+    status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
+    status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override;
+    status_t setAsyncMode(bool async) override;
+    status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
+            uint32_t h, ::android::PixelFormat format, uint32_t usage,
+            FrameEventHistoryDelta* outTimestamps) override;
+    status_t detachBuffer(int slot) override;
+    status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence)
+            override;
+    status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer)
+            override;
+    status_t queueBuffer(int slot,
+            const QueueBufferInput& input,
+            QueueBufferOutput* output) override;
+    status_t cancelBuffer(int slot, const sp<Fence>& fence) override;
+    int query(int what, int* value) override;
+    status_t connect(const sp<IProducerListener>& listener, int api,
+            bool producerControlledByApp, QueueBufferOutput* output) override;
+    status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api)
+            override;
+    status_t setSidebandStream(const sp<NativeHandle>& stream) override;
+    void allocateBuffers(uint32_t width, uint32_t height,
+            ::android::PixelFormat format, uint32_t usage) override;
+    status_t allowAllocation(bool allow) override;
+    status_t setGenerationNumber(uint32_t generationNumber) override;
+    String8 getConsumerName() const override;
+    status_t setSharedBufferMode(bool sharedBufferMode) override;
+    status_t setAutoRefresh(bool autoRefresh) override;
+    status_t setDequeueTimeout(nsecs_t timeout) override;
+    status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+          sp<Fence>* outFence, float outTransformMatrix[16]) override;
+    void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
+    status_t getUniqueId(uint64_t* outId) const override;
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace bufferqueue
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H
diff --git a/include/private/ui/RegionHelper.h b/include/private/ui/RegionHelper.h
index a22b2cb..a86c586 100644
--- a/include/private/ui/RegionHelper.h
+++ b/include/private/ui/RegionHelper.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H
 #define ANDROID_UI_PRIVATE_REGION_HELPER_H
 
+#include <limits>
 #include <stdint.h>
 #include <sys/types.h>
 
diff --git a/include/ui/BufferQueueDefs.h b/include/ui/BufferQueueDefs.h
new file mode 100644
index 0000000..56de181
--- /dev/null
+++ b/include/ui/BufferQueueDefs.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 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_UI_BUFFERQUEUEDEFS_H
+#define ANDROID_UI_BUFFERQUEUEDEFS_H
+
+namespace android {
+    namespace BufferQueueDefs {
+        // BufferQueue will keep track of at most this value of buffers.
+        // Attempts at runtime to increase the number of buffers past this
+        // will fail.
+        static constexpr int NUM_BUFFER_SLOTS = 64;
+    } // namespace BufferQueueDefs
+} // namespace android
+
+#endif
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 2cb44eb..93b8684 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -34,7 +34,6 @@
         "IResultReceiver.cpp",
         "IServiceManager.cpp",
         "IShellCallback.cpp",
-        "HalToken.cpp",
         "MemoryBase.cpp",
         "MemoryDealer.cpp",
         "MemoryHeapBase.cpp",
@@ -66,8 +65,6 @@
         "liblog",
         "libcutils",
         "libutils",
-        "libhidlbase",
-        "android.hidl.token@1.0",
     ],
     export_shared_lib_headers: [
         "libbase",
diff --git a/libs/binder/HalToken.cpp b/libs/binder/HalToken.cpp
deleted file mode 100644
index 6e71a52..0000000
--- a/libs/binder/HalToken.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2005 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 "HalToken"
-
-#include <utils/Log.h>
-#include <binder/HalToken.h>
-
-#include <android/hidl/token/1.0/ITokenManager.h>
-
-namespace android {
-
-using ::android::hidl::token::V1_0::ITokenManager;
-
-sp<ITokenManager> gTokenManager = nullptr;
-
-ITokenManager* getTokenManager() {
-    if (gTokenManager != nullptr) {
-        return gTokenManager.get();
-    }
-    gTokenManager = ITokenManager::getService();
-    if (gTokenManager == nullptr) {
-        ALOGE("Cannot retrieve TokenManager.");
-    }
-    return gTokenManager.get();
-}
-
-sp<HInterface> retrieveHalInterface(const HalToken& token) {
-    auto transaction = getTokenManager()->get(token);
-    if (!transaction.isOk()) {
-        ALOGE("getHalInterface: Cannot obtain interface from token.");
-        return nullptr;
-    }
-    return static_cast<sp<HInterface> >(transaction);
-}
-
-bool createHalToken(const sp<HInterface>& interface, HalToken* token) {
-    auto transaction = getTokenManager()->createToken(interface);
-    if (!transaction.isOk()) {
-        ALOGE("createHalToken: Cannot create token from interface.");
-        return false;
-    }
-    *token = static_cast<HalToken>(transaction);
-    return true;
-}
-
-bool deleteHalToken(const HalToken& token) {
-    auto transaction = getTokenManager()->unregister(token);
-    if (!transaction.isOk()) {
-        ALOGE("deleteHalToken: Cannot unregister hal token.");
-        return false;
-    }
-    return true;
-}
-
-}; // namespace android
-
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index a1b4abc..5f5fb91 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -41,6 +41,15 @@
         // We are aware of the risks inherent in comparing floats for equality
         "-Wno-float-equal",
 
+        // Pure abstract classes trigger this warning
+        "-Wno-weak-vtables",
+
+        // Allow four-character integer literals
+	"-Wno-four-char-constants",
+
+        // Allow documentation warnings
+        "-Wno-documentation",
+
         "-DDEBUG_ONLY_CODE=0",
     ],
 
@@ -88,6 +97,8 @@
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
         "view/Surface.cpp",
+        "bufferqueue/1.0/B2HProducerListener.cpp",
+        "bufferqueue/1.0/H2BGraphicBufferProducer.cpp"
     ],
 
     shared_libs: [
@@ -100,9 +111,18 @@
         "libutils",
         "libnativewindow",
         "liblog",
+        "libhidlbase",
+        "android.hidl.base@1.0",
+        "android.hidl.token@1.0-utils",
+        "android.hardware.graphics.bufferqueue@1.0",
     ],
 
-    export_shared_lib_headers: ["libbinder", "libui"],
+    export_shared_lib_headers: [
+        "libbinder",
+        "libui",
+        "android.hidl.token@1.0-utils",
+        "android.hardware.graphics.bufferqueue@1.0",
+    ],
 }
 
 subdirs = ["tests"]
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index abdf649..74117c8 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -30,9 +30,14 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IProducerListener.h>
 
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
+using ::android::hardware::graphics::bufferqueue::V1_0::utils::
+        H2BGraphicBufferProducer;
+
 enum {
     REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
     DEQUEUE_BUFFER,
@@ -485,7 +490,123 @@
 // translation unit (see clang warning -Wweak-vtables)
 BpGraphicBufferProducer::~BpGraphicBufferProducer() {}
 
-IMPLEMENT_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer");
+class HpGraphicBufferProducer : public HpInterface<
+        BpGraphicBufferProducer, H2BGraphicBufferProducer> {
+public:
+    HpGraphicBufferProducer(const sp<IBinder>& base) : PBase(base) {}
+
+    status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override {
+        return mBase->requestBuffer(slot, buf);
+    }
+
+    status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
+        return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+    }
+
+    status_t setAsyncMode(bool async) override {
+        return mBase->setAsyncMode(async);
+    }
+
+    status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
+            PixelFormat format, uint32_t usage,
+            FrameEventHistoryDelta* outTimestamps) override {
+        return mBase->dequeueBuffer(
+                slot, fence, w, h, format, usage, outTimestamps);
+    }
+
+    status_t detachBuffer(int slot) override {
+        return mBase->detachBuffer(slot);
+    }
+
+    status_t detachNextBuffer(
+            sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
+        return mBase->detachNextBuffer(outBuffer, outFence);
+    }
+
+    status_t attachBuffer(
+            int* outSlot, const sp<GraphicBuffer>& buffer) override {
+        return mBase->attachBuffer(outSlot, buffer);
+    }
+
+    status_t queueBuffer(
+            int slot,
+            const QueueBufferInput& input,
+            QueueBufferOutput* output) override {
+        return mBase->queueBuffer(slot, input, output);
+    }
+
+    status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
+        return mBase->cancelBuffer(slot, fence);
+    }
+
+    int query(int what, int* value) override {
+        return mBase->query(what, value);
+    }
+
+    status_t connect(
+            const sp<IProducerListener>& listener,
+            int api, bool producerControlledByApp,
+            QueueBufferOutput* output) override {
+        return mBase->connect(listener, api, producerControlledByApp, output);
+    }
+
+    status_t disconnect(
+            int api, DisconnectMode mode = DisconnectMode::Api) override {
+        return mBase->disconnect(api, mode);
+    }
+
+    status_t setSidebandStream(const sp<NativeHandle>& stream) override {
+        return mBase->setSidebandStream(stream);
+    }
+
+    void allocateBuffers(uint32_t width, uint32_t height,
+            PixelFormat format, uint32_t usage) override {
+        return mBase->allocateBuffers(width, height, format, usage);
+    }
+
+    status_t allowAllocation(bool allow) override {
+        return mBase->allowAllocation(allow);
+    }
+
+    status_t setGenerationNumber(uint32_t generationNumber) override {
+        return mBase->setGenerationNumber(generationNumber);
+    }
+
+    String8 getConsumerName() const override {
+        return mBase->getConsumerName();
+    }
+
+    status_t setSharedBufferMode(bool sharedBufferMode) override {
+        return mBase->setSharedBufferMode(sharedBufferMode);
+    }
+
+    status_t setAutoRefresh(bool autoRefresh) override {
+        return mBase->setAutoRefresh(autoRefresh);
+    }
+
+    status_t setDequeueTimeout(nsecs_t timeout) override {
+        return mBase->setDequeueTimeout(timeout);
+    }
+
+    status_t getLastQueuedBuffer(
+            sp<GraphicBuffer>* outBuffer,
+            sp<Fence>* outFence,
+            float outTransformMatrix[16]) override {
+        return mBase->getLastQueuedBuffer(
+                outBuffer, outFence, outTransformMatrix);
+    }
+
+    void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override {
+        return mBase->getFrameTimestamps(outDelta);
+    }
+
+    status_t getUniqueId(uint64_t* outId) const override {
+        return mBase->getUniqueId(outId);
+    }
+};
+
+IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer,
+        "android.gui.IGraphicBufferProducer");
 
 // ----------------------------------------------------------------------
 
diff --git a/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp
new file mode 100644
index 0000000..a5f28cd
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, 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 <gui/bufferqueue/1.0/B2HProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+// B2HProducerListener
+B2HProducerListener::B2HProducerListener(
+        sp<BProducerListener> const& base):
+    mBase(base) {
+}
+
+Return<void> B2HProducerListener::onBufferReleased() {
+    mBase->onBufferReleased();
+    return Void();
+}
+
+Return<bool> B2HProducerListener::needsReleaseNotify() {
+    return mBase->needsReleaseNotify();
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace bufferqueue
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
new file mode 100644
index 0000000..eafd296
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -0,0 +1,1234 @@
+/*
+ * Copyright 2017, 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 "H2BGraphicBufferProducer"
+
+#include <android-base/logging.h>
+
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <gui/bufferqueue/1.0/B2HProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using Status = HGraphicBufferProducer::Status;
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+typedef ::android::hardware::media::V1_0::Rect HRect;
+typedef ::android::hardware::media::V1_0::Region HRegion;
+
+// Conversion functions
+
+// native_handle_t helper functions.
+
+/**
+ * \brief Take an fd and create a native handle containing only the given fd.
+ * The created handle will need to be deleted manually with
+ * `native_handle_delete()`.
+ *
+ * \param[in] fd The source file descriptor (of type `int`).
+ * \return The create `native_handle_t*` that contains the given \p fd. If the
+ * supplied \p fd is negative, the created native handle will contain no file
+ * descriptors.
+ *
+ * If the native handle cannot be created, the return value will be
+ * `nullptr`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline native_handle_t* native_handle_create_from_fd(int fd) {
+    if (fd < 0) {
+        return native_handle_create(0, 0);
+    }
+    native_handle_t* nh = native_handle_create(1, 0);
+    if (nh == nullptr) {
+        return nullptr;
+    }
+    nh->data[0] = fd;
+    return nh;
+}
+
+/**
+ * \brief Extract a file descriptor from a native handle.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \param[in] index The index of the file descriptor in \p nh to read from. This
+ * input has the default value of `0`.
+ * \return The `index`-th file descriptor in \p nh. If \p nh does not have
+ * enough file descriptors, the returned value will be `-1`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) {
+    return ((nh == nullptr) || (nh->numFds == 0) ||
+            (nh->numFds <= index) || (index < 0)) ?
+            -1 : nh->data[index];
+}
+
+/**
+ * \brief Convert `Return<Status>` to `status_t`. This is for legacy binder
+ * calls.
+ *
+ * \param[in] t The source `Return<Status>`.
+ * \return The corresponding `status_t`.
+ *
+ * This function first check if \p t has a transport error. If it does, then the
+ * return value is the transport error code. Otherwise, the return value is
+ * converted from `Status` contained inside \p t.
+ *
+ * Note:
+ * - This `Status` is omx-specific. It is defined in `types.hal`.
+ * - The name of this function is not `convert`.
+ */
+// convert: Return<Status> -> status_t
+inline status_t toStatusT(Return<Status> const& t) {
+    return t.isOk() ? static_cast<status_t>(static_cast<Status>(t)) : UNKNOWN_ERROR;
+}
+
+/**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+inline status_t toStatusT(Return<void> const& t) {
+    return t.isOk() ? OK : UNKNOWN_ERROR;
+}
+
+/**
+ * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
+ *
+ * \param[out] t The wrapper of type `AnwBuffer`.
+ * \param[in] l The source `GraphicBuffer`.
+ */
+// wrap: GraphicBuffer -> AnwBuffer
+inline void wrapAs(AnwBuffer* t, GraphicBuffer const& l) {
+    t->attr.width = l.getWidth();
+    t->attr.height = l.getHeight();
+    t->attr.stride = l.getStride();
+    t->attr.format = static_cast<PixelFormat>(l.getPixelFormat());
+    t->attr.layerCount = l.getLayerCount();
+    t->attr.usage = l.getUsage();
+    t->attr.id = l.getId();
+    t->attr.generationNumber = l.getGenerationNumber();
+    t->nativeHandle = hidl_handle(l.handle);
+}
+
+/**
+ * \brief Convert `AnwBuffer` to `GraphicBuffer`.
+ *
+ * \param[out] l The destination `GraphicBuffer`.
+ * \param[in] t The source `AnwBuffer`.
+ *
+ * This function will duplicate all file descriptors in \p t.
+ */
+// convert: AnwBuffer -> GraphicBuffer
+// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten
+inline bool convertTo(GraphicBuffer* l, AnwBuffer const& t) {
+    native_handle_t* handle = t.nativeHandle == nullptr ?
+            nullptr : native_handle_clone(t.nativeHandle);
+
+    size_t const numInts = 12 +
+            static_cast<size_t>(handle ? handle->numInts : 0);
+    int32_t* ints = new int32_t[numInts];
+
+    size_t numFds = static_cast<size_t>(handle ? handle->numFds : 0);
+    int* fds = new int[numFds];
+
+    ints[0] = 'GBFR';
+    ints[1] = static_cast<int32_t>(t.attr.width);
+    ints[2] = static_cast<int32_t>(t.attr.height);
+    ints[3] = static_cast<int32_t>(t.attr.stride);
+    ints[4] = static_cast<int32_t>(t.attr.format);
+    ints[5] = static_cast<int32_t>(t.attr.layerCount);
+    ints[6] = static_cast<int32_t>(t.attr.usage);
+    ints[7] = static_cast<int32_t>(t.attr.id >> 32);
+    ints[8] = static_cast<int32_t>(t.attr.id & 0xFFFFFFFF);
+    ints[9] = static_cast<int32_t>(t.attr.generationNumber);
+    ints[10] = 0;
+    ints[11] = 0;
+    if (handle) {
+        ints[10] = static_cast<int32_t>(handle->numFds);
+        ints[11] = static_cast<int32_t>(handle->numInts);
+        int* intsStart = handle->data + handle->numFds;
+        std::copy(handle->data, intsStart, fds);
+        std::copy(intsStart, intsStart + handle->numInts, &ints[12]);
+    }
+
+    void const* constBuffer = static_cast<void const*>(ints);
+    size_t size = numInts * sizeof(int32_t);
+    int const* constFds = static_cast<int const*>(fds);
+    status_t status = l->unflatten(constBuffer, size, constFds, numFds);
+
+    delete [] fds;
+    delete [] ints;
+    native_handle_delete(handle);
+    return status == NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/ui/Fence.cpp
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return The required size of the flat buffer.
+ *
+ * The current version of this function always returns 4, which is the number of
+ * bytes required to store the number of file descriptors contained in the fd
+ * part of the flat buffer.
+ */
+inline size_t getFenceFlattenedSize(hidl_handle const& /* fence */) {
+    return 4;
+};
+
+/**
+ * \brief Return the number of file descriptors contained in a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return `0` if \p fence does not contain a valid file descriptor, or `1`
+ * otherwise.
+ */
+inline size_t getFenceFdCount(hidl_handle const& fence) {
+    return native_handle_read_fd(fence) == -1 ? 0 : 1;
+}
+
+/**
+ * \brief Unflatten `Fence` to `hidl_handle`.
+ *
+ * \param[out] fence The destination `hidl_handle`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will point to a newly created
+ * native handle, which needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+    if (size < 4) {
+        return NO_MEMORY;
+    }
+
+    uint32_t numFdsInHandle;
+    FlattenableUtils::read(buffer, size, numFdsInHandle);
+
+    if (numFdsInHandle > 1) {
+        return BAD_VALUE;
+    }
+
+    if (numFds < numFdsInHandle) {
+        return NO_MEMORY;
+    }
+
+    if (numFdsInHandle) {
+        *nh = native_handle_create_from_fd(*fds);
+        if (*nh == nullptr) {
+            return NO_MEMORY;
+        }
+        *fence = *nh;
+        ++fds;
+        --numFds;
+    } else {
+        *nh = nullptr;
+        *fence = hidl_handle();
+    }
+
+    return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `hidl_handle` as `Fence`.
+ *
+ * \param[in] fence The source `hidl_handle`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t flattenFence(hidl_handle const& fence,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (size < getFenceFlattenedSize(fence) ||
+            numFds < getFenceFdCount(fence)) {
+        return NO_MEMORY;
+    }
+    // Cast to uint32_t since the size of a size_t can vary between 32- and
+    // 64-bit processes
+    FlattenableUtils::write(buffer, size,
+            static_cast<uint32_t>(getFenceFdCount(fence)));
+    int fd = native_handle_read_fd(fence);
+    if (fd != -1) {
+        *fds = fd;
+        ++fds;
+        --numFds;
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Wrap `Fence` in `hidl_handle`.
+ *
+ * \param[out] t The wrapper of type `hidl_handle`.
+ * \param[out] nh The native handle pointed to by \p t.
+ * \param[in] l The source `Fence`.
+ *
+ * On success, \p nh will hold a newly created native handle, which must be
+ * deleted manually with `native_handle_delete()` afterwards.
+ */
+// wrap: Fence -> hidl_handle
+inline bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) {
+    size_t const baseSize = l.getFlattenedSize();
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = l.getFdCount();
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (unflattenFence(t, nh, constBuffer, size, constFds, numFds)
+            != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * \brief Convert `hidl_handle` to `Fence`.
+ *
+ * \param[out] l The destination `Fence`. `l` must not have been used
+ * (`l->isValid()` must return `false`) before this function is called.
+ * \param[in] t The source `hidl_handle`.
+ *
+ * If \p t contains a valid file descriptor, it will be duplicated.
+ */
+// convert: hidl_handle -> Fence
+inline bool convertTo(Fence* l, hidl_handle const& t) {
+    int fd = native_handle_read_fd(t);
+    if (fd != -1) {
+        fd = dup(fd);
+        if (fd == -1) {
+            return false;
+        }
+    }
+    native_handle_t* nh = native_handle_create_from_fd(fd);
+    if (nh == nullptr) {
+        if (fd != -1) {
+            close(fd);
+        }
+        return false;
+    }
+
+    size_t const baseSize = getFenceFlattenedSize(t);
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        native_handle_delete(nh);
+        return false;
+    }
+
+    size_t const baseNumFds = getFenceFdCount(t);
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        native_handle_delete(nh);
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) {
+        native_handle_delete(nh);
+        return false;
+    }
+    native_handle_delete(nh);
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+// Ref: frameworks/native/libs/ui/Region.cpp
+
+/**
+ * \brief Unflatten `HRegion`.
+ *
+ * \param[out] t The destination `HRegion`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t unflatten(HRegion* t, void const*& buffer, size_t& size) {
+    if (size < sizeof(uint32_t)) {
+        return NO_MEMORY;
+    }
+
+    uint32_t numRects = 0;
+    FlattenableUtils::read(buffer, size, numRects);
+    if (size < numRects * sizeof(HRect)) {
+        return NO_MEMORY;
+    }
+    if (numRects > (UINT32_MAX / sizeof(HRect))) {
+        return NO_MEMORY;
+    }
+
+    t->resize(numRects);
+    for (size_t r = 0; r < numRects; ++r) {
+        ::android::Rect rect(::android::Rect::EMPTY_RECT);
+        status_t status = rect.unflatten(buffer, size);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        FlattenableUtils::advance(buffer, size, sizeof(rect));
+        (*t)[r] = HRect{
+                static_cast<int32_t>(rect.left),
+                static_cast<int32_t>(rect.top),
+                static_cast<int32_t>(rect.right),
+                static_cast<int32_t>(rect.bottom)};
+    }
+    return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp:
+//      IGraphicBufferProducer::QueueBufferInput
+
+/**
+ * \brief Return a lower bound on the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+        HGraphicBufferProducer::QueueBufferInput const& /* t */) {
+    return sizeof(int64_t) + // timestamp
+            sizeof(int) + // isAutoTimestamp
+            sizeof(android_dataspace) + // dataSpace
+            sizeof(::android::Rect) + // crop
+            sizeof(int) + // scalingMode
+            sizeof(uint32_t) + // transform
+            sizeof(uint32_t) + // stickyTransform
+            sizeof(bool); // getFrameTimestamps
+}
+
+/**
+ * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflatten(
+        HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+    if (size < minFlattenedSize(*t)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, t->timestamp);
+    int lIsAutoTimestamp;
+    FlattenableUtils::read(buffer, size, lIsAutoTimestamp);
+    t->isAutoTimestamp = static_cast<int32_t>(lIsAutoTimestamp);
+    android_dataspace_t lDataSpace;
+    FlattenableUtils::read(buffer, size, lDataSpace);
+    t->dataSpace = static_cast<Dataspace>(lDataSpace);
+    ::android::Rect lCrop;
+    FlattenableUtils::read(buffer, size, lCrop);
+    t->crop = HRect{
+            static_cast<int32_t>(lCrop.left),
+            static_cast<int32_t>(lCrop.top),
+            static_cast<int32_t>(lCrop.right),
+            static_cast<int32_t>(lCrop.bottom)};
+    int lScalingMode;
+    FlattenableUtils::read(buffer, size, lScalingMode);
+    t->scalingMode = static_cast<int32_t>(lScalingMode);
+    FlattenableUtils::read(buffer, size, t->transform);
+    FlattenableUtils::read(buffer, size, t->stickyTransform);
+    FlattenableUtils::read(buffer, size, t->getFrameTimestamps);
+
+    status_t status = unflattenFence(&(t->fence), nh,
+            buffer, size, fds, numFds);
+    if (status != NO_ERROR) {
+        return status;
+    }
+    return unflatten(&(t->surfaceDamage), buffer, size);
+}
+
+/**
+ * \brief Wrap `IGraphicBufferProducer::QueueBufferInput` in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in] l The source `IGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If the return value is `true` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline bool wrapAs(
+        HGraphicBufferProducer::QueueBufferInput* t,
+        native_handle_t** nh,
+        BGraphicBufferProducer::QueueBufferInput const& l) {
+
+    size_t const baseSize = l.getFlattenedSize();
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = l.getFdCount();
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = baseFds.get();
+    size_t numFds = baseNumFds;
+    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+    constexpr size_t min = sizeof(t.state);
+    switch (t.state) {
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+            return min;
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+            return min + getFenceFlattenedSize(t.fence);
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+            return min + sizeof(
+                    ::android::FenceTime::Snapshot::signalTime);
+    }
+    return 0;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The number of file descriptors contained in \p snapshot.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+    return t.state ==
+            HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ?
+            getFenceFdCount(t.fence) : 0;
+}
+
+/**
+ * \brief Flatten `FenceTimeSnapshot`.
+ *
+ * \param[in] t The source `FenceTimeSnapshot`.
+ * \param[out] nh The cloned native handle, if necessary.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence` if `t.state ==
+ * FENCE`, in which case \p nh will be returned.
+ */
+inline status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
+        native_handle_t** nh,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (size < getFlattenedSize(t)) {
+        return NO_MEMORY;
+    }
+
+    *nh = nullptr;
+    switch (t.state) {
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::EMPTY);
+            return NO_ERROR;
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::FENCE);
+            *nh = t.fence.getNativeHandle() == nullptr ?
+                    nullptr : native_handle_clone(t.fence);
+            return flattenFence(hidl_handle(*nh), buffer, size, fds, numFds);
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::SIGNAL_TIME);
+            FlattenableUtils::write(buffer, size, t.signalTimeNs);
+            return NO_ERROR;
+    }
+    return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta
+
+/**
+ * \brief Return a lower bound on the size of the non-fd buffer required to
+ * flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+        HGraphicBufferProducer::FrameEventsDelta const& /* t */) {
+    return sizeof(uint64_t) + // mFrameNumber
+            sizeof(uint8_t) + // mIndex
+            sizeof(uint8_t) + // mAddPostCompositeCalled
+            sizeof(uint8_t) + // mAddRetireCalled
+            sizeof(uint8_t) + // mAddReleaseCalled
+            sizeof(nsecs_t) + // mPostedTime
+            sizeof(nsecs_t) + // mRequestedPresentTime
+            sizeof(nsecs_t) + // mLatchTime
+            sizeof(nsecs_t) + // mFirstRefreshStartTime
+            sizeof(nsecs_t); // mLastRefreshStartTime
+}
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FrameEventsDelta const& t) {
+    return minFlattenedSize(t) +
+            getFlattenedSize(t.gpuCompositionDoneFence) +
+            getFlattenedSize(t.displayPresentFence) +
+            getFlattenedSize(t.displayRetireFence) +
+            getFlattenedSize(t.releaseFence);
+};
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FrameEventsDelta const& t) {
+    return getFdCount(t.gpuCompositionDoneFence) +
+            getFdCount(t.displayPresentFence) +
+            getFdCount(t.displayRetireFence) +
+            getFdCount(t.releaseFence);
+};
+
+/**
+ * \brief Flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The source `FrameEventsDelta`.
+ * \param[out] nh The array of native handles that are cloned.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * On success, this function will duplicate file descriptors contained in \p t.
+ * The cloned native handles will be stored in \p nh. These native handles will
+ * need to be closed by the caller.
+ */
+// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp:
+//      FrameEventsDelta::flatten
+inline status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
+        std::vector<native_handle_t*>* nh,
+        void*& buffer, size_t& size, int*& fds, size_t numFds) {
+    // Check that t.index is within a valid range.
+    if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
+            || t.index > std::numeric_limits<uint8_t>::max()) {
+        return BAD_VALUE;
+    }
+
+    FlattenableUtils::write(buffer, size, t.frameNumber);
+
+    // These are static_cast to uint8_t for alignment.
+    FlattenableUtils::write(buffer, size, static_cast<uint8_t>(t.index));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addPostCompositeCalled));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addRetireCalled));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addReleaseCalled));
+
+    FlattenableUtils::write(buffer, size, t.postedTimeNs);
+    FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs);
+    FlattenableUtils::write(buffer, size, t.latchTimeNs);
+    FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs);
+    FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs);
+    FlattenableUtils::write(buffer, size, t.dequeueReadyTime);
+
+    // Fences
+    HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4];
+    tSnapshot[0] = &t.gpuCompositionDoneFence;
+    tSnapshot[1] = &t.displayPresentFence;
+    tSnapshot[2] = &t.displayRetireFence;
+    tSnapshot[3] = &t.releaseFence;
+    nh->resize(4);
+    for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
+        status_t status = flatten(
+                *(tSnapshot[snapshotIndex]),
+                &((*nh)[snapshotIndex]),
+                buffer, size, fds, numFds);
+        if (status != NO_ERROR) {
+            while (snapshotIndex > 0) {
+                --snapshotIndex;
+                native_handle_close((*nh)[snapshotIndex]);
+                native_handle_delete((*nh)[snapshotIndex]);
+                (*nh)[snapshotIndex] = nullptr;
+            }
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+    size_t size = 4 + // mDeltas.size()
+            sizeof(t.compositorTiming);
+    for (size_t i = 0; i < t.deltas.size(); ++i) {
+        size += getFlattenedSize(t.deltas[i]);
+    }
+    return size;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+    size_t numFds = 0;
+    for (size_t i = 0; i < t.deltas.size(); ++i) {
+        numFds += getFdCount(t.deltas[i]);
+    }
+    return numFds;
+}
+
+/**
+ * \brief Flatten `FrameEventHistoryDelta`.
+ *
+ * \param[in] t The source `FrameEventHistoryDelta`.
+ * \param[out] nh The array of arrays of cloned native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * On success, this function will duplicate file descriptors contained in \p t.
+ * The cloned native handles will be stored in \p nh. Before making the call, \p
+ * nh should have enough space to store `n` pointers to arrays of native
+ * handles, where `n` is the length of `t.deltas`, and each `nh[i]` should have
+ * enough space to store `4` native handles.
+ */
+inline status_t flatten(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t,
+        std::vector<std::vector<native_handle_t*> >* nh,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    if (size < getFlattenedSize(t)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, t.compositorTiming);
+
+    FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.deltas.size()));
+    nh->resize(t.deltas.size());
+    for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) {
+        status_t status = flatten(
+                t.deltas[deltaIndex], &((*nh)[deltaIndex]),
+                buffer, size, fds, numFds);
+        if (status != NO_ERROR) {
+            while (deltaIndex > 0) {
+                --deltaIndex;
+                for (size_t snapshotIndex = 0;
+                        snapshotIndex < 4; ++snapshotIndex) {
+                    native_handle_close((*nh)[deltaIndex][snapshotIndex]);
+                    native_handle_delete((*nh)[deltaIndex][snapshotIndex]);
+                    (*nh)[deltaIndex][snapshotIndex] = nullptr;
+                }
+            }
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
+ * `::android::FrameEventHistoryDelta`.
+ *
+ * \param[out] l The destination `::android::FrameEventHistoryDelta`.
+ * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+inline bool convertTo(
+        ::android::FrameEventHistoryDelta* l,
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+
+    size_t const baseSize = getFlattenedSize(t);
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = getFdCount(t);
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    std::vector<std::vector<native_handle_t*> > nhAA;
+    if (flatten(t, &nhAA, buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+        for (auto nhA : nhAA) {
+            for (auto nh : nhA) {
+                if (nh != nullptr) {
+                    native_handle_close(nh);
+                    native_handle_delete(nh);
+                }
+            }
+        }
+        return false;
+    }
+
+    for (auto nhA : nhAA) {
+        for (auto nh : nhA) {
+            if (nh != nullptr) {
+                native_handle_delete(nh);
+            }
+        }
+    }
+    return true;
+}
+
+// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp:
+//      IGraphicBufferProducer::QueueBufferOutput
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
+ * `IGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] l The destination `IGraphicBufferProducer::QueueBufferOutput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+// convert: HGraphicBufferProducer::QueueBufferOutput ->
+// IGraphicBufferProducer::QueueBufferOutput
+inline bool convertTo(
+        BGraphicBufferProducer::QueueBufferOutput* l,
+        HGraphicBufferProducer::QueueBufferOutput const& t) {
+    if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) {
+        return false;
+    }
+    l->width = t.width;
+    l->height = t.height;
+    l->transformHint = t.transformHint;
+    l->numPendingBuffers = t.numPendingBuffers;
+    l->nextFrameNumber = t.nextFrameNumber;
+    l->bufferReplaced = t.bufferReplaced;
+    return true;
+}
+
+/**
+ * \brief Convert `IGraphicBufferProducer::DisconnectMode` to
+ * `HGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `IGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `HGraphicBufferProducer::DisconnectMode`.
+ */
+inline HGraphicBufferProducer::DisconnectMode toHDisconnectMode(
+        BGraphicBufferProducer::DisconnectMode l) {
+    switch (l) {
+        case BGraphicBufferProducer::DisconnectMode::Api:
+            return HGraphicBufferProducer::DisconnectMode::API;
+        case BGraphicBufferProducer::DisconnectMode::AllLocal:
+            return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL;
+    }
+    return HGraphicBufferProducer::DisconnectMode::API;
+}
+
+// H2BGraphicBufferProducer
+
+status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+    *buf = new GraphicBuffer();
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->requestBuffer(
+            static_cast<int32_t>(slot),
+            [&fnStatus, &buf] (Status status, AnwBuffer const& buffer) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(buf->get(), buffer)) {
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::setMaxDequeuedBufferCount(
+        int maxDequeuedBuffers) {
+    return toStatusT(mBase->setMaxDequeuedBufferCount(
+            static_cast<int32_t>(maxDequeuedBuffers)));
+}
+
+status_t H2BGraphicBufferProducer::setAsyncMode(bool async) {
+    return toStatusT(mBase->setAsyncMode(async));
+}
+
+status_t H2BGraphicBufferProducer::dequeueBuffer(
+        int* slot, sp<Fence>* fence,
+        uint32_t w, uint32_t h, ::android::PixelFormat format,
+        uint32_t usage, FrameEventHistoryDelta* outTimestamps) {
+    *fence = new Fence();
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->dequeueBuffer(
+            w, h, static_cast<PixelFormat>(format), usage,
+            outTimestamps != nullptr,
+            [&fnStatus, slot, fence, outTimestamps] (
+                    Status status,
+                    int32_t tSlot,
+                    hidl_handle const& tFence,
+                    HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) {
+                fnStatus = toStatusT(status);
+                *slot = tSlot;
+                if (!convertTo(fence->get(), tFence)) {
+                    ALOGE("H2BGraphicBufferProducer::dequeueBuffer - "
+                            "Invalid output fence");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                if (outTimestamps && !convertTo(outTimestamps, tTs)) {
+                    ALOGE("H2BGraphicBufferProducer::dequeueBuffer - "
+                            "Invalid output timestamps");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::detachBuffer(int slot) {
+    return toStatusT(mBase->detachBuffer(static_cast<int>(slot)));
+}
+
+status_t H2BGraphicBufferProducer::detachNextBuffer(
+        sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+    *outBuffer = new GraphicBuffer();
+    *outFence = new Fence();
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->detachNextBuffer(
+            [&fnStatus, outBuffer, outFence] (
+                    Status status,
+                    AnwBuffer const& tBuffer,
+                    hidl_handle const& tFence) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(outFence->get(), tFence)) {
+                    ALOGE("H2BGraphicBufferProducer::detachNextBuffer - "
+                            "Invalid output fence");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                if (!convertTo(outBuffer->get(), tBuffer)) {
+                    ALOGE("H2BGraphicBufferProducer::detachNextBuffer - "
+                            "Invalid output buffer");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::attachBuffer(
+        int* outSlot, const sp<GraphicBuffer>& buffer) {
+    AnwBuffer tBuffer;
+    wrapAs(&tBuffer, *buffer);
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->attachBuffer(tBuffer,
+            [&fnStatus, outSlot] (Status status, int32_t slot) {
+                fnStatus = toStatusT(status);
+                *outSlot = slot;
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::queueBuffer(
+        int slot,
+        const QueueBufferInput& input,
+        QueueBufferOutput* output) {
+    HGraphicBufferProducer::QueueBufferInput tInput;
+    native_handle_t* nh;
+    if (!wrapAs(&tInput, &nh, input)) {
+        ALOGE("H2BGraphicBufferProducer::queueBuffer - "
+                "Invalid input");
+        return BAD_VALUE;
+    }
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->queueBuffer(slot, tInput,
+            [&fnStatus, output] (
+                    Status status,
+                    HGraphicBufferProducer::QueueBufferOutput const& tOutput) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(output, tOutput)) {
+                    ALOGE("H2BGraphicBufferProducer::queueBuffer - "
+                            "Invalid output");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    native_handle_delete(nh);
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
+    hidl_handle tFence;
+    native_handle_t* nh = nullptr;
+    if ((fence == nullptr) || !wrapAs(&tFence, &nh, *fence)) {
+        ALOGE("H2BGraphicBufferProducer::cancelBuffer - "
+                "Invalid input fence");
+        return BAD_VALUE;
+    }
+
+    status_t status = toStatusT(mBase->cancelBuffer(
+            static_cast<int32_t>(slot), tFence));
+    native_handle_delete(nh);
+    return status;
+}
+
+int H2BGraphicBufferProducer::query(int what, int* value) {
+    int result;
+    status_t transStatus = toStatusT(mBase->query(
+            static_cast<int32_t>(what),
+            [&result, value] (int32_t tResult, int32_t tValue) {
+                result = static_cast<int>(tResult);
+                *value = static_cast<int>(tValue);
+            }));
+    return transStatus == NO_ERROR ? result : static_cast<int>(transStatus);
+}
+
+status_t H2BGraphicBufferProducer::connect(
+        const sp<IProducerListener>& listener, int api,
+        bool producerControlledByApp, QueueBufferOutput* output) {
+    sp<HProducerListener> tListener = listener == nullptr ?
+            nullptr : new B2HProducerListener(listener);
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->connect(
+            tListener, static_cast<int32_t>(api), producerControlledByApp,
+            [&fnStatus, output] (
+                    Status status,
+                    HGraphicBufferProducer::QueueBufferOutput const& tOutput) {
+                fnStatus = toStatusT(status);
+                if (!convertTo(output, tOutput)) {
+                    ALOGE("H2BGraphicBufferProducer::connect - "
+                            "Invalid output");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::disconnect(int api, DisconnectMode mode) {
+    return toStatusT(mBase->disconnect(
+            static_cast<int32_t>(api), toHDisconnectMode(mode)));
+}
+
+status_t H2BGraphicBufferProducer::setSidebandStream(
+        const sp<NativeHandle>& stream) {
+    return toStatusT(mBase->setSidebandStream(stream->handle()));
+}
+
+void H2BGraphicBufferProducer::allocateBuffers(uint32_t width, uint32_t height,
+        ::android::PixelFormat format, uint32_t usage) {
+    mBase->allocateBuffers(
+            width, height, static_cast<PixelFormat>(format), usage);
+}
+
+status_t H2BGraphicBufferProducer::allowAllocation(bool allow) {
+    return toStatusT(mBase->allowAllocation(allow));
+}
+
+status_t H2BGraphicBufferProducer::setGenerationNumber(uint32_t generationNumber) {
+    return toStatusT(mBase->setGenerationNumber(generationNumber));
+}
+
+String8 H2BGraphicBufferProducer::getConsumerName() const {
+    String8 lName;
+    mBase->getConsumerName([&lName] (hidl_string const& name) {
+                lName = name.c_str();
+            });
+    return lName;
+}
+
+status_t H2BGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) {
+    return toStatusT(mBase->setSharedBufferMode(sharedBufferMode));
+}
+
+status_t H2BGraphicBufferProducer::setAutoRefresh(bool autoRefresh) {
+    return toStatusT(mBase->setAutoRefresh(autoRefresh));
+}
+
+status_t H2BGraphicBufferProducer::setDequeueTimeout(nsecs_t timeout) {
+    return toStatusT(mBase->setDequeueTimeout(static_cast<int64_t>(timeout)));
+}
+
+status_t H2BGraphicBufferProducer::getLastQueuedBuffer(
+        sp<GraphicBuffer>* outBuffer,
+        sp<Fence>* outFence,
+        float outTransformMatrix[16]) {
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->getLastQueuedBuffer(
+            [&fnStatus, outBuffer, outFence, &outTransformMatrix] (
+                    Status status,
+                    AnwBuffer const& buffer,
+                    hidl_handle const& fence,
+                    hidl_array<float, 16> const& transformMatrix) {
+                fnStatus = toStatusT(status);
+                *outBuffer = new GraphicBuffer();
+                if (!convertTo(outBuffer->get(), buffer)) {
+                    ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
+                            "Invalid output buffer");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                *outFence = new Fence();
+                if (!convertTo(outFence->get(), fence)) {
+                    ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
+                            "Invalid output fence");
+                    fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+                }
+                std::copy(transformMatrix.data(),
+                        transformMatrix.data() + 16,
+                        outTransformMatrix);
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+void H2BGraphicBufferProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+    mBase->getFrameTimestamps([outDelta] (
+            HGraphicBufferProducer::FrameEventHistoryDelta const& tDelta) {
+                convertTo(outDelta, tDelta);
+            });
+}
+
+status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const {
+    status_t fnStatus;
+    status_t transStatus = toStatusT(mBase->getUniqueId(
+            [&fnStatus, outId] (Status status, uint64_t id) {
+                fnStatus = toStatusT(status);
+                *outId = id;
+            }));
+    return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace bufferqueue
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
index 3ca8bd1..ef1e45f 100644
--- a/libs/math/include/math/half.h
+++ b/libs/math/include/math/half.h
@@ -21,6 +21,7 @@
 #include <limits>
 #include <type_traits>
 
+#ifndef LIKELY
 #ifdef __cplusplus
 #   define LIKELY( exp )    (__builtin_expect( !!(exp), true ))
 #   define UNLIKELY( exp )  (__builtin_expect( !!(exp), false ))
@@ -28,6 +29,7 @@
 #   define LIKELY( exp )    (__builtin_expect( !!(exp), 1 ))
 #   define UNLIKELY( exp )  (__builtin_expect( !!(exp), 0 ))
 #endif
+#endif
 
 #if __cplusplus >= 201402L
 #define CONSTEXPR constexpr
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 3fbab17..2d9fc93 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -428,6 +428,14 @@
     return reinterpret_cast<GraphicBuffer*>(buffer);
 }
 
+const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer) {
+    return AHardwareBuffer_to_GraphicBuffer(buffer)->getNativeBuffer();
+}
+
+ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(AHardwareBuffer* buffer) {
+    return AHardwareBuffer_to_GraphicBuffer(buffer)->getNativeBuffer();
+}
+
 AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer) {
     return reinterpret_cast<AHardwareBuffer*>(buffer);
 }
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 34c136a..a85d16e 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -69,3 +69,20 @@
 int32_t ANativeWindow_unlockAndPost(ANativeWindow* window) {
     return window->perform(window, NATIVE_WINDOW_UNLOCK_AND_POST);
 }
+
+int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform) {
+    static_assert(ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL == NATIVE_WINDOW_TRANSFORM_FLIP_H);
+    static_assert(ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL == NATIVE_WINDOW_TRANSFORM_FLIP_V);
+    static_assert(ANATIVEWINDOW_TRANSFORM_ROTATE_90 == NATIVE_WINDOW_TRANSFORM_ROT_90);
+
+    constexpr int32_t kAllTransformBits =
+            ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL |
+            ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL |
+            ANATIVEWINDOW_TRANSFORM_ROTATE_90;
+    if (!window || !getWindowProp(window, NATIVE_WINDOW_IS_VALID))
+        return -EINVAL;
+    if ((transform & ~kAllTransformBits) != 0)
+        return -EINVAL;
+
+    return native_window_set_buffers_transform(window, transform);
+}
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 9c5f096..2668927 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -34,6 +34,10 @@
 
     clang: true,
 
+    cppflags: [
+        "-std=c++1z"
+    ],
+
     srcs: [
         "AHardwareBuffer.cpp",
         "ANativeWindow.cpp",
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 6a46d7f..6cd5df3 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -50,6 +50,25 @@
     WINDOW_FORMAT_RGB_565            = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
 };
 
+/**
+ * Transforms that can be applied to buffers as they are displayed to a window.
+ *
+ * Supported transforms are any combination of horizontal mirror, vertical
+ * mirror, and clockwise 90 degree rotation, in that order. Rotations of 180
+ * and 270 degrees are made up of those basic transforms.
+ */
+enum ANativeWindowTransform {
+    ANATIVEWINDOW_TRANSFORM_IDENTITY            = 0x00,
+    ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL   = 0x01,
+    ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL     = 0x02,
+    ANATIVEWINDOW_TRANSFORM_ROTATE_90           = 0x04,
+
+    ANATIVEWINDOW_TRANSFORM_ROTATE_180          = ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL |
+                                                  ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL,
+    ANATIVEWINDOW_TRANSFORM_ROTATE_270          = ANATIVEWINDOW_TRANSFORM_ROTATE_180 |
+                                                  ANATIVEWINDOW_TRANSFORM_ROTATE_90,
+};
+
 struct ANativeWindow;
 /**
  * {@link ANativeWindow} is opaque type that provides access to a native window.
@@ -147,6 +166,19 @@
  */
 int32_t ANativeWindow_unlockAndPost(ANativeWindow* window);
 
+#if __ANDROID_API__ >= __ANDROID_API_O__
+
+/**
+ * Set a transform that will be applied to future buffers posted to the window.
+ *
+ * @param transform combination of {@link ANativeWindowTransform} flags
+ * @return 0 if successful
+ * @return -EINVAL if @param transform is invalid
+ */
+int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform);
+
+#endif // __ANDROID_API__ >= __ANDROID_API_O__
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h
index 612ee80..ee5da84 100644
--- a/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h
@@ -28,6 +28,7 @@
 #include <stdint.h>
 
 struct AHardwareBuffer;
+struct ANativeWindowBuffer;
 
 namespace android {
 
@@ -44,8 +45,11 @@
 class GraphicBuffer;
 const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer);
 GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer);
-AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer);
 
+const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer);
+ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(AHardwareBuffer* buffer);
+
+AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer);
 } // namespace android
 
 #endif // ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index dcfabac..b29c888 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -20,6 +20,7 @@
     ANativeWindow_lock;
     ANativeWindow_release;
     ANativeWindow_setBuffersGeometry;
+    ANativeWindow_setBuffersTransform;
     ANativeWindow_unlockAndPost;
   local:
     *;
diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp
index 6a86bb5..49390d9 100644
--- a/libs/ui/ColorSpace.cpp
+++ b/libs/ui/ColorSpace.cpp
@@ -211,8 +211,8 @@
         "Display P3",
         {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
         {0.3127f, 0.3290f},
-        std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
-        std::bind(response,    _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f)
+        std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f),
+        std::bind(response,    _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f)
     };
 }
 
diff --git a/libs/vr/libbufferhubqueue/Android.mk b/libs/vr/libbufferhubqueue/Android.mk
index 3ed7ff2..d53f06a 100644
--- a/libs/vr/libbufferhubqueue/Android.mk
+++ b/libs/vr/libbufferhubqueue/Android.mk
@@ -36,10 +36,12 @@
 	liblog \
 	libui \
 	libutils \
+        libgui \
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(sourceFiles)
 LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"libbufferhubqueue\"
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
 LOCAL_STATIC_LIBRARIES := $(staticLibraries)
 LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index 0576b21..bad9503 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -1,5 +1,7 @@
 #include "include/private/dvr/buffer_hub_queue_client.h"
 
+//#define LOG_NDEBUG 0
+
 #include <inttypes.h>
 #include <log/log.h>
 #include <sys/epoll.h>
@@ -24,6 +26,7 @@
       meta_size_(meta_size),
       meta_buffer_tmp_(meta_size ? new uint8_t[meta_size] : nullptr),
       buffers_(BufferHubQueue::kMaxQueueCapacity),
+      epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false),
       available_buffers_(BufferHubQueue::kMaxQueueCapacity),
       capacity_(0) {
   Initialize();
@@ -36,6 +39,7 @@
       meta_size_(meta_size),
       meta_buffer_tmp_(meta_size ? new uint8_t[meta_size] : nullptr),
       buffers_(BufferHubQueue::kMaxQueueCapacity),
+      epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false),
       available_buffers_(BufferHubQueue::kMaxQueueCapacity),
       capacity_(0) {
   Initialize();
@@ -101,31 +105,12 @@
 
       ALOGD("New BufferHubQueue event %d: index=%" PRId64, i, index);
 
-      if (is_buffer_event_index(index) && (events[i].events & EPOLLIN)) {
-        auto buffer = buffers_[index];
-        ret = OnBufferReady(buffer);
-        if (ret < 0) {
-          ALOGE("Failed to set buffer ready: %s", strerror(-ret));
-          continue;
-        }
-        Enqueue(buffer, index);
-      } else if (is_buffer_event_index(index) &&
-                 (events[i].events & EPOLLHUP)) {
-        // This maybe caused by producer replacing an exising buffer slot.
-        // Currently the epoll FD is cleaned up when the replacement consumer
-        // client is imported.
-        ALOGW("Receives EPOLLHUP at slot: %" PRId64, index);
-      } else if (is_queue_event_index(index) && (events[i].events & EPOLLIN)) {
-        // Note that after buffer imports, if |count()| still returns 0, epoll
-        // wait will be tried again to acquire the newly imported buffer.
-        ret = OnBufferAllocated();
-        if (ret < 0) {
-          ALOGE("Failed to import buffer: %s", strerror(-ret));
-          continue;
-        }
+      if (is_buffer_event_index(index)) {
+        HandleBufferEvent(static_cast<size_t>(index), events[i]);
+      } else if (is_queue_event_index(index)) {
+        HandleQueueEvent(events[i]);
       } else {
-        ALOGW("Unknown event %d: u64=%" PRId64 ": events=%" PRIu32, i, index,
-              events[i].events);
+        ALOGW("Unknown event index: %" PRId64, index);
       }
     }
   }
@@ -133,6 +118,68 @@
   return true;
 }
 
+void BufferHubQueue::HandleBufferEvent(size_t slot, const epoll_event& event) {
+  auto buffer = buffers_[slot];
+  if (!buffer) {
+    ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot);
+    return;
+  }
+
+  auto status = buffer->GetEventMask(event.events);
+  if (!status) {
+    ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s",
+          status.GetErrorMessage().c_str());
+    return;
+  }
+
+  int events = status.get();
+  if (events & EPOLLIN) {
+    int ret = OnBufferReady(buffer);
+    if (ret < 0) {
+      ALOGE("Failed to set buffer ready: %s", strerror(-ret));
+      return;
+    }
+    Enqueue(buffer, slot);
+  } else if (events & EPOLLHUP) {
+    // This might be caused by producer replacing an existing buffer slot, or
+    // when BufferHubQueue is shutting down. For the first case, currently the
+    // epoll FD is cleaned up when the replacement consumer client is imported,
+    // we shouldn't detach again if |epollhub_pending_[slot]| is set.
+    ALOGW(
+        "Receives EPOLLHUP at slot: %zu, buffer event fd: %d, EPOLLHUP "
+        "pending: %d",
+        slot, buffer->event_fd(), epollhup_pending_[slot]);
+    if (epollhup_pending_[slot]) {
+      epollhup_pending_[slot] = false;
+    } else {
+      DetachBuffer(slot);
+    }
+  } else {
+    ALOGW("Unknown event, slot=%zu, epoll events=%d", slot, events);
+  }
+}
+
+void BufferHubQueue::HandleQueueEvent(const epoll_event& event) {
+  auto status = GetEventMask(event.events);
+  if (!status) {
+    ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s",
+          status.GetErrorMessage().c_str());
+    return;
+  }
+
+  int events = status.get();
+  if (events & EPOLLIN) {
+    // Note that after buffer imports, if |count()| still returns 0, epoll
+    // wait will be tried again to acquire the newly imported buffer.
+    int ret = OnBufferAllocated();
+    if (ret < 0) {
+      ALOGE("Failed to import buffer: %s", strerror(-ret));
+    }
+  } else {
+    ALOGW("Unknown epoll events=%d", events);
+  }
+}
+
 int BufferHubQueue::AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf,
                               size_t slot) {
   if (is_full()) {
@@ -146,8 +193,9 @@
   if (buffers_[slot] != nullptr) {
     // Replace the buffer if the slot is preoccupied. This could happen when the
     // producer side replaced the slot with a newly allocated buffer. Detach the
-    // buffer and set up with the new one.
+    // buffer before setting up with the new one.
     DetachBuffer(slot);
+    epollhup_pending_[slot] = true;
   }
 
   epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u64 = slot}};
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp
index 1ea3994..02bca09 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp
@@ -1,5 +1,7 @@
 #include "include/private/dvr/buffer_hub_queue_consumer.h"
 
+//#define LOG_NDEBUG 0
+
 namespace android {
 namespace dvr {
 
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
index a108042..b013c85 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
@@ -1,5 +1,8 @@
 #include "include/private/dvr/buffer_hub_queue_core.h"
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "BufferHubQueueCore"
+
 #include <log/log.h>
 
 namespace android {
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
index 752e8c4..7ddf49b 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -1,5 +1,7 @@
 #include "include/private/dvr/buffer_hub_queue_producer.h"
 
+//#define LOG_NDEBUG 0
+
 #include <inttypes.h>
 #include <log/log.h>
 
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
index 83e77d4..1f2830a 100644
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -49,6 +49,14 @@
     return buffers_[slot];
   }
 
+  Status<int> GetEventMask(int events) {
+    if (auto* client_channel = GetChannel()) {
+      return client_channel->GetEventMask(events);
+    } else {
+      return pdx::ErrorStatus(EINVAL);
+    }
+  }
+
   // Enqueue a buffer marks buffer to be available (|Gain|'ed for producer
   // and |Acquire|'ed for consumer. This is only used for internal bookkeeping.
   void Enqueue(std::shared_ptr<BufferHubBuffer> buf, size_t slot);
@@ -87,6 +95,9 @@
 
   // Wait for buffers to be released and re-add them to the queue.
   bool WaitForBuffers(int timeout);
+  void HandleBufferEvent(size_t slot, const epoll_event& event);
+  void HandleQueueEvent(const epoll_event& event);
+
   virtual int OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) = 0;
 
   // Called when a buffer is allocated remotely.
@@ -160,6 +171,30 @@
   // |buffers_| tracks all |BufferHubBuffer|s created by this |BufferHubQueue|.
   std::vector<std::shared_ptr<BufferHubBuffer>> buffers_;
 
+  // |epollhup_pending_| tracks whether a slot of |buffers_| get detached before
+  // its corresponding EPOLLHUP event got handled. This could happen as the
+  // following sequence:
+  // 1. Producer queue's client side allocates a new buffer (at slot 1).
+  // 2. Producer queue's client side replaces an existing buffer (at slot 0).
+  //    This is implemented by first detaching the buffer and then allocating a
+  //    new buffer.
+  // 3. During the same epoll_wait, Consumer queue's client side gets EPOLLIN
+  //    event on the queue which indicates a new buffer is avaiable and the
+  //    EPOLLHUP event for slot 0. Consumer handles these two events in order.
+  // 4. Consumer client calls BufferHubRPC::ConsumerQueueImportBuffers and both
+  //    slot 0 and (the new) slot 1 buffer will be imported. During the import
+  //    of the buffer at slot 1, consuemr client detaches the old buffer so that
+  //    the new buffer can be registered. At the same time
+  //    |epollhup_pending_[slot]| is marked to indicate that buffer at this slot
+  //    was detached prior to EPOLLHUP event.
+  // 5. Consumer client continues to handle the EPOLLHUP. Since
+  //    |epollhup_pending_[slot]| is marked as true, it can safely ignore the
+  //    event without detaching the newly allocated buffer at slot 1.
+  //
+  // In normal situations where the previously described sequence doesn't
+  // happen, an EPOLLHUP event should trigger a regular buffer detach.
+  std::vector<bool> epollhup_pending_;
+
   // |available_buffers_| uses |dvr::RingBuffer| to implementation queue
   // sematics. When |Dequeue|, we pop the front element from
   // |available_buffers_|, and  that buffer's reference count will decrease by
@@ -225,7 +260,7 @@
   // Returns Zero on success and negative error code when buffer allocation
   // fails.
   int AllocateBuffer(int width, int height, int format, int usage,
-                     size_t buffer_count, size_t* out_slot);
+                     size_t slice_count, size_t* out_slot);
 
   // Add a producer buffer to populate the queue. Once added, a producer buffer
   // is available to use (i.e. in |Gain|'ed mode).
diff --git a/libs/vr/libdisplay/Android.mk b/libs/vr/libdisplay/Android.mk
index 6a9458c..60f73c6 100644
--- a/libs/vr/libdisplay/Android.mk
+++ b/libs/vr/libdisplay/Android.mk
@@ -15,7 +15,6 @@
 LOCAL_PATH := $(call my-dir)
 
 sourceFiles := \
-	native_window.cpp \
 	native_buffer_queue.cpp \
 	display_client.cpp \
 	display_manager_client.cpp \
@@ -72,8 +71,7 @@
 LOCAL_MODULE := libdisplay
 include $(BUILD_STATIC_LIBRARY)
 
-
-testFiles := \
+graphicsAppTestFiles := \
   tests/graphics_app_tests.cpp
 
 include $(CLEAR_VARS)
@@ -81,7 +79,7 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := \
-  $(testFiles) \
+  $(graphicsAppTestFiles) \
 
 LOCAL_C_INCLUDES := \
   $(includeFiles) \
@@ -94,3 +92,25 @@
   $(staticLibraries) \
 
 include $(BUILD_NATIVE_TEST)
+
+dummyNativeWindowTestFiles := \
+  tests/dummy_native_window_tests.cpp \
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dummy_native_window_tests
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+  $(dummyNativeWindowTestFiles) \
+
+LOCAL_C_INCLUDES := \
+  $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := \
+  $(sharedLibraries) \
+
+LOCAL_STATIC_LIBRARIES := \
+  libdisplay \
+  $(staticLibraries) \
+include $(BUILD_NATIVE_TEST)
+
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
index 8fd3627..fe18619 100644
--- a/libs/vr/libdisplay/display_manager_client.cpp
+++ b/libs/vr/libdisplay/display_manager_client.cpp
@@ -98,14 +98,9 @@
 int dvrDisplayManagerClientGetSurfaceBuffers(
     DvrDisplayManagerClient* client, int surface_id,
     DvrDisplayManagerClientSurfaceBuffers** surface_buffers) {
-  std::vector<std::unique_ptr<android::dvr::BufferConsumer>> buffer_list;
-  int ret = client->client->GetSurfaceBuffers(surface_id, &buffer_list);
-  if (ret < 0)
-    return ret;
-
-  *surface_buffers =
-      new DvrDisplayManagerClientSurfaceBuffers(std::move(buffer_list));
-  return ret;
+  // TODO(jwcai, hendrikw) Remove this after we replacing
+  // dvrDisplayManagerClientGetSurfaceBuffers is dvr_api.
+  return -1;
 }
 
 void dvrDisplayManagerClientSurfaceBuffersDestroy(
diff --git a/libs/vr/libdisplay/display_manager_client_impl.cpp b/libs/vr/libdisplay/display_manager_client_impl.cpp
index 82198b9..3fbd1e0 100644
--- a/libs/vr/libdisplay/display_manager_client_impl.cpp
+++ b/libs/vr/libdisplay/display_manager_client_impl.cpp
@@ -31,27 +31,5 @@
   return 0;
 }
 
-int DisplayManagerClient::GetSurfaceBuffers(
-    int surface_id, std::vector<std::unique_ptr<BufferConsumer>>* consumers) {
-  auto status =
-      InvokeRemoteMethod<DisplayManagerRPC::GetSurfaceBuffers>(surface_id);
-  if (!status) {
-    ALOGE(
-        "DisplayManagerClient::GetSurfaceBuffers: Failed to get buffers for "
-        "surface_id=%d: %s",
-        surface_id, status.GetErrorMessage().c_str());
-    return -status.error();
-  }
-
-  std::vector<std::unique_ptr<BufferConsumer>> consumer_buffers;
-  std::vector<LocalChannelHandle> channel_handles = status.take();
-  for (auto&& handle : channel_handles) {
-    consumer_buffers.push_back(BufferConsumer::Import(std::move(handle)));
-  }
-
-  *consumers = std::move(consumer_buffers);
-  return 0;
-}
-
 }  // namespace dvr
 }  // namespace android
diff --git a/libs/vr/libdisplay/dummy_native_window.cpp b/libs/vr/libdisplay/dummy_native_window.cpp
index 5547f53..4628b8e 100644
--- a/libs/vr/libdisplay/dummy_native_window.cpp
+++ b/libs/vr/libdisplay/dummy_native_window.cpp
@@ -30,6 +30,11 @@
 
 int DummyNativeWindow::Query(const ANativeWindow*, int what, int* value) {
   switch (what) {
+    // This must be 1 in order for eglCreateWindowSurface to not trigger an
+    // error
+    case NATIVE_WINDOW_IS_VALID:
+      *value = 1;
+      return NO_ERROR;
     case NATIVE_WINDOW_WIDTH:
     case NATIVE_WINDOW_HEIGHT:
     case NATIVE_WINDOW_FORMAT:
diff --git a/libs/vr/libdisplay/graphics.cpp b/libs/vr/libdisplay/graphics.cpp
index d0557a9..61f6fea 100644
--- a/libs/vr/libdisplay/graphics.cpp
+++ b/libs/vr/libdisplay/graphics.cpp
@@ -483,25 +483,6 @@
   return 0;
 }
 
-extern "C" int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width,
-                                        int* height, int* format) {
-  ANativeWindow* nwin = reinterpret_cast<ANativeWindow*>(win);
-  int w, h, f;
-
-  nwin->query(nwin, NATIVE_WINDOW_DEFAULT_WIDTH, &w);
-  nwin->query(nwin, NATIVE_WINDOW_DEFAULT_HEIGHT, &h);
-  nwin->query(nwin, NATIVE_WINDOW_FORMAT, &f);
-
-  if (width)
-    *width = w;
-  if (height)
-    *height = h;
-  if (format)
-    *format = f;
-
-  return 0;
-}
-
 struct DvrGraphicsContext : public android::ANativeObjectBase<
                                 ANativeWindow, DvrGraphicsContext,
                                 android::LightRefBase<DvrGraphicsContext>> {
diff --git a/libs/vr/libdisplay/include/dvr/graphics.h b/libs/vr/libdisplay/include/dvr/graphics.h
index 50d2754..19deec3 100644
--- a/libs/vr/libdisplay/include/dvr/graphics.h
+++ b/libs/vr/libdisplay/include/dvr/graphics.h
@@ -21,11 +21,6 @@
 
 __BEGIN_DECLS
 
-// Create a stereo surface that will be lens-warped by the system.
-EGLNativeWindowType dvrCreateWarpedDisplaySurface(int* display_width,
-                                                  int* display_height);
-EGLNativeWindowType dvrCreateDisplaySurface(void);
-
 // Display surface parameters used to specify display surface options.
 enum {
   DVR_SURFACE_PARAMETER_NONE = 0,
@@ -163,9 +158,16 @@
   float right_fov[4];
 };
 
-// Creates a display surface with the given parameters. The list of parameters
-// is terminated with an entry where key == DVR_SURFACE_PARAMETER_NONE.
-// For example, the parameters array could be built as follows:
+int dvrGetNativeDisplayDimensions(int* native_width, int* native_height);
+
+// Opaque struct that represents a graphics context, the texture swap chain,
+// and surfaces.
+typedef struct DvrGraphicsContext DvrGraphicsContext;
+
+// Create the graphics context. with the given parameters. The list of
+// parameters is terminated with an entry where key ==
+// DVR_SURFACE_PARAMETER_NONE. For example, the parameters array could be built
+// as follows:
 //   int display_width = 0, display_height = 0;
 //   int surface_width = 0, surface_height = 0;
 //   float inter_lens_meters = 0.0f;
@@ -183,38 +185,6 @@
 //       DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
 //       DVR_SURFACE_PARAMETER_LIST_END,
 //   };
-EGLNativeWindowType dvrCreateDisplaySurfaceExtended(
-    struct DvrSurfaceParameter* parameters);
-
-int dvrGetNativeDisplayDimensions(int* native_width, int* native_height);
-
-int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width, int* height,
-                             int* format);
-
-// NOTE: Only call the functions below on windows created with the API above.
-
-// Sets the display surface visible based on the boolean evaluation of
-// |visible|.
-void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window, int visible);
-
-// Sets the application z-order of the display surface. Higher values display on
-// top of lower values.
-void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window, int z_order);
-
-// Post the next buffer early. This allows the application to race with either
-// the async EDS process or the scanline for applications that are not using
-// system distortion. When this is called, the next buffer in the queue is
-// posted for display. It is up to the application to kick its GPU rendering
-// work in time. If the rendering is incomplete there will be significant,
-// undesirable tearing artifacts.
-// It is not recommended to use this feature with system distortion.
-void dvrDisplayPostEarly(EGLNativeWindowType window);
-
-// Opaque struct that represents a graphics context, the texture swap chain,
-// and surfaces.
-typedef struct DvrGraphicsContext DvrGraphicsContext;
-
-// Create the graphics context.
 int dvrGraphicsContextCreate(struct DvrSurfaceParameter* parameters,
                              DvrGraphicsContext** return_graphics_context);
 
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
index 8ba9175..f515b8f 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
@@ -62,10 +62,8 @@
 bool dvrDisplayManagerClientSurfaceListGetClientIsVisible(
     DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
 
-// Populates |surface_buffers| with the list of buffers for |surface_id|.
-// |surface_id| should be a valid ID from the list of surfaces.
-//
-// @return Returns 0 on success. Otherwise it returns a negative error value.
+// TODO(jwcai, hendrikw) Remove this after we replacing
+// dvrDisplayManagerClientGetSurfaceBuffers is dvr_api.
 int dvrDisplayManagerClientGetSurfaceBuffers(
     DvrDisplayManagerClient* client, int surface_id,
     DvrDisplayManagerClientSurfaceBuffers** surface_buffers);
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
index 4ecd8d4..0897126 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
@@ -17,9 +17,6 @@
 
   int GetSurfaceList(std::vector<DisplaySurfaceInfo>* surface_list);
 
-  int GetSurfaceBuffers(
-      int surface_id, std::vector<std::unique_ptr<BufferConsumer>>* consumers);
-
   using Client::event_fd;
   using Client::GetChannel;
 
diff --git a/libs/vr/libdisplay/include/private/dvr/display_rpc.h b/libs/vr/libdisplay/include/private/dvr/display_rpc.h
index 1c1a5e0..d37aed7 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_rpc.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_rpc.h
@@ -258,7 +258,6 @@
   // Op codes.
   enum {
     kOpGetSurfaceList = 0,
-    kOpGetSurfaceBuffers,
     kOpUpdateSurfaces,
   };
 
@@ -269,8 +268,6 @@
   // Methods.
   PDX_REMOTE_METHOD(GetSurfaceList, kOpGetSurfaceList,
                     std::vector<DisplaySurfaceInfo>(Void));
-  PDX_REMOTE_METHOD(GetSurfaceBuffers, kOpGetSurfaceBuffers,
-                    std::vector<LocalChannelHandle>(int surface_id));
   PDX_REMOTE_METHOD(
       UpdateSurfaces, kOpUpdateSurfaces,
       int(const std::map<int, DisplaySurfaceAttributes>& updates));
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_client_api.h b/libs/vr/libdisplay/include/private/dvr/vsync_client_api.h
index 4cdbc71..0ae5cd5 100644
--- a/libs/vr/libdisplay/include/private/dvr/vsync_client_api.h
+++ b/libs/vr/libdisplay/include/private/dvr/vsync_client_api.h
@@ -12,7 +12,7 @@
 // The "dvr_vsync_client" structure wraps a client connection to the
 // system vsync service. It is used to synchronize application drawing
 // with the scanout of the display.
-typedef struct dvr_vsync_client dreamos_vsync_client;
+typedef struct dvr_vsync_client dvr_vsync_client;
 
 // Creates a new client to the system vsync service.
 dvr_vsync_client* dvr_vsync_client_create();
@@ -20,22 +20,14 @@
 // Destroys the vsync client.
 void dvr_vsync_client_destroy(dvr_vsync_client* client);
 
-// Blocks until the next vsync signal.
-// The timestamp (in ns) is written into |*timestamp_ns| when it is non-NULL.
-// Returns 0 upon success, or -errno.
-int dvr_vsync_client_wait(dvr_vsync_client* client, int64_t* timestamp_ns);
-
-// Returns the file descriptor used to communicate with the vsync service.
-int dvr_vsync_client_get_fd(dvr_vsync_client* client);
-
-// Clears the select/poll/epoll event so that subsequent calls to these
-// will not signal until the next vsync.
-int dvr_vsync_client_acknowledge(dvr_vsync_client* client);
-
-// Gets the timestamp of the last vsync signal in ns. This call has the
-// same side effects on events as acknowledge.
-int dvr_vsync_client_get_last_timestamp(dvr_vsync_client* client,
-                                        int64_t* timestamp_ns);
+// Get the estimated timestamp of the next GPU lens warp preemption event in
+// ns. Also returns the corresponding vsync count that the next lens warp
+// operation will target. This call has the same side effect on events as
+// Acknowledge, which saves an IPC message.
+int dvr_vsync_client_get_sched_info(dvr_vsync_client* client,
+                                    int64_t* vsync_period_ns,
+                                    int64_t* next_timestamp_ns,
+                                    uint32_t* next_vsync_count);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/libs/vr/libdisplay/native_window.cpp b/libs/vr/libdisplay/native_window.cpp
deleted file mode 100644
index 24ecd8a..0000000
--- a/libs/vr/libdisplay/native_window.cpp
+++ /dev/null
@@ -1,458 +0,0 @@
-#include <EGL/egl.h>
-
-#include <android/native_window.h>
-#include <cutils/native_handle.h>
-#include <errno.h>
-#include <pthread.h>
-#include <semaphore.h>
-#include <stdarg.h>
-#include <string.h>
-#include <sys/timerfd.h>
-#include <system/window.h>
-#include <time.h>
-#include <ui/ANativeObjectBase.h>
-#include <utils/Errors.h>
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <utils/Trace.h>
-
-#include <log/log.h>
-
-#include <memory>
-#include <mutex>
-
-#include <dvr/graphics.h>
-#include <private/dvr/clock_ns.h>
-#include <private/dvr/display_client.h>
-#include <private/dvr/native_buffer.h>
-#include <private/dvr/native_buffer_queue.h>
-
-namespace {
-
-constexpr int kDefaultDisplaySurfaceUsage =
-    GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
-constexpr int kDefaultDisplaySurfaceFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-constexpr int kWarpedDisplaySurfaceFlags = 0;
-constexpr int kUnwarpedDisplaySurfaceFlags =
-    DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS |
-    DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION |
-    DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC;
-constexpr int kDefaultBufferCount = 4;
-
-}  // anonymous namespace
-
-namespace android {
-namespace dvr {
-
-// NativeWindow is an implementation of ANativeWindow. This class interacts with
-// displayd through the DisplaySurfaceClient and NativeBufferQueue.
-class NativeWindow : public ANativeObjectBase<ANativeWindow, NativeWindow,
-                                              LightRefBase<NativeWindow> > {
- public:
-  explicit NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface);
-
-  void SetVisible(bool visible);
-  void SetZOrder(int z_order);
-  void PostEarly();
-
- private:
-  friend class LightRefBase<NativeWindow>;
-
-  void Post(sp<NativeBufferProducer> buffer, int fence_fd);
-
-  static int SetSwapInterval(ANativeWindow* window, int interval);
-  static int DequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
-                           int* fence_fd);
-  static int QueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
-                         int fence_fd);
-  static int CancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
-                          int fence_fd);
-  static int Query(const ANativeWindow* window, int what, int* value);
-  static int Perform(ANativeWindow* window, int operation, ...);
-
-  static int DequeueBuffer_DEPRECATED(ANativeWindow* window,
-                                      ANativeWindowBuffer** buffer);
-  static int CancelBuffer_DEPRECATED(ANativeWindow* window,
-                                     ANativeWindowBuffer* buffer);
-  static int QueueBuffer_DEPRECATED(ANativeWindow* window,
-                                    ANativeWindowBuffer* buffer);
-  static int LockBuffer_DEPRECATED(ANativeWindow* window,
-                                   ANativeWindowBuffer* buffer);
-
-  std::shared_ptr<DisplaySurfaceClient> surface_;
-
-  std::mutex lock_;
-  NativeBufferQueue buffer_queue_;
-  sp<NativeBufferProducer> next_post_buffer_;
-  bool next_buffer_already_posted_;
-
-  NativeWindow(const NativeWindow&) = delete;
-  void operator=(NativeWindow&) = delete;
-};
-
-NativeWindow::NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface)
-    : surface_(surface),
-      buffer_queue_(surface, kDefaultBufferCount),
-      next_post_buffer_(nullptr),
-      next_buffer_already_posted_(false) {
-  ANativeWindow::setSwapInterval = SetSwapInterval;
-  ANativeWindow::dequeueBuffer = DequeueBuffer;
-  ANativeWindow::cancelBuffer = CancelBuffer;
-  ANativeWindow::queueBuffer = QueueBuffer;
-  ANativeWindow::query = Query;
-  ANativeWindow::perform = Perform;
-
-  ANativeWindow::dequeueBuffer_DEPRECATED = DequeueBuffer_DEPRECATED;
-  ANativeWindow::cancelBuffer_DEPRECATED = CancelBuffer_DEPRECATED;
-  ANativeWindow::lockBuffer_DEPRECATED = LockBuffer_DEPRECATED;
-  ANativeWindow::queueBuffer_DEPRECATED = QueueBuffer_DEPRECATED;
-}
-
-void NativeWindow::SetVisible(bool visible) { surface_->SetVisible(visible); }
-
-void NativeWindow::SetZOrder(int z_order) { surface_->SetZOrder(z_order); }
-
-void NativeWindow::PostEarly() {
-  ATRACE_NAME("NativeWindow::PostEarly");
-  ALOGI_IF(TRACE, "NativeWindow::PostEarly");
-
-  std::lock_guard<std::mutex> autolock(lock_);
-
-  if (!next_buffer_already_posted_) {
-    next_buffer_already_posted_ = true;
-
-    if (!next_post_buffer_.get()) {
-      next_post_buffer_ = buffer_queue_.Dequeue();
-    }
-    ATRACE_ASYNC_BEGIN("BufferPost", next_post_buffer_->buffer()->id());
-    Post(next_post_buffer_, -1);
-  }
-}
-
-void NativeWindow::Post(sp<NativeBufferProducer> buffer, int fence_fd) {
-  ATRACE_NAME(__PRETTY_FUNCTION__);
-  ALOGI_IF(TRACE, "NativeWindow::Post: buffer_id=%d, fence_fd=%d",
-           buffer->buffer()->id(), fence_fd);
-  ALOGW_IF(!surface_->visible(),
-           "NativeWindow::Post: Posting buffer on invisible surface!!!");
-  buffer->Post(fence_fd, 0);
-}
-
-int NativeWindow::SetSwapInterval(ANativeWindow* window, int interval) {
-  ALOGI_IF(TRACE, "SetSwapInterval: window=%p interval=%d", window, interval);
-  return 0;
-}
-
-int NativeWindow::DequeueBuffer(ANativeWindow* window,
-                                ANativeWindowBuffer** buffer, int* fence_fd) {
-  ATRACE_NAME(__PRETTY_FUNCTION__);
-
-  NativeWindow* self = getSelf(window);
-  std::lock_guard<std::mutex> autolock(self->lock_);
-
-  if (!self->next_post_buffer_.get()) {
-    self->next_post_buffer_ = self->buffer_queue_.Dequeue();
-  }
-  ATRACE_ASYNC_BEGIN("BufferDraw", self->next_post_buffer_->buffer()->id());
-  *fence_fd = self->next_post_buffer_->ClaimReleaseFence().Release();
-  *buffer = self->next_post_buffer_.get();
-
-  ALOGI_IF(TRACE, "NativeWindow::DequeueBuffer: fence_fd=%d", *fence_fd);
-  return 0;
-}
-
-int NativeWindow::QueueBuffer(ANativeWindow* window,
-                              ANativeWindowBuffer* buffer, int fence_fd) {
-  ATRACE_NAME("NativeWindow::QueueBuffer");
-  ALOGI_IF(TRACE, "NativeWindow::QueueBuffer: fence_fd=%d", fence_fd);
-
-  NativeWindow* self = getSelf(window);
-  std::lock_guard<std::mutex> autolock(self->lock_);
-
-  NativeBufferProducer* native_buffer =
-      static_cast<NativeBufferProducer*>(buffer);
-  ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
-  bool do_post = true;
-  if (self->next_buffer_already_posted_) {
-    // Check that the buffer is the one we expect, but handle it if this happens
-    // in production by allowing this buffer to post on top of the previous one.
-    LOG_FATAL_IF(native_buffer != self->next_post_buffer_.get());
-    if (native_buffer == self->next_post_buffer_.get()) {
-      do_post = false;
-      if (fence_fd >= 0)
-        close(fence_fd);
-    }
-  }
-  if (do_post) {
-    ATRACE_ASYNC_BEGIN("BufferPost", native_buffer->buffer()->id());
-    self->Post(native_buffer, fence_fd);
-  }
-  self->next_buffer_already_posted_ = false;
-  self->next_post_buffer_ = nullptr;
-
-  return NO_ERROR;
-}
-
-int NativeWindow::CancelBuffer(ANativeWindow* window,
-                               ANativeWindowBuffer* buffer, int fence_fd) {
-  ATRACE_NAME("NativeWindow::CancelBuffer");
-  ALOGI_IF(TRACE, "NativeWindow::CancelBuffer: fence_fd: %d", fence_fd);
-
-  NativeWindow* self = getSelf(window);
-  std::lock_guard<std::mutex> autolock(self->lock_);
-
-  NativeBufferProducer* native_buffer =
-      static_cast<NativeBufferProducer*>(buffer);
-  ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
-  ATRACE_INT("CancelBuffer", native_buffer->buffer()->id());
-  bool do_enqueue = true;
-  if (self->next_buffer_already_posted_) {
-    // Check that the buffer is the one we expect, but handle it if this happens
-    // in production by returning this buffer to the buffer queue.
-    LOG_FATAL_IF(native_buffer != self->next_post_buffer_.get());
-    if (native_buffer == self->next_post_buffer_.get()) {
-      do_enqueue = false;
-    }
-  }
-  if (do_enqueue) {
-    self->buffer_queue_.Enqueue(native_buffer);
-  }
-  if (fence_fd >= 0)
-    close(fence_fd);
-  self->next_buffer_already_posted_ = false;
-  self->next_post_buffer_ = nullptr;
-
-  return NO_ERROR;
-}
-
-int NativeWindow::Query(const ANativeWindow* window, int what, int* value) {
-  NativeWindow* self = getSelf(const_cast<ANativeWindow*>(window));
-  std::lock_guard<std::mutex> autolock(self->lock_);
-
-  switch (what) {
-    case NATIVE_WINDOW_WIDTH:
-      *value = self->surface_->width();
-      return NO_ERROR;
-    case NATIVE_WINDOW_HEIGHT:
-      *value = self->surface_->height();
-      return NO_ERROR;
-    case NATIVE_WINDOW_FORMAT:
-      *value = self->surface_->format();
-      return NO_ERROR;
-    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
-      *value = 1;
-      return NO_ERROR;
-    case NATIVE_WINDOW_CONCRETE_TYPE:
-      *value = NATIVE_WINDOW_SURFACE;
-      return NO_ERROR;
-    case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
-      *value = 1;
-      return NO_ERROR;
-    case NATIVE_WINDOW_DEFAULT_WIDTH:
-      *value = self->surface_->width();
-      return NO_ERROR;
-    case NATIVE_WINDOW_DEFAULT_HEIGHT:
-      *value = self->surface_->height();
-      return NO_ERROR;
-    case NATIVE_WINDOW_TRANSFORM_HINT:
-      *value = 0;
-      return NO_ERROR;
-  }
-
-  *value = 0;
-  return BAD_VALUE;
-}
-
-int NativeWindow::Perform(ANativeWindow* window, int operation, ...) {
-  NativeWindow* self = getSelf(window);
-  std::lock_guard<std::mutex> autolock(self->lock_);
-
-  va_list args;
-  va_start(args, operation);
-
-  // TODO(eieio): The following operations are not used at this time. They are
-  // included here to help document which operations may be useful and what
-  // parameters they take.
-  switch (operation) {
-    case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: {
-      int w = va_arg(args, int);
-      int h = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: w=%d h=%d", w, h);
-      return NO_ERROR;
-    }
-
-    case NATIVE_WINDOW_SET_BUFFERS_FORMAT: {
-      int format = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_FORMAT: format=%d", format);
-      return NO_ERROR;
-    }
-
-    case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: {
-      int transform = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: transform=%d",
-               transform);
-      return NO_ERROR;
-    }
-
-    case NATIVE_WINDOW_SET_USAGE: {
-      int usage = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_USAGE: usage=%d", usage);
-      return NO_ERROR;
-    }
-
-    case NATIVE_WINDOW_CONNECT:
-    case NATIVE_WINDOW_DISCONNECT:
-    case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
-    case NATIVE_WINDOW_API_CONNECT:
-    case NATIVE_WINDOW_API_DISCONNECT:
-      // TODO(eieio): we should implement these
-      return NO_ERROR;
-
-    case NATIVE_WINDOW_SET_BUFFER_COUNT: {
-      int buffer_count = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFER_COUNT: bufferCount=%d",
-               buffer_count);
-      return NO_ERROR;
-    }
-    case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: {
-      android_dataspace_t data_space =
-          static_cast<android_dataspace_t>(va_arg(args, int));
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DATASPACE: dataSpace=%d",
-               data_space);
-      return NO_ERROR;
-    }
-    case NATIVE_WINDOW_SET_SCALING_MODE: {
-      int mode = va_arg(args, int);
-      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_SCALING_MODE: mode=%d", mode);
-      return NO_ERROR;
-    }
-
-    case NATIVE_WINDOW_LOCK:
-    case NATIVE_WINDOW_UNLOCK_AND_POST:
-    case NATIVE_WINDOW_SET_CROP:
-    case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
-      return INVALID_OPERATION;
-  }
-
-  return NAME_NOT_FOUND;
-}
-
-int NativeWindow::DequeueBuffer_DEPRECATED(ANativeWindow* window,
-                                           ANativeWindowBuffer** buffer) {
-  int fence_fd = -1;
-  int ret = DequeueBuffer(window, buffer, &fence_fd);
-
-  // wait for fence
-  if (ret == NO_ERROR && fence_fd != -1)
-    close(fence_fd);
-
-  return ret;
-}
-
-int NativeWindow::CancelBuffer_DEPRECATED(ANativeWindow* window,
-                                          ANativeWindowBuffer* buffer) {
-  return CancelBuffer(window, buffer, -1);
-}
-
-int NativeWindow::QueueBuffer_DEPRECATED(ANativeWindow* window,
-                                         ANativeWindowBuffer* buffer) {
-  return QueueBuffer(window, buffer, -1);
-}
-
-int NativeWindow::LockBuffer_DEPRECATED(ANativeWindow* /*window*/,
-                                        ANativeWindowBuffer* /*buffer*/) {
-  return NO_ERROR;
-}
-
-}  // namespace dvr
-}  // namespace android
-
-static EGLNativeWindowType CreateDisplaySurface(int* display_width,
-                                                int* display_height, int format,
-                                                int usage, int flags) {
-  auto client = android::dvr::DisplayClient::Create();
-  if (!client) {
-    ALOGE("Failed to create display client!");
-    return nullptr;
-  }
-
-  // TODO(eieio,jbates): Consider passing flags and other parameters to get
-  // metrics based on specific surface requirements.
-  android::dvr::SystemDisplayMetrics metrics;
-  const int ret = client->GetDisplayMetrics(&metrics);
-  if (ret < 0) {
-    ALOGE("Failed to get display metrics: %s", strerror(-ret));
-    return nullptr;
-  }
-
-  int width, height;
-
-  if (flags & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION) {
-    width = metrics.display_native_width;
-    height = metrics.display_native_height;
-  } else {
-    width = metrics.distorted_width;
-    height = metrics.distorted_height;
-  }
-
-  std::shared_ptr<android::dvr::DisplaySurfaceClient> surface =
-      client->CreateDisplaySurface(width, height, format, usage, flags);
-
-  if (display_width)
-    *display_width = metrics.display_native_width;
-  if (display_height)
-    *display_height = metrics.display_native_height;
-
-  // Set the surface visible by default.
-  // TODO(eieio,jbates): Remove this from here and set visible somewhere closer
-  // to the application to account for situations where the application wants to
-  // create surfaces that will be used later or shouldn't be visible yet.
-  surface->SetVisible(true);
-
-  return new android::dvr::NativeWindow(surface);
-}
-
-std::shared_ptr<android::dvr::DisplaySurfaceClient> CreateDisplaySurfaceClient(
-    struct DvrSurfaceParameter* parameters,
-    /*out*/ android::dvr::SystemDisplayMetrics* metrics);
-
-extern "C" EGLNativeWindowType dvrCreateDisplaySurfaceExtended(
-    struct DvrSurfaceParameter* parameters) {
-  android::dvr::SystemDisplayMetrics metrics;
-  auto surface = CreateDisplaySurfaceClient(parameters, &metrics);
-  if (!surface) {
-    ALOGE("Failed to create display surface client");
-    return nullptr;
-  }
-  return new android::dvr::NativeWindow(surface);
-}
-
-extern "C" EGLNativeWindowType dvrCreateDisplaySurface() {
-  return CreateDisplaySurface(NULL, NULL, kDefaultDisplaySurfaceFormat,
-                              kDefaultDisplaySurfaceUsage,
-                              kUnwarpedDisplaySurfaceFlags);
-}
-
-extern "C" EGLNativeWindowType dvrCreateWarpedDisplaySurface(
-    int* display_width, int* display_height) {
-  return CreateDisplaySurface(
-      display_width, display_height, kDefaultDisplaySurfaceFormat,
-      kDefaultDisplaySurfaceUsage, kWarpedDisplaySurfaceFlags);
-}
-
-extern "C" void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window,
-                                            int visible) {
-  auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
-  native_window->SetVisible(visible);
-}
-
-extern "C" void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window,
-                                           int z_order) {
-  auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
-  native_window->SetZOrder(z_order);
-}
-
-extern "C" void dvrDisplayPostEarly(EGLNativeWindowType window) {
-  auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
-  native_window->PostEarly();
-}
diff --git a/libs/vr/libdisplay/native_window_test.cpp b/libs/vr/libdisplay/native_window_test.cpp
deleted file mode 100644
index 2f3bc33..0000000
--- a/libs/vr/libdisplay/native_window_test.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#include <iostream>
-#include <memory>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <dvr/graphics.h>
-#include <private/dvr/display_client.h>
-
-#include <cpp_free_mock/cpp_free_mock.h>
-
-// Checks querying the VSync of the device on display surface creation.
-TEST(CreateDisplaySurface, QueryVSyncPeriod) {
-  using ::testing::_;
-
-  const uint64_t kExpectedVSync = 123456;
-
-  // We only care about the expected VSync value
-  android::dvr::DisplayMetrics metrics;
-  metrics.vsync_period_ns = kExpectedVSync;
-
-  uint64_t outPeriod;
-
-  DvrSurfaceParameter display_params[] = {
-      DVR_SURFACE_PARAMETER_IN(WIDTH, 256),
-      DVR_SURFACE_PARAMETER_IN(HEIGHT, 256),
-      DVR_SURFACE_PARAMETER_OUT(VSYNC_PERIOD, &outPeriod),
-      DVR_SURFACE_PARAMETER_LIST_END,
-  };
-
-  // inject the mocking code to the target method
-  auto mocked_function =
-      MOCKER(&android::dvr::DisplayClient::GetDisplayMetrics);
-
-  // instrument the mock function to return our custom metrics
-  EXPECT_CALL(*mocked_function, MOCK_FUNCTION(_, _))
-      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(metrics),
-                                 ::testing::Return(0)));
-
-  ASSERT_NE(nullptr, dvrCreateDisplaySurfaceExtended(display_params));
-
-  EXPECT_EQ(kExpectedVSync, outPeriod);
-}
diff --git a/libs/vr/libdisplay/tests/dummy_native_window_tests.cpp b/libs/vr/libdisplay/tests/dummy_native_window_tests.cpp
new file mode 100644
index 0000000..5f3ff53
--- /dev/null
+++ b/libs/vr/libdisplay/tests/dummy_native_window_tests.cpp
@@ -0,0 +1,64 @@
+#include <private/dvr/dummy_native_window.h>
+#include <gtest/gtest.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+
+class DummyNativeWindowTests : public ::testing::Test {
+ public:
+  EGLDisplay display_;
+  bool initialized_;
+
+  DummyNativeWindowTests()
+      : display_(nullptr)
+      , initialized_(false)
+  {
+  }
+
+  virtual void SetUp() {
+    display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+    ASSERT_NE(nullptr, display_);
+    initialized_ = eglInitialize(display_, nullptr, nullptr);
+
+    ASSERT_TRUE(initialized_);
+  }
+
+  virtual void TearDown() {
+    if (display_ && initialized_) {
+      eglTerminate(display_);
+    }
+  }
+};
+
+// Test that eglCreateWindowSurface works with DummyNativeWindow
+TEST_F(DummyNativeWindowTests, TryCreateEglWindow) {
+  EGLint attribs[] = {
+      EGL_NONE,
+  };
+
+  EGLint num_configs;
+  EGLConfig config;
+  ASSERT_TRUE(eglChooseConfig(display_, attribs, &config, 1, &num_configs));
+
+  std::unique_ptr<android::dvr::DummyNativeWindow> dummy_window(
+      new android::dvr::DummyNativeWindow());
+
+  EGLint context_attribs[] = {
+    EGL_NONE,
+  };
+
+  EGLSurface surface = eglCreateWindowSurface(display_, config,
+                                              dummy_window.get(),
+                                              context_attribs);
+
+  EXPECT_NE(nullptr, surface);
+
+  bool destroyed = eglDestroySurface(display_, surface);
+
+  EXPECT_TRUE(destroyed);
+}
+
diff --git a/libs/vr/libdisplay/tests/graphics_app_tests.cpp b/libs/vr/libdisplay/tests/graphics_app_tests.cpp
index 7ea3952..f51dd8a 100644
--- a/libs/vr/libdisplay/tests/graphics_app_tests.cpp
+++ b/libs/vr/libdisplay/tests/graphics_app_tests.cpp
@@ -1,55 +1,6 @@
 #include <dvr/graphics.h>
 #include <gtest/gtest.h>
 
-TEST(GraphicsAppTests, CreateWarpedDisplaySurfaceParams) {
-  int width = 0, height = 0;
-  EGLNativeWindowType window = dvrCreateWarpedDisplaySurface(&width, &height);
-  EXPECT_GT(width, 0);
-  EXPECT_GT(height, 0);
-  EXPECT_NE(window, nullptr);
-}
-
-TEST(GraphicsAppTests, CreateDisplaySurface) {
-  EGLNativeWindowType window = dvrCreateDisplaySurface();
-  EXPECT_NE(window, nullptr);
-}
-
-TEST(GraphicsAppTests, CreateDisplaySurfaceExtended) {
-  int display_width = 0, display_height = 0;
-  int surface_width = 0, surface_height = 0;
-  float inter_lens_meters = 0.0f;
-  float left_fov[4] = {0.0f};
-  float right_fov[4] = {0.0f};
-  int disable_warp = 0;
-  DvrSurfaceParameter surface_params[] = {
-      DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, disable_warp),
-      DVR_SURFACE_PARAMETER_OUT(DISPLAY_WIDTH, &display_width),
-      DVR_SURFACE_PARAMETER_OUT(DISPLAY_HEIGHT, &display_height),
-      DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
-      DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
-      DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &inter_lens_meters),
-      DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, left_fov),
-      DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
-      DVR_SURFACE_PARAMETER_LIST_END,
-  };
-
-  EGLNativeWindowType window = dvrCreateDisplaySurfaceExtended(surface_params);
-  EXPECT_NE(window, nullptr);
-  EXPECT_GT(display_width, 0);
-  EXPECT_GT(display_height, 0);
-  EXPECT_GT(surface_width, 0);
-  EXPECT_GT(surface_height, 0);
-  EXPECT_GT(inter_lens_meters, 0);
-  EXPECT_GT(left_fov[0], 0);
-  EXPECT_GT(left_fov[1], 0);
-  EXPECT_GT(left_fov[2], 0);
-  EXPECT_GT(left_fov[3], 0);
-  EXPECT_GT(right_fov[0], 0);
-  EXPECT_GT(right_fov[1], 0);
-  EXPECT_GT(right_fov[2], 0);
-  EXPECT_GT(right_fov[3], 0);
-}
-
 TEST(GraphicsAppTests, GetNativeDisplayDimensions) {
   int width, height;
   dvrGetNativeDisplayDimensions(&width, &height);
@@ -57,17 +8,6 @@
   EXPECT_GT(height, 0);
 }
 
-TEST(GraphicsAppTests, GetDisplaySurfaceInfo) {
-  int ret, width, height, format;
-  EGLNativeWindowType window = dvrCreateDisplaySurface();
-  ASSERT_NE(window, nullptr);
-  ret = dvrGetDisplaySurfaceInfo(window, &width, &height, &format);
-  ASSERT_EQ(0, ret);
-  ASSERT_GT(width, 0);
-  ASSERT_GT(height, 0);
-  ASSERT_NE(0, format);
-}
-
 // TODO(jpoichet) How to check it worked?
 TEST(GraphicsAppTests, GraphicsSurfaceSetVisible) {
   DvrSurfaceParameter surface_params[] = {DVR_SURFACE_PARAMETER_LIST_END};
diff --git a/libs/vr/libdisplay/vsync_client_api.cpp b/libs/vr/libdisplay/vsync_client_api.cpp
index 56103ed..00af525 100644
--- a/libs/vr/libdisplay/vsync_client_api.cpp
+++ b/libs/vr/libdisplay/vsync_client_api.cpp
@@ -13,22 +13,12 @@
   delete static_cast<android::dvr::VSyncClient*>(client);
 }
 
-int dvr_vsync_client_wait(dvr_vsync_client* client, int64_t* timestamp_ns) {
-  return static_cast<android::dvr::VSyncClient*>(client)->Wait(timestamp_ns);
-}
-
-int dvr_vsync_client_get_fd(dvr_vsync_client* client) {
-  return static_cast<android::dvr::VSyncClient*>(client)->GetFd();
-}
-
-int dvr_vsync_client_acknowledge(dvr_vsync_client* client) {
-  return static_cast<android::dvr::VSyncClient*>(client)->Acknowledge();
-}
-
-int dvr_vsync_client_get_last_timestamp(dvr_vsync_client* client,
-                                        int64_t* timestamp_ns) {
-  return static_cast<android::dvr::VSyncClient*>(client)->GetLastTimestamp(
-      timestamp_ns);
+int dvr_vsync_client_get_sched_info(dvr_vsync_client* client,
+                                    int64_t* vsync_period_ns,
+                                    int64_t* next_timestamp_ns,
+                                    uint32_t* next_vsync_count) {
+  return static_cast<android::dvr::VSyncClient*>(client)->GetSchedInfo(
+      vsync_period_ns, next_timestamp_ns, next_vsync_count);
 }
 
 }  // extern "C"
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
index 6730ba8..6df1642 100644
--- a/libs/vr/libvrflinger/display_manager_service.cpp
+++ b/libs/vr/libvrflinger/display_manager_service.cpp
@@ -77,11 +77,6 @@
           *this, &DisplayManagerService::OnGetSurfaceList, message);
       return 0;
 
-    case DisplayManagerRPC::GetSurfaceBuffers::Opcode:
-      DispatchRemoteMethod<DisplayManagerRPC::GetSurfaceBuffers>(
-          *this, &DisplayManagerService::OnGetSurfaceBuffers, message);
-      return 0;
-
     case DisplayManagerRPC::UpdateSurfaces::Opcode:
       DispatchRemoteMethod<DisplayManagerRPC::UpdateSurfaces>(
           *this, &DisplayManagerService::OnUpdateSurfaces, message);
@@ -128,26 +123,6 @@
   return items;
 }
 
-std::vector<LocalChannelHandle> DisplayManagerService::OnGetSurfaceBuffers(
-    pdx::Message& message, int surface_id) {
-  std::shared_ptr<DisplaySurface> surface =
-      display_service_->GetDisplaySurface(surface_id);
-  if (!surface)
-    REPLY_ERROR_RETURN(message, ENOENT, {});
-
-  std::vector<LocalChannelHandle> consumers;
-  int ret = surface->GetConsumers(&consumers);
-  if (ret < 0) {
-    ALOGE(
-        "DisplayManagerService::OnGetDisplaySurfaceBuffers: Failed to get "
-        "consumers for surface %d: %s",
-        surface_id, strerror(-ret));
-    REPLY_ERROR_RETURN(message, -ret, {});
-  }
-
-  return consumers;
-}
-
 int DisplayManagerService::OnUpdateSurfaces(
     pdx::Message& /*message*/,
     const std::map<int, DisplaySurfaceAttributes>& updates) {
diff --git a/libs/vr/libvrflinger/display_manager_service.h b/libs/vr/libvrflinger/display_manager_service.h
index 46401fa..3f83c7d 100644
--- a/libs/vr/libvrflinger/display_manager_service.h
+++ b/libs/vr/libvrflinger/display_manager_service.h
@@ -51,8 +51,6 @@
       const std::shared_ptr<DisplayService>& display_service);
 
   std::vector<DisplaySurfaceInfo> OnGetSurfaceList(pdx::Message& message);
-  std::vector<pdx::LocalChannelHandle> OnGetSurfaceBuffers(
-      pdx::Message& message, int surface_id);
   int OnUpdateSurfaces(pdx::Message& message,
                        const std::map<int, DisplaySurfaceAttributes>& updates);
 
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
index 7c821bf..d427ea6 100644
--- a/libs/vr/libvrflinger/display_surface.cpp
+++ b/libs/vr/libvrflinger/display_surface.cpp
@@ -84,22 +84,6 @@
   client_blur_behind_ = blur_behind;
 }
 
-size_t DisplaySurface::GetBufferCount() const {
-  std::lock_guard<std::mutex> autolock(lock_);
-  return buffers_.size();
-}
-
-std::vector<std::shared_ptr<BufferConsumer>> DisplaySurface::GetBuffers() {
-  std::lock_guard<std::mutex> autolock(lock_);
-  std::vector<std::shared_ptr<BufferConsumer>> return_vector(buffers_.size());
-
-  for (const auto pair : buffers_) {
-    return_vector.push_back(pair.second);
-  }
-
-  return return_vector;
-}
-
 AcquiredBuffer DisplaySurface::AcquireNewestAvailableBuffer(
     AcquiredBuffer* skipped_buffer) {
   std::lock_guard<std::mutex> autolock(lock_);
diff --git a/libs/vr/libvrflinger/display_surface.h b/libs/vr/libvrflinger/display_surface.h
index b7bcd97..fa34057 100644
--- a/libs/vr/libvrflinger/display_surface.h
+++ b/libs/vr/libvrflinger/display_surface.h
@@ -65,9 +65,6 @@
     return buffer_id_to_index_[buffer_id];
   }
 
-  size_t GetBufferCount() const;
-  std::vector<std::shared_ptr<BufferConsumer>> GetBuffers();
-
   // Gets a new set of consumers for all of the surface's buffers. These
   // consumers are independent from the consumers maintained internally to the
   // surface and may be passed to other processes over IPC.
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 740ead3..4c4cd4e 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -670,12 +670,42 @@
 #endif
 #endif
 
-#ifndef EGL_KHR_pixel_format_float
-#define EGL_KHR_pixel_format_float 1
-#define EGL_COLOR_COMPONENT_TYPE_EXT 0x3339  // eglChooseConfig and eglGetConfigAttrib attribute
-#define EGL_COLOR_COMPONENT_TYPE_FIXED_EXT 0x333A  // Attribute value for COLOR_COMPONENT_TYPE
-#define EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT 0x333B  // Attribute value for COLOR_COMPONENT_TYPE
-#endif
+#ifndef EGL_EXT_gl_colorspace_bt2020_linear
+#define EGL_EXT_gl_colorspace_bt2020_linear 1
+#define EGL_GL_COLORSPACE_BT2020_LINEAR_EXT 0x333F
+#endif /* EGL_EXT_gl_colorspace_bt2020_linear */
+
+#ifndef EGL_EXT_gl_colorspace_bt2020_pq
+#define EGL_EXT_gl_colorspace_bt2020_pq 1
+#define EGL_GL_COLORSPACE_BT2020_PQ_EXT   0x3340
+#endif /* EGL_EXT_gl_colorspace_bt2020_pq */
+
+#ifndef EGL_EXT_gl_colorspace_scrgb_linear
+#define EGL_EXT_gl_colorspace_scrgb_linear 1
+#define EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT 0x3350
+#endif /* EGL_EXT_gl_colorspace_scrgb_linear */
+
+#ifndef EGL_EXT_pixel_format_float
+#define EGL_EXT_pixel_format_float 1
+#define EGL_COLOR_COMPONENT_TYPE_EXT      0x3339
+#define EGL_COLOR_COMPONENT_TYPE_FIXED_EXT 0x333A
+#define EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT 0x333B
+#endif /* EGL_EXT_pixel_format_float */
+
+#ifndef EGL_EXT_surface_SMPTE2086_metadata
+#define EGL_EXT_surface_SMPTE2086_metadata 1
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT 0x3341
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT 0x3342
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT 0x3343
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT 0x3344
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT 0x3345
+#define EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT 0x3346
+#define EGL_SMPTE2086_WHITE_POINT_X_EXT   0x3347
+#define EGL_SMPTE2086_WHITE_POINT_Y_EXT   0x3348
+#define EGL_SMPTE2086_MAX_LUMINANCE_EXT   0x3349
+#define EGL_SMPTE2086_MIN_LUMINANCE_EXT   0x334A
+#define EGL_METADATA_SCALING_EXT          50000
+#endif /* EGL_EXT_surface_SMPTE2086_metadata */
 
 #ifdef __cplusplus
 }
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 4fa6a33..6a62e8d 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -58,6 +58,8 @@
         "-fvisibility=hidden",
     ],
     shared_libs: [
+        // ***** DO NOT ADD NEW DEPENDENCIES HERE *****
+        // In particular, DO NOT add libutils or anything "above" libcutils
         "libcutils",
         "liblog",
         "libdl",
@@ -77,10 +79,11 @@
         "-DLOG_TAG=\"libEGL\"",
     ],
     shared_libs: [
-        "libbinder",
-        "libutils",
+        // ***** DO NOT ADD NEW DEPENDENCIES HERE *****
+        // In particular, DO NOT add libutils nor anything "above" libui
         "libui",
         "libnativewindow",
+        "libbacktrace",
     ],
 }
 
@@ -106,9 +109,20 @@
         "EGL/egl.cpp",
         "EGL/eglApi.cpp",
         "EGL/Loader.cpp",
+        "EGL/BlobCache.cpp",
     ],
     static_libs: ["libEGL_getProcAddress"],
     ldflags: ["-Wl,--exclude-libs=ALL"],
+    export_include_dirs: ["EGL/include"],
+}
+
+cc_test {
+    name: "libEGL_test",
+    defaults: ["egl_libs_defaults"],
+    srcs: [
+        "EGL/BlobCache.cpp",
+        "EGL/BlobCache_test.cpp",
+    ],
 }
 
 cc_defaults {
@@ -132,7 +146,6 @@
     name: "libGLESv1_CM",
     defaults: ["gles_libs_defaults"],
     srcs: ["GLES_CM/gl.cpp"],
-
     cflags: ["-DLOG_TAG=\"libGLESv1\""],
 }
 
@@ -143,9 +156,6 @@
     name: "libGLESv2",
     defaults: ["gles_libs_defaults"],
     srcs: ["GLES2/gl2.cpp"],
-
-    shared_libs: ["libutils"],
-
     cflags: ["-DLOG_TAG=\"libGLESv2\""],
 }
 
@@ -156,8 +166,5 @@
     name: "libGLESv3",
     defaults: ["gles_libs_defaults"],
     srcs: ["GLES2/gl2.cpp"],
-
-    shared_libs: ["libutils"],
-
     cflags: ["-DLOG_TAG=\"libGLESv3\""],
 }
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
new file mode 100644
index 0000000..f1b30c7
--- /dev/null
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -0,0 +1,373 @@
+/*
+ ** Copyright 2011, 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_NDEBUG 0
+
+#include "BlobCache.h"
+
+#include <inttypes.h>
+
+#include <cutils/properties.h>
+#include <log/log.h>
+#include <chrono>
+
+namespace android {
+
+// BlobCache::Header::mMagicNumber value
+static const uint32_t blobCacheMagic = ('_' << 24) + ('B' << 16) + ('b' << 8) + '$';
+
+// BlobCache::Header::mBlobCacheVersion value
+static const uint32_t blobCacheVersion = 3;
+
+// BlobCache::Header::mDeviceVersion value
+static const uint32_t blobCacheDeviceVersion = 1;
+
+BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
+        mMaxKeySize(maxKeySize),
+        mMaxValueSize(maxValueSize),
+        mMaxTotalSize(maxTotalSize),
+        mTotalSize(0) {
+    int64_t now = std::chrono::steady_clock::now().time_since_epoch().count();
+#ifdef _WIN32
+    srand(now);
+#else
+    mRandState[0] = (now >> 0) & 0xFFFF;
+    mRandState[1] = (now >> 16) & 0xFFFF;
+    mRandState[2] = (now >> 32) & 0xFFFF;
+#endif
+    ALOGV("initializing random seed using %lld", (unsigned long long)now);
+}
+
+void BlobCache::set(const void* key, size_t keySize, const void* value,
+        size_t valueSize) {
+    if (mMaxKeySize < keySize) {
+        ALOGV("set: not caching because the key is too large: %zu (limit: %zu)",
+                keySize, mMaxKeySize);
+        return;
+    }
+    if (mMaxValueSize < valueSize) {
+        ALOGV("set: not caching because the value is too large: %zu (limit: %zu)",
+                valueSize, mMaxValueSize);
+        return;
+    }
+    if (mMaxTotalSize < keySize + valueSize) {
+        ALOGV("set: not caching because the combined key/value size is too "
+                "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize);
+        return;
+    }
+    if (keySize == 0) {
+        ALOGW("set: not caching because keySize is 0");
+        return;
+    }
+    if (valueSize <= 0) {
+        ALOGW("set: not caching because valueSize is 0");
+        return;
+    }
+
+    std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
+    CacheEntry dummyEntry(dummyKey, NULL);
+
+    while (true) {
+        auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry);
+        if (index == mCacheEntries.end() || dummyEntry < *index) {
+            // Create a new cache entry.
+            std::shared_ptr<Blob> keyBlob(new Blob(key, keySize, true));
+            std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true));
+            size_t newTotalSize = mTotalSize + keySize + valueSize;
+            if (mMaxTotalSize < newTotalSize) {
+                if (isCleanable()) {
+                    // Clean the cache and try again.
+                    clean();
+                    continue;
+                } else {
+                    ALOGV("set: not caching new key/value pair because the "
+                            "total cache size limit would be exceeded: %zu "
+                            "(limit: %zu)",
+                            keySize + valueSize, mMaxTotalSize);
+                    break;
+                }
+            }
+            mCacheEntries.insert(index, CacheEntry(keyBlob, valueBlob));
+            mTotalSize = newTotalSize;
+            ALOGV("set: created new cache entry with %zu byte key and %zu byte value",
+                    keySize, valueSize);
+        } else {
+            // Update the existing cache entry.
+            std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true));
+            std::shared_ptr<Blob> oldValueBlob(index->getValue());
+            size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize();
+            if (mMaxTotalSize < newTotalSize) {
+                if (isCleanable()) {
+                    // Clean the cache and try again.
+                    clean();
+                    continue;
+                } else {
+                    ALOGV("set: not caching new value because the total cache "
+                            "size limit would be exceeded: %zu (limit: %zu)",
+                            keySize + valueSize, mMaxTotalSize);
+                    break;
+                }
+            }
+            index->setValue(valueBlob);
+            mTotalSize = newTotalSize;
+            ALOGV("set: updated existing cache entry with %zu byte key and %zu byte "
+                    "value", keySize, valueSize);
+        }
+        break;
+    }
+}
+
+size_t BlobCache::get(const void* key, size_t keySize, void* value,
+        size_t valueSize) {
+    if (mMaxKeySize < keySize) {
+        ALOGV("get: not searching because the key is too large: %zu (limit %zu)",
+                keySize, mMaxKeySize);
+        return 0;
+    }
+    std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
+    CacheEntry dummyEntry(dummyKey, NULL);
+    auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry);
+    if (index == mCacheEntries.end() || dummyEntry < *index) {
+        ALOGV("get: no cache entry found for key of size %zu", keySize);
+        return 0;
+    }
+
+    // The key was found. Return the value if the caller's buffer is large
+    // enough.
+    std::shared_ptr<Blob> valueBlob(index->getValue());
+    size_t valueBlobSize = valueBlob->getSize();
+    if (valueBlobSize <= valueSize) {
+        ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize);
+        memcpy(value, valueBlob->getData(), valueBlobSize);
+    } else {
+        ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)",
+                valueSize, valueBlobSize);
+    }
+    return valueBlobSize;
+}
+
+static inline size_t align4(size_t size) {
+    return (size + 3) & ~3;
+}
+
+size_t BlobCache::getFlattenedSize() const {
+    size_t size = align4(sizeof(Header) + PROPERTY_VALUE_MAX);
+    for (const CacheEntry& e :  mCacheEntries) {
+        std::shared_ptr<Blob> const& keyBlob = e.getKey();
+        std::shared_ptr<Blob> const& valueBlob = e.getValue();
+        size += align4(sizeof(EntryHeader) + keyBlob->getSize() + valueBlob->getSize());
+    }
+    return size;
+}
+
+int BlobCache::flatten(void* buffer, size_t size) const {
+    // Write the cache header
+    if (size < sizeof(Header)) {
+        ALOGE("flatten: not enough room for cache header");
+        return 0;
+    }
+    Header* header = reinterpret_cast<Header*>(buffer);
+    header->mMagicNumber = blobCacheMagic;
+    header->mBlobCacheVersion = blobCacheVersion;
+    header->mDeviceVersion = blobCacheDeviceVersion;
+    header->mNumEntries = mCacheEntries.size();
+    char buildId[PROPERTY_VALUE_MAX];
+    header->mBuildIdLength = property_get("ro.build.id", buildId, "");
+    memcpy(header->mBuildId, buildId, header->mBuildIdLength);
+
+    // Write cache entries
+    uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
+    off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
+    for (const CacheEntry& e :  mCacheEntries) {
+        std::shared_ptr<Blob> const& keyBlob = e.getKey();
+        std::shared_ptr<Blob> const& valueBlob = e.getValue();
+        size_t keySize = keyBlob->getSize();
+        size_t valueSize = valueBlob->getSize();
+
+        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+        size_t totalSize = align4(entrySize);
+        if (byteOffset + totalSize > size) {
+            ALOGE("flatten: not enough room for cache entries");
+            return -EINVAL;
+        }
+
+        EntryHeader* eheader = reinterpret_cast<EntryHeader*>(&byteBuffer[byteOffset]);
+        eheader->mKeySize = keySize;
+        eheader->mValueSize = valueSize;
+
+        memcpy(eheader->mData, keyBlob->getData(), keySize);
+        memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
+
+        if (totalSize > entrySize) {
+            // We have padding bytes. Those will get written to storage, and contribute to the CRC,
+            // so make sure we zero-them to have reproducible results.
+            memset(eheader->mData + keySize + valueSize, 0, totalSize - entrySize);
+        }
+
+        byteOffset += totalSize;
+    }
+
+    return 0;
+}
+
+int BlobCache::unflatten(void const* buffer, size_t size) {
+    // All errors should result in the BlobCache being in an empty state.
+    mCacheEntries.clear();
+
+    // Read the cache header
+    if (size < sizeof(Header)) {
+        ALOGE("unflatten: not enough room for cache header");
+        return -EINVAL;
+    }
+    const Header* header = reinterpret_cast<const Header*>(buffer);
+    if (header->mMagicNumber != blobCacheMagic) {
+        ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber);
+        return -EINVAL;
+    }
+    char buildId[PROPERTY_VALUE_MAX];
+    int len = property_get("ro.build.id", buildId, "");
+    if (header->mBlobCacheVersion != blobCacheVersion ||
+            header->mDeviceVersion != blobCacheDeviceVersion ||
+            len != header->mBuildIdLength ||
+            strncmp(buildId, header->mBuildId, len)) {
+        // We treat version mismatches as an empty cache.
+        return 0;
+    }
+
+    // Read cache entries
+    const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer);
+    off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
+    size_t numEntries = header->mNumEntries;
+    for (size_t i = 0; i < numEntries; i++) {
+        if (byteOffset + sizeof(EntryHeader) > size) {
+            mCacheEntries.clear();
+            ALOGE("unflatten: not enough room for cache entry headers");
+            return -EINVAL;
+        }
+
+        const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
+                &byteBuffer[byteOffset]);
+        size_t keySize = eheader->mKeySize;
+        size_t valueSize = eheader->mValueSize;
+        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+
+        size_t totalSize = align4(entrySize);
+        if (byteOffset + totalSize > size) {
+            mCacheEntries.clear();
+            ALOGE("unflatten: not enough room for cache entry headers");
+            return -EINVAL;
+        }
+
+        const uint8_t* data = eheader->mData;
+        set(data, keySize, data + keySize, valueSize);
+
+        byteOffset += totalSize;
+    }
+
+    return 0;
+}
+
+long int BlobCache::blob_random() {
+#ifdef _WIN32
+    return rand();
+#else
+    return nrand48(mRandState);
+#endif
+}
+
+void BlobCache::clean() {
+    // Remove a random cache entry until the total cache size gets below half
+    // the maximum total cache size.
+    while (mTotalSize > mMaxTotalSize / 2) {
+        size_t i = size_t(blob_random() % (mCacheEntries.size()));
+        const CacheEntry& entry(mCacheEntries[i]);
+        mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize();
+        mCacheEntries.erase(mCacheEntries.begin() + i);
+    }
+}
+
+bool BlobCache::isCleanable() const {
+    return mTotalSize > mMaxTotalSize / 2;
+}
+
+BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) :
+        mData(copyData ? malloc(size) : data),
+        mSize(size),
+        mOwnsData(copyData) {
+    if (data != NULL && copyData) {
+        memcpy(const_cast<void*>(mData), data, size);
+    }
+}
+
+BlobCache::Blob::~Blob() {
+    if (mOwnsData) {
+        free(const_cast<void*>(mData));
+    }
+}
+
+bool BlobCache::Blob::operator<(const Blob& rhs) const {
+    if (mSize == rhs.mSize) {
+        return memcmp(mData, rhs.mData, mSize) < 0;
+    } else {
+        return mSize < rhs.mSize;
+    }
+}
+
+const void* BlobCache::Blob::getData() const {
+    return mData;
+}
+
+size_t BlobCache::Blob::getSize() const {
+    return mSize;
+}
+
+BlobCache::CacheEntry::CacheEntry() {
+}
+
+BlobCache::CacheEntry::CacheEntry(
+        const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value):
+        mKey(key),
+        mValue(value) {
+}
+
+BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
+        mKey(ce.mKey),
+        mValue(ce.mValue) {
+}
+
+bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
+    return *mKey < *rhs.mKey;
+}
+
+const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) {
+    mKey = rhs.mKey;
+    mValue = rhs.mValue;
+    return *this;
+}
+
+std::shared_ptr<BlobCache::Blob> BlobCache::CacheEntry::getKey() const {
+    return mKey;
+}
+
+std::shared_ptr<BlobCache::Blob> BlobCache::CacheEntry::getValue() const {
+    return mValue;
+}
+
+void BlobCache::CacheEntry::setValue(const std::shared_ptr<Blob>& value) {
+    mValue = value;
+}
+
+} // namespace android
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
new file mode 100644
index 0000000..a0a270a
--- /dev/null
+++ b/opengl/libs/EGL/BlobCache.h
@@ -0,0 +1,245 @@
+/*
+ ** Copyright 2011, 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_BLOB_CACHE_H
+#define ANDROID_BLOB_CACHE_H
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+namespace android {
+
+// A BlobCache is an in-memory cache for binary key/value pairs.  A BlobCache
+// does NOT provide any thread-safety guarantees.
+//
+// The cache contents can be serialized to an in-memory buffer or mmap'd file
+// and then reloaded in a subsequent execution of the program.  This
+// serialization is non-portable and the data should only be used by the device
+// that generated it.
+class BlobCache {
+public:
+    // Create an empty blob cache. The blob cache will cache key/value pairs
+    // with key and value sizes less than or equal to maxKeySize and
+    // maxValueSize, respectively. The total combined size of ALL cache entries
+    // (key sizes plus value sizes) will not exceed maxTotalSize.
+    BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize);
+
+    // set inserts a new binary value into the cache and associates it with the
+    // given binary key.  If the key or value are too large for the cache then
+    // the cache remains unchanged.  This includes the case where a different
+    // value was previously associated with the given key - the old value will
+    // remain in the cache.  If the given key and value are small enough to be
+    // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize
+    // values specified to the BlobCache constructor), then the key/value pair
+    // will be in the cache after set returns.  Note, however, that a subsequent
+    // call to set may evict old key/value pairs from the cache.
+    //
+    // Preconditions:
+    //   key != NULL
+    //   0 < keySize
+    //   value != NULL
+    //   0 < valueSize
+    void set(const void* key, size_t keySize, const void* value,
+            size_t valueSize);
+
+    // get retrieves from the cache the binary value associated with a given
+    // binary key.  If the key is present in the cache then the length of the
+    // binary value associated with that key is returned.  If the value argument
+    // is non-NULL and the size of the cached value is less than valueSize bytes
+    // then the cached value is copied into the buffer pointed to by the value
+    // argument.  If the key is not present in the cache then 0 is returned and
+    // the buffer pointed to by the value argument is not modified.
+    //
+    // Note that when calling get multiple times with the same key, the later
+    // calls may fail, returning 0, even if earlier calls succeeded.  The return
+    // value must be checked for each call.
+    //
+    // Preconditions:
+    //   key != NULL
+    //   0 < keySize
+    //   0 <= valueSize
+    size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
+
+
+    // getFlattenedSize returns the number of bytes needed to store the entire
+    // serialized cache.
+    size_t getFlattenedSize() const;
+
+    // flatten serializes the current contents of the cache into the memory
+    // pointed to by 'buffer'.  The serialized cache contents can later be
+    // loaded into a BlobCache object using the unflatten method.  The contents
+    // of the BlobCache object will not be modified.
+    //
+    // Preconditions:
+    //   size >= this.getFlattenedSize()
+    int flatten(void* buffer, size_t size) const;
+
+    // unflatten replaces the contents of the cache with the serialized cache
+    // contents in the memory pointed to by 'buffer'.  The previous contents of
+    // the BlobCache will be evicted from the cache.  If an error occurs while
+    // unflattening the serialized cache contents then the BlobCache will be
+    // left in an empty state.
+    //
+    int unflatten(void const* buffer, size_t size);
+
+private:
+    // Copying is disallowed.
+    BlobCache(const BlobCache&);
+    void operator=(const BlobCache&);
+
+    // A random function helper to get around MinGW not having nrand48()
+    long int blob_random();
+
+    // clean evicts a randomly chosen set of entries from the cache such that
+    // the total size of all remaining entries is less than mMaxTotalSize/2.
+    void clean();
+
+    // isCleanable returns true if the cache is full enough for the clean method
+    // to have some effect, and false otherwise.
+    bool isCleanable() const;
+
+    // A Blob is an immutable sized unstructured data blob.
+    class Blob {
+    public:
+        Blob(const void* data, size_t size, bool copyData);
+        ~Blob();
+
+        bool operator<(const Blob& rhs) const;
+
+        const void* getData() const;
+        size_t getSize() const;
+
+    private:
+        // Copying is not allowed.
+        Blob(const Blob&);
+        void operator=(const Blob&);
+
+        // mData points to the buffer containing the blob data.
+        const void* mData;
+
+        // mSize is the size of the blob data in bytes.
+        size_t mSize;
+
+        // mOwnsData indicates whether or not this Blob object should free the
+        // memory pointed to by mData when the Blob gets destructed.
+        bool mOwnsData;
+    };
+
+    // A CacheEntry is a single key/value pair in the cache.
+    class CacheEntry {
+    public:
+        CacheEntry();
+        CacheEntry(const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value);
+        CacheEntry(const CacheEntry& ce);
+
+        bool operator<(const CacheEntry& rhs) const;
+        const CacheEntry& operator=(const CacheEntry&);
+
+        std::shared_ptr<Blob> getKey() const;
+        std::shared_ptr<Blob> getValue() const;
+
+        void setValue(const std::shared_ptr<Blob>& value);
+
+    private:
+
+        // mKey is the key that identifies the cache entry.
+        std::shared_ptr<Blob> mKey;
+
+        // mValue is the cached data associated with the key.
+        std::shared_ptr<Blob> mValue;
+    };
+
+    // A Header is the header for the entire BlobCache serialization format. No
+    // need to make this portable, so we simply write the struct out.
+    struct Header {
+        // mMagicNumber is the magic number that identifies the data as
+        // serialized BlobCache contents.  It must always contain 'Blb$'.
+        uint32_t mMagicNumber;
+
+        // mBlobCacheVersion is the serialization format version.
+        uint32_t mBlobCacheVersion;
+
+        // mDeviceVersion is the device-specific version of the cache.  This can
+        // be used to invalidate the cache.
+        uint32_t mDeviceVersion;
+
+        // mNumEntries is number of cache entries following the header in the
+        // data.
+        size_t mNumEntries;
+
+        // mBuildId is the build id of the device when the cache was created.
+        // When an update to the build happens (via an OTA or other update) this
+        // is used to invalidate the cache.
+        int mBuildIdLength;
+        char mBuildId[];
+    };
+
+    // An EntryHeader is the header for a serialized cache entry.  No need to
+    // make this portable, so we simply write the struct out.  Each EntryHeader
+    // is followed imediately by the key data and then the value data.
+    //
+    // The beginning of each serialized EntryHeader is 4-byte aligned, so the
+    // number of bytes that a serialized cache entry will occupy is:
+    //
+    //   ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
+    //
+    struct EntryHeader {
+        // mKeySize is the size of the entry key in bytes.
+        size_t mKeySize;
+
+        // mValueSize is the size of the entry value in bytes.
+        size_t mValueSize;
+
+        // mData contains both the key and value data for the cache entry.  The
+        // key comes first followed immediately by the value.
+        uint8_t mData[];
+    };
+
+    // mMaxKeySize is the maximum key size that will be cached. Calls to
+    // BlobCache::set with a keySize parameter larger than mMaxKeySize will
+    // simply not add the key/value pair to the cache.
+    const size_t mMaxKeySize;
+
+    // mMaxValueSize is the maximum value size that will be cached. Calls to
+    // BlobCache::set with a valueSize parameter larger than mMaxValueSize will
+    // simply not add the key/value pair to the cache.
+    const size_t mMaxValueSize;
+
+    // mMaxTotalSize is the maximum size that all cache entries can occupy. This
+    // includes space for both keys and values. When a call to BlobCache::set
+    // would otherwise cause this limit to be exceeded, either the key/value
+    // pair passed to BlobCache::set will not be cached or other cache entries
+    // will be evicted from the cache to make room for the new entry.
+    const size_t mMaxTotalSize;
+
+    // mTotalSize is the total combined size of all keys and values currently in
+    // the cache.
+    size_t mTotalSize;
+
+    // mRandState is the pseudo-random number generator state. It is passed to
+    // nrand48 to generate random numbers when needed.
+    unsigned short mRandState[3];
+
+    // mCacheEntries stores all the cache entries that are resident in memory.
+    // Cache entries are added to it by the 'set' method.
+    std::vector<CacheEntry> mCacheEntries;
+};
+
+}
+
+#endif // ANDROID_BLOB_CACHE_H
diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp
new file mode 100644
index 0000000..edbaaf0
--- /dev/null
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -0,0 +1,434 @@
+/*
+ ** Copyright 2011, 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 <fcntl.h>
+#include <stdio.h>
+
+#include <memory>
+
+#include <gtest/gtest.h>
+
+#include "BlobCache.h"
+
+namespace android {
+
+template<typename T> using sp = std::shared_ptr<T>;
+
+class BlobCacheTest : public ::testing::Test {
+protected:
+
+    enum {
+        OK = 0,
+        BAD_VALUE = -EINVAL
+    };
+
+    enum {
+        MAX_KEY_SIZE = 6,
+        MAX_VALUE_SIZE = 8,
+        MAX_TOTAL_SIZE = 13,
+    };
+
+    virtual void SetUp() {
+        mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
+    }
+
+    virtual void TearDown() {
+        mBC.reset();
+    }
+
+    std::unique_ptr<BlobCache> mBC;
+};
+
+TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+    ASSERT_EQ('e', buf[0]);
+    ASSERT_EQ('f', buf[1]);
+    ASSERT_EQ('g', buf[2]);
+    ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
+    unsigned char buf[2] = { 0xee, 0xee };
+    mBC->set("ab", 2, "cd", 2);
+    mBC->set("ef", 2, "gh", 2);
+    ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
+    ASSERT_EQ('c', buf[0]);
+    ASSERT_EQ('d', buf[1]);
+    ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2));
+    ASSERT_EQ('g', buf[0]);
+    ASSERT_EQ('h', buf[1]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
+    unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
+    ASSERT_EQ(0xee, buf[0]);
+    ASSERT_EQ('e', buf[1]);
+    ASSERT_EQ('f', buf[2]);
+    ASSERT_EQ('g', buf[3]);
+    ASSERT_EQ('h', buf[4]);
+    ASSERT_EQ(0xee, buf[5]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
+    unsigned char buf[3] = { 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
+    ASSERT_EQ(0xee, buf[0]);
+    ASSERT_EQ(0xee, buf[1]);
+    ASSERT_EQ(0xee, buf[2]);
+}
+
+TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    mBC->set("abcd", 4, "ijkl", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+    ASSERT_EQ('i', buf[0]);
+    ASSERT_EQ('j', buf[1]);
+    ASSERT_EQ('k', buf[2]);
+    ASSERT_EQ('l', buf[3]);
+}
+
+TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
+    unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+    ASSERT_EQ('e', buf[0]);
+    ASSERT_EQ('f', buf[1]);
+    ASSERT_EQ('g', buf[2]);
+    ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
+    char key[MAX_KEY_SIZE+1];
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
+        key[i] = 'a';
+    }
+    mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
+    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
+    ASSERT_EQ(0xee, buf[0]);
+    ASSERT_EQ(0xee, buf[1]);
+    ASSERT_EQ(0xee, buf[2]);
+    ASSERT_EQ(0xee, buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
+    char buf[MAX_VALUE_SIZE+1];
+    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+        buf[i] = 'b';
+    }
+    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+        buf[i] = 0xee;
+    }
+    ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
+    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+        SCOPED_TRACE(i);
+        ASSERT_EQ(0xee, buf[i]);
+    }
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) {
+    // Check a testing assumptions
+    ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE);
+    ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+    enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 };
+
+    char key[MAX_KEY_SIZE];
+    char buf[bufSize];
+    for (int i = 0; i < MAX_KEY_SIZE; i++) {
+        key[i] = 'a';
+    }
+    for (int i = 0; i < bufSize; i++) {
+        buf[i] = 'b';
+    }
+
+    mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
+    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
+    char key[MAX_KEY_SIZE];
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    for (int i = 0; i < MAX_KEY_SIZE; i++) {
+        key[i] = 'a';
+    }
+    mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
+    ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
+    ASSERT_EQ('w', buf[0]);
+    ASSERT_EQ('x', buf[1]);
+    ASSERT_EQ('y', buf[2]);
+    ASSERT_EQ('z', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) {
+    char buf[MAX_VALUE_SIZE];
+    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+        buf[i] = 'b';
+    }
+    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
+    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+        buf[i] = 0xee;
+    }
+    ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
+            MAX_VALUE_SIZE));
+    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+        SCOPED_TRACE(i);
+        ASSERT_EQ('b', buf[i]);
+    }
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) {
+    // Check a testing assumption
+    ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+    enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
+
+    char key[MAX_KEY_SIZE];
+    char buf[bufSize];
+    for (int i = 0; i < MAX_KEY_SIZE; i++) {
+        key[i] = 'a';
+    }
+    for (int i = 0; i < bufSize; i++) {
+        buf[i] = 'b';
+    }
+
+    mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
+    ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
+    unsigned char buf[1] = { 0xee };
+    mBC->set("x", 1, "y", 1);
+    ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
+    ASSERT_EQ('y', buf[0]);
+}
+
+TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) {
+    for (int i = 0; i < 256; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, "x", 1);
+    }
+    int numCached = 0;
+    for (int i = 0; i < 256; i++) {
+        uint8_t k = i;
+        if (mBC->get(&k, 1, NULL, 0) == 1) {
+            numCached++;
+        }
+    }
+    ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
+}
+
+TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, "x", 1);
+    }
+    // Insert one more entry, causing a cache overflow.
+    {
+        uint8_t k = maxEntries;
+        mBC->set(&k, 1, "x", 1);
+    }
+    // Count the number of entries in the cache.
+    int numCached = 0;
+    for (int i = 0; i < maxEntries+1; i++) {
+        uint8_t k = i;
+        if (mBC->get(&k, 1, NULL, 0) == 1) {
+            numCached++;
+        }
+    }
+    ASSERT_EQ(maxEntries/2 + 1, numCached);
+}
+
+class BlobCacheFlattenTest : public BlobCacheTest {
+protected:
+    virtual void SetUp() {
+        BlobCacheTest::SetUp();
+        mBC2.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
+    }
+
+    virtual void TearDown() {
+        mBC2.reset();
+        BlobCacheTest::TearDown();
+    }
+
+    void roundTrip() {
+        size_t size = mBC->getFlattenedSize();
+        uint8_t* flat = new uint8_t[size];
+        ASSERT_EQ(OK, mBC->flatten(flat, size));
+        ASSERT_EQ(OK, mBC2->unflatten(flat, size));
+        delete[] flat;
+    }
+
+    sp<BlobCache> mBC2;
+};
+
+TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    roundTrip();
+    ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
+    ASSERT_EQ('e', buf[0]);
+    ASSERT_EQ('f', buf[1]);
+    ASSERT_EQ('g', buf[2]);
+    ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    roundTrip();
+
+    // Verify the deserialized cache
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        uint8_t v = 0xee;
+        ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
+        ASSERT_EQ(k, v);
+    }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size));
+    delete[] flat;
+
+    // Verify the cache that we just serialized
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        uint8_t v = 0xee;
+        ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
+        ASSERT_EQ(k, v);
+    }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    size_t size = mBC->getFlattenedSize() - 1;
+    uint8_t* flat = new uint8_t[size];
+    // ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
+    // TODO: The above fails. I expect this is so because getFlattenedSize()
+    // overstimates the size by using PROPERTY_VALUE_MAX.
+    delete[] flat;
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size));
+    flat[1] = ~flat[1];
+
+    // Bad magic should cause an error.
+    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size));
+    delete[] flat;
+
+    // The error should cause the unflatten to result in an empty cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size));
+    flat[5] = ~flat[5];
+
+    // Version mismatches shouldn't cause errors, but should not use the
+    // serialized entries
+    ASSERT_EQ(OK, mBC2->unflatten(flat, size));
+    delete[] flat;
+
+    // The version mismatch should cause the unflatten to result in an empty
+    // cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size));
+    flat[10] = ~flat[10];
+
+    // Version mismatches shouldn't cause errors, but should not use the
+    // serialized entries
+    ASSERT_EQ(OK, mBC2->unflatten(flat, size));
+    delete[] flat;
+
+    // The version mismatch should cause the unflatten to result in an empty
+    // cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
+    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size));
+
+    // A buffer truncation shouldt cause an error
+    // ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
+    // TODO: The above appears to fail because getFlattenedSize() is
+    // conservative.
+    delete[] flat;
+
+    // The error should cause the unflatten to result in an empty cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+} // namespace android
diff --git a/opengl/libs/EGL/CallStack.h b/opengl/libs/EGL/CallStack.h
new file mode 100644
index 0000000..0e2a9b3
--- /dev/null
+++ b/opengl/libs/EGL/CallStack.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 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 <log/log.h>
+#include <backtrace/Backtrace.h>
+#include <memory>
+
+class CallStack {
+public:
+    // Create a callstack with the current thread's stack trace.
+    // Immediately dump it to logcat using the given logtag.
+    static void log(const char* logtag) noexcept {
+        std::unique_ptr<Backtrace> backtrace(
+                Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+        if (backtrace->Unwind(2)) {
+            for (size_t i = 0, c = backtrace->NumFrames(); i < c; i++) {
+                __android_log_print(ANDROID_LOG_DEBUG, logtag, "%s",
+                        backtrace->FormatFrameData(i).c_str());
+            }
+        }
+    }
+};
+
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 9aedc61..683e6ca 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -19,6 +19,8 @@
 
 #include "Loader.h"
 
+#include <string>
+
 #include <dirent.h>
 #include <dlfcn.h>
 
@@ -26,11 +28,9 @@
 #include <cutils/properties.h>
 #include <log/log.h>
 
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
 #include <ui/GraphicsEnv.h>
 
+#include "egl_trace.h"
 #include "egldefs.h"
 
 // ----------------------------------------------------------------------------
@@ -61,7 +61,10 @@
  *
  */
 
-ANDROID_SINGLETON_STATIC_INSTANCE( Loader )
+Loader& Loader::getInstance() {
+    static Loader loader;
+    return loader;
+}
 
 /* This function is called to check whether we run inside the emulator,
  * and if this is the case whether GLES GPU emulation is supported.
@@ -122,7 +125,7 @@
     }
 }
 
-status_t Loader::driver_t::set(void* hnd, int32_t api)
+int Loader::driver_t::set(void* hnd, int32_t api)
 {
     switch (api) {
         case EGL:
@@ -135,9 +138,9 @@
             dso[2] = hnd;
             break;
         default:
-            return BAD_INDEX;
+            return -EOVERFLOW;
     }
-    return NO_ERROR;
+    return 0;
 }
 
 // ----------------------------------------------------------------------------
@@ -233,11 +236,10 @@
     return (void*)hnd;
 }
 
-status_t Loader::close(void* driver)
+void Loader::close(void* driver)
 {
     driver_t* hnd = (driver_t*)driver;
     delete hnd;
-    return NO_ERROR;
 }
 
 void Loader::init_api(void* dso,
@@ -302,23 +304,23 @@
     ATRACE_CALL();
     class MatchFile {
     public:
-        static String8 find(const char* kind) {
-            String8 result;
+        static std::string find(const char* kind) {
+            std::string result;
             int emulationStatus = checkGlesEmulationStatus();
             switch (emulationStatus) {
                 case 0:
 #if defined(__LP64__)
-                    result.setTo("/system/lib64/egl/libGLES_android.so");
+                    result = "/system/lib64/egl/libGLES_android.so";
 #else
-                    result.setTo("/system/lib/egl/libGLES_android.so");
+                    result = "/system/lib/egl/libGLES_android.so";
 #endif
                     return result;
                 case 1:
                     // Use host-side OpenGL through the "emulation" library
 #if defined(__LP64__)
-                    result.appendFormat("/system/lib64/egl/lib%s_emulation.so", kind);
+                    result = std::string("/system/lib64/egl/lib") + kind + "_emulation.so";
 #else
-                    result.appendFormat("/system/lib/egl/lib%s_emulation.so", kind);
+                    result = std::string("/system/lib/egl/lib") + kind + "_emulation.so";
 #endif
                     return result;
                 default:
@@ -326,8 +328,7 @@
                     break;
             }
 
-            String8 pattern;
-            pattern.appendFormat("lib%s", kind);
+            std::string pattern = std::string("lib") + kind;
             const char* const searchPaths[] = {
 #if defined(__LP64__)
                     "/vendor/lib64/egl",
@@ -368,12 +369,11 @@
         }
 
     private:
-        static bool find(String8& result,
-                const String8& pattern, const char* const search, bool exact) {
+        static bool find(std::string& result,
+                const std::string& pattern, const char* const search, bool exact) {
             if (exact) {
-                String8 absolutePath;
-                absolutePath.appendFormat("%s/%s.so", search, pattern.string());
-                if (!access(absolutePath.string(), R_OK)) {
+                std::string absolutePath = std::string(search) + "/" + pattern;
+                if (!access(absolutePath.c_str(), R_OK)) {
                     result = absolutePath;
                     return true;
                 }
@@ -392,10 +392,9 @@
                         // always skip the software renderer
                         continue;
                     }
-                    if (strstr(e->d_name, pattern.string()) == e->d_name) {
+                    if (strstr(e->d_name, pattern.c_str()) == e->d_name) {
                         if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) {
-                            result.clear();
-                            result.appendFormat("%s/%s", search, e->d_name);
+                            result = std::string(search) + "/" + e->d_name;
                             closedir(d);
                             return true;
                         }
@@ -408,17 +407,17 @@
     };
 
 
-    String8 absolutePath = MatchFile::find(kind);
-    if (absolutePath.isEmpty()) {
+    std::string absolutePath = MatchFile::find(kind);
+    if (absolutePath.empty()) {
         // this happens often, we don't want to log an error
         return 0;
     }
-    const char* const driver_absolute_path = absolutePath.string();
+    const char* const driver_absolute_path = absolutePath.c_str();
 
     void* dso = do_dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);
     if (dso == 0) {
         const char* err = dlerror();
-        ALOGE("load_driver(%s): %s", driver_absolute_path, err?err:"unknown");
+        ALOGE("load_driver(%s): %s", driver_absolute_path, err ? err : "unknown");
         return 0;
     }
 
@@ -447,9 +446,8 @@
     char prop[PROPERTY_VALUE_MAX + 1];
     for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
         if (property_get(key, prop, nullptr) > 0) {
-            String8 name;
-            name.appendFormat("lib%s_%s.so", kind, prop);
-            so = do_android_dlopen_ext(name.string(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+            std::string name = std::string("lib") + kind + "_" + prop + ".so";
+            so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
             if (so) {
                 return so;
             }
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 2dd4206..6a32bb3 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -19,9 +19,6 @@
 
 #include <stdint.h>
 
-#include <utils/Errors.h>
-#include <utils/Singleton.h>
-
 #include <EGL/egl.h>
 
 // ----------------------------------------------------------------------------
@@ -30,9 +27,7 @@
 
 struct egl_connection_t;
 
-class Loader : public Singleton<Loader> {
-    friend class Singleton<Loader>;
-
+class Loader {
     typedef __eglMustCastToProperFunctionPointerType (* getProcAddressType)(const char*);
    
     enum {
@@ -43,17 +38,19 @@
     struct driver_t {
         explicit driver_t(void* gles);
         ~driver_t();
-        status_t set(void* hnd, int32_t api);
+        // returns -errno
+        int set(void* hnd, int32_t api);
         void* dso[3];
     };
     
     getProcAddressType getProcAddress;
 
 public:
+    static Loader& getInstance();
     ~Loader();
     
     void* open(egl_connection_t* cnx);
-    status_t close(void* driver);
+    void close(void* driver);
     
 private:
     Loader();
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 34b0ba2..f53cf3f 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -20,19 +20,17 @@
 
 #include <EGL/egl.h>
 
-#include <cutils/atomic.h>
 #include <cutils/properties.h>
 
 #include <log/log.h>
 
-#include <utils/CallStack.h>
-
 #include "../egl_impl.h"
 
 #include "egldefs.h"
 #include "egl_tls.h"
 #include "egl_display.h"
 #include "egl_object.h"
+#include "CallStack.h"
 #include "Loader.h"
 
 typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
@@ -66,7 +64,7 @@
         char value[PROPERTY_VALUE_MAX];
         property_get("debug.egl.callstack", value, "0");
         if (atoi(value)) {
-            CallStack stack(LOG_TAG);
+            CallStack::log(LOG_TAG);
         }
     }
     return 0;
@@ -128,7 +126,7 @@
     if (name != GL_EXTENSIONS)
         return NULL;
 
-    return (const GLubyte *)c->gl_extensions.string();
+    return (const GLubyte *)c->gl_extensions.c_str();
 }
 
 const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index) {
@@ -151,7 +149,7 @@
     if (index >= c->tokenized_gl_extensions.size())
         return NULL;
 
-    return (const GLubyte *)c->tokenized_gl_extensions.itemAt(index).string();
+    return (const GLubyte *)c->tokenized_gl_extensions[index].c_str();
 }
 
 GLint egl_get_num_extensions_for_current_context() {
@@ -208,14 +206,14 @@
 }
 
 static pthread_mutex_t sLogPrintMutex = PTHREAD_MUTEX_INITIALIZER;
-static nsecs_t sLogPrintTime = 0;
-#define NSECS_DURATION 1000000000
+static std::chrono::steady_clock::time_point sLogPrintTime;
+static constexpr std::chrono::seconds DURATION(1);
 
 void gl_unimplemented() {
     bool printLog = false;
-    nsecs_t now = systemTime();
+    auto now = std::chrono::steady_clock::now();
     pthread_mutex_lock(&sLogPrintMutex);
-    if ((now - sLogPrintTime) > NSECS_DURATION) {
+    if ((now - sLogPrintTime) > DURATION) {
         sLogPrintTime = now;
         printLog = true;
     }
@@ -225,7 +223,7 @@
         char value[PROPERTY_VALUE_MAX];
         property_get("debug.egl.callstack", value, "0");
         if (atoi(value)) {
-            CallStack stack(LOG_TAG);
+            CallStack::log(LOG_TAG);
         }
     }
 }
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index aed27c8..d124c89 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -33,25 +33,19 @@
 #include <cutils/properties.h>
 #include <log/log.h>
 
-#include <gui/ISurfaceComposer.h>
-
-#include <ui/GraphicBuffer.h>
-
-
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-#include <utils/Thread.h>
-
-#include "binder/Binder.h"
-#include "binder/Parcel.h"
-#include "binder/IServiceManager.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_tls.h"
+#include "egl_trace.h"
 
 using namespace android;
 
@@ -59,6 +53,8 @@
 
 namespace android {
 
+using nsecs_t = int64_t;
+
 struct extention_map_t {
     const char* name;
     __eglMustCastToProperFunctionPointerType address;
@@ -87,7 +83,6 @@
         "EGL_KHR_get_all_proc_addresses "
         "EGL_ANDROID_presentation_time "
         "EGL_KHR_swap_buffers_with_damage "
-        "EGL_ANDROID_create_native_client_buffer "
         "EGL_ANDROID_get_native_client_buffer "
         "EGL_ANDROID_front_buffer_auto_refresh "
         "EGL_ANDROID_get_frame_timestamps "
@@ -184,10 +179,6 @@
     { "eglSwapBuffersWithDamageKHR",
             (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
 
-    // EGL_ANDROID_create_native_client_buffer
-    { "eglCreateNativeClientBufferANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateNativeClientBufferANDROID },
-
     // EGL_ANDROID_get_native_client_buffer
     { "eglGetNativeClientBufferANDROID",
             (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
@@ -247,7 +238,8 @@
 
 
 // accesses protected by sExtensionMapMutex
-static DefaultKeyedVector<String8, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
+static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
+
 static int sGLExtentionSlot = 0;
 static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
 
@@ -489,7 +481,7 @@
         }
 
         int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
-        if (result != OK) {
+        if (result < 0) {
             ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
                     "failed (%#x) (already connected to another API?)",
                     window, result);
@@ -1002,8 +994,11 @@
          *
          */
 
-        const String8 name(procname);
-        addr = sGLExtentionMap.valueFor(name);
+        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,
@@ -1025,7 +1020,7 @@
 
             if (found) {
                 addr = gExtensionForwarders[slot];
-                sGLExtentionMap.add(name, addr);
+                extentionMap[name] = addr;
                 sGLExtentionSlot++;
             }
         }
@@ -1034,45 +1029,57 @@
     return addr;
 }
 
-class FrameCompletionThread : public Thread {
+class FrameCompletionThread {
 public:
 
     static void queueSync(EGLSyncKHR sync) {
-        static sp<FrameCompletionThread> thread(new FrameCompletionThread);
-        static bool running = false;
-        if (!running) {
-            thread->run("GPUFrameCompletion");
-            running = true;
-        }
-        {
-            Mutex::Autolock lock(thread->mMutex);
-            ScopedTrace st(ATRACE_TAG, String8::format("kicked off frame %d",
-                    thread->mFramesQueued).string());
-            thread->mQueue.push_back(sync);
-            thread->mCondition.signal();
-            thread->mFramesQueued++;
-            ATRACE_INT("GPU Frames Outstanding", int32_t(thread->mQueue.size()));
-        }
+        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) {}
 
-    virtual bool threadLoop() {
+    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;
         {
-            Mutex::Autolock lock(mMutex);
-            while (mQueue.isEmpty()) {
-                mCondition.wait(mMutex);
+            std::unique_lock<std::mutex> lock(mMutex);
+            while (mQueue.empty()) {
+                mCondition.wait(lock);
             }
             sync = mQueue[0];
             frameNum = mFramesCompleted;
         }
         EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
         {
-            ScopedTrace st(ATRACE_TAG, String8::format("waiting for frame %d",
-                    frameNum).string());
+            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());
@@ -1082,19 +1089,18 @@
             eglDestroySyncKHR(dpy, sync);
         }
         {
-            Mutex::Autolock lock(mMutex);
-            mQueue.removeAt(0);
+            std::lock_guard<std::mutex> lock(mMutex);
+            mQueue.pop_front();
             mFramesCompleted++;
             ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size()));
         }
-        return true;
     }
 
     uint32_t mFramesQueued;
     uint32_t mFramesCompleted;
-    Vector<EGLSyncKHR> mQueue;
-    Condition mCondition;
-    Mutex mMutex;
+    std::deque<EGLSyncKHR> mQueue;
+    std::condition_variable mCondition;
+    std::mutex mMutex;
 };
 
 EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw,
@@ -1133,7 +1139,7 @@
         return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
     }
 
-    Vector<android_native_rect_t> androidRects;
+    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];
@@ -1147,8 +1153,7 @@
         androidRect.bottom = y;
         androidRects.push_back(androidRect);
     }
-    native_window_set_surface_damage(s->win.get(), androidRects.array(),
-            androidRects.size());
+    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,
@@ -1252,19 +1257,19 @@
     egl_surface_t * const s = get_surface(surface);
 
     if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
-        if (!s->win.get()) {
+        if (!s->getNativeWindow()) {
             setError(EGL_BAD_SURFACE, EGL_FALSE);
         }
-        int err = native_window_set_auto_refresh(s->win.get(), value ? true : false);
-        return (err == NO_ERROR) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)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->win.get()) {
+        if (!s->getNativeWindow()) {
             return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
         }
-        int err = native_window_enable_frame_timestamps(s->win.get(), value ? true : false);
-        return (err == NO_ERROR) ? EGL_TRUE : 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->cnx->egl.eglSurfaceAttrib) {
@@ -1839,169 +1844,15 @@
     }
 
     egl_surface_t const * const s = get_surface(surface);
-    native_window_set_buffers_timestamp(s->win.get(), time);
+    native_window_set_buffers_timestamp(s->getNativeWindow(), time);
 
     return EGL_TRUE;
 }
 
-EGLClientBuffer eglCreateNativeClientBufferANDROID(const EGLint *attrib_list)
-{
-    clearError();
-
-    uint64_t producerUsage = 0;
-    uint64_t consumerUsage = 0;
-    uint32_t width = 0;
-    uint32_t height = 0;
-    uint32_t format = 0;
-    uint32_t layer_count = 1;
-    uint32_t red_size = 0;
-    uint32_t green_size = 0;
-    uint32_t blue_size = 0;
-    uint32_t alpha_size = 0;
-
-#define GET_NONNEGATIVE_VALUE(case_name, target) \
-    case case_name: \
-        if (value >= 0) { \
-            target = value; \
-        } else { \
-            return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0); \
-        } \
-        break
-
-    if (attrib_list) {
-        while (*attrib_list != EGL_NONE) {
-            GLint attr = *attrib_list++;
-            GLint value = *attrib_list++;
-            switch (attr) {
-                GET_NONNEGATIVE_VALUE(EGL_WIDTH, width);
-                GET_NONNEGATIVE_VALUE(EGL_HEIGHT, height);
-                GET_NONNEGATIVE_VALUE(EGL_RED_SIZE, red_size);
-                GET_NONNEGATIVE_VALUE(EGL_GREEN_SIZE, green_size);
-                GET_NONNEGATIVE_VALUE(EGL_BLUE_SIZE, blue_size);
-                GET_NONNEGATIVE_VALUE(EGL_ALPHA_SIZE, alpha_size);
-                GET_NONNEGATIVE_VALUE(EGL_LAYER_COUNT_ANDROID, layer_count);
-                case EGL_NATIVE_BUFFER_USAGE_ANDROID:
-                    if (value & EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID) {
-                        producerUsage |= GRALLOC1_PRODUCER_USAGE_PROTECTED;
-                    }
-                    if (value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID) {
-                        producerUsage |= GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET;
-                    }
-                    if (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID) {
-                        consumerUsage |= GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE;
-                    }
-                    break;
-                default:
-                    return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
-            }
-        }
-    }
-#undef GET_NONNEGATIVE_VALUE
-
-    // Validate format.
-    if (red_size == 8 && green_size == 8 && blue_size == 8) {
-        if (alpha_size == 8) {
-            format = HAL_PIXEL_FORMAT_RGBA_8888;
-        } else {
-            format = HAL_PIXEL_FORMAT_RGB_888;
-        }
-    } else if (red_size == 5 && green_size == 6 && blue_size == 5 &&
-               alpha_size == 0) {
-        format = HAL_PIXEL_FORMAT_RGB_565;
-    } else {
-        ALOGE("Invalid native pixel format { r=%u, g=%u, b=%u, a=%u }",
-                red_size, green_size, blue_size, alpha_size);
-        return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
-    }
-
-#define CHECK_ERROR_CONDITION(message) \
-    if (err != NO_ERROR) { \
-        ALOGE(message); \
-        goto error_condition; \
-    }
-
-    // The holder is used to destroy the buffer if an error occurs.
-    GraphicBuffer* gBuffer = new GraphicBuffer();
-    sp<IServiceManager> sm = defaultServiceManager();
-    sp<IBinder> surfaceFlinger = sm->getService(String16("SurfaceFlinger"));
-    sp<IBinder> allocator;
-    Parcel sc_data, sc_reply, data, reply;
-    status_t err = NO_ERROR;
-    if (sm == NULL) {
-        ALOGE("Unable to connect to ServiceManager");
-        goto error_condition;
-    }
-
-    // Obtain an allocator.
-    if (surfaceFlinger == NULL) {
-        ALOGE("Unable to connect to SurfaceFlinger");
-        goto error_condition;
-    }
-    sc_data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
-    err = surfaceFlinger->transact(
-            BnSurfaceComposer::CREATE_GRAPHIC_BUFFER_ALLOC, sc_data, &sc_reply);
-    CHECK_ERROR_CONDITION("Unable to obtain allocator from SurfaceFlinger");
-    allocator = sc_reply.readStrongBinder();
-
-    if (allocator == NULL) {
-        ALOGE("Unable to obtain an ISurfaceComposer");
-        goto error_condition;
-    }
-    data.writeInterfaceToken(String16("android.ui.IGraphicBufferAlloc"));
-    err = data.writeUint32(width);
-    CHECK_ERROR_CONDITION("Unable to write width");
-    err = data.writeUint32(height);
-    CHECK_ERROR_CONDITION("Unable to write height");
-    err = data.writeInt32(static_cast<int32_t>(format));
-    CHECK_ERROR_CONDITION("Unable to write format");
-    err = data.writeUint32(layer_count);
-    CHECK_ERROR_CONDITION("Unable to write layer count");
-    err = data.writeUint64(producerUsage);
-    CHECK_ERROR_CONDITION("Unable to write producer usage");
-    err = data.writeUint64(consumerUsage);
-    CHECK_ERROR_CONDITION("Unable to write consumer usage");
-    err = data.writeUtf8AsUtf16(
-            std::string("[eglCreateNativeClientBufferANDROID pid ") +
-            std::to_string(getpid()) + ']');
-    CHECK_ERROR_CONDITION("Unable to write requestor name");
-    err = allocator->transact(IBinder::FIRST_CALL_TRANSACTION, data,
-            &reply);
-    CHECK_ERROR_CONDITION(
-            "Unable to request buffer allocation from surface composer");
-    err = reply.readInt32();
-    CHECK_ERROR_CONDITION("Unable to obtain buffer from surface composer");
-    err = reply.read(*gBuffer);
-    CHECK_ERROR_CONDITION("Unable to read buffer from surface composer");
-
-    err = gBuffer->initCheck();
-    if (err != NO_ERROR) {
-        ALOGE("Unable to create native buffer "
-                "{ w=%u, h=%u, f=%u, pu=%" PRIx64 " cu=%" PRIx64 ", lc=%u} %#x",
-                width, height, format, producerUsage, consumerUsage,
-                layer_count, err);
-        goto error_condition;
-    }
-    ALOGV("Created new native buffer %p { w=%u, h=%u, f=%u, pu=%" PRIx64
-          " cu=%" PRIx64 ", lc=%u}",
-            gBuffer, width, height, format, producerUsage, consumerUsage,
-            layer_count);
-    return static_cast<EGLClientBuffer>(gBuffer->getNativeBuffer());
-
-#undef CHECK_ERROR_CONDITION
-
-error_condition:
-    // Delete the buffer.
-    sp<GraphicBuffer> holder(gBuffer);
-    return setError(EGL_BAD_ALLOC, (EGLClientBuffer)0);
-}
-
 EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) {
     clearError();
-
     if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
-
-    const GraphicBuffer* graphicBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
-    return static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer());
+    return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
 }
 
 // ----------------------------------------------------------------------------
@@ -2088,14 +1939,14 @@
 
     egl_surface_t const * const s = get_surface(surface);
 
-    if (!s->win.get()) {
+    if (!s->getNativeWindow()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
     uint64_t nextFrameId = 0;
-    status_t ret = native_window_get_next_frame_id(s->win.get(), &nextFrameId);
+    int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId);
 
-    if (ret != NO_ERROR) {
+    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.");
@@ -2123,7 +1974,7 @@
 
     egl_surface_t const * const s = get_surface(surface);
 
-    if (!s->win.get()) {
+    if (!s->getNativeWindow()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
@@ -2147,13 +1998,13 @@
         }
     }
 
-    status_t ret = native_window_get_compositor_timing(s->win.get(),
+    int ret = native_window_get_compositor_timing(s->getNativeWindow(),
             compositeDeadline, compositeInterval, compositeToPresentLatency);
 
     switch (ret) {
-      case NO_ERROR:
+      case 0:
         return EGL_TRUE;
-      case INVALID_OPERATION:
+      case -ENOSYS:
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
       default:
         // This should not happen. Return an error that is not in the spec
@@ -2180,7 +2031,7 @@
 
     egl_surface_t const * const s = get_surface(surface);
 
-    ANativeWindow* window = s->win.get();
+    ANativeWindow* window = s->getNativeWindow();
     if (!window) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
@@ -2213,7 +2064,7 @@
 
     egl_surface_t const * const s = get_surface(surface);
 
-    if (!s->win.get()) {
+    if (!s->getNativeWindow()) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
@@ -2265,19 +2116,19 @@
         }
     }
 
-    status_t ret = native_window_get_frame_timestamps(s->win.get(), frameId,
+    int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
             requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
             lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
             displayRetireTime, dequeueReadyTime, releaseTime);
 
     switch (ret) {
-        case NO_ERROR:
+        case 0:
             return EGL_TRUE;
-        case NAME_NOT_FOUND:
+        case -ENOENT:
             return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
-        case INVALID_OPERATION:
+        case -ENOSYS:
             return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-        case BAD_VALUE:
+        case -EINVAL:
             return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
         default:
             // This should not happen. Return an error that is not in the spec
@@ -2304,7 +2155,7 @@
 
     egl_surface_t const * const s = get_surface(surface);
 
-    ANativeWindow* window = s->win.get();
+    ANativeWindow* window = s->getNativeWindow();
     if (!window) {
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index e46716c..dc1a4af 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -20,11 +20,16 @@
 
 #include "egl_display.h"
 
+
+#include <private/EGL/cache.h>
+
 #include <inttypes.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 
-#include <utils/Thread.h>
+#include <thread>
+
+#include <log/log.h>
 
 // Cache size limits.
 static const size_t maxKeySize = 12 * 1024;
@@ -44,6 +49,11 @@
 
 #define BC_EXT_STR "EGL_ANDROID_blob_cache"
 
+// called from android_view_ThreadedRenderer.cpp
+void egl_set_cache_filename(const char* filename) {
+    egl_cache_t::get()->setCacheFilename(filename);
+}
+
 //
 // Callback functions passed to EGL.
 //
@@ -61,8 +71,7 @@
 // egl_cache_t definition
 //
 egl_cache_t::egl_cache_t() :
-        mInitialized(false),
-        mBlobCache(NULL) {
+        mInitialized(false) {
 }
 
 egl_cache_t::~egl_cache_t() {
@@ -75,7 +84,7 @@
 }
 
 void egl_cache_t::initialize(egl_display_t *display) {
-    Mutex::Autolock lock(mMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
 
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
@@ -113,14 +122,14 @@
 }
 
 void egl_cache_t::terminate() {
-    Mutex::Autolock lock(mMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
     saveBlobCacheLocked();
     mBlobCache = NULL;
 }
 
 void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
         const void* value, EGLsizeiANDROID valueSize) {
-    Mutex::Autolock lock(mMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
 
     if (keySize < 0 || valueSize < 0) {
         ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
@@ -128,38 +137,27 @@
     }
 
     if (mInitialized) {
-        sp<BlobCache> bc = getBlobCacheLocked();
+        BlobCache* bc = getBlobCacheLocked();
         bc->set(key, keySize, value, valueSize);
 
         if (!mSavePending) {
-            class DeferredSaveThread : public Thread {
-            public:
-                DeferredSaveThread() : Thread(false) {}
-
-                virtual bool threadLoop() {
-                    sleep(deferredSaveDelay);
-                    egl_cache_t* c = egl_cache_t::get();
-                    Mutex::Autolock lock(c->mMutex);
-                    if (c->mInitialized) {
-                        c->saveBlobCacheLocked();
-                    }
-                    c->mSavePending = false;
-                    return false;
-                }
-            };
-
-            // The thread will hold a strong ref to itself until it has finished
-            // running, so there's no need to keep a ref around.
-            sp<Thread> deferredSaveThread(new DeferredSaveThread());
             mSavePending = true;
-            deferredSaveThread->run("DeferredSaveThread");
+            std::thread deferredSaveThread([this]() {
+                sleep(deferredSaveDelay);
+                std::lock_guard<std::mutex> lock(mMutex);
+                if (mInitialized) {
+                    saveBlobCacheLocked();
+                }
+                mSavePending = false;
+            });
+            deferredSaveThread.detach();
         }
     }
 }
 
 EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
         void* value, EGLsizeiANDROID valueSize) {
-    Mutex::Autolock lock(mMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
 
     if (keySize < 0 || valueSize < 0) {
         ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed");
@@ -167,23 +165,23 @@
     }
 
     if (mInitialized) {
-        sp<BlobCache> bc = getBlobCacheLocked();
+        BlobCache* bc = getBlobCacheLocked();
         return bc->get(key, keySize, value, valueSize);
     }
     return 0;
 }
 
 void egl_cache_t::setCacheFilename(const char* filename) {
-    Mutex::Autolock lock(mMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
     mFilename = filename;
 }
 
-sp<BlobCache> egl_cache_t::getBlobCacheLocked() {
-    if (mBlobCache == NULL) {
-        mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize);
+BlobCache* egl_cache_t::getBlobCacheLocked() {
+    if (mBlobCache == nullptr) {
+        mBlobCache.reset(new BlobCache(maxKeySize, maxValueSize, maxTotalSize));
         loadBlobCacheLocked();
     }
-    return mBlobCache;
+    return mBlobCache.get();
 }
 
 static uint32_t crc32c(const uint8_t* buf, size_t len) {
@@ -206,7 +204,7 @@
     if (mFilename.length() > 0 && mBlobCache != NULL) {
         size_t cacheSize = mBlobCache->getFlattenedSize();
         size_t headerSize = cacheFileHeaderSize;
-        const char* fname = mFilename.string();
+        const char* fname = mFilename.c_str();
 
         // Try to create the file with no permissions so we can write it
         // without anyone trying to read it.
@@ -241,8 +239,8 @@
             return;
         }
 
-        status_t err = mBlobCache->flatten(buf + headerSize, cacheSize);
-        if (err != OK) {
+        int err = mBlobCache->flatten(buf + headerSize, cacheSize);
+        if (err < 0) {
             ALOGE("error writing cache contents: %s (%d)", strerror(-err),
                     -err);
             delete [] buf;
@@ -275,10 +273,10 @@
     if (mFilename.length() > 0) {
         size_t headerSize = cacheFileHeaderSize;
 
-        int fd = open(mFilename.string(), O_RDONLY, 0);
+        int fd = open(mFilename.c_str(), O_RDONLY, 0);
         if (fd == -1) {
             if (errno != ENOENT) {
-                ALOGE("error opening cache file %s: %s (%d)", mFilename.string(),
+                ALOGE("error opening cache file %s: %s (%d)", mFilename.c_str(),
                         strerror(errno), errno);
             }
             return;
@@ -323,8 +321,8 @@
             return;
         }
 
-        status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize);
-        if (err != OK) {
+        int err = mBlobCache->unflatten(buf + headerSize, cacheSize);
+        if (err < 0) {
             ALOGE("error reading cache contents: %s (%d)", strerror(-err),
                     -err);
             munmap(buf, fileSize);
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index e7f1712..56360f0 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -20,10 +20,11 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <utils/BlobCache.h>
-#include <utils/Mutex.h>
-#include <utils/String8.h>
-#include <utils/StrongPointer.h>
+#include "BlobCache.h"
+
+#include <memory>
+#include <mutex>
+#include <string>
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -79,7 +80,7 @@
     // key/value blob pairs.  If the BlobCache object has not yet been created,
     // this will do so, loading the serialized cache contents from disk if
     // possible.
-    sp<BlobCache> getBlobCacheLocked();
+    BlobCache* getBlobCacheLocked();
 
     // saveBlobCache attempts to save the current contents of mBlobCache to
     // disk.
@@ -100,14 +101,14 @@
     // mBlobCache is the cache in which the key/value blob pairs are stored.  It
     // is initially NULL, and will be initialized by getBlobCacheLocked the
     // first time it's needed.
-    sp<BlobCache> mBlobCache;
+    std::unique_ptr<BlobCache> mBlobCache;
 
     // mFilename is the name of the file for storing cache contents in between
     // program invocations.  It is initialized to an empty string at
     // construction time, and can be set with the setCacheFilename method.  An
     // empty string indicates that the cache should not be saved to or restored
     // from disk.
-    String8 mFilename;
+    std::string mFilename;
 
     // mSavePending indicates whether or not a deferred save operation is
     // pending.  Each time a key/value pair is inserted into the cache via
@@ -118,7 +119,7 @@
 
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables. It must be locked whenever the member variables are accessed.
-    mutable Mutex mMutex;
+    mutable std::mutex mMutex;
 
     // sCache is the singleton egl_cache_t object.
     static egl_cache_t sCache;
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index c804fa7..b696920 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -21,14 +21,15 @@
 
 #include "../egl_impl.h"
 
+#include <private/EGL/display.h>
+
 #include "egl_cache.h"
 #include "egl_object.h"
 #include "egl_tls.h"
+#include "egl_trace.h"
 #include "Loader.h"
 #include <cutils/properties.h>
 
-#include <utils/Trace.h>
-
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
@@ -55,6 +56,11 @@
     return false;
 }
 
+int egl_get_init_count(EGLDisplay dpy) {
+    egl_display_t* eglDisplay = egl_display_t::get(dpy);
+    return eglDisplay ? eglDisplay->getRefsCount() : 0;
+}
+
 egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
 
 egl_display_t::egl_display_t() :
@@ -75,18 +81,18 @@
 }
 
 void egl_display_t::addObject(egl_object_t* object) {
-    Mutex::Autolock _l(lock);
-    objects.add(object);
+    std::lock_guard<std::mutex> _l(lock);
+    objects.insert(object);
 }
 
 void egl_display_t::removeObject(egl_object_t* object) {
-    Mutex::Autolock _l(lock);
-    objects.remove(object);
+    std::lock_guard<std::mutex> _l(lock);
+    objects.erase(object);
 }
 
 bool egl_display_t::getObject(egl_object_t* object) const {
-    Mutex::Autolock _l(lock);
-    if (objects.indexOf(object) >= 0) {
+    std::lock_guard<std::mutex> _l(lock);
+    if (objects.find(object) != objects.end()) {
         if (object->getDisplay() == this) {
             object->incRef();
             return true;
@@ -104,7 +110,7 @@
 
 EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) {
 
-    Mutex::Autolock _l(lock);
+    std::lock_guard<std::mutex> _l(lock);
     ATRACE_CALL();
 
     // get our driver loader
@@ -125,24 +131,26 @@
 
 EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) {
 
-    {
-        Mutex::Autolock _rf(refLock);
-
+    { // scope for refLock
+        std::unique_lock<std::mutex> _l(refLock);
         refs++;
         if (refs > 1) {
             if (major != NULL)
                 *major = VERSION_MAJOR;
             if (minor != NULL)
                 *minor = VERSION_MINOR;
-            while(!eglIsInitialized) refCond.wait(refLock);
+            while(!eglIsInitialized) {
+                refCond.wait(_l);
+            }
             return EGL_TRUE;
         }
-
-        while(eglIsInitialized) refCond.wait(refLock);
+        while(eglIsInitialized) {
+            refCond.wait(_l);
+        }
     }
 
-    {
-        Mutex::Autolock _l(lock);
+    { // scope for lock
+        std::lock_guard<std::mutex> _l(lock);
 
         setGLHooksThreadSpecific(&gHooksNoContext);
 
@@ -179,20 +187,19 @@
         }
 
         // the query strings are per-display
-        mVendorString.setTo(sVendorString);
-        mVersionString.setTo(sVersionString);
-        mClientApiString.setTo(sClientApiString);
+        mVendorString = sVendorString;
+        mVersionString = sVersionString;
+        mClientApiString = sClientApiString;
 
-        mExtensionString.setTo(gBuiltinExtensionString);
+        mExtensionString = gBuiltinExtensionString;
         char const* start = gExtensionString;
         do {
             // length of the extension name
             size_t len = strcspn(start, " ");
             if (len) {
                 // NOTE: we could avoid the copy if we had strnstr.
-                const String8 ext(start, len);
-                if (findExtension(disp.queryString.extensions, ext.string(),
-                        len)) {
+                const std::string ext(start, len);
+                if (findExtension(disp.queryString.extensions, ext.c_str(), len)) {
                     mExtensionString.append(ext + " ");
                 }
                 // advance to the next extension name, skipping the space.
@@ -220,10 +227,10 @@
             *minor = VERSION_MINOR;
     }
 
-    {
-        Mutex::Autolock _rf(refLock);
+    { // scope for refLock
+        std::unique_lock<std::mutex> _l(refLock);
         eglIsInitialized = true;
-        refCond.broadcast();
+        refCond.notify_all();
     }
 
     return EGL_TRUE;
@@ -231,8 +238,8 @@
 
 EGLBoolean egl_display_t::terminate() {
 
-    {
-        Mutex::Autolock _rl(refLock);
+    { // scope for refLock
+        std::unique_lock<std::mutex> _rl(refLock);
         if (refs == 0) {
             /*
              * From the EGL spec (3.2):
@@ -252,8 +259,8 @@
 
     EGLBoolean res = EGL_FALSE;
 
-    {
-        Mutex::Autolock _l(lock);
+    { // scope for lock
+        std::lock_guard<std::mutex> _l(lock);
 
         egl_connection_t* const cnx = &gEGLImpl;
         if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
@@ -268,15 +275,14 @@
 
         // Reset the extension string since it will be regenerated if we get
         // reinitialized.
-        mExtensionString.setTo("");
+        mExtensionString.clear();
 
         // Mark all objects remaining in the list as terminated, unless
         // there are no reference to them, it which case, we're free to
         // delete them.
         size_t count = objects.size();
         ALOGW_IF(count, "eglTerminate() called w/ %zu objects remaining", count);
-        for (size_t i=0 ; i<count ; i++) {
-            egl_object_t* o = objects.itemAt(i);
+        for (auto o : objects) {
             o->destroy();
         }
 
@@ -284,10 +290,10 @@
         objects.clear();
     }
 
-    {
-        Mutex::Autolock _rl(refLock);
+    { // scope for refLock
+        std::unique_lock<std::mutex> _rl(refLock);
         eglIsInitialized = false;
-        refCond.broadcast();
+        refCond.notify_all();
     }
 
     return res;
@@ -312,7 +318,7 @@
     SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
 
     { // scope for the lock
-        Mutex::Autolock _l(lock);
+        std::lock_guard<std::mutex> _l(lock);
         cur_c->onLooseCurrent();
 
     }
@@ -338,7 +344,7 @@
     SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
 
     { // scope for the lock
-        Mutex::Autolock _l(lock);
+        std::lock_guard<std::mutex> _l(lock);
         if (c) {
             result = c->cnx->egl.eglMakeCurrent(
                     disp.dpy, impl_draw, impl_read, impl_ctx);
@@ -370,7 +376,7 @@
     if (!nameLen) {
         nameLen = strlen(name);
     }
-    return findExtension(mExtensionString.string(), name, nameLen);
+    return findExtension(mExtensionString.c_str(), name, nameLen);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index f5e7294..661f47e 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -21,14 +21,15 @@
 #include <stdint.h>
 #include <stddef.h>
 
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <unordered_set>
+
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
 #include <cutils/compiler.h>
-#include <utils/SortedVector.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-#include <utils/String8.h>
 
 #include "egldefs.h"
 #include "../hooks.h"
@@ -80,10 +81,10 @@
     inline bool isValid() const { return magic == '_dpy'; }
     inline bool isAlive() const { return isValid(); }
 
-    char const * getVendorString() const { return mVendorString.string(); }
-    char const * getVersionString() const { return mVersionString.string(); }
-    char const * getClientApiString() const { return mClientApiString.string(); }
-    char const * getExtensionString() const { return mExtensionString.string(); }
+    char const * getVendorString() const { return mVendorString.c_str(); }
+    char const * getVersionString() const { return mVersionString.c_str(); }
+    char const * getClientApiString() const { return mClientApiString.c_str(); }
+    char const * getExtensionString() const { return mExtensionString.c_str(); }
 
     bool haveExtension(const char* name, size_t nameLen = 0) const;
 
@@ -116,13 +117,14 @@
 
             uint32_t                    refs;
             bool                        eglIsInitialized;
-    mutable Mutex                       lock, refLock;
-    mutable Condition                   refCond;
-            SortedVector<egl_object_t*> objects;
-            String8 mVendorString;
-            String8 mVersionString;
-            String8 mClientApiString;
-            String8 mExtensionString;
+    mutable std::mutex                  lock;
+    mutable std::mutex                  refLock;
+    mutable std::condition_variable     refCond;
+            std::unordered_set<egl_object_t*> objects;
+            std::string mVendorString;
+            std::string mVersionString;
+            std::string mClientApiString;
+            std::string mExtensionString;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index b553d71..7ed34be 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -60,21 +60,24 @@
         egl_connection_t const* cnx) :
     egl_object_t(dpy), surface(surface), config(config), win(win), cnx(cnx),
     connected(true)
-{}
+{
+    if (win) {
+        win->incStrong(this);
+    }
+}
 
 egl_surface_t::~egl_surface_t() {
-    ANativeWindow* const window = win.get();
-    if (window != NULL) {
+    if (win != NULL) {
         disconnect();
+        win->decStrong(this);
     }
 }
 
 void egl_surface_t::disconnect() {
-    ANativeWindow* const window = win.get();
-    if (window != NULL && connected) {
-        native_window_set_buffers_format(window, 0);
-        if (native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL)) {
-            ALOGW("EGLNativeWindowType %p disconnect failed", window);
+    if (win != NULL && connected) {
+        native_window_set_buffers_format(win, 0);
+        if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) {
+            ALOGW("EGLNativeWindowType %p disconnect failed", win);
         }
         connected = false;
     }
@@ -107,22 +110,20 @@
      * add the extensions always handled by the wrapper
      */
 
-    if (gl_extensions.isEmpty()) {
+    if (gl_extensions.empty()) {
         // call the implementation's glGetString(GL_EXTENSIONS)
         const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
-        gl_extensions.setTo(exts);
-        if (gl_extensions.find("GL_EXT_debug_marker") < 0) {
-            String8 temp("GL_EXT_debug_marker ");
-            temp.append(gl_extensions);
-            gl_extensions.setTo(temp);
+        gl_extensions = exts;
+        if (gl_extensions.find("GL_EXT_debug_marker") != std::string::npos) {
+            gl_extensions.insert(0, "GL_EXT_debug_marker ");
         }
 
         // tokenize the supported extensions for the glGetStringi() wrapper
         std::stringstream ss;
         std::string str;
-        ss << gl_extensions.string();
+        ss << gl_extensions;
         while (ss >> str) {
-            tokenized_gl_extensions.push(String8(str.c_str()));
+            tokenized_gl_extensions.push_back(str);
         }
     }
 }
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index f4012ab..8988905 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -17,18 +17,20 @@
 #ifndef ANDROID_EGL_OBJECT_H
 #define ANDROID_EGL_OBJECT_H
 
+#include <atomic>
 #include <stdint.h>
 #include <stddef.h>
 
+#include <string>
+#include <vector>
+
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <utils/StrongPointer.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-
 #include <system/window.h>
 
+#include <log/log.h>
+
 #include "egl_display.h"
 
 // ----------------------------------------------------------------------------
@@ -133,9 +135,16 @@
             EGLNativeWindowType win, EGLSurface surface,
             egl_connection_t const* cnx);
 
+    ANativeWindow* getNativeWindow() { return win; }
+    ANativeWindow* getNativeWindow() const { return win; }
+
+    // Try to keep the order of these fields and size unchanged. It's not public API, but
+    // it's not hard to imagine native games accessing them.
     EGLSurface surface;
     EGLConfig config;
-    sp<ANativeWindow> win;
+private:
+    ANativeWindow* win;
+public:
     egl_connection_t const* cnx;
 private:
     bool connected;
@@ -161,8 +170,8 @@
     EGLSurface draw;
     egl_connection_t const* cnx;
     int version;
-    String8 gl_extensions;
-    Vector<String8> tokenized_gl_extensions;
+    std::string gl_extensions;
+    std::vector<std::string> tokenized_gl_extensions;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index ca8684e..8508c5f 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -20,8 +20,7 @@
 
 #include <cutils/properties.h>
 #include <log/log.h>
-#include <utils/CallStack.h>
-
+#include "CallStack.h"
 
 namespace android {
 
@@ -74,7 +73,7 @@
             char value[PROPERTY_VALUE_MAX];
             property_get("debug.egl.callstack", value, "0");
             if (atoi(value)) {
-                CallStack stack(LOG_TAG);
+                CallStack::log(LOG_TAG);
             }
         }
         tls->error = error;
diff --git a/opengl/libs/EGL/egl_trace.h b/opengl/libs/EGL/egl_trace.h
new file mode 100644
index 0000000..7664de2
--- /dev/null
+++ b/opengl/libs/EGL/egl_trace.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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
+
+#if defined(__ANDROID__)
+
+#include <stdint.h>
+
+#include <cutils/trace.h>
+
+// See <cutils/trace.h> for more ATRACE_* macros.
+
+// ATRACE_NAME traces from its location until the end of its enclosing scope.
+#define _PASTE(x, y) x ## y
+#define PASTE(x, y) _PASTE(x,y)
+#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
+
+// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
+#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
+
+namespace android {
+
+class EglScopedTrace {
+public:
+    inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) {
+        atrace_begin(mTag, name);
+    }
+
+    inline ~EglScopedTrace() {
+        atrace_end(mTag);
+    }
+
+private:
+    uint64_t mTag;
+};
+
+}; // namespace android
+
+#else // !__ANDROID__
+
+#define ATRACE_NAME(...)
+#define ATRACE_CALL()
+
+#endif // __ANDROID__
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index 450c402..c05e840 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -18,7 +18,7 @@
 #include <errno.h>
 #include <stdlib.h>
 
-#include <android/log.h>
+#include <log/log.h>
 
 #include "egldefs.h"
 
diff --git a/opengl/libs/EGL/include/private/EGL/cache.h b/opengl/libs/EGL/include/private/EGL/cache.h
new file mode 100644
index 0000000..0a176a8
--- /dev/null
+++ b/opengl/libs/EGL/include/private/EGL/cache.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 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 <cutils/compiler.h>
+
+namespace android {
+
+ANDROID_API void egl_set_cache_filename(const char* filename);
+
+} // namespace android
diff --git a/opengl/libs/EGL/include/private/EGL/display.h b/opengl/libs/EGL/include/private/EGL/display.h
new file mode 100644
index 0000000..9560de2
--- /dev/null
+++ b/opengl/libs/EGL/include/private/EGL/display.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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 <EGL/egl.h>
+
+#include <cutils/compiler.h>
+
+namespace android {
+
+ANDROID_API int egl_get_init_count(EGLDisplay dpy);
+
+} // namespace android
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index c206041..f7fde96 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -19,7 +19,7 @@
 #include <string.h>
 #include <sys/ioctl.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <cutils/properties.h>
 
 #include "../hooks.h"
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index e698fcd..bacd4b4 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -19,7 +19,7 @@
 #include <string.h>
 #include <sys/ioctl.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <cutils/properties.h>
 
 #include <GLES/gl.h>
diff --git a/opengl/tests/lib/Android.mk b/opengl/tests/lib/Android.mk
index 0f1c925..ea94bc1 100644
--- a/opengl/tests/lib/Android.mk
+++ b/opengl/tests/lib/Android.mk
@@ -24,6 +24,7 @@
 
 LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -Wall -Wextra -Werror
 
+LOCAL_SHARED_LIBRARIES += libgui
 LOCAL_STATIC_LIBRARIES := libarect
 
 include $(BUILD_STATIC_LIBRARY)
diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml
index 518c369..af13395 100755
--- a/opengl/tools/glgen2/registry/egl.xml
+++ b/opengl/tools/glgen2/registry/egl.xml
@@ -723,6 +723,23 @@
         <enum value="0x3339" name="EGL_COLOR_COMPONENT_TYPE_EXT"/>
         <enum value="0x333A" name="EGL_COLOR_COMPONENT_TYPE_FIXED_EXT"/>
         <enum value="0x333B" name="EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT"/>
+            <unused start="0x333C" end="0x333E"/>
+        <enum value="0x333F" name="EGL_GL_COLORSPACE_BT2020_LINEAR_EXT"/>
+        <enum value="0x3340" name="EGL_GL_COLORSPACE_BT2020_PQ_EXT"/>
+        <enum value="0x3341" name="EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT"/>
+        <enum value="0x3342" name="EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT"/>
+        <enum value="0x3343" name="EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT"/>
+        <enum value="0x3344" name="EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT"/>
+        <enum value="0x3345" name="EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT"/>
+        <enum value="0x3346" name="EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT"/>
+        <enum value="0x3347" name="EGL_SMPTE2086_WHITE_POINT_X_EXT"/>
+        <enum value="0x3348" name="EGL_SMPTE2086_WHITE_POINT_Y_EXT"/>
+        <enum value="0x3349" name="EGL_SMPTE2086_MAX_LUMINANCE_EXT"/>
+        <enum value="0x334A" name="EGL_SMPTE2086_MIN_LUMINANCE_EXT"/>
+        <enum value="50000"  name="EGL_METADATA_SCALING_EXT"/>
+            <unused start="0x334B" end="0x334F"/>
+        <enum value="0x3350" name="EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT"/>
+            <unused start="0x3351" end="0x339F"/>
     </enums>
 
     <enums namespace="EGL" start="0x33A0" end="0x33AF" vendor="ANGLE" comment="Reserved for Shannon Woods (Bug 13175)">
@@ -765,8 +782,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="0x3420" end="0x3FFF" vendor="KHR">
-            <unused start="0x3420" end="0x3FFF" comment="Reserved for future use"/>
+    <enums namespace="EGL" start="0x3470" end="0x3FFF" vendor="KHR">
+            <unused start="0x3470" end="0x3FFF" comment="Reserved for future use"/>
     </enums>
 
     <enums namespace="EGL" start="0x8F70" end="0x8F7F" vendor="HI" comment="For Mark Callow, Khronos bug 4055. Shared with GL.">
@@ -1859,6 +1876,21 @@
                 <command name="eglQueryDisplayAttribEXT"/>
             </require>
         </extension>
+        <extension name="EGL_EXT_gl_colorspace_bt2020_linear" supported="egl">
+            <require>
+                <enum name="EGL_GL_COLORSPACE_BT2020_LINEAR_EXT"/>
+            </require>
+        </extension>
+        <extension name="EGL_EXT_gl_colorspace_bt2020_pq" supported="egl">
+            <require>
+                <enum name="EGL_GL_COLORSPACE_BT2020_PQ_EXT"/>
+            </require>
+        </extension>
+        <extension name="EGL_EXT_gl_colorspace_scrgb_linear" supported="egl">
+            <require>
+                <enum name="EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT"/>
+            </require>
+        </extension>
         <extension name="EGL_EXT_image_dma_buf_import" supported="egl">
             <require>
                 <enum name="EGL_LINUX_DMA_BUF_EXT"/>
@@ -1955,6 +1987,22 @@
                 <command name="eglStreamConsumerOutputEXT"/>
             </require>
         </extension>
+        <extension name="EGL_EXT_surface_SMPTE2086_metadata" supported="egl">
+            <require>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT"/>
+                <enum name="EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT"/>
+                <enum name="EGL_SMPTE2086_WHITE_POINT_X_EXT"/>
+                <enum name="EGL_SMPTE2086_WHITE_POINT_Y_EXT"/>
+                <enum name="EGL_SMPTE2086_MAX_LUMINANCE_EXT"/>
+                <enum name="EGL_SMPTE2086_MIN_LUMINANCE_EXT"/>
+                <enum name="EGL_METADATA_SCALING_EXT"/>
+            </require>
+        </extension>
+
         <extension name="EGL_EXT_swap_buffers_with_damage" supported="egl">
             <require>
                 <command name="eglSwapBuffersWithDamageEXT"/>
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 89779af..ad5955a 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -25,6 +25,7 @@
     DisplayHardware/ComposerHal.cpp \
     DisplayHardware/FramebufferSurface.cpp \
     DisplayHardware/HWC2.cpp \
+    DisplayHardware/HWComposerBufferCache.cpp \
     DisplayHardware/PowerHAL.cpp \
     DisplayHardware/VirtualDisplaySurface.cpp \
     Effects/Daltonizer.cpp \
@@ -64,13 +65,6 @@
         DisplayHardware/HWComposer_hwc1.cpp
 endif
 
-ifeq ($(TARGET_BOARD_PLATFORM),omap4)
-    LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
-endif
-ifeq ($(TARGET_BOARD_PLATFORM),s5pc110)
-    LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
-endif
-
 ifeq ($(TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS),true)
     LOCAL_CFLAGS += -DFORCE_HWC_COPY_FOR_VIRTUAL_DISPLAYS
 endif
@@ -83,39 +77,6 @@
     LOCAL_CFLAGS += -DRUNNING_WITHOUT_SYNC_FRAMEWORK
 endif
 
-# The following two BoardConfig variables define (respectively):
-#
-#   - The phase offset between hardware vsync and when apps are woken up by the
-#     Choreographer callback
-#   - The phase offset between hardware vsync and when SurfaceFlinger wakes up
-#     to consume input
-#
-# Their values can be tuned to trade off between display pipeline latency (both
-# overall latency and the lengths of the app --> SF and SF --> display phases)
-# and frame delivery jitter (which typically manifests as "jank" or "jerkiness"
-# while interacting with the device). The default values should produce a
-# relatively low amount of jitter at the expense of roughly two frames of
-# app --> display latency, and unless significant testing is performed to avoid
-# increased display jitter (both manual investigation using systrace [1] and
-# automated testing using dumpsys gfxinfo [2] are recommended), they should not
-# be modified.
-#
-# [1] https://developer.android.com/studio/profile/systrace.html
-# [2] https://developer.android.com/training/testing/performance.html
-
-# These are left just for non-treble devices
-ifneq ($(VSYNC_EVENT_PHASE_OFFSET_NS),)
-    LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=$(VSYNC_EVENT_PHASE_OFFSET_NS)
-else
-    LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=1000000
-endif
-
-ifneq ($(SF_VSYNC_EVENT_PHASE_OFFSET_NS),)
-    LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=$(SF_VSYNC_EVENT_PHASE_OFFSET_NS)
-else
-    LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=1000000
-endif
-
 ifneq ($(PRESENT_TIME_OFFSET_FROM_VSYNC_NS),)
     LOCAL_CFLAGS += -DPRESENT_TIME_OFFSET_FROM_VSYNC_NS=$(PRESENT_TIME_OFFSET_FROM_VSYNC_NS)
 else
@@ -205,6 +166,7 @@
     libbinder \
     libutils \
     libui \
+    libgui \
     libdl
 
 LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index ae6e0cc..9a0e94e 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -138,7 +138,7 @@
     status_t err = acquireBufferLocked(&item, 0);
     if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
 #ifdef USE_HWC2
-        mHwcBufferCache->getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
+        mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
                 &outSlot, &outBuffer);
 #else
         outBuffer = mCurrentBuffer;
@@ -179,7 +179,7 @@
 
     outFence = item.mFence;
 #ifdef USE_HWC2
-    mHwcBufferCache->getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
+    mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
             &outSlot, &outBuffer);
     outDataspace = item.mDataSpace;
 #else
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 5eea6b6..69a72d7 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -18,14 +18,13 @@
 #define ANDROID_SF_FRAMEBUFFER_SURFACE_H
 
 #include "DisplaySurface.h"
+#include "HWComposerBufferCache.h"
 
 #include <stdint.h>
 #include <sys/types.h>
 
 #include <gui/ConsumerBase.h>
 
-#include <memory>
-
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -33,7 +32,6 @@
 class Rect;
 class String8;
 class HWComposer;
-class HWComposerBufferCache;
 
 // ---------------------------------------------------------------------------
 
@@ -96,8 +94,7 @@
     HWComposer& mHwc;
 
 #ifdef USE_HWC2
-    std::unique_ptr<HWComposerBufferCache> mHwcBufferCache =
-        std::make_unique<HWComposerBufferCache>();
+    HWComposerBufferCache mHwcBufferCache;
 
     // Previous buffer to release after getting an updated retire fence
     bool mHasPendingRelease;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index e1138af..6644bd9 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -925,41 +925,5 @@
     *this = DisplayData();
 }
 
-void HWComposerBufferCache::clear()
-{
-    mBuffers.clear();
-}
-
-void HWComposerBufferCache::getHwcBuffer(int slot,
-        const sp<GraphicBuffer>& buffer,
-        uint32_t* outSlot, sp<GraphicBuffer>* outBuffer)
-{
-#ifdef BYPASS_IHWC
-    *outSlot = slot;
-    *outBuffer = buffer;
-#else
-    if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0) {
-        // default to slot 0
-        slot = 0;
-    }
-
-    if (static_cast<size_t>(slot) >= mBuffers.size()) {
-        mBuffers.resize(slot + 1);
-    }
-
-    *outSlot = slot;
-
-    if (mBuffers[slot] == buffer) {
-        // already cached in HWC, skip sending the buffer
-        *outBuffer = nullptr;
-    } else {
-        *outBuffer = buffer;
-
-        // update cache
-        mBuffers[slot] = buffer;
-    }
-#endif
-}
-
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 20cca39..117db4a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -26,8 +26,6 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/BufferQueue.h>
-
 #include <ui/Fence.h>
 
 #include <utils/BitSet.h>
@@ -236,19 +234,6 @@
     mutable std::atomic<bool> mDumpMayLockUp;
 };
 
-class HWComposerBufferCache {
-public:
-    void clear();
-
-    void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer,
-            uint32_t* outSlot, sp<GraphicBuffer>* outBuffer);
-
-private:
-    // a vector as we expect "slot" to be in the range of [0, 63] (that is,
-    // less than BufferQueue::NUM_BUFFER_SLOTS).
-    std::vector<sp<GraphicBuffer>> mBuffers;
-};
-
 // ---------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp
new file mode 100644
index 0000000..6b91224
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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 "HWComposerBufferCache.h"
+
+#include <gui/BufferQueue.h>
+
+namespace android {
+
+HWComposerBufferCache::HWComposerBufferCache()
+{
+    mBuffers.reserve(BufferQueue::NUM_BUFFER_SLOTS);
+}
+
+void HWComposerBufferCache::getHwcBuffer(int slot,
+        const sp<GraphicBuffer>& buffer,
+        uint32_t* outSlot, sp<GraphicBuffer>* outBuffer)
+{
+#ifdef BYPASS_IHWC
+    *outSlot = slot;
+    *outBuffer = buffer;
+#else
+    if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0) {
+        // default to slot 0
+        slot = 0;
+    }
+
+    if (static_cast<size_t>(slot) >= mBuffers.size()) {
+        mBuffers.resize(slot + 1);
+    }
+
+    *outSlot = slot;
+
+    if (mBuffers[slot] == buffer) {
+        // already cached in HWC, skip sending the buffer
+        *outBuffer = nullptr;
+    } else {
+        *outBuffer = buffer;
+
+        // update cache
+        mBuffers[slot] = buffer;
+    }
+#endif
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h
new file mode 100644
index 0000000..a008ca9
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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_SF_HWCOMPOSERBUFFERCACHE_H
+#define ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
+
+#include <stdint.h>
+
+#include <utils/StrongPointer.h>
+
+#include <vector>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class GraphicBuffer;
+
+// With HIDLized hwcomposer HAL, the HAL can maintain a buffer cache for each
+// HWC display and layer.  When updating a display target or a layer buffer,
+// we have the option to send the buffer handle over or to request the HAL to
+// retrieve it from its cache.  The latter is cheaper since it eliminates the
+// overhead to transfer the handle over the trasport layer, and the overhead
+// for the HAL to clone and retain the handle.
+//
+// To be able to find out whether a buffer is already in the HAL's cache, we
+// use HWComposerBufferCache to mirror the cache in SF.
+class HWComposerBufferCache {
+public:
+    HWComposerBufferCache();
+
+    // Given a buffer queue slot and buffer, return the HWC cache slot and
+    // buffer to be sent to HWC.
+    //
+    // outBuffer is set to buffer when buffer is not in the HWC cache;
+    // otherwise, outBuffer is set to nullptr.
+    void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer,
+            uint32_t* outSlot, sp<GraphicBuffer>* outBuffer);
+
+private:
+    // a vector as we expect "slot" to be in the range of [0, 63] (that is,
+    // less than BufferQueue::NUM_BUFFER_SLOTS).
+    std::vector<sp<GraphicBuffer>> mBuffers;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index c5a4f99..6f253ab 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -224,7 +224,7 @@
 #ifdef USE_HWC2
         uint32_t hwcSlot = 0;
         sp<GraphicBuffer> hwcBuffer;
-        mHwcBufferCache->getHwcBuffer(mFbProducerSlot, fbBuffer,
+        mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer,
                 &hwcSlot, &hwcBuffer);
 
         // TODO: Correctly propagate the dataspace from GL composition
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index fb5fcc8..ee2772a 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -18,18 +18,16 @@
 #define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
 
 #include "DisplaySurface.h"
+#include "HWComposerBufferCache.h"
 
 #include <gui/ConsumerBase.h>
 #include <gui/IGraphicBufferProducer.h>
 
-#include <memory>
-
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
 
 class HWComposer;
-class HWComposerBufferCache;
 class IProducerListener;
 
 /* This DisplaySurface implementation supports virtual displays, where GLES
@@ -255,8 +253,7 @@
     bool mMustRecompose;
 
 #ifdef USE_HWC2
-    std::unique_ptr<HWComposerBufferCache> mHwcBufferCache =
-        std::make_unique<HWComposerBufferCache>();
+    HWComposerBufferCache mHwcBufferCache;
 #endif
 };
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index c3f8665..295b229 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -276,16 +276,6 @@
     }
 }
 
-void Layer::onBuffersReleased() {
-#ifdef USE_HWC2
-    Mutex::Autolock lock(mHwcBufferCacheMutex);
-
-    for (auto info : mHwcBufferCaches) {
-        info.second.clear();
-    }
-#endif
-}
-
 void Layer::onSidebandStreamChanged() {
     if (android_atomic_release_cas(false, true, &mSidebandStreamChanged) == 0) {
         // mSidebandStreamChanged was false
@@ -780,7 +770,8 @@
     const auto& viewport = displayDevice->getViewport();
     Region visible = tr.transform(visibleRegion.intersect(viewport));
     auto hwcId = displayDevice->getHwcDisplayId();
-    auto& hwcLayer = mHwcLayers[hwcId].layer;
+    auto& hwcInfo = mHwcLayers[hwcId];
+    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(),
@@ -809,7 +800,7 @@
     }
 
     // Client layers
-    if (mHwcLayers[hwcId].forceClientComposition ||
+    if (hwcInfo.forceClientComposition ||
             (mActiveBuffer != nullptr && mActiveBuffer->handle == nullptr)) {
         ALOGV("[%s] Requesting Client composition", mName.string());
         setCompositionType(hwcId, HWC2::Composition::Client);
@@ -858,11 +849,8 @@
     uint32_t hwcSlot = 0;
     buffer_handle_t hwcHandle = nullptr;
     {
-        Mutex::Autolock lock(mHwcBufferCacheMutex);
-
-        auto& hwcBufferCache = mHwcBufferCaches[hwcId];
         sp<GraphicBuffer> hwcBuffer;
-        hwcBufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer,
+        hwcInfo.bufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer,
                 &hwcSlot, &hwcBuffer);
         if (hwcBuffer != nullptr) {
             hwcHandle = hwcBuffer->handle;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 0efdf54..6b228b0 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -47,6 +47,7 @@
 #include "Transform.h"
 
 #include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/HWComposerBufferCache.h"
 #include "RenderEngine/Mesh.h"
 #include "RenderEngine/Texture.h"
 
@@ -383,20 +384,13 @@
 #ifdef USE_HWC2
     // -----------------------------------------------------------------------
 
-    void eraseHwcLayer(int32_t hwcId) {
-        mHwcLayers.erase(hwcId);
-
-        Mutex::Autolock lock(mHwcBufferCacheMutex);
-        mHwcBufferCaches.erase(hwcId);
-    }
-
     bool hasHwcLayer(int32_t hwcId) {
         if (mHwcLayers.count(hwcId) == 0) {
             return false;
         }
         if (mHwcLayers[hwcId].layer->isAbandoned()) {
             ALOGI("Erasing abandoned layer %s on %d", mName.string(), hwcId);
-            eraseHwcLayer(hwcId);
+            mHwcLayers.erase(hwcId);
             return false;
         }
         return true;
@@ -412,11 +406,8 @@
     void setHwcLayer(int32_t hwcId, std::shared_ptr<HWC2::Layer>&& layer) {
         if (layer) {
             mHwcLayers[hwcId].layer = layer;
-
-            Mutex::Autolock lock(mHwcBufferCacheMutex);
-            mHwcBufferCaches[hwcId] = HWComposerBufferCache();
         } else {
-            eraseHwcLayer(hwcId);
+            mHwcLayers.erase(hwcId);
         }
     }
 
@@ -511,7 +502,6 @@
     // Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener
     virtual void onFrameAvailable(const BufferItem& item) override;
     virtual void onFrameReplaced(const BufferItem& item) override;
-    virtual void onBuffersReleased() override;
     virtual void onSidebandStreamChanged() override;
 
     void commitTransaction(const State& stateToCommit);
@@ -703,6 +693,7 @@
         bool clearClientTarget;
         Rect displayFrame;
         FloatRect sourceCrop;
+        HWComposerBufferCache bufferCache;
     };
 
     // A layer can be attached to multiple displays when operating in mirror mode
@@ -710,12 +701,6 @@
     // case we need to keep track. In non-mirror mode, a layer will have only one
     // HWCInfo. This map key is a display layerStack.
     std::unordered_map<int32_t, HWCInfo> mHwcLayers;
-
-    // We need one HWComposerBufferCache for each HWC display.  We cannot have
-    // HWComposerBufferCache in HWCInfo because HWCInfo can only be accessed
-    // from the main thread.
-    Mutex mHwcBufferCacheMutex;
-    std::unordered_map<int32_t, HWComposerBufferCache> mHwcBufferCaches;
 #else
     bool mIsGlesComposition;
 #endif
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index 9909bf9..e3dbecc 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -23,6 +23,9 @@
 #include "GLExtensions.h"
 #include "Mesh.h"
 
+#include <vector>
+#include <SurfaceFlinger.h>
+
 EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
 // ---------------------------------------------------------------------------
@@ -76,18 +79,21 @@
         LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
     }
 
-    // Also create our EGLContext
-    EGLint contextAttributes[] = {
-            EGL_CONTEXT_CLIENT_VERSION, contextClientVersion,      // MUST be first
+    std::vector<EGLint> contextAttributes;
+    contextAttributes.reserve(6);
+    contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+    contextAttributes.push_back(contextClientVersion);
 #ifdef EGL_IMG_context_priority
-#ifdef HAS_CONTEXT_PRIORITY
-#warning "using EGL_IMG_context_priority"
-            EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG,
+    if (SurfaceFlinger::useContextPriority) {
+        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+    }
 #endif
-#endif
-            EGL_NONE, EGL_NONE
-    };
-    EGLContext ctxt = eglCreateContext(display, config, NULL, contextAttributes);
+    contextAttributes.push_back(EGL_NONE);
+    contextAttributes.push_back(EGL_NONE);
+
+    EGLContext ctxt = eglCreateContext(display, config, NULL,
+                                       contextAttributes.data());
 
     // if can't create a GL context, we can only abort.
     LOG_ALWAYS_FATAL_IF(ctxt==EGL_NO_CONTEXT, "EGLContext creation failed");
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 46f5a1f..b93de7e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -102,33 +102,6 @@
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
 
-// This is the phase offset in nanoseconds of the software vsync event
-// relative to the vsync event reported by HWComposer.  The software vsync
-// event is when SurfaceFlinger and Choreographer-based applications run each
-// frame.
-//
-// This phase offset allows adjustment of the minimum latency from application
-// wake-up (by Choregographer) time to the time at which the resulting window
-// image is displayed.  This value may be either positive (after the HW vsync)
-// or negative (before the HW vsync).  Setting it to 0 will result in a
-// minimum latency of two vsync periods because the app and SurfaceFlinger
-// will run just after the HW vsync.  Setting it to a positive number will
-// result in the minimum latency being:
-//
-//     (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
-//
-// Note that reducing this latency makes it more likely for the applications
-// to not have their window content image ready in time.  When this happens
-// the latency will end up being an additional vsync period, and animations
-// will hiccup.  Therefore, this latency should be tuned somewhat
-// conservatively (or at least with awareness of the trade-off being made).
-static int64_t vsyncPhaseOffsetNs = getInt64<
-        ISurfaceFlingerConfigs,
-        &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
-
-// This is the phase offset at which SurfaceFlinger's composition runs.
-static constexpr int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS;
-
 // ---------------------------------------------------------------------------
 
 const String16 sHardwareTest("android.permission.HARDWARE_TEST");
@@ -137,6 +110,9 @@
 const String16 sDump("android.permission.DUMP");
 
 // ---------------------------------------------------------------------------
+int64_t SurfaceFlinger::vsyncPhaseOffsetNs;
+int64_t SurfaceFlinger::sfVsyncPhaseOffsetNs;
+bool SurfaceFlinger::useContextPriority;
 
 SurfaceFlinger::SurfaceFlinger()
     :   BnSurfaceComposer(),
@@ -180,8 +156,18 @@
         ,mEnterVrMode(false)
 #endif
 {
+
+    vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
+
+    sfVsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(1000000);
+
     ALOGI("SurfaceFlinger is starting");
 
+    useContextPriority = getBool< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::useContextPriority>(false);
+
     // debugging stuff...
     char value[PROPERTY_VALUE_MAX];
 
@@ -750,7 +736,7 @@
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
         info.presentationDeadline = hwConfig->getVsyncPeriod() -
-                SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000;
+                sfVsyncPhaseOffsetNs + 1000000;
 
         // All non-virtual displays are currently considered secure.
         info.secure = true;
@@ -2546,6 +2532,8 @@
 }
 
 status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) {
+    Mutex::Autolock _l(mStateLock);
+
     const auto& p = layer->getParent();
     const ssize_t index = (p != nullptr) ? p->removeChild(layer) :
         mCurrentState.layersSortedByZ.remove(layer);
@@ -3240,12 +3228,8 @@
 void SurfaceFlinger::appendSfConfigString(String8& result) const
 {
     result.append(" [sf");
-#ifdef HAS_CONTEXT_PRIORITY
-    result.append(" HAS_CONTEXT_PRIORITY");
-#endif
-#ifdef NEVER_DEFAULT_TO_ASYNC_MODE
-    result.append(" NEVER_DEFAULT_TO_ASYNC_MODE");
-#endif
+    result.appendFormat(" HAS_CONTEXT_PRIORITY=%d", useContextPriority);
+
     if (isLayerTripleBufferingDisabled())
         result.append(" DISABLE_TRIPLE_BUFFERING");
     result.append("]");
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d63c0bb..bfe0c9c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -104,6 +104,34 @@
                        private HWComposer::EventHandler
 {
 public:
+
+
+    // This is the phase offset in nanoseconds of the software vsync event
+    // relative to the vsync event reported by HWComposer.  The software vsync
+    // event is when SurfaceFlinger and Choreographer-based applications run each
+    // frame.
+    //
+    // This phase offset allows adjustment of the minimum latency from application
+    // wake-up time (by Choreographer) to the time at which the resulting window
+    // image is displayed.  This value may be either positive (after the HW vsync)
+    // or negative (before the HW vsync). Setting it to 0 will result in a lower
+    // latency bound of two vsync periods because the app and SurfaceFlinger
+    // will run just after the HW vsync.  Setting it to a positive number will
+    // result in the minimum latency being:
+    //
+    //     (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
+    //
+    // Note that reducing this latency makes it more likely for the applications
+    // to not have their window content image ready in time.  When this happens
+    // the latency will end up being an additional vsync period, and animations
+    // will hiccup.  Therefore, this latency should be tuned somewhat
+    // conservatively (or at least with awareness of the trade-off being made).
+    static int64_t vsyncPhaseOffsetNs;
+    static int64_t sfVsyncPhaseOffsetNs;
+
+    // Instruct the Render Engine to use EGL_IMG_context_priority is available.
+    static bool useContextPriority;
+
     static char const* getServiceName() ANDROID_API {
         return "SurfaceFlinger";
     }
@@ -659,7 +687,6 @@
     std::atomic<bool> mEnterVrMode;
 #endif
     };
-
 }; // namespace android
 
 #endif // ANDROID_SURFACE_FLINGER_H
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 2fcbdba..01226e0 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -183,7 +183,7 @@
     // we don't need to factor that in here.  Pad a little to avoid
     // weird effects if apps might be requesting times right on the edge.
     nsecs_t extraPadding = 0;
-    if (VSYNC_EVENT_PHASE_OFFSET_NS == 0) {
+    if (SurfaceFlinger::vsyncPhaseOffsetNs == 0) {
         extraPadding = 1000000;        // 1ms (6% of 60Hz)
     }
 
@@ -237,19 +237,6 @@
     mContentsChangedListener = listener;
 }
 
-void SurfaceFlingerConsumer::onBuffersReleased() {
-    sp<ContentsChangedListener> listener;
-    {   // scope for the lock
-        Mutex::Autolock lock(mMutex);
-        ALOG_ASSERT(mFrameAvailableListener.unsafe_get() == mContentsChangedListener.unsafe_get());
-        listener = mContentsChangedListener.promote();
-    }
-
-    if (listener != NULL) {
-        listener->onBuffersReleased();
-    }
-}
-
 void SurfaceFlingerConsumer::onSidebandStreamChanged() {
     sp<ContentsChangedListener> listener;
     {   // scope for the lock
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index cfa70ed..1126233 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -35,7 +35,6 @@
     static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8;
 
     struct ContentsChangedListener: public FrameAvailableListener {
-        virtual void onBuffersReleased() = 0;
         virtual void onSidebandStreamChanged() = 0;
     };
 
@@ -93,7 +92,6 @@
             FrameEventHistoryDelta* outDelta) override;
 
 private:
-    virtual void onBuffersReleased();
     virtual void onSidebandStreamChanged();
 
     wp<ContentsChangedListener> mContentsChangedListener;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 6cd7152..c567e61 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -84,6 +84,9 @@
 #include "RenderEngine/RenderEngine.h"
 #include <cutils/compiler.h>
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
 #define DISPLAY_COUNT       1
 
 /*
@@ -95,40 +98,20 @@
 EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
 namespace android {
-
-// This is the phase offset in nanoseconds of the software vsync event
-// relative to the vsync event reported by HWComposer.  The software vsync
-// event is when SurfaceFlinger and Choreographer-based applications run each
-// frame.
-//
-// This phase offset allows adjustment of the minimum latency from application
-// wake-up (by Choregographer) time to the time at which the resulting window
-// image is displayed.  This value may be either positive (after the HW vsync)
-// or negative (before the HW vsync).  Setting it to 0 will result in a
-// minimum latency of two vsync periods because the app and SurfaceFlinger
-// will run just after the HW vsync.  Setting it to a positive number will
-// result in the minimum latency being:
-//
-//     (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
-//
-// Note that reducing this latency makes it more likely for the applications
-// to not have their window content image ready in time.  When this happens
-// the latency will end up being an additional vsync period, and animations
-// will hiccup.  Therefore, this latency should be tuned somewhat
-// conservatively (or at least with awareness of the trade-off being made).
-static const int64_t vsyncPhaseOffsetNs = VSYNC_EVENT_PHASE_OFFSET_NS;
-
-// This is the phase offset at which SurfaceFlinger's composition runs.
-static const int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS;
-
 // ---------------------------------------------------------------------------
 
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
 const String16 sHardwareTest("android.permission.HARDWARE_TEST");
 const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sDump("android.permission.DUMP");
 
 // ---------------------------------------------------------------------------
+int64_t SurfaceFlinger::vsyncPhaseOffsetNs;
+int64_t SurfaceFlinger::sfVsyncPhaseOffsetNs;
+bool SurfaceFlinger::useContextPriority;
 
 SurfaceFlinger::SurfaceFlinger()
     :   BnSurfaceComposer(),
@@ -165,8 +148,17 @@
         mLastSwapTime(0),
         mNumLayers(0)
 {
+    vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
+
+    sfVsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(1000000);
+
     ALOGI("SurfaceFlinger is starting");
 
+    useContextPriority = getBool< ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::useContextPriority>(false);
+
     char value[PROPERTY_VALUE_MAX];
 
     property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
@@ -719,7 +711,7 @@
         info.xdpi = xdpi;
         info.ydpi = ydpi;
         info.fps = float(1e9 / hwConfig.refresh);
-        info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;
+        info.appVsyncOffset = vsyncPhaseOffsetNs;
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -734,7 +726,7 @@
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
         info.presentationDeadline =
-                hwConfig.refresh - SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000;
+                hwConfig.refresh - sfVsyncPhaseOffsetNs + 1000000;
 
         // All non-virtual displays are currently considered secure.
         info.secure = true;
@@ -2317,6 +2309,8 @@
 }
 
 status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) {
+    Mutex::Autolock _l(mStateLock);
+
     const auto& p = layer->getParent();
     const ssize_t index = (p != nullptr) ? p->removeChild(layer) :
              mCurrentState.layersSortedByZ.remove(layer);
@@ -3006,12 +3000,8 @@
 void SurfaceFlinger::appendSfConfigString(String8& result) const
 {
     result.append(" [sf");
-#ifdef HAS_CONTEXT_PRIORITY
-    result.append(" HAS_CONTEXT_PRIORITY");
-#endif
-#ifdef NEVER_DEFAULT_TO_ASYNC_MODE
-    result.append(" NEVER_DEFAULT_TO_ASYNC_MODE");
-#endif
+    result.appendFormat(" HAS_CONTEXT_PRIORITY=%d", useContextPriority);
+
     if (isLayerTripleBufferingDisabled())
         result.append(" DISABLE_TRIPLE_BUFFERING");
     result.append("]");
diff --git a/services/surfaceflinger/tests/hwc2/Android.mk b/services/surfaceflinger/tests/hwc2/Android.mk
new file mode 100644
index 0000000..203ced5
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Android.mk
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2016 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := test-hwc2
+LOCAL_MODULE_TAGS := tests
+LOCAL_CFLAGS += \
+    -fstack-protector-all \
+    -g \
+    -Wall -Wextra \
+    -Werror \
+    -fno-builtin \
+    -DEGL_EGLEXT_PROTOTYPES \
+    -DGL_GLEXT_PROTOTYPES
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libutils \
+    libhardware \
+    libEGL \
+    libGLESv2 \
+    libui \
+    libgui \
+    liblog \
+    libsync
+LOCAL_STATIC_LIBRARIES := \
+    libbase \
+    libadf \
+    libadfhwc \
+    libmath
+LOCAL_SRC_FILES := \
+    Hwc2Test.cpp \
+    Hwc2TestProperties.cpp \
+    Hwc2TestLayer.cpp \
+    Hwc2TestLayers.cpp \
+    Hwc2TestBuffer.cpp \
+    Hwc2TestClientTarget.cpp \
+    Hwc2TestVirtualDisplay.cpp
+
+include $(BUILD_NATIVE_TEST)
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
new file mode 100644
index 0000000..062485e
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
@@ -0,0 +1,4556 @@
+/*
+ * Copyright (C) 2016 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 <array>
+#include <unordered_set>
+#include <unordered_map>
+#include <gtest/gtest.h>
+#include <dlfcn.h>
+#include <android-base/unique_fd.h>
+#include <hardware/hardware.h>
+#include <sync/sync.h>
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include "Hwc2TestLayer.h"
+#include "Hwc2TestLayers.h"
+#include "Hwc2TestClientTarget.h"
+#include "Hwc2TestVirtualDisplay.h"
+
+void hwc2TestHotplugCallback(hwc2_callback_data_t callbackData,
+        hwc2_display_t display, int32_t connected);
+void hwc2TestVsyncCallback(hwc2_callback_data_t callbackData,
+        hwc2_display_t display, int64_t timestamp);
+
+class Hwc2Test : public testing::Test {
+public:
+
+    virtual void SetUp()
+    {
+        hw_module_t const* hwc2Module;
+
+        int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &hwc2Module);
+        ASSERT_GE(err, 0) << "failed to get hwc hardware module: "
+                << strerror(-err);
+
+        /* The following method will fail if you have not run
+         * "adb shell stop" */
+        err = hwc2_open(hwc2Module, &mHwc2Device);
+        ASSERT_GE(err, 0) << "failed to open hwc hardware module: "
+                << strerror(-err);
+
+        populateDisplays();
+    }
+
+    virtual void TearDown()
+    {
+
+        for (auto itr = mLayers.begin(); itr != mLayers.end();) {
+            hwc2_display_t display = itr->first;
+            hwc2_layer_t layer = itr->second;
+            itr++;
+            /* Destroys and removes the layer from mLayers */
+            destroyLayer(display, layer);
+        }
+
+        for (auto itr = mActiveDisplays.begin(); itr != mActiveDisplays.end();) {
+            hwc2_display_t display = *itr;
+            itr++;
+            /* Sets power mode to off and removes the display from
+             * mActiveDisplays */
+            setPowerMode(display, HWC2_POWER_MODE_OFF);
+        }
+
+        for (auto itr = mVirtualDisplays.begin(); itr != mVirtualDisplays.end();) {
+            hwc2_display_t display = *itr;
+            itr++;
+            /* Destroys virtual displays */
+            destroyVirtualDisplay(display);
+        }
+
+        if (mHwc2Device)
+            hwc2_close(mHwc2Device);
+    }
+
+    void registerCallback(hwc2_callback_descriptor_t descriptor,
+            hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_REGISTER_CALLBACK>(
+                getFunction(HWC2_FUNCTION_REGISTER_CALLBACK));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, descriptor,
+                callbackData, pointer));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to register callback";
+        }
+    }
+
+    void getDisplayType(hwc2_display_t display, hwc2_display_type_t* outType,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_TYPE>(
+                getFunction(HWC2_FUNCTION_GET_DISPLAY_TYPE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    reinterpret_cast<int32_t*>(outType)));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display type";
+        }
+    }
+
+    /* If the populateDisplays function is still receiving displays and the
+     * display is connected, the display handle is stored in mDisplays. */
+    void hotplugCallback(hwc2_display_t display, int32_t connected)
+    {
+        std::lock_guard<std::mutex> lock(mHotplugMutex);
+
+        if (mHotplugStatus != Hwc2TestHotplugStatus::Receiving)
+            return;
+
+        if (connected == HWC2_CONNECTION_CONNECTED)
+            mDisplays.insert(display);
+
+        mHotplugCv.notify_all();
+    }
+
+    void createLayer(hwc2_display_t display, hwc2_layer_t* outLayer,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_CREATE_LAYER>(
+                getFunction(HWC2_FUNCTION_CREATE_LAYER));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                outLayer));
+
+        if (err == HWC2_ERROR_NONE)
+            mLayers.insert(std::make_pair(display, *outLayer));
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to create layer";
+        }
+    }
+
+    void destroyLayer(hwc2_display_t display, hwc2_layer_t layer,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_DESTROY_LAYER>(
+                getFunction(HWC2_FUNCTION_DESTROY_LAYER));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer));
+
+        if (err == HWC2_ERROR_NONE)
+            mLayers.erase(std::make_pair(display, layer));
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to destroy layer "
+                    << layer;
+        }
+    }
+
+    void getDisplayAttribute(hwc2_display_t display, hwc2_config_t config,
+            hwc2_attribute_t attribute, int32_t* outValue,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(
+                getFunction(HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, config,
+                attribute, outValue));
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display attribute "
+                    << getAttributeName(attribute) << " for config " << config;
+        }
+    }
+
+    void getDisplayConfigs(hwc2_display_t display,
+            std::vector<hwc2_config_t>* outConfigs,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_CONFIGS>(
+                getFunction(HWC2_FUNCTION_GET_DISPLAY_CONFIGS));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numConfigs = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                &numConfigs, nullptr));
+
+        if (err == HWC2_ERROR_NONE) {
+            outConfigs->resize(numConfigs);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    &numConfigs, outConfigs->data()));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get configs for"
+                    " display " << display;
+        }
+    }
+
+    void getActiveConfig(hwc2_display_t display, hwc2_config_t* outConfig,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_ACTIVE_CONFIG>(
+                getFunction(HWC2_FUNCTION_GET_ACTIVE_CONFIG));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                outConfig));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get active config on"
+                    " display " << display;
+        }
+    }
+
+    void setActiveConfig(hwc2_display_t display, hwc2_config_t config,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_ACTIVE_CONFIG>(
+                getFunction(HWC2_FUNCTION_SET_ACTIVE_CONFIG));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, config));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set active config "
+                    << config;
+        }
+    }
+
+    void getDozeSupport(hwc2_display_t display, int32_t* outSupport,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DOZE_SUPPORT>(
+                getFunction(HWC2_FUNCTION_GET_DOZE_SUPPORT));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                outSupport));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get doze support on"
+                    " display " << display;
+        }
+    }
+
+    void setPowerMode(hwc2_display_t display, hwc2_power_mode_t mode,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_POWER_MODE>(
+                getFunction(HWC2_FUNCTION_SET_POWER_MODE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                mode));
+        if (outErr) {
+            *outErr = err;
+            if (err != HWC2_ERROR_NONE)
+                return;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set power mode "
+                    << getPowerModeName(mode) << " on display " << display;
+        }
+
+        if (mode == HWC2_POWER_MODE_OFF) {
+            mActiveDisplays.erase(display);
+        } else {
+            mActiveDisplays.insert(display);
+        }
+    }
+
+    void setVsyncEnabled(hwc2_display_t display, hwc2_vsync_t enabled,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_VSYNC_ENABLED>(
+                getFunction(HWC2_FUNCTION_SET_VSYNC_ENABLED));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                enabled));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set vsync enabled "
+                    << getVsyncName(enabled);
+        }
+    }
+
+    void vsyncCallback(hwc2_display_t display, int64_t timestamp)
+    {
+        std::lock_guard<std::mutex> lock(mVsyncMutex);
+        mVsyncDisplay = display;
+        mVsyncTimestamp = timestamp;
+        mVsyncCv.notify_all();
+    }
+
+    void getDisplayName(hwc2_display_t display, std::string* outName,
+                hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_NAME>(
+                getFunction(HWC2_FUNCTION_GET_DISPLAY_NAME));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t size = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, &size,
+                nullptr));
+
+        if (err == HWC2_ERROR_NONE) {
+            std::vector<char> name(size);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, &size,
+                    name.data()));
+
+            outName->assign(name.data());
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display name for "
+                    << display;
+        }
+    }
+
+    void setLayerCompositionType(hwc2_display_t display, hwc2_layer_t layer,
+            hwc2_composition_t composition, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                composition));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer composition"
+                    " type " << getCompositionName(composition);
+        }
+    }
+
+    void setCursorPosition(hwc2_display_t display, hwc2_layer_t layer,
+            int32_t x, int32_t y, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_CURSOR_POSITION>(
+                getFunction(HWC2_FUNCTION_SET_CURSOR_POSITION));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer, x,
+                y));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set cursor position";
+        }
+    }
+
+    void setLayerBlendMode(hwc2_display_t display, hwc2_layer_t layer,
+            hwc2_blend_mode_t mode, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_BLEND_MODE>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_BLEND_MODE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                mode));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer blend mode "
+                    << getBlendModeName(mode);
+        }
+    }
+
+    void setLayerBuffer(hwc2_display_t display, hwc2_layer_t layer,
+            buffer_handle_t buffer, int32_t acquireFence,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_BUFFER>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_BUFFER));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                buffer, acquireFence));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer buffer";
+        }
+    }
+
+    void setLayerColor(hwc2_display_t display, hwc2_layer_t layer,
+            hwc_color_t color, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_COLOR>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_COLOR));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                color));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer color";
+        }
+    }
+
+    void setLayerDataspace(hwc2_display_t display, hwc2_layer_t layer,
+            android_dataspace_t dataspace, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_DATASPACE>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_DATASPACE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                layer, dataspace));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer dataspace";
+        }
+    }
+
+    void setLayerDisplayFrame(hwc2_display_t display, hwc2_layer_t layer,
+            const hwc_rect_t& displayFrame, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_DISPLAY_FRAME));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                displayFrame));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer display"
+                    " frame";
+        }
+    }
+
+    void setLayerPlaneAlpha(hwc2_display_t display, hwc2_layer_t layer,
+            float alpha, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                alpha));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer plane alpha "
+                    << alpha;
+        }
+    }
+
+    void setLayerSourceCrop(hwc2_display_t display, hwc2_layer_t layer,
+            const hwc_frect_t& sourceCrop, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_SOURCE_CROP>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_SOURCE_CROP));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                sourceCrop));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer source crop";
+        }
+    }
+
+    void setLayerSurfaceDamage(hwc2_display_t display, hwc2_layer_t layer,
+            const hwc_region_t& surfaceDamage, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_SURFACE_DAMAGE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                surfaceDamage));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer surface"
+                    " damage";
+        }
+    }
+
+    void setLayerTransform(hwc2_display_t display, hwc2_layer_t layer,
+            hwc_transform_t transform, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_TRANSFORM>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_TRANSFORM));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                transform));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer transform "
+                    << getTransformName(transform);
+        }
+    }
+
+    void setLayerVisibleRegion(hwc2_display_t display, hwc2_layer_t layer,
+            const hwc_region_t& visibleRegion, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_VISIBLE_REGION));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                visibleRegion));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer visible"
+                    " region";
+        }
+    }
+
+    void setLayerZOrder(hwc2_display_t display, hwc2_layer_t layer,
+            uint32_t zOrder, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_Z_ORDER>(
+                getFunction(HWC2_FUNCTION_SET_LAYER_Z_ORDER));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
+                zOrder));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer z order "
+                    << zOrder;
+        }
+    }
+
+    void validateDisplay(hwc2_display_t display, uint32_t* outNumTypes,
+            uint32_t* outNumRequests, hwc2_error_t* outErr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_VALIDATE_DISPLAY>(
+                getFunction(HWC2_FUNCTION_VALIDATE_DISPLAY));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        *outErr = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                outNumTypes, outNumRequests));
+    }
+
+    void validateDisplay(hwc2_display_t display, uint32_t* outNumTypes,
+            uint32_t* outNumRequests, bool* outHasChanges)
+    {
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        EXPECT_NO_FATAL_FAILURE(validateDisplay(display, outNumTypes,
+                outNumRequests, &err));
+
+        if (err != HWC2_ERROR_HAS_CHANGES) {
+            *outHasChanges = false;
+            EXPECT_EQ(err, HWC2_ERROR_NONE) << "failed to validate display";
+        } else {
+            *outHasChanges = true;
+        }
+    }
+
+    void getDisplayRequests(hwc2_display_t display,
+            hwc2_display_request_t* outDisplayRequests,
+            std::vector<hwc2_layer_t>* outLayers,
+            std::vector<hwc2_layer_request_t>* outLayerRequests,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_REQUESTS>(
+                getFunction(HWC2_FUNCTION_GET_DISPLAY_REQUESTS));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numElements = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                reinterpret_cast<int32_t*>(outDisplayRequests), &numElements,
+                nullptr, nullptr));
+
+        if (err == HWC2_ERROR_NONE && numElements > 0) {
+            outLayers->resize(numElements);
+            outLayerRequests->resize(numElements);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    reinterpret_cast<int32_t*>(outDisplayRequests), &numElements,
+                    reinterpret_cast<uint64_t*>(outLayers->data()),
+                    reinterpret_cast<int32_t*>(outLayerRequests->data())));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display requests";
+        }
+    }
+
+    void handleRequests(hwc2_display_t display,
+            const std::vector<hwc2_layer_t>& layers, uint32_t numRequests,
+            std::set<hwc2_layer_t>* outClearLayers = nullptr,
+            bool* outFlipClientTarget = nullptr)
+    {
+        hwc2_display_request_t displayRequest =
+                static_cast<hwc2_display_request_t>(0);
+        std::vector<hwc2_layer_t> requestedLayers;
+        std::vector<hwc2_layer_request_t> requests;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayRequests(display, &displayRequest,
+                &requestedLayers, &requests));
+
+        EXPECT_EQ(numRequests, requests.size()) << "validate returned "
+                << numRequests << " requests and get display requests returned "
+                << requests.size() << " requests";
+
+        for (size_t i = 0; i < requests.size(); i++) {
+            hwc2_layer_t requestedLayer = requestedLayers.at(i);
+            hwc2_layer_request_t request = requests.at(i);
+
+            EXPECT_EQ(std::count(layers.begin(), layers.end(), requestedLayer),
+                    0) << "get display requests returned an unknown layer";
+            EXPECT_NE(request, 0) << "returned empty request for layer "
+                    << requestedLayer;
+
+            if (outClearLayers && request
+                    == HWC2_LAYER_REQUEST_CLEAR_CLIENT_TARGET)
+                outClearLayers->insert(requestedLayer);
+        }
+
+        if (outFlipClientTarget)
+            *outFlipClientTarget = displayRequest
+                    & HWC2_DISPLAY_REQUEST_FLIP_CLIENT_TARGET;
+    }
+
+    void getChangedCompositionTypes(hwc2_display_t display,
+            std::vector<hwc2_layer_t>* outLayers,
+            std::vector<hwc2_composition_t>* outTypes,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(
+                getFunction(HWC2_FUNCTION_GET_CHANGED_COMPOSITION_TYPES));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numElements = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                &numElements, nullptr, nullptr));
+
+        if (err == HWC2_ERROR_NONE && numElements > 0) {
+            outLayers->resize(numElements);
+            outTypes->resize(numElements);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    &numElements, reinterpret_cast<uint64_t*>(outLayers->data()),
+                    reinterpret_cast<int32_t*>(outTypes->data())));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get changed"
+                    " composition types";
+        }
+    }
+
+    void handleCompositionChanges(hwc2_display_t display,
+            const Hwc2TestLayers& testLayers,
+            const std::vector<hwc2_layer_t>& layers, uint32_t numTypes,
+            std::set<hwc2_layer_t>* outClientLayers = nullptr)
+    {
+        std::vector<hwc2_layer_t> changedLayers;
+        std::vector<hwc2_composition_t> types;
+
+        ASSERT_NO_FATAL_FAILURE(getChangedCompositionTypes(display,
+                &changedLayers, &types));
+
+        EXPECT_EQ(numTypes, types.size()) << "validate returned "
+                << numTypes << " types and get changed composition types"
+                " returned " << types.size() << " types";
+
+        for (size_t i = 0; i < types.size(); i++) {
+
+            auto layer = std::find(layers.begin(), layers.end(),
+                    changedLayers.at(i));
+
+            EXPECT_TRUE(layer != layers.end() || !testLayers.contains(*layer))
+                    << "get changed composition types returned an unknown layer";
+
+            hwc2_composition_t requestedType = testLayers.getComposition(*layer);
+            hwc2_composition_t returnedType = types.at(i);
+
+            EXPECT_NE(returnedType, HWC2_COMPOSITION_INVALID) << "get changed"
+                    " composition types returned invalid composition";
+
+            switch (requestedType) {
+            case HWC2_COMPOSITION_CLIENT:
+                EXPECT_TRUE(false) << getCompositionName(returnedType)
+                        << " cannot be changed";
+                break;
+            case HWC2_COMPOSITION_DEVICE:
+            case HWC2_COMPOSITION_SOLID_COLOR:
+                EXPECT_EQ(returnedType, HWC2_COMPOSITION_CLIENT)
+                        << "composition of type "
+                        << getCompositionName(requestedType)
+                        << " can only be changed to "
+                        << getCompositionName(HWC2_COMPOSITION_CLIENT);
+                break;
+            case HWC2_COMPOSITION_CURSOR:
+            case HWC2_COMPOSITION_SIDEBAND:
+                EXPECT_TRUE(returnedType == HWC2_COMPOSITION_CLIENT
+                        || returnedType == HWC2_COMPOSITION_DEVICE)
+                        << "composition of type "
+                        << getCompositionName(requestedType)
+                        << " can only be changed to "
+                        << getCompositionName(HWC2_COMPOSITION_CLIENT) << " or "
+                        << getCompositionName(HWC2_COMPOSITION_DEVICE);
+                break;
+            default:
+                EXPECT_TRUE(false) << "unknown type "
+                        << getCompositionName(requestedType);
+                break;
+            }
+
+            if (outClientLayers)
+                if (returnedType == HWC2_COMPOSITION_CLIENT)
+                    outClientLayers->insert(*layer);
+        }
+
+        if (outClientLayers) {
+            for (auto layer : layers) {
+                if (testLayers.getComposition(layer) == HWC2_COMPOSITION_CLIENT)
+                    outClientLayers->insert(layer);
+            }
+        }
+    }
+
+    void acceptDisplayChanges(hwc2_display_t display,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(
+                getFunction(HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to accept display changes";
+        }
+    }
+
+    void getClientTargetSupport(hwc2_display_t display, int32_t width,
+            int32_t height, android_pixel_format_t format,
+            android_dataspace_t dataspace, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>(
+                getFunction(HWC2_FUNCTION_GET_CLIENT_TARGET_SUPPORT));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, width,
+                height, format, dataspace));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get client target"
+                    " support";
+        }
+    }
+
+    void setClientTarget(hwc2_display_t display, buffer_handle_t handle,
+            int32_t acquireFence, android_dataspace_t dataspace,
+            hwc_region_t damage, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_CLIENT_TARGET>(
+                getFunction(HWC2_FUNCTION_SET_CLIENT_TARGET));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, handle,
+                acquireFence, dataspace, damage));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set client target";
+        }
+    }
+
+    void presentDisplay(hwc2_display_t display, int32_t* outPresentFence,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_PRESENT_DISPLAY>(
+                getFunction(HWC2_FUNCTION_PRESENT_DISPLAY));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                outPresentFence));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to present display";
+        }
+    }
+
+    void getReleaseFences(hwc2_display_t display,
+            std::vector<hwc2_layer_t>* outLayers,
+            std::vector<int32_t>* outFences, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_RELEASE_FENCES>(
+                getFunction(HWC2_FUNCTION_GET_RELEASE_FENCES));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numElements = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                &numElements, nullptr, nullptr));
+
+        if (err == HWC2_ERROR_NONE) {
+            outLayers->resize(numElements);
+            outFences->resize(numElements);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    &numElements, outLayers->data(), outFences->data()));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get release fences";
+        }
+    }
+
+    void getColorModes(hwc2_display_t display,
+            std::vector<android_color_mode_t>* outColorModes,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_COLOR_MODES>(
+                getFunction(HWC2_FUNCTION_GET_COLOR_MODES));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numColorModes = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                &numColorModes, nullptr));
+        if (err == HWC2_ERROR_NONE) {
+            outColorModes->resize(numColorModes);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                    &numColorModes,
+                    reinterpret_cast<int32_t*>(outColorModes->data())));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get color modes for"
+                    " display " << display;
+        }
+    }
+
+    void setColorMode(hwc2_display_t display, android_color_mode_t colorMode,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_COLOR_MODE>(
+                getFunction(HWC2_FUNCTION_SET_COLOR_MODE));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                static_cast<int32_t>(colorMode)));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set color mode "
+                    << colorMode;
+        }
+    }
+
+    void getHdrCapabilities(hwc2_display_t display,
+            std::vector<android_hdr_t>* outTypes, float* outMaxLuminance,
+            float* outMaxAverageLuminance, float* outMinLuminance,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_HDR_CAPABILITIES>(
+                getFunction(HWC2_FUNCTION_GET_HDR_CAPABILITIES));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t numTypes = 0;
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                &numTypes, nullptr, outMaxLuminance, outMaxAverageLuminance,
+                outMinLuminance));
+
+        if (err == HWC2_ERROR_NONE) {
+            outTypes->resize(numTypes);
+
+            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, &numTypes,
+                    reinterpret_cast<int32_t*>(outTypes->data()), outMaxLuminance,
+                    outMaxAverageLuminance, outMinLuminance));
+        }
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get hdr capabilities"
+                    " for display " << display;
+        }
+    }
+
+    void setColorTransform(hwc2_display_t display,
+            const std::array<float, 16>& matrix, android_color_transform_t hint,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_COLOR_TRANSFORM>(
+                getFunction(HWC2_FUNCTION_SET_COLOR_TRANSFORM));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
+                matrix.data(), hint));
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set color transform "
+                    << hint;
+        }
+    }
+
+    void createVirtualDisplay(uint32_t width, uint32_t height,
+            android_pixel_format_t* outFormat, hwc2_display_t* outDisplay,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(
+                getFunction(HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, width, height,
+                reinterpret_cast<int32_t*>(outFormat), outDisplay));
+
+        if (err == HWC2_ERROR_NONE)
+            mVirtualDisplays.insert(*outDisplay);
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to create virtual display";
+        }
+    }
+
+    void destroyVirtualDisplay(hwc2_display_t display,
+            hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>(
+                getFunction(HWC2_FUNCTION_DESTROY_VIRTUAL_DISPLAY));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display));
+
+        if (err == HWC2_ERROR_NONE)
+            mVirtualDisplays.erase(display);
+
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to destroy virtual display";
+        }
+    }
+
+    void getMaxVirtualDisplayCount(uint32_t* outMaxCnt)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>(
+                getFunction(HWC2_FUNCTION_GET_MAX_VIRTUAL_DISPLAY_COUNT));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        *outMaxCnt = pfn(mHwc2Device);
+    }
+
+    void setOutputBuffer(hwc2_display_t display, buffer_handle_t buffer,
+            int32_t releaseFence, hwc2_error_t* outErr = nullptr)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_SET_OUTPUT_BUFFER>(
+                getFunction(HWC2_FUNCTION_SET_OUTPUT_BUFFER));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, buffer,
+                releaseFence));
+        if (outErr) {
+            *outErr = err;
+        } else {
+            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set output buffer";
+        }
+    }
+
+    void dump(std::string* outBuffer)
+    {
+        auto pfn = reinterpret_cast<HWC2_PFN_DUMP>(
+                getFunction(HWC2_FUNCTION_DUMP));
+        ASSERT_TRUE(pfn) << "failed to get function";
+
+        uint32_t size = 0;
+
+        pfn(mHwc2Device, &size, nullptr);
+
+        std::vector<char> buffer(size);
+
+        pfn(mHwc2Device, &size, buffer.data());
+
+        outBuffer->assign(buffer.data());
+    }
+
+    void getBadDisplay(hwc2_display_t* outDisplay)
+    {
+        for (hwc2_display_t display = 0; display < UINT64_MAX; display++) {
+            if (mDisplays.count(display) == 0) {
+                *outDisplay = display;
+                return;
+            }
+        }
+        ASSERT_TRUE(false) << "Unable to find bad display. UINT64_MAX displays"
+                " are registered. This should never happen.";
+    }
+
+    void waitForVsync(hwc2_display_t* outDisplay = nullptr,
+            int64_t* outTimestamp = nullptr)
+    {
+        std::unique_lock<std::mutex> lock(mVsyncMutex);
+        ASSERT_EQ(mVsyncCv.wait_for(lock, std::chrono::seconds(3)),
+                std::cv_status::no_timeout) << "timed out attempting to get"
+                " vsync callback";
+        if (outDisplay)
+            *outDisplay = mVsyncDisplay;
+        if (outTimestamp)
+            *outTimestamp = mVsyncTimestamp;
+    }
+
+    void enableVsync(hwc2_display_t display)
+    {
+        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, this,
+                reinterpret_cast<hwc2_function_pointer_t>(
+                hwc2TestVsyncCallback)));
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+    }
+
+    void disableVsync(hwc2_display_t display)
+    {
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+    }
+
+protected:
+    hwc2_function_pointer_t getFunction(hwc2_function_descriptor_t descriptor)
+    {
+        return mHwc2Device->getFunction(mHwc2Device, descriptor);
+    }
+
+    void getCapabilities(std::vector<hwc2_capability_t>* outCapabilities)
+    {
+        uint32_t num = 0;
+
+        mHwc2Device->getCapabilities(mHwc2Device, &num, nullptr);
+
+        outCapabilities->resize(num);
+
+        mHwc2Device->getCapabilities(mHwc2Device, &num,
+                reinterpret_cast<int32_t*>(outCapabilities->data()));
+    }
+
+    /* Registers a hotplug callback and waits for hotplug callbacks. This
+     * function will have no effect if called more than once. */
+    void populateDisplays()
+    {
+        /* Sets the hotplug status to receiving */
+        {
+            std::lock_guard<std::mutex> lock(mHotplugMutex);
+
+            if (mHotplugStatus != Hwc2TestHotplugStatus::Init)
+                return;
+            mHotplugStatus = Hwc2TestHotplugStatus::Receiving;
+        }
+
+        /* Registers the callback. This function call cannot be locked because
+         * a callback could happen on the same thread */
+        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_HOTPLUG, this,
+                reinterpret_cast<hwc2_function_pointer_t>(
+                hwc2TestHotplugCallback)));
+
+        /* Waits for hotplug events. If a hotplug event has not come within 1
+         * second, stop waiting. */
+        std::unique_lock<std::mutex> lock(mHotplugMutex);
+
+        while (mHotplugCv.wait_for(lock, std::chrono::seconds(1)) !=
+                std::cv_status::timeout) { }
+
+        /* Sets the hotplug status to done. Future calls will have no effect */
+        mHotplugStatus = Hwc2TestHotplugStatus::Done;
+    }
+
+    /* NOTE: will create min(newlayerCnt, max supported layers) layers */
+    void createLayers(hwc2_display_t display,
+            std::vector<hwc2_layer_t>* outLayers, size_t newLayerCnt)
+    {
+        std::vector<hwc2_layer_t> newLayers;
+        hwc2_layer_t layer;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        for (size_t i = 0; i < newLayerCnt; i++) {
+
+            EXPECT_NO_FATAL_FAILURE(createLayer(display, &layer, &err));
+            if (err == HWC2_ERROR_NO_RESOURCES)
+                break;
+            if (err != HWC2_ERROR_NONE) {
+                newLayers.clear();
+                ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to create layer";
+            }
+            newLayers.push_back(layer);
+        }
+
+        *outLayers = std::move(newLayers);
+    }
+
+    void destroyLayers(hwc2_display_t display,
+            std::vector<hwc2_layer_t>&& layers)
+    {
+        for (hwc2_layer_t layer : layers) {
+            EXPECT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+        }
+    }
+
+    void getInvalidConfig(hwc2_display_t display, hwc2_config_t* outConfig)
+    {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        hwc2_config_t CONFIG_MAX = UINT32_MAX;
+
+        ASSERT_LE(configs.size() - 1, CONFIG_MAX) << "every config value"
+                " (2^32 values) has been taken which shouldn't happen";
+
+        hwc2_config_t config;
+        for (config = 0; config < CONFIG_MAX; config++) {
+            if (std::count(configs.begin(), configs.end(), config) == 0)
+                break;
+        }
+
+        *outConfig = config;
+    }
+
+    /* Calls a set property function from Hwc2Test to set a property value from
+     * Hwc2TestLayer to hwc2_layer_t on hwc2_display_t */
+    using TestLayerPropertyFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, hwc2_layer_t layer,
+            Hwc2TestLayer* testLayer, hwc2_error_t* outErr);
+
+    /* Calls a set property function from Hwc2Test to set property values from
+     * Hwc2TestLayers to hwc2_layer_t on hwc2_display_t */
+    using TestLayerPropertiesFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, hwc2_layer_t layer,
+            Hwc2TestLayers* testLayers);
+
+    /* Calls a set property function from Hwc2Test to set a bad property value
+     * on hwc2_layer_t on hwc2_display_t */
+    using TestLayerPropertyBadLayerFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, hwc2_layer_t layer,
+            Hwc2TestLayer* testLayer, hwc2_error_t* outErr);
+
+    /* Calls a set property function from Hwc2Test to set a bad property value
+     * on hwc2_layer_t on hwc2_display_t */
+    using TestLayerPropertyBadParameterFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, hwc2_layer_t layer, hwc2_error_t* outErr);
+
+    /* Is called after a display is powered on and all layer properties have
+     * been set. It should be used to test functions such as validate, accepting
+     * changes, present, etc. */
+    using TestDisplayLayersFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, const std::vector<hwc2_layer_t>& layers,
+            Hwc2TestLayers* testLayers);
+
+    /* It is called on an non validated display */
+    using TestDisplayNonValidatedLayersFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, std::vector<hwc2_layer_t>* layers);
+
+    /* Tests client target support on a particular display and config */
+    using TestClientTargetSupportFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display,
+            const Hwc2TestClientTargetSupport& testClientTargetSupport);
+
+    /* Tests a particular active display config */
+    using TestActiveDisplayConfigFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display);
+
+    /* Tests a newly created virtual display */
+    using TestCreateVirtualDisplayFunction = void (*)(Hwc2Test* test,
+            hwc2_display_t display, Hwc2TestVirtualDisplay* testVirtualDisplay);
+
+    /* Advances a property of Hwc2TestLayer */
+    using AdvanceProperty = bool (*)(Hwc2TestLayer* testLayer);
+
+    /* Advances properties of Hwc2TestLayers */
+    using AdvanceProperties = bool (*)(Hwc2TestLayers* testLayer);
+
+    /* Advances properties of Hwc2TestClientTargetSupport */
+    using AdvanceClientTargetSupport = bool (*)(
+            Hwc2TestClientTargetSupport* testClientTargetSupport);
+
+    /* For each active display it cycles through each display config and tests
+     * each property value. It creates a layer, sets the property and then
+     * destroys the layer */
+    void setLayerProperty(Hwc2TestCoverage coverage,
+            TestLayerPropertyFunction function, AdvanceProperty advance)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                hwc2_layer_t layer;
+                Area displayArea;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+                Hwc2TestLayer testLayer(coverage, displayArea);
+
+                do {
+                    ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+                    ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
+                            &testLayer, nullptr));
+
+                    ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+                } while (advance(&testLayer));
+            }
+        }
+    }
+
+    /* For each active display it cycles through each display config and tests
+     * each property value. It creates a layer, cycles through each property
+     * value and updates the layer property value and then destroys the layer */
+    void setLayerPropertyUpdate(Hwc2TestCoverage coverage,
+            TestLayerPropertyFunction function, AdvanceProperty advance)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                hwc2_layer_t layer;
+                Area displayArea;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+                Hwc2TestLayer testLayer(coverage, displayArea);
+
+                ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+                do {
+                    ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
+                            &testLayer, nullptr));
+                } while (advance(&testLayer));
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+            }
+        }
+    }
+
+    /* For each active display it cycles through each display config and tests
+     * each property value. It creates multiple layers, calls the
+     * TestLayerPropertiesFunction to set property values and then
+     * destroys the layers */
+    void setLayerProperties(Hwc2TestCoverage coverage, size_t layerCnt,
+            TestLayerPropertiesFunction function, AdvanceProperties advance)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                std::vector<hwc2_layer_t> layers;
+                Area displayArea;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+
+                ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+                Hwc2TestLayers testLayers(layers, coverage, displayArea);
+
+                do {
+                    for (auto layer : layers) {
+                        EXPECT_NO_FATAL_FAILURE(function(this, display, layer,
+                                &testLayers));
+                    }
+                } while (advance(&testLayers));
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
+            }
+        }
+    }
+
+    /* For each active display it cycles through each display config.
+     * 1) It attempts to set a valid property value to bad layer handle.
+     * 2) It creates a layer x and attempts to set a valid property value to
+     *    layer x + 1
+     * 3) It destroys the layer x and attempts to set a valid property value to
+     *    the destroyed layer x.
+     */
+    void setLayerPropertyBadLayer(Hwc2TestCoverage coverage,
+            TestLayerPropertyBadLayerFunction function)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                hwc2_layer_t layer = 0;
+                Area displayArea;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+                Hwc2TestLayer testLayer(coverage, displayArea);
+
+                ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
+                        &testLayer, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+                ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+                ASSERT_NO_FATAL_FAILURE(function(this, display, layer + 1,
+                        &testLayer, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+
+                ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
+                        &testLayer, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+            }
+        }
+    }
+
+    /* For each active display it cycles through each display config and tests
+     * each property value. It creates a layer, sets a bad property value and
+     * then destroys the layer */
+    void setLayerPropertyBadParameter(TestLayerPropertyBadParameterFunction function)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                hwc2_layer_t layer;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+
+                ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+                ASSERT_NO_FATAL_FAILURE(function(this, display, layer, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong"
+                        " error code";
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+            }
+        }
+    }
+
+    /* For each active display it powers on the display, cycles through each
+     * config and creates a set of layers with a certain amount of coverage.
+     * For each active display, for each config and for each set of layers,
+     * it calls the TestDisplayLayersFunction */
+    void displayLayers(Hwc2TestCoverage coverage, size_t layerCnt,
+            TestDisplayLayersFunction function)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                Area displayArea;
+                std::vector<hwc2_layer_t> layers;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display, &displayArea));
+
+                ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+                Hwc2TestLayers testLayers(layers, coverage, displayArea);
+
+                do {
+                    bool skip;
+
+                    ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers,
+                            &testLayers, &skip));
+                    if (!skip)
+                        EXPECT_NO_FATAL_FAILURE(function(this, display, layers,
+                                &testLayers));
+
+                } while (testLayers.advance());
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayers(display,
+                        std::move(layers)));
+            }
+
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+        }
+    }
+
+    /* For each active display, it calls the
+     * TestDisplayNonValidatedLayersFunction on a variety on non-validated
+     * layer combinations */
+    void displayNonValidatedLayers(size_t layerCnt,
+            TestDisplayNonValidatedLayersFunction function)
+    {
+        for (auto display : mDisplays) {
+            uint32_t numTypes, numRequests;
+            std::vector<hwc2_layer_t> layers;
+            bool hasChanges;
+
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
+
+            ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
+
+            for (auto layer : layers) {
+                ASSERT_NO_FATAL_FAILURE(setLayerCompositionType(display, layer,
+                        HWC2_COMPOSITION_CLIENT));
+            }
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
+
+            ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes,
+                    &numRequests, &hasChanges));
+
+            for (auto layer : layers) {
+                ASSERT_NO_FATAL_FAILURE(setLayerCompositionType(display, layer,
+                        HWC2_COMPOSITION_DEVICE));
+            }
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
+
+            ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
+
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+        }
+    }
+
+    /* Test client target support on each config on each active display */
+    void setClientTargetSupport(Hwc2TestCoverage coverage,
+            TestClientTargetSupportFunction function,
+            AdvanceClientTargetSupport advance)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                Area displayArea;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+                Hwc2TestClientTargetSupport testClientTargetSupport(coverage,
+                        displayArea);
+
+                do {
+                    EXPECT_NO_FATAL_FAILURE(function(this, display,
+                            testClientTargetSupport));
+
+                } while (advance(&testClientTargetSupport));
+            }
+        }
+    }
+
+    /* Cycles through each config on each active display and calls
+     * a TestActiveDisplayConfigFunction */
+    void setActiveDisplayConfig(TestActiveDisplayConfigFunction function)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+
+                EXPECT_NO_FATAL_FAILURE(function(this, display));
+            }
+        }
+    }
+
+    /* Creates a virtual display for testing */
+    void createVirtualDisplay(Hwc2TestCoverage coverage,
+            TestCreateVirtualDisplayFunction function)
+    {
+        Hwc2TestVirtualDisplay testVirtualDisplay(coverage);
+
+        do {
+            hwc2_display_t display;
+            hwc2_error_t err = HWC2_ERROR_NONE;
+
+            const UnsignedArea& dimension =
+                    testVirtualDisplay.getDisplayDimension();
+            android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+
+            ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(dimension.width,
+                    dimension.height, &desiredFormat, &display, &err));
+
+            EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_NO_RESOURCES
+                    || err == HWC2_ERROR_UNSUPPORTED)
+                    << "returned wrong error code";
+            EXPECT_GE(desiredFormat, 0) << "invalid format";
+
+            if (err != HWC2_ERROR_NONE)
+                continue;
+
+            EXPECT_NO_FATAL_FAILURE(function(this, display,
+                    &testVirtualDisplay));
+
+            ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display));
+
+        } while (testVirtualDisplay.advance());
+    }
+
+
+    void getActiveConfigAttribute(hwc2_display_t display,
+            hwc2_attribute_t attribute, int32_t* outValue)
+    {
+        hwc2_config_t config;
+        ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &config));
+        ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                attribute, outValue));
+        ASSERT_GE(*outValue, 0) << "failed to get valid "
+                << getAttributeName(attribute);
+    }
+
+    void getActiveDisplayArea(hwc2_display_t display, Area* displayArea)
+    {
+        ASSERT_NO_FATAL_FAILURE(getActiveConfigAttribute(display,
+                HWC2_ATTRIBUTE_WIDTH, &displayArea->width));
+        ASSERT_NO_FATAL_FAILURE(getActiveConfigAttribute(display,
+                HWC2_ATTRIBUTE_HEIGHT, &displayArea->height));
+    }
+
+    void closeFences(hwc2_display_t display, int32_t presentFence)
+    {
+        std::vector<hwc2_layer_t> layers;
+        std::vector<int32_t> fences;
+        const int msWait = 3000;
+
+        if (presentFence >= 0) {
+            ASSERT_GE(sync_wait(presentFence, msWait), 0);
+            close(presentFence);
+        }
+
+        ASSERT_NO_FATAL_FAILURE(getReleaseFences(display, &layers, &fences));
+        EXPECT_EQ(layers.size(), fences.size());
+
+        for (int32_t fence : fences) {
+            EXPECT_GE(sync_wait(fence, msWait), 0);
+            if (fence >= 0)
+                close(fence);
+        }
+    }
+
+    void setLayerProperties(hwc2_display_t display, hwc2_layer_t layer,
+            Hwc2TestLayers* testLayers, bool* outSkip)
+    {
+        hwc2_composition_t composition;
+        buffer_handle_t handle = nullptr;
+        int32_t acquireFence;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+        *outSkip = true;
+
+        if (!testLayers->contains(layer))
+            return;
+
+        composition = testLayers->getComposition(layer);
+
+        /* If the device cannot support a buffer format, then do not continue */
+        if ((composition == HWC2_COMPOSITION_DEVICE
+                || composition == HWC2_COMPOSITION_CURSOR)
+                && testLayers->getBuffer(layer, &handle, &acquireFence) < 0)
+            return;
+
+        EXPECT_NO_FATAL_FAILURE(setLayerCompositionType(display, layer,
+                composition, &err));
+        if (err == HWC2_ERROR_UNSUPPORTED)
+            EXPECT_TRUE(composition != HWC2_COMPOSITION_CLIENT
+                    && composition != HWC2_COMPOSITION_DEVICE);
+
+        const hwc_rect_t cursor = testLayers->getCursorPosition(layer);
+
+        EXPECT_NO_FATAL_FAILURE(setLayerBuffer(display, layer, handle,
+                acquireFence));
+        EXPECT_NO_FATAL_FAILURE(setLayerBlendMode(display, layer,
+                testLayers->getBlendMode(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerColor(display, layer,
+                testLayers->getColor(layer)));
+        EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer, cursor.left,
+                cursor.top));
+        EXPECT_NO_FATAL_FAILURE(setLayerDataspace(display, layer,
+                testLayers->getDataspace(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerDisplayFrame(display, layer,
+                testLayers->getDisplayFrame(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerPlaneAlpha(display, layer,
+                testLayers->getPlaneAlpha(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerSourceCrop(display, layer,
+                testLayers->getSourceCrop(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerSurfaceDamage(display, layer,
+                testLayers->getSurfaceDamage(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerTransform(display, layer,
+                testLayers->getTransform(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerVisibleRegion(display, layer,
+                testLayers->getVisibleRegion(layer)));
+        EXPECT_NO_FATAL_FAILURE(setLayerZOrder(display, layer,
+                testLayers->getZOrder(layer)));
+
+        *outSkip = false;
+    }
+
+    void setLayerProperties(hwc2_display_t display,
+            const std::vector<hwc2_layer_t>& layers,
+            Hwc2TestLayers* testLayers, bool* outSkip)
+    {
+        for (auto layer : layers) {
+            EXPECT_NO_FATAL_FAILURE(setLayerProperties(display, layer,
+                    testLayers, outSkip));
+            if (*outSkip)
+                return;
+        }
+    }
+
+    void setClientTarget(hwc2_display_t display,
+            Hwc2TestClientTarget* testClientTarget,
+            const Hwc2TestLayers& testLayers,
+            const std::set<hwc2_layer_t>& clientLayers,
+            const std::set<hwc2_layer_t>& clearLayers, bool flipClientTarget,
+            const Area& displayArea)
+    {
+        android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
+        hwc_region_t damage = { };
+        buffer_handle_t handle;
+        int32_t acquireFence;
+
+        ASSERT_EQ(testClientTarget->getBuffer(testLayers, clientLayers,
+                clearLayers, flipClientTarget, displayArea, &handle,
+                &acquireFence), 0);
+        EXPECT_NO_FATAL_FAILURE(setClientTarget(display, handle, acquireFence,
+                dataspace, damage));
+    }
+
+    void presentDisplays(size_t layerCnt, Hwc2TestCoverage coverage,
+            const std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>&
+            coverageExceptions, bool optimize)
+    {
+        for (auto display : mDisplays) {
+            std::vector<hwc2_config_t> configs;
+
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+            ASSERT_NO_FATAL_FAILURE(enableVsync(display));
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+            for (auto config : configs) {
+                Area displayArea;
+                std::vector<hwc2_layer_t> layers;
+
+                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+                        &displayArea));
+
+                ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+                Hwc2TestLayers testLayers(layers, coverage, displayArea,
+                        coverageExceptions);
+
+                if (optimize && !testLayers.optimizeLayouts())
+                    continue;
+
+                std::set<hwc2_layer_t> clientLayers;
+                std::set<hwc2_layer_t> clearLayers;
+                Hwc2TestClientTarget testClientTarget;
+
+                do {
+                    uint32_t numTypes, numRequests;
+                    bool hasChanges, skip;
+                    bool flipClientTarget;
+                    int32_t presentFence;
+
+                    ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers,
+                            &testLayers, &skip));
+                    if (skip)
+                        continue;
+
+                    ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes,
+                            &numRequests, &hasChanges));
+                    if (hasChanges)
+                        EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size()))
+                                << "wrong number of requests";
+
+                    ASSERT_NO_FATAL_FAILURE(handleCompositionChanges(display,
+                            testLayers, layers, numTypes, &clientLayers));
+                    ASSERT_NO_FATAL_FAILURE(handleRequests(display, layers,
+                            numRequests, &clearLayers, &flipClientTarget));
+                    ASSERT_NO_FATAL_FAILURE(setClientTarget(display,
+                            &testClientTarget, testLayers, clientLayers,
+                            clearLayers, flipClientTarget, displayArea));
+                    ASSERT_NO_FATAL_FAILURE(acceptDisplayChanges(display));
+
+                    ASSERT_NO_FATAL_FAILURE(waitForVsync());
+
+                    EXPECT_NO_FATAL_FAILURE(presentDisplay(display,
+                            &presentFence));
+
+                    ASSERT_NO_FATAL_FAILURE(closeFences(display, presentFence));
+
+                } while (testLayers.advance());
+
+                ASSERT_NO_FATAL_FAILURE(destroyLayers(display,
+                        std::move(layers)));
+            }
+
+            ASSERT_NO_FATAL_FAILURE(disableVsync(display));
+            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+        }
+    }
+
+    hwc2_device_t* mHwc2Device = nullptr;
+
+    enum class Hwc2TestHotplugStatus {
+        Init = 1,
+        Receiving,
+        Done,
+    };
+
+    std::mutex mHotplugMutex;
+    std::condition_variable mHotplugCv;
+    Hwc2TestHotplugStatus mHotplugStatus = Hwc2TestHotplugStatus::Init;
+    std::unordered_set<hwc2_display_t> mDisplays;
+
+    /* Store all created layers that have not been destroyed. If an ASSERT_*
+     * fails, then destroy the layers on exit */
+    std::set<std::pair<hwc2_display_t, hwc2_layer_t>> mLayers;
+
+    /* Store the power mode state. If it is not HWC2_POWER_MODE_OFF when
+     * tearing down the test cases, change it to HWC2_POWER_MODE_OFF */
+    std::set<hwc2_display_t> mActiveDisplays;
+
+    /* Store all created virtual displays that have not been destroyed. If an
+     * ASSERT_* fails, then destroy the virtual displays on exit */
+    std::set<hwc2_display_t> mVirtualDisplays;
+
+    std::mutex mVsyncMutex;
+    std::condition_variable mVsyncCv;
+    hwc2_display_t mVsyncDisplay;
+    int64_t mVsyncTimestamp = -1;
+};
+
+void hwc2TestHotplugCallback(hwc2_callback_data_t callbackData,
+        hwc2_display_t display, int32_t connection)
+{
+    if (callbackData)
+        static_cast<Hwc2Test*>(callbackData)->hotplugCallback(display,
+                connection);
+}
+
+void hwc2TestVsyncCallback(hwc2_callback_data_t callbackData,
+        hwc2_display_t display, int64_t timestamp)
+{
+    if (callbackData)
+        static_cast<Hwc2Test*>(callbackData)->vsyncCallback(display,
+                timestamp);
+}
+
+void setBlendMode(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerBlendMode(display, layer,
+            testLayer->getBlendMode(), outErr));
+}
+
+void setBuffer(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    buffer_handle_t handle;
+    android::base::unique_fd acquireFence;
+    hwc2_composition_t composition = testLayer->getComposition();
+
+    if (composition == HWC2_COMPOSITION_CLIENT
+            || composition == HWC2_COMPOSITION_SOLID_COLOR
+            || composition == HWC2_COMPOSITION_SIDEBAND)
+        return;
+
+    if (testLayer->getBuffer(&handle, &acquireFence) < 0)
+        return;
+
+    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display, layer,
+            composition));
+    EXPECT_NO_FATAL_FAILURE(test->setLayerBuffer(display, layer,
+            handle, acquireFence, outErr));
+}
+
+void setColor(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display,
+            layer, HWC2_COMPOSITION_SOLID_COLOR));
+    ASSERT_NO_FATAL_FAILURE(test->setLayerPlaneAlpha(display,
+            layer, testLayer->getPlaneAlpha()));
+    ASSERT_NO_FATAL_FAILURE(test->setLayerBlendMode(display,
+            layer, testLayer->getBlendMode()));
+    EXPECT_NO_FATAL_FAILURE(test->setLayerColor(display, layer,
+            testLayer->getColor(), outErr));
+}
+
+void setComposition(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    hwc2_composition_t composition = testLayer->getComposition();
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display, layer,
+            composition, &err));
+    if (outErr) {
+        *outErr = err;
+        return;
+    }
+
+    if (composition != HWC2_COMPOSITION_SIDEBAND) {
+        EXPECT_EQ(err, HWC2_ERROR_NONE) << "returned wrong error code";
+    } else {
+        EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_UNSUPPORTED)
+                 << "returned wrong error code";
+    }
+}
+
+void setCursorPosition(Hwc2Test* test, hwc2_display_t display,
+        hwc2_layer_t layer, Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display,
+            layer, HWC2_COMPOSITION_CURSOR));
+
+    const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
+    EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display, layer,
+            cursorPosition.left, cursorPosition.top, outErr));
+}
+
+void setDataspace(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerDataspace(display, layer,
+            testLayer->getDataspace(), outErr));
+}
+
+void setDisplayFrame(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerDisplayFrame(display, layer,
+            testLayer->getDisplayFrame(), outErr));
+}
+
+void setPlaneAlpha(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t *outErr)
+{
+    ASSERT_NO_FATAL_FAILURE(test->setLayerBlendMode(display, layer,
+            testLayer->getBlendMode()));
+    EXPECT_NO_FATAL_FAILURE(test->setLayerPlaneAlpha(display, layer,
+            testLayer->getPlaneAlpha(), outErr));
+}
+
+void setSourceCrop(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerSourceCrop(display, layer,
+            testLayer->getSourceCrop(), outErr));
+}
+
+void setSurfaceDamage(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerSurfaceDamage(display, layer,
+            testLayer->getSurfaceDamage(), outErr));
+}
+
+void setTransform(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerTransform(display, layer,
+            testLayer->getTransform(), outErr));
+}
+
+void setVisibleRegion(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerVisibleRegion(display, layer,
+            testLayer->getVisibleRegion(), outErr));
+}
+
+void setZOrder(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
+{
+    EXPECT_NO_FATAL_FAILURE(test->setLayerZOrder(display, layer,
+            testLayer->getZOrder(), outErr));
+}
+
+bool advanceBlendMode(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceBlendMode();
+}
+
+bool advanceBuffer(Hwc2TestLayer* testLayer)
+{
+    if (testLayer->advanceComposition())
+        return true;
+    return testLayer->advanceBufferArea();
+}
+
+bool advanceColor(Hwc2TestLayer* testLayer)
+{
+    /* Color depends on blend mode so advance blend mode last so color is not
+     * force to update as often */
+    if (testLayer->advancePlaneAlpha())
+        return true;
+    if (testLayer->advanceColor())
+        return true;
+    return testLayer->advanceBlendMode();
+}
+
+bool advanceComposition(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceComposition();
+}
+
+bool advanceCursorPosition(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceCursorPosition();
+}
+
+bool advanceDataspace(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceDataspace();
+}
+
+bool advanceDisplayFrame(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceDisplayFrame();
+}
+
+bool advancePlaneAlpha(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advancePlaneAlpha();
+}
+
+bool advanceSourceCrop(Hwc2TestLayer* testLayer)
+{
+    if (testLayer->advanceSourceCrop())
+        return true;
+    return testLayer->advanceBufferArea();
+}
+
+bool advanceSurfaceDamage(Hwc2TestLayer* testLayer)
+{
+    if (testLayer->advanceSurfaceDamage())
+        return true;
+    return testLayer->advanceBufferArea();
+}
+
+bool advanceTransform(Hwc2TestLayer* testLayer)
+{
+    return testLayer->advanceTransform();
+}
+
+bool advanceVisibleRegions(Hwc2TestLayers* testLayers)
+{
+    return testLayers->advanceVisibleRegions();
+}
+
+bool advanceClientTargetSupport(
+        Hwc2TestClientTargetSupport* testClientTargetSupport)
+{
+    return testClientTargetSupport->advance();
+}
+
+static const std::array<hwc2_function_descriptor_t, 42> requiredFunctions = {{
+    HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES,
+    HWC2_FUNCTION_CREATE_LAYER,
+    HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY,
+    HWC2_FUNCTION_DESTROY_LAYER,
+    HWC2_FUNCTION_DESTROY_VIRTUAL_DISPLAY,
+    HWC2_FUNCTION_DUMP,
+    HWC2_FUNCTION_GET_ACTIVE_CONFIG,
+    HWC2_FUNCTION_GET_CHANGED_COMPOSITION_TYPES,
+    HWC2_FUNCTION_GET_CLIENT_TARGET_SUPPORT,
+    HWC2_FUNCTION_GET_COLOR_MODES,
+    HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE,
+    HWC2_FUNCTION_GET_DISPLAY_CONFIGS,
+    HWC2_FUNCTION_GET_DISPLAY_NAME,
+    HWC2_FUNCTION_GET_DISPLAY_REQUESTS,
+    HWC2_FUNCTION_GET_DISPLAY_TYPE,
+    HWC2_FUNCTION_GET_DOZE_SUPPORT,
+    HWC2_FUNCTION_GET_HDR_CAPABILITIES,
+    HWC2_FUNCTION_GET_MAX_VIRTUAL_DISPLAY_COUNT,
+    HWC2_FUNCTION_GET_RELEASE_FENCES,
+    HWC2_FUNCTION_PRESENT_DISPLAY,
+    HWC2_FUNCTION_REGISTER_CALLBACK,
+    HWC2_FUNCTION_SET_ACTIVE_CONFIG,
+    HWC2_FUNCTION_SET_CLIENT_TARGET,
+    HWC2_FUNCTION_SET_COLOR_MODE,
+    HWC2_FUNCTION_SET_COLOR_TRANSFORM,
+    HWC2_FUNCTION_SET_CURSOR_POSITION,
+    HWC2_FUNCTION_SET_LAYER_BLEND_MODE,
+    HWC2_FUNCTION_SET_LAYER_BUFFER,
+    HWC2_FUNCTION_SET_LAYER_COLOR,
+    HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE,
+    HWC2_FUNCTION_SET_LAYER_DATASPACE,
+    HWC2_FUNCTION_SET_LAYER_DISPLAY_FRAME,
+    HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA,
+    HWC2_FUNCTION_SET_LAYER_SOURCE_CROP,
+    HWC2_FUNCTION_SET_LAYER_SURFACE_DAMAGE,
+    HWC2_FUNCTION_SET_LAYER_TRANSFORM,
+    HWC2_FUNCTION_SET_LAYER_VISIBLE_REGION,
+    HWC2_FUNCTION_SET_LAYER_Z_ORDER,
+    HWC2_FUNCTION_SET_OUTPUT_BUFFER,
+    HWC2_FUNCTION_SET_POWER_MODE,
+    HWC2_FUNCTION_SET_VSYNC_ENABLED,
+    HWC2_FUNCTION_VALIDATE_DISPLAY,
+}};
+
+/* TESTCASE: Tests that the HWC2 supports all required functions. */
+TEST_F(Hwc2Test, GET_FUNCTION)
+{
+    for (hwc2_function_descriptor_t descriptor : requiredFunctions) {
+        hwc2_function_pointer_t pfn = getFunction(descriptor);
+        EXPECT_TRUE(pfn) << "failed to get function "
+                << getFunctionDescriptorName(descriptor);
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 fails to retrieve and invalid function. */
+TEST_F(Hwc2Test, GET_FUNCTION_invalid_function)
+{
+    hwc2_function_pointer_t pfn = getFunction(HWC2_FUNCTION_INVALID);
+    EXPECT_FALSE(pfn) << "failed to get invalid function";
+}
+
+/* TESTCASE: Tests that the HWC2 does not return an invalid capability. */
+TEST_F(Hwc2Test, GET_CAPABILITIES)
+{
+    std::vector<hwc2_capability_t> capabilities;
+
+    getCapabilities(&capabilities);
+
+    EXPECT_EQ(std::count(capabilities.begin(), capabilities.end(),
+            HWC2_CAPABILITY_INVALID), 0);
+}
+
+static const std::array<hwc2_callback_descriptor_t, 3> callbackDescriptors = {{
+    HWC2_CALLBACK_HOTPLUG,
+    HWC2_CALLBACK_REFRESH,
+    HWC2_CALLBACK_VSYNC,
+}};
+
+/* TESTCASE: Tests that the HWC2 can successfully register all required
+ * callback functions. */
+TEST_F(Hwc2Test, REGISTER_CALLBACK)
+{
+    hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+            const_cast<char*>("data"));
+
+    for (auto descriptor : callbackDescriptors) {
+        ASSERT_NO_FATAL_FAILURE(registerCallback(descriptor, data,
+                []() { return; }));
+    }
+}
+
+/* TESTCASE: Test that the HWC2 fails to register invalid callbacks. */
+TEST_F(Hwc2Test, REGISTER_CALLBACK_bad_parameter)
+{
+    hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+            const_cast<char*>("data"));
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_INVALID, data,
+            []() { return; }, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can register a callback with null data. */
+TEST_F(Hwc2Test, REGISTER_CALLBACK_null_data)
+{
+    hwc2_callback_data_t data = nullptr;
+
+    for (auto descriptor : callbackDescriptors) {
+        ASSERT_NO_FATAL_FAILURE(registerCallback(descriptor, data,
+                []() { return; }));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 returns the correct display type for each
+ * physical display. */
+TEST_F(Hwc2Test, GET_DISPLAY_TYPE)
+{
+    for (auto display : mDisplays) {
+        hwc2_display_type_t type;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayType(display, &type));
+        EXPECT_EQ(type, HWC2_DISPLAY_TYPE_PHYSICAL) << "failed to return"
+                " correct display type";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 returns an error when the display type of a bad
+ * display is requested. */
+TEST_F(Hwc2Test, GET_DISPLAY_TYPE_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_display_type_t type;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getDisplayType(display, &type, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can create and destroy layers. */
+TEST_F(Hwc2Test, CREATE_DESTROY_LAYER)
+{
+    for (auto display : mDisplays) {
+        hwc2_layer_t layer;
+
+        ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot create a layer for a bad display */
+TEST_F(Hwc2Test, CREATE_LAYER_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_layer_t layer;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 will either support a large number of resources
+ * or will return no resources. */
+TEST_F(Hwc2Test, CREATE_LAYER_no_resources)
+{
+    const size_t layerCnt = 1000;
+
+    for (auto display : mDisplays) {
+        std::vector<hwc2_layer_t> layers;
+
+        ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot destroy a layer for a bad display */
+TEST_F(Hwc2Test, DESTROY_LAYER_bad_display)
+{
+    hwc2_display_t badDisplay;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&badDisplay));
+
+    for (auto display : mDisplays) {
+        hwc2_layer_t layer = 0;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(badDisplay, layer, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(badDisplay, layer, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot destory a bad layer */
+TEST_F(Hwc2Test, DESTROY_LAYER_bad_layer)
+{
+    for (auto display : mDisplays) {
+        hwc2_layer_t layer;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, UINT64_MAX / 2, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, 0, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, UINT64_MAX - 1, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, 1, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, UINT64_MAX, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer + 1, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+
+        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
+    }
+}
+
+static const std::array<hwc2_attribute_t, 2> requiredAttributes = {{
+    HWC2_ATTRIBUTE_WIDTH,
+    HWC2_ATTRIBUTE_HEIGHT,
+}};
+
+static const std::array<hwc2_attribute_t, 3> optionalAttributes = {{
+    HWC2_ATTRIBUTE_VSYNC_PERIOD,
+    HWC2_ATTRIBUTE_DPI_X,
+    HWC2_ATTRIBUTE_DPI_Y,
+}};
+
+/* TESTCASE: Tests that the HWC2 can return display attributes for a valid
+ * config. */
+TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            int32_t value;
+
+            for (auto attribute : requiredAttributes) {
+                ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                        attribute, &value));
+                EXPECT_GE(value, 0) << "missing required attribute "
+                        << getAttributeName(attribute) << " for config "
+                        << config;
+            }
+            for (auto attribute : optionalAttributes) {
+                ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                        attribute, &value));
+            }
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will return a value of -1 for an invalid
+ * attribute */
+TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE_invalid_attribute)
+{
+    const hwc2_attribute_t attribute = HWC2_ATTRIBUTE_INVALID;
+
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            int32_t value;
+            hwc2_error_t err = HWC2_ERROR_NONE;
+
+            ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                    attribute, &value, &err));
+            EXPECT_EQ(value, -1) << "failed to return -1 for an invalid"
+                    " attribute for config " << config;
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will fail to get attributes for a bad display */
+TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE_bad_display)
+{
+    hwc2_display_t display;
+    const hwc2_config_t config = 0;
+    int32_t value;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    for (auto attribute : requiredAttributes) {
+        ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config, attribute,
+                &value, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+    }
+
+    for (auto attribute : optionalAttributes) {
+        ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config, attribute,
+                &value, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will fail to get attributes for a bad config */
+TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE_bad_config)
+{
+    for (auto display : mDisplays) {
+        hwc2_config_t config;
+        int32_t value;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(getInvalidConfig(display, &config));
+
+        for (auto attribute : requiredAttributes) {
+            ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                    attribute, &value, &err));
+            EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
+        }
+
+        for (auto attribute : optionalAttributes) {
+            ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
+                    attribute, &value, &err));
+            EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will get display configs for active displays */
+TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will not get display configs for bad displays */
+TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<hwc2_config_t> configs;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs, &err));
+
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+    EXPECT_TRUE(configs.empty()) << "returned configs for bad display";
+}
+
+/* TESTCASE: Tests that the HWC2 will return the same config list multiple
+ * times in a row. */
+TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS_same)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs1, configs2;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs1));
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs2));
+
+        EXPECT_TRUE(std::is_permutation(configs1.begin(), configs1.end(),
+                configs2.begin())) << "returned two different config sets";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 does not return duplicate display configs */
+TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS_duplicate)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        std::unordered_set<hwc2_config_t> configsSet(configs.begin(),
+                configs.end());
+        EXPECT_EQ(configs.size(), configsSet.size()) << "returned duplicate"
+                " configs";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 returns the active config for a display */
+TEST_F(Hwc2Test, GET_ACTIVE_CONFIG)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            hwc2_config_t activeConfig;
+
+            ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+            ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &activeConfig));
+
+            EXPECT_EQ(activeConfig, config) << "failed to get active config";
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 does not return an active config for a bad
+ * display. */
+TEST_F(Hwc2Test, GET_ACTIVE_CONFIG_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_config_t activeConfig;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &activeConfig, &err));
+
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 either begins with a valid active config
+ * or returns an error when getActiveConfig is called. */
+TEST_F(Hwc2Test, GET_ACTIVE_CONFIG_bad_config)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+        hwc2_config_t activeConfig;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        if (configs.empty())
+            return;
+
+        ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &activeConfig, &err));
+        if (err == HWC2_ERROR_NONE) {
+            EXPECT_NE(std::count(configs.begin(), configs.end(),
+                    activeConfig), 0) << "active config is not found in "
+                    " configs for display";
+        } else {
+            EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can set every display config as an active
+ * config */
+TEST_F(Hwc2Test, SET_ACTIVE_CONFIG)
+{
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            EXPECT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an active config for a bad display */
+TEST_F(Hwc2Test, SET_ACTIVE_CONFIG_bad_display)
+{
+    hwc2_display_t display;
+    const hwc2_config_t config = 0;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid active config */
+TEST_F(Hwc2Test, SET_ACTIVE_CONFIG_bad_config)
+{
+    for (auto display : mDisplays) {
+        hwc2_config_t config;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(getInvalidConfig(display, &config));
+
+        ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 returns a valid value for getDozeSupport. */
+TEST_F(Hwc2Test, GET_DOZE_SUPPORT)
+{
+    for (auto display : mDisplays) {
+        int32_t support = -1;
+
+        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support));
+
+        EXPECT_TRUE(support == 0 || support == 1) << "invalid doze support value";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get doze support for a bad display. */
+TEST_F(Hwc2Test, GET_DOZE_SUPPORT_bad_display)
+{
+    hwc2_display_t display;
+    int32_t support = -1;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support, &err));
+
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can set all supported power modes */
+TEST_F(Hwc2Test, SET_POWER_MODE)
+{
+    for (auto display : mDisplays) {
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+
+        int32_t support = -1;
+        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support));
+        if (support != 1)
+            return;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
+                HWC2_POWER_MODE_DOZE_SUSPEND));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a power mode for a bad display. */
+TEST_F(Hwc2Test, SET_POWER_MODE_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+    int32_t support = -1;
+    ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support, &err));
+    if (support != 1)
+        return;
+
+    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE_SUSPEND,
+            &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid power mode value. */
+TEST_F(Hwc2Test, SET_POWER_MODE_bad_parameter)
+{
+    for (auto display : mDisplays) {
+        hwc2_power_mode_t mode = static_cast<hwc2_power_mode_t>(
+                HWC2_POWER_MODE_DOZE_SUSPEND + 1);
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, mode, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code "
+                << mode;
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 will return unsupported if it does not support
+ * an optional power mode. */
+TEST_F(Hwc2Test, SET_POWER_MODE_unsupported)
+{
+    for (auto display : mDisplays) {
+        int32_t support = -1;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support, &err));
+        if (support == 1)
+            return;
+
+        ASSERT_EQ(support, 0) << "invalid doze support value";
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE,
+                &err));
+        EXPECT_EQ(err, HWC2_ERROR_UNSUPPORTED) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
+                HWC2_POWER_MODE_DOZE_SUSPEND, &err));
+        EXPECT_EQ(err, HWC2_ERROR_UNSUPPORTED) <<  "returned wrong error code";
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can set the same power mode multiple times. */
+TEST_F(Hwc2Test, SET_POWER_MODE_stress)
+{
+    for (auto display : mDisplays) {
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+
+        int32_t support = -1;
+        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support));
+        if (support != 1)
+            return;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
+                HWC2_POWER_MODE_DOZE_SUSPEND));
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
+                HWC2_POWER_MODE_DOZE_SUSPEND));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can enable and disable vsync on active
+ * displays */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED)
+{
+    for (auto display : mDisplays) {
+        hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+                const_cast<char*>("data"));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
+                []() { return; }));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 issues a valid vsync callback. */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_callback)
+{
+    for (auto display : mDisplays) {
+        hwc2_display_t receivedDisplay;
+        int64_t receivedTimestamp;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(enableVsync(display));
+
+        ASSERT_NO_FATAL_FAILURE(waitForVsync(&receivedDisplay,
+                &receivedTimestamp));
+
+        EXPECT_EQ(receivedDisplay, display) << "failed to get correct display";
+        EXPECT_GE(receivedTimestamp, 0) << "failed to get valid timestamp";
+
+        ASSERT_NO_FATAL_FAILURE(disableVsync(display));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot enable a vsync for a bad display */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+            const_cast<char*>("data"));
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
+            []() { return; }));
+
+    ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+
+    ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot enable an invalid vsync value */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_bad_parameter)
+{
+    for (auto display : mDisplays) {
+        hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+                const_cast<char*>("data"));
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
+                []() { return; }));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_INVALID,
+                &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can enable and disable a vsync value multiple
+ * times. */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_stress)
+{
+    for (auto display : mDisplays) {
+        hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
+                const_cast<char*>("data"));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
+                []() { return; }));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can set a vsync enable value when the display
+ * is off and no callback is registered. */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_no_callback_no_power)
+{
+    const uint secs = 1;
+
+    for (auto display : mDisplays) {
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+
+        sleep(secs);
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can set a vsync enable value when no callback
+ * is registered. */
+TEST_F(Hwc2Test, SET_VSYNC_ENABLED_no_callback)
+{
+    const uint secs = 1;
+
+    for (auto display : mDisplays) {
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
+
+        sleep(secs);
+
+        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 returns a display name for each display */
+TEST_F(Hwc2Test, GET_DISPLAY_NAME)
+{
+    for (auto display : mDisplays) {
+        std::string name;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayName(display, &name));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 does not return a display name for a bad
+ * display */
+TEST_F(Hwc2Test, GET_DISPLAY_NAME_bad_display)
+{
+    hwc2_display_t display;
+    std::string name;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getDisplayName(display, &name, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can set basic composition types. */
+TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setComposition, advanceComposition));
+}
+
+/* TESTCASE: Tests that the HWC2 can update a basic composition type on a
+ * layer. */
+TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setComposition, advanceComposition));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a composition type for a bad layer */
+TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setComposition));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a bad composition type */
+TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadParameter(
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    hwc2_error_t* outErr) {
+
+                ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display,
+                        layer, HWC2_COMPOSITION_INVALID, outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the cursor position of a layer. */
+TEST_F(Hwc2Test, SET_CURSOR_POSITION)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            ::setCursorPosition, advanceCursorPosition));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the cursor position of a layer. */
+TEST_F(Hwc2Test, SET_CURSOR_POSITION_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            ::setCursorPosition, advanceCursorPosition));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the cursor position of a layer when the
+ * composition type has not been set to HWC2_COMPOSITION_CURSOR. */
+TEST_F(Hwc2Test, SET_CURSOR_POSITION_composition_type_unset)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
+
+                const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
+                EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display, layer,
+                        cursorPosition.left, cursorPosition.top, outErr));
+            },
+
+            advanceCursorPosition));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the cursor position of a bad
+ * display. */
+TEST_F(Hwc2Test, SET_CURSOR_POSITION_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_layer_t layer = 0;
+    int32_t x = 0, y = 0;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(setCursorPosition(display, layer, x, y, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the cursor position of a bad layer. */
+TEST_F(Hwc2Test, SET_CURSOR_POSITION_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
+
+                const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
+                EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display,
+                        badLayer, cursorPosition.left, cursorPosition.top,
+                        outErr));
+            }
+   ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set a blend mode value of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setBlendMode, advanceBlendMode));
+}
+
+/* TESTCASE: Tests that the HWC2 can update a blend mode value of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setBlendMode, advanceBlendMode));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a blend mode for a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setBlendMode));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid blend mode. */
+TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadParameter(
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    hwc2_error_t* outErr) {
+
+                ASSERT_NO_FATAL_FAILURE(test->setLayerBlendMode(display,
+                        layer, HWC2_BLEND_MODE_INVALID, outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the buffer of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_BUFFER)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setBuffer, advanceBuffer));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the buffer of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_BUFFER_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setBuffer, advanceBuffer));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the buffer of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_BUFFER_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
+
+                buffer_handle_t handle = nullptr;
+                android::base::unique_fd acquireFence;
+
+                /* If there is not available buffer for the given buffer
+                 * properties, it should not fail this test case */
+                if (testLayer->getBuffer(&handle, &acquireFence) == 0) {
+                    *outErr = HWC2_ERROR_BAD_LAYER;
+                    return;
+                }
+
+                ASSERT_NO_FATAL_FAILURE(test->setLayerBuffer(display, badLayer,
+                        handle, acquireFence, outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set an invalid buffer for a layer. */
+TEST_F(Hwc2Test, SET_LAYER_BUFFER_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadParameter(
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    hwc2_error_t* outErr) {
+
+                buffer_handle_t handle = nullptr;
+                int32_t acquireFence = -1;
+
+                ASSERT_NO_FATAL_FAILURE(test->setLayerBuffer(display, layer,
+                        handle, acquireFence, outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the color of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_COLOR)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setColor, advanceColor));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the color of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_COLOR_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setColor, advanceColor));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the color of a layer when the
+ * composition type has not been set to HWC2_COMPOSITION_SOLID_COLOR. */
+TEST_F(Hwc2Test, SET_LAYER_COLOR_composition_type_unset)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Basic,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
+
+                EXPECT_NO_FATAL_FAILURE(test->setLayerColor(display, layer,
+                        testLayer->getColor(), outErr));
+            },
+
+            advanceColor));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the color of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_COLOR_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
+
+                EXPECT_NO_FATAL_FAILURE(test->setLayerColor(display, badLayer,
+                        testLayer->getColor(), outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the dataspace of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_DATASPACE)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setDataspace, advanceDataspace));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the dataspace of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_DATASPACE_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setDataspace, advanceDataspace));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a dataspace for a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_DATASPACE_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setDataspace));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the display frame of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_DISPLAY_FRAME)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setDisplayFrame, advanceDisplayFrame));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the display frame of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_DISPLAY_FRAME_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setDisplayFrame, advanceDisplayFrame));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the display frame of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_DISPLAY_FRAME_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setDisplayFrame));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the plane alpha of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_PLANE_ALPHA)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setPlaneAlpha, advancePlaneAlpha));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the plane alpha of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_PLANE_ALPHA_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setPlaneAlpha, advancePlaneAlpha));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a plane alpha for a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_PLANE_ALPHA_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
+                    Hwc2TestLayer* testLayer, hwc2_error_t *outErr) {
+
+                    EXPECT_NO_FATAL_FAILURE(test->setLayerPlaneAlpha(display,
+                            badLayer, testLayer->getPlaneAlpha(), outErr));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the source crop of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_SOURCE_CROP)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setSourceCrop, advanceSourceCrop));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the source crop of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_SOURCE_CROP_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setSourceCrop, advanceSourceCrop));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the source crop of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_SOURCE_CROP_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setSourceCrop));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the surface damage of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_SURFACE_DAMAGE)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setSurfaceDamage, advanceSurfaceDamage));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the surface damage of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_SURFACE_DAMAGE_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setSurfaceDamage, advanceSurfaceDamage));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the surface damage of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_SURFACE_DAMAGE_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setSurfaceDamage));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the transform value of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_TRANSFORM)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
+            setTransform, advanceTransform));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the transform value of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_TRANSFORM_update)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
+            setTransform, advanceTransform));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the transform for a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_TRANSFORM_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setTransform));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the visible region of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_VISIBLE_REGION)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperties(Hwc2TestCoverage::Basic, 5,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    Hwc2TestLayers* testLayers) {
+
+                EXPECT_NO_FATAL_FAILURE(test->setLayerVisibleRegion(display,
+                        layer, testLayers->getVisibleRegion(layer)));
+            },
+
+            advanceVisibleRegions));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the visible region of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_VISIBLE_REGION_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setVisibleRegion));
+}
+
+/* TESTCASE: Tests that the HWC2 can set the z order of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_Z_ORDER)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerProperties(Hwc2TestCoverage::Complete, 10,
+            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
+                    Hwc2TestLayers* testLayers) {
+
+                EXPECT_NO_FATAL_FAILURE(test->setLayerZOrder(display, layer,
+                        testLayers->getZOrder(layer)));
+            },
+
+            /* TestLayer z orders are set during the construction of TestLayers
+             * and cannot be updated. There is no need (or ability) to cycle
+             * through additional z order configurations. */
+            [] (Hwc2TestLayers* /*testLayers*/) {
+                return false;
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can update the z order of a layer. */
+TEST_F(Hwc2Test, SET_LAYER_Z_ORDER_update)
+{
+    const std::vector<uint32_t> zOrders = { static_cast<uint32_t>(0),
+            static_cast<uint32_t>(1), static_cast<uint32_t>(UINT32_MAX / 4),
+            static_cast<uint32_t>(UINT32_MAX / 2),
+            static_cast<uint32_t>(UINT32_MAX) };
+
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            hwc2_layer_t layer;
+
+            ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+
+            ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
+
+            for (uint32_t zOrder : zOrders) {
+                EXPECT_NO_FATAL_FAILURE(setLayerZOrder(display, layer, zOrder));
+            }
+
+            ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
+        }
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the z order of a bad layer. */
+TEST_F(Hwc2Test, SET_LAYER_Z_ORDER_bad_layer)
+{
+    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
+            setZOrder));
+}
+
+/* TESTCASE: Tests that the HWC2 can display a layer with basic property
+ * coverage */
+TEST_F(Hwc2Test, VALIDATE_DISPLAY_basic)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& layers,
+                    Hwc2TestLayers* /*testLayers*/) {
+
+                uint32_t numTypes, numRequests;
+                bool hasChanges = false;
+
+                EXPECT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size()))
+                            << "wrong number of requests";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can display 5 layers with default coverage. */
+TEST_F(Hwc2Test, VALIDATE_DISPLAY_default_5)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Default, 5,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& layers,
+                    Hwc2TestLayers* /*testLayers*/) {
+
+                uint32_t numTypes, numRequests;
+                bool hasChanges = false;
+
+                EXPECT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size()))
+                            << "wrong number of requests";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot validate a bad display */
+TEST_F(Hwc2Test, VALIDATE_DISPLAY_bad_display)
+{
+    hwc2_display_t display;
+    uint32_t numTypes, numRequests;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes, &numRequests,
+            &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can get display requests after validating a
+ * basic layer. */
+TEST_F(Hwc2Test, GET_DISPLAY_REQUESTS_basic)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& layers,
+                    Hwc2TestLayers* /*testLayers*/) {
+
+                uint32_t numTypes, numRequests;
+                bool hasChanges = false;
+
+                ASSERT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, layers.size())
+                            << "wrong number of requests";
+
+                EXPECT_NO_FATAL_FAILURE(test->handleRequests(display, layers,
+                        numRequests));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get display requests from a bad display */
+TEST_F(Hwc2Test, GET_DISPLAY_REQUESTS_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_display_request_t displayRequests;
+    std::vector<hwc2_layer_t> layers;
+    std::vector<hwc2_layer_request_t> layerRequests;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    EXPECT_NO_FATAL_FAILURE(getDisplayRequests(display, &displayRequests,
+            &layers, &layerRequests, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get display requests from an non
+ * validated display. */
+TEST_F(Hwc2Test, GET_DISPLAY_REQUESTS_not_validated)
+{
+    ASSERT_NO_FATAL_FAILURE(displayNonValidatedLayers(5,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    std::vector<hwc2_layer_t>* layers) {
+
+                hwc2_display_request_t displayRequests;
+                std::vector<hwc2_layer_request_t> layerRequests;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->getDisplayRequests(display,
+                        &displayRequests, layers, &layerRequests, &err));
+                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
+                        << "returned wrong error code";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can get changed composition types after
+ * validating a basic layer. */
+TEST_F(Hwc2Test, GET_CHANGED_COMPOSITION_TYPES_basic)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& layers,
+                    Hwc2TestLayers* testLayers) {
+
+                uint32_t numTypes, numRequests;
+                bool hasChanges = false;
+
+                ASSERT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, layers.size())
+                            << "wrong number of requests";
+
+                EXPECT_NO_FATAL_FAILURE(test->handleCompositionChanges(display,
+                        *testLayers, layers, numTypes));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get changed composition types from a bad
+ * display */
+TEST_F(Hwc2Test, GET_CHANGED_COMPOSITION_TYPES_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<hwc2_layer_t> layers;
+    std::vector<hwc2_composition_t> types;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    EXPECT_NO_FATAL_FAILURE(getChangedCompositionTypes(display, &layers,
+            &types, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get changed composition types from an non
+ * validated display. */
+TEST_F(Hwc2Test, GET_CHANGED_COMPOSITION_TYPES_not_validated)
+{
+    ASSERT_NO_FATAL_FAILURE(displayNonValidatedLayers(5,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    std::vector<hwc2_layer_t>* layers) {
+
+                std::vector<hwc2_composition_t> types;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->getChangedCompositionTypes(
+                        display, layers, &types, &err));
+                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
+                        << "returned wrong error code";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can accept display changes after validating a
+ * basic layer. */
+TEST_F(Hwc2Test, ACCEPT_DISPLAY_CHANGES_basic)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& layers,
+                    Hwc2TestLayers* testLayers) {
+
+                uint32_t numTypes, numRequests;
+                bool hasChanges = false;
+
+                ASSERT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, layers.size())
+                            << "wrong number of requests";
+
+                ASSERT_NO_FATAL_FAILURE(test->handleCompositionChanges(display,
+                        *testLayers, layers, numTypes));
+
+                EXPECT_NO_FATAL_FAILURE(test->acceptDisplayChanges(display));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot accept display changes from a bad
+ * display */
+TEST_F(Hwc2Test, ACCEPT_DISPLAY_CHANGES_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    EXPECT_NO_FATAL_FAILURE(acceptDisplayChanges(display, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot accept display changes from an non
+ * validated display. */
+TEST_F(Hwc2Test, ACCEPT_DISPLAY_CHANGES_not_validated)
+{
+    ASSERT_NO_FATAL_FAILURE(displayNonValidatedLayers(5,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    std::vector<hwc2_layer_t>* /*layers*/) {
+
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->acceptDisplayChanges(display, &err));
+                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
+                        << "returned wrong error code";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 supports client target with required values */
+TEST_F(Hwc2Test, GET_CLIENT_TARGET_SUPPORT)
+{
+    ASSERT_NO_FATAL_FAILURE(setClientTargetSupport(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const Hwc2TestClientTargetSupport& testClientTargetSupport) {
+
+                const Area bufferArea = testClientTargetSupport.getBufferArea();
+                const android_pixel_format_t format = HAL_PIXEL_FORMAT_RGBA_8888;
+
+                ASSERT_NO_FATAL_FAILURE(test->getClientTargetSupport(display,
+                        bufferArea.width, bufferArea.height, format,
+                        testClientTargetSupport.getDataspace()));
+            },
+
+            advanceClientTargetSupport));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get client target support for a bad
+ * display. */
+TEST_F(Hwc2Test, GET_CLIENT_TARGET_SUPPORT_bad_display)
+{
+    ASSERT_NO_FATAL_FAILURE(setClientTargetSupport(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t /*display*/,
+                    const Hwc2TestClientTargetSupport& testClientTargetSupport) {
+
+                const Area bufferArea = testClientTargetSupport.getBufferArea();
+                const android_pixel_format_t format = HAL_PIXEL_FORMAT_RGBA_8888;
+                hwc2_display_t badDisplay;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->getBadDisplay(&badDisplay));
+
+                ASSERT_NO_FATAL_FAILURE(test->getClientTargetSupport(badDisplay,
+                        bufferArea.width, bufferArea.height, format,
+                        testClientTargetSupport.getDataspace(), &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+            },
+
+            advanceClientTargetSupport));
+}
+
+/* TESTCASE: Tests that the HWC2 either supports or returns error unsupported
+ * for a variety of client target values. */
+TEST_F(Hwc2Test, GET_CLIENT_TARGET_SUPPORT_unsupported)
+{
+    ASSERT_NO_FATAL_FAILURE(setClientTargetSupport(Hwc2TestCoverage::Complete,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const Hwc2TestClientTargetSupport& testClientTargetSupport) {
+
+                const Area bufferArea = testClientTargetSupport.getBufferArea();
+                const android_pixel_format_t format = HAL_PIXEL_FORMAT_RGBA_8888;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->getClientTargetSupport(display,
+                        bufferArea.width, bufferArea.height, format,
+                        testClientTargetSupport.getDataspace(), &err));
+                EXPECT_TRUE(err == HWC2_ERROR_NONE
+                        || err == HWC2_ERROR_UNSUPPORTED)
+                        << "returned wrong error code";
+            },
+
+            advanceClientTargetSupport));
+}
+
+/* TESTCASE: Tests that the HWC2 can set a client target buffer for a basic
+ * layer. */
+TEST_F(Hwc2Test, SET_CLIENT_TARGET_basic)
+{
+    const android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
+    const hwc_region_t damage = { };
+    const size_t layerCnt = 1;
+
+    for (auto display : mDisplays) {
+        std::vector<hwc2_config_t> configs;
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+
+        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+        for (auto config : configs) {
+            Area displayArea;
+            std::vector<hwc2_layer_t> layers;
+
+            ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+            ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display, &displayArea));
+
+            ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
+            Hwc2TestLayers testLayers(layers, Hwc2TestCoverage::Basic,
+                    displayArea);
+
+            if (!testLayers.optimizeLayouts())
+                continue;
+
+            Hwc2TestClientTarget testClientTarget;
+
+            do {
+                std::set<hwc2_layer_t> clientLayers;
+                std::set<hwc2_layer_t> clearLayers;
+                uint32_t numTypes, numRequests;
+                bool hasChanges, skip;
+                bool flipClientTarget;
+                buffer_handle_t handle;
+                int32_t acquireFence;
+
+                ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers,
+                        &testLayers, &skip));
+                if (skip)
+                    continue;
+
+                ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes,
+                        &numRequests, &hasChanges));
+                if (hasChanges)
+                    EXPECT_LE(numTypes, layers.size())
+                            << "wrong number of requests";
+
+                ASSERT_NO_FATAL_FAILURE(handleCompositionChanges(display,
+                        testLayers, layers, numTypes, &clientLayers));
+                ASSERT_NO_FATAL_FAILURE(handleRequests(display, layers,
+                        numRequests, &clearLayers, &flipClientTarget));
+                ASSERT_EQ(testClientTarget.getBuffer(testLayers, clientLayers,
+                        clearLayers, flipClientTarget, displayArea, &handle,
+                        &acquireFence), 0);
+                EXPECT_NO_FATAL_FAILURE(setClientTarget(display, handle,
+                        acquireFence, dataspace, damage));
+
+                if (acquireFence >= 0)
+                    close(acquireFence);
+
+            } while (testLayers.advance());
+
+            ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
+        }
+
+        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a client target for a bad display. */
+TEST_F(Hwc2Test, SET_CLIENT_TARGET_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<hwc2_layer_t> layers;
+    const Area displayArea = {0, 0};
+    Hwc2TestLayers testLayers(layers, Hwc2TestCoverage::Default, displayArea);
+    std::set<hwc2_layer_t> clientLayers;
+    std::set<hwc2_layer_t> flipClientTargetLayers;
+    bool flipClientTarget = true;
+    const android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
+    const hwc_region_t damage = { };
+    buffer_handle_t handle;
+    int32_t acquireFence;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    Hwc2TestClientTarget testClientTarget;
+
+    ASSERT_EQ(testClientTarget.getBuffer(testLayers, clientLayers,
+            flipClientTargetLayers, flipClientTarget, displayArea, &handle,
+            &acquireFence), 0);
+
+    EXPECT_NO_FATAL_FAILURE(setClientTarget(display, handle, acquireFence,
+            dataspace, damage, &err));
+
+    if (acquireFence >= 0)
+        close(acquireFence);
+
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 default layer. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 default layers. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 3 default layers. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_3)
+{
+    const size_t layerCnt = 3;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 4 default layers. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_4)
+{
+    const size_t layerCnt = 4;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 5 default layers. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_5)
+{
+    const size_t layerCnt = 5;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 6 default layers. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_default_6)
+{
+    const size_t layerCnt = 6;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * blend mode. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_blend_mode_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::Transform, Hwc2TestCoverage::Basic},
+            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Basic}};
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * blend mode. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_blend_mode_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Basic}};
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * buffer. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_buffer_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::BufferArea, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * color. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_color_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::Color, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * color. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_color_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Basic},
+            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Basic},
+            {Hwc2TestPropertyName::Color, Hwc2TestCoverage::Basic}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * composition. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_composition_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * cursor. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_cursor_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::CursorPosition, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * cursor. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_cursor_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::CursorPosition, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Basic}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * dataspace. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_dataspace_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Dataspace, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * display frame. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * display frame. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 3 layers with complete coverage of
+ * display frame. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_3)
+{
+    const size_t layerCnt = 3;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 4 layers with complete coverage of
+ * display frame. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_4)
+{
+    const size_t layerCnt = 4;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * plane alpha. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_plane_alpha_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Basic},
+            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Complete}};
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * plane alpha. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_plane_alpha_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Basic},
+            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Complete}};
+    bool optimize = false;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * source crop. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_source_crop_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::SourceCrop, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * source crop. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_source_crop_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::SourceCrop, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * surface damage. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_surface_damage_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::SurfaceDamage, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * transform. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_transform_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Transform, Hwc2TestCoverage::Complete}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
+ * transform. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_transform_2)
+{
+    const size_t layerCnt = 2;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
+            {{Hwc2TestPropertyName::Transform, Hwc2TestCoverage::Complete},
+            {Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Basic}};
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
+ * basic. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_basic_1)
+{
+    const size_t layerCnt = 1;
+    Hwc2TestCoverage coverage = Hwc2TestCoverage::Basic;
+    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
+    bool optimize = true;
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
+            optimize));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot present a bad display.  */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_bad_display)
+{
+    hwc2_display_t display;
+    int32_t presentFence;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(presentDisplay(display, &presentFence, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot present an unvalidated display. */
+TEST_F(Hwc2Test, PRESENT_DISPLAY_not_validated)
+{
+    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Default, 1,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    const std::vector<hwc2_layer_t>& /*layers*/,
+                    Hwc2TestLayers* /*testLayers*/) {
+
+                int32_t presentFence;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->setPowerMode(display,
+                        HWC2_POWER_MODE_ON));
+                ASSERT_NO_FATAL_FAILURE(test->enableVsync(display));
+
+                ASSERT_NO_FATAL_FAILURE(test->waitForVsync());
+
+                ASSERT_NO_FATAL_FAILURE(test->presentDisplay(display,
+                        &presentFence, &err));
+                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
+                        << "returned wrong error code";
+
+                ASSERT_NO_FATAL_FAILURE(test->disableVsync(display));
+                ASSERT_NO_FATAL_FAILURE(test->setPowerMode(display,
+                        HWC2_POWER_MODE_OFF));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get release fences from a bad display. */
+TEST_F(Hwc2Test, GET_RELEASE_FENCES_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<hwc2_layer_t> layers;
+    std::vector<int32_t> fences;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getReleaseFences(display, &layers, &fences, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+static const std::array<android_color_mode, 9> androidColorModes = {{
+    HAL_COLOR_MODE_NATIVE,
+    HAL_COLOR_MODE_STANDARD_BT601_625,
+    HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED,
+    HAL_COLOR_MODE_STANDARD_BT601_525,
+    HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED,
+    HAL_COLOR_MODE_STANDARD_BT709,
+    HAL_COLOR_MODE_DCI_P3,
+    HAL_COLOR_MODE_SRGB,
+    HAL_COLOR_MODE_ADOBE_RGB,
+}};
+
+/* TESTCASE: Tests that the HWC2 can get the color modes for a display. The
+ * display must support HAL_COLOR_MODE_NATIVE */
+TEST_F(Hwc2Test, GET_COLOR_MODES)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                std::vector<android_color_mode_t> colorModes;
+
+                ASSERT_NO_FATAL_FAILURE(test->getColorModes(display,
+                        &colorModes));
+
+                EXPECT_NE(std::count(colorModes.begin(), colorModes.end(),
+                        HAL_COLOR_MODE_NATIVE), 0) << "all displays"
+                        " must support HAL_COLOR_MODE_NATIVE";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get color modes from a bad display. */
+TEST_F(Hwc2Test, GET_COLOR_MODES_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<android_color_mode_t> colorModes;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getColorModes(display, &colorModes, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can set the required color mode on a display. */
+TEST_F(Hwc2Test, SET_COLOR_MODES)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                const android_color_mode_t colorMode = HAL_COLOR_MODE_NATIVE;
+
+                EXPECT_NO_FATAL_FAILURE(test->setColorMode(display, colorMode));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set a color mode on a bad display. */
+TEST_F(Hwc2Test, SET_COLOR_MODES_bad_display)
+{
+    hwc2_display_t display;
+    const android_color_mode_t colorMode = HAL_COLOR_MODE_NATIVE;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(setColorMode(display, colorMode, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid color mode. */
+TEST_F(Hwc2Test, SET_COLOR_MODES_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                const android_color_mode_t colorMode =
+                        static_cast<android_color_mode_t>(-1);
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->setColorMode(display, colorMode,
+                        &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER)
+                        << "returned wrong error code";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 either supports or returns error unsupported
+ * for all valid color modes. */
+TEST_F(Hwc2Test, SET_COLOR_MODES_unsupported)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                for (auto colorMode : androidColorModes) {
+                    hwc2_error_t err = HWC2_ERROR_NONE;
+
+                    ASSERT_NO_FATAL_FAILURE(test->setColorMode(display,
+                            colorMode, &err));
+
+                    EXPECT_TRUE(err == HWC2_ERROR_NONE
+                            || err == HWC2_ERROR_UNSUPPORTED)
+                            << "returned wrong error code";
+                }
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 gets the HDR capabilities for a display and
+ * test if they are valid. */
+TEST_F(Hwc2Test, GET_HDR_CAPABILITIES)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                std::vector<android_hdr_t> hdrCapabilities;
+                float maxLuminance, maxAverageLuminance, minLuminance;
+
+                EXPECT_NO_FATAL_FAILURE(test->getHdrCapabilities(display,
+                        &hdrCapabilities, &maxLuminance, &maxAverageLuminance,
+                        &minLuminance));
+
+                if (hdrCapabilities.empty())
+                    return;
+
+                EXPECT_GE(maxLuminance, maxAverageLuminance);
+                EXPECT_GE(maxAverageLuminance, minLuminance);
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot get hdr capabilities from a bad display */
+TEST_F(Hwc2Test, GET_HDR_CAPABILITIES_bad_display)
+{
+    hwc2_display_t display;
+    std::vector<android_hdr_t> hdrCapabilities;
+    float maxLuminance, maxAverageLuminance, minLuminance;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(getHdrCapabilities(display, &hdrCapabilities,
+            &maxLuminance, &maxAverageLuminance, &minLuminance, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+static const std::array<float, 16> identityMatrix = {{
+    1.0,  0.0,  0.0,  0.0,
+    0.0,  1.0,  0.0,  0.0,
+    0.0,  0.0,  1.0,  0.0,
+    0.0,  0.0,  0.0,  1.0,
+}};
+
+/* Values for the color transform matrices were precomputed using the source code
+ * in surfaceflinger/Effects/Daltonizer.cpp. */
+
+static const std::array<const std::array<float, 16>, 5> exampleMatrices = {{
+    identityMatrix,
+    /* Converts RGB color to the XYZ space */
+    {{ 0.4124, 0.2126, 0.0193, 0,
+       0.3576, 0.7152, 0.1192, 0,
+       0.1805, 0.0722, 0.9505, 0,
+       0     , 0     , 0     , 1 }},
+    /* Protanomaly */
+    {{ 0.068493,  0.931506,  0,  0,
+       0.068493,  0.931507,  0,  0,
+       0.013626, -0.013626,  1,  0,
+       0,         0,         0,  1 }},
+    /* Deuteranomaly */
+    {{ 0.288299, 0.711701,  0,  0,
+       0.052709, 0.947291,  0,  0,
+      -0.257912, 0.257912,  1,  0,
+       0,        0,         0,  1 }},
+    /* Tritanomaly */
+    {{ 1, -0.805712, 0.805712,  0,
+       0,  0.378838, 0.621162,  0,
+       0,  0.104823, 0.895177,  0,
+       0,  0,        0,         1 }},
+}};
+
+/* TESTCASE: Tests that the HWC2 can set the identity color transform */
+TEST_F(Hwc2Test, SET_COLOR_TRANSFORM)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                EXPECT_NO_FATAL_FAILURE(test->setColorTransform(display,
+                        identityMatrix, HAL_COLOR_TRANSFORM_IDENTITY));
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set the color transform for a bad
+ * display. */
+TEST_F(Hwc2Test, SET_COLOR_TRANSFORM_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(setColorTransform(display, identityMatrix,
+            HAL_COLOR_TRANSFORM_IDENTITY, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid color transform. */
+TEST_F(Hwc2Test, SET_COLOR_TRANSFORM_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                const android_color_transform_t hint =
+                        static_cast<android_color_transform_t>(-1);
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->setColorTransform(display,
+                        identityMatrix, hint, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER)
+                        << "returned wrong error code";
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 can set an arbitrary color matrix. */
+TEST_F(Hwc2Test, SET_COLOR_TRANSFORM_arbitrary_matrix)
+{
+    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
+            [] (Hwc2Test* test, hwc2_display_t display) {
+
+                const android_color_transform_t hint =
+                        HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
+
+                for (const std::array<float, 16>& matrix : exampleMatrices) {
+                    EXPECT_NO_FATAL_FAILURE(test->setColorTransform(display,
+                            matrix, hint));
+                }
+            }
+    ));
+}
+
+/* TESTCASE: Tests that the HWC2 create an destory virtual displays. */
+TEST_F(Hwc2Test, CREATE_DESTROY_VIRTUAL_DISPLAY)
+{
+    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Complete,
+            [] (Hwc2Test* /*test*/, hwc2_display_t /*display*/,
+                    Hwc2TestVirtualDisplay* /*testVirtualDisplay*/) { }));
+}
+
+/* TESTCASE: Tests that the HWC2 can create and destroy multiple virtual
+ * displays. */
+TEST_F(Hwc2Test, CREATE_DESTROY_VIRTUAL_DISPLAY_multiple)
+{
+    Hwc2TestVirtualDisplay testVirtualDisplay(Hwc2TestCoverage::Complete);
+    std::vector<hwc2_display_t> displays;
+
+    do {
+        const UnsignedArea& dimension =
+                testVirtualDisplay.getDisplayDimension();
+        android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+        hwc2_display_t display;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(dimension.width,
+                dimension.height, &desiredFormat, &display, &err));
+
+        EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_NO_RESOURCES
+                || err == HWC2_ERROR_UNSUPPORTED) << "returned wrong error code";
+        EXPECT_GE(desiredFormat, 0) << "invalid format";
+
+        if (err == HWC2_ERROR_NONE)
+            displays.push_back(display);
+
+    } while (testVirtualDisplay.advance());
+
+    for (hwc2_display_t display : displays) {
+        EXPECT_NO_FATAL_FAILURE(destroyVirtualDisplay(display));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 cannot destroy a bad virtual displays.  */
+TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_display)
+{
+    hwc2_display_t display;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
+
+    ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 cannot destroy a physical display. */
+TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_parameter)
+{
+    hwc2_display_t display = HWC_DISPLAY_PRIMARY;
+    hwc2_error_t err = HWC2_ERROR_NONE;
+
+    ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
+    EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+}
+
+/* TESTCASE: Tests that the HWC2 can get the max virtual display count. */
+TEST_F(Hwc2Test, GET_MAX_VIRTUAL_DISPLAY_COUNT)
+{
+    uint32_t maxCnt;
+
+    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt));
+}
+
+/* TESTCASE: Tests that the HWC2 returns the same max virtual display count for
+ * each call. */
+TEST_F(Hwc2Test, GET_MAX_VIRTUAL_DISPLAY_COUNT_duplicate)
+{
+    uint32_t maxCnt1, maxCnt2;
+
+    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt1));
+    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt2));
+
+    EXPECT_EQ(maxCnt1, maxCnt2) << "returned two different max virtual display"
+            " counts";
+}
+
+/* TESTCASE: Tests that the HWC2 can create the max number of virtual displays
+ * that it reports. */
+TEST_F(Hwc2Test, GET_MAX_VIRTUAL_DISPLAY_COUNT_create_max)
+{
+    std::vector<hwc2_display_t> displays;
+    uint32_t maxCnt;
+
+    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt));
+
+    while (displays.size() < maxCnt) {
+        uint32_t width = 1920, height = 1080;
+        android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+        hwc2_display_t display;
+        hwc2_error_t err = HWC2_ERROR_NONE;
+
+        ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(width, height,
+                    &desiredFormat, &display, &err));
+
+        EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_UNSUPPORTED)
+                << "returned wrong error code";
+        if (err != HWC2_ERROR_NONE)
+            break;
+
+        displays.push_back(display);
+    }
+
+    for (hwc2_display_t display : displays) {
+        EXPECT_NO_FATAL_FAILURE(destroyVirtualDisplay(display));
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can set an output buffer for a virtual
+ * display. */
+TEST_F(Hwc2Test, SET_OUTPUT_BUFFER)
+{
+    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Complete,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    Hwc2TestVirtualDisplay* testVirtualDisplay) {
+
+                buffer_handle_t handle;
+                android::base::unique_fd acquireFence;
+
+                if (testVirtualDisplay->getBuffer(&handle, &acquireFence) >= 0)
+                    EXPECT_NO_FATAL_FAILURE(test->setOutputBuffer(display,
+                            handle, acquireFence));
+            }));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an output buffer for a bad display */
+TEST_F(Hwc2Test, SET_OUTPUT_BUFFER_bad_display)
+{
+    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t /*display*/,
+                    Hwc2TestVirtualDisplay* testVirtualDisplay) {
+
+                hwc2_display_t badDisplay;
+                buffer_handle_t handle;
+                android::base::unique_fd acquireFence;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->getBadDisplay(&badDisplay));
+
+                if (testVirtualDisplay->getBuffer(&handle, &acquireFence) < 0)
+                    return;
+
+                ASSERT_NO_FATAL_FAILURE(test->setOutputBuffer(badDisplay,
+                        handle, acquireFence, &err));
+                EXPECT_TRUE(err == HWC2_ERROR_BAD_DISPLAY)
+                        << "returned wrong error code";
+            }));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an invalid output buffer. */
+TEST_F(Hwc2Test, SET_OUTPUT_BUFFER_bad_parameter)
+{
+    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Default,
+            [] (Hwc2Test* test, hwc2_display_t display,
+                    Hwc2TestVirtualDisplay* /*testVirtualDisplay*/) {
+
+                const buffer_handle_t handle = nullptr;
+                uint32_t releaseFence = -1;
+                hwc2_error_t err = HWC2_ERROR_NONE;
+
+                ASSERT_NO_FATAL_FAILURE(test->setOutputBuffer(display, handle,
+                        releaseFence, &err));
+                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER)
+                        << "returned wrong error code";
+            }));
+}
+
+/* TESTCASE: Tests that the HWC2 cannot set an output buffer for non virtual
+ * display */
+TEST_F(Hwc2Test, SET_OUTPUT_BUFFER_unsupported)
+{
+    for (auto display : mDisplays) {
+        Hwc2TestVirtualDisplay testVirtualDisplay(Hwc2TestCoverage::Complete);
+
+        do {
+            buffer_handle_t handle;
+            android::base::unique_fd acquireFence;
+            hwc2_error_t err = HWC2_ERROR_NONE;
+
+            if (testVirtualDisplay.getBuffer(&handle, &acquireFence) < 0)
+                continue;
+
+            ASSERT_NO_FATAL_FAILURE(setOutputBuffer(display, handle,
+                    acquireFence, &err));
+            EXPECT_EQ(err, HWC2_ERROR_UNSUPPORTED) << "returned wrong error code";
+
+        } while (testVirtualDisplay.advance());
+    }
+}
+
+/* TESTCASE: Tests that the HWC2 can dump debug information. */
+TEST_F(Hwc2Test, DUMP)
+{
+    std::string buffer;
+
+    ASSERT_NO_FATAL_FAILURE(dump(&buffer));
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
new file mode 100644
index 0000000..1d3a1d3
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
@@ -0,0 +1,695 @@
+/*
+ * Copyright (C) 2016 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 <mutex>
+#include <array>
+#include <sstream>
+#include <algorithm>
+
+#include <gui/Surface.h>
+#include <gui/BufferItemConsumer.h>
+
+#include <ui/GraphicBuffer.h>
+#include <math/vec4.h>
+
+#include <GLES3/gl3.h>
+
+#include "Hwc2TestBuffer.h"
+#include "Hwc2TestLayers.h"
+
+using namespace android;
+
+/* Returns a fence from egl */
+typedef void (*FenceCallback)(int32_t fence, void* callbackArgs);
+
+/* Returns fence to fence generator */
+static void setFence(int32_t fence, void* fenceGenerator);
+
+
+/* Used to receive the surfaces and fences from egl. The egl buffers are thrown
+ * away. The fences are sent to the requester via a callback */
+class Hwc2TestSurfaceManager {
+public:
+    /* Listens for a new frame, detaches the buffer and returns the fence
+     * through saved callback. */
+    class BufferListener : public ConsumerBase::FrameAvailableListener {
+    public:
+        BufferListener(sp<IGraphicBufferConsumer> consumer,
+                FenceCallback callback, void* callbackArgs)
+            : mConsumer(consumer),
+              mCallback(callback),
+              mCallbackArgs(callbackArgs) { }
+
+        void onFrameAvailable(const BufferItem& /*item*/)
+        {
+            BufferItem item;
+
+            if (mConsumer->acquireBuffer(&item, 0))
+                return;
+            if (mConsumer->detachBuffer(item.mSlot))
+                return;
+
+            mCallback(item.mFence->dup(), mCallbackArgs);
+        }
+
+    private:
+        sp<IGraphicBufferConsumer> mConsumer;
+        FenceCallback mCallback;
+        void* mCallbackArgs;
+    };
+
+    /* Creates a buffer listener that waits on a new frame from the buffer
+     * queue. */
+    void initialize(const Area& bufferArea, android_pixel_format_t format,
+            FenceCallback callback, void* callbackArgs)
+    {
+        sp<IGraphicBufferProducer> producer;
+        sp<IGraphicBufferConsumer> consumer;
+        BufferQueue::createBufferQueue(&producer, &consumer);
+
+        consumer->setDefaultBufferSize(bufferArea.width, bufferArea.height);
+        consumer->setDefaultBufferFormat(format);
+
+        mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
+
+        mListener = new BufferListener(consumer, callback, callbackArgs);
+        mBufferItemConsumer->setFrameAvailableListener(mListener);
+
+        mSurface = new Surface(producer, true);
+    }
+
+    /* Used by Egl manager. The surface is never displayed. */
+    sp<Surface> getSurface() const
+    {
+        return mSurface;
+    }
+
+private:
+    sp<BufferItemConsumer> mBufferItemConsumer;
+    sp<BufferListener> mListener;
+    /* Used by Egl manager. The surface is never displayed */
+    sp<Surface> mSurface;
+};
+
+
+/* Used to generate valid fences. It is not possible to create a dummy sync
+ * fence for testing. Egl can generate buffers along with a valid fence.
+ * The buffer cannot be guaranteed to be the same format across all devices so
+ * a CPU filled buffer is used instead. The Egl fence is used along with the
+ * CPU filled buffer. */
+class Hwc2TestEglManager {
+public:
+    Hwc2TestEglManager()
+        : mEglDisplay(EGL_NO_DISPLAY),
+          mEglSurface(EGL_NO_SURFACE),
+          mEglContext(EGL_NO_CONTEXT) { }
+
+    ~Hwc2TestEglManager()
+    {
+        cleanup();
+    }
+
+    int initialize(sp<Surface> surface)
+    {
+        mSurface = surface;
+
+        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        if (mEglDisplay == EGL_NO_DISPLAY) return false;
+
+        EGLint major;
+        EGLint minor;
+        if (!eglInitialize(mEglDisplay, &major, &minor)) {
+            ALOGW("Could not initialize EGL");
+            return false;
+        }
+
+        /* We're going to use a 1x1 pbuffer surface later on
+         * The configuration distance doesn't really matter for what we're
+         * trying to do */
+        EGLint configAttrs[] = {
+                EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+                EGL_RED_SIZE, 8,
+                EGL_GREEN_SIZE, 8,
+                EGL_BLUE_SIZE, 8,
+                EGL_ALPHA_SIZE, 0,
+                EGL_DEPTH_SIZE, 24,
+                EGL_STENCIL_SIZE, 0,
+                EGL_NONE
+        };
+
+        EGLConfig configs[1];
+        EGLint configCnt;
+        if (!eglChooseConfig(mEglDisplay, configAttrs, configs, 1,
+                &configCnt)) {
+            ALOGW("Could not select EGL configuration");
+            eglReleaseThread();
+            eglTerminate(mEglDisplay);
+            return false;
+        }
+
+        if (configCnt <= 0) {
+            ALOGW("Could not find EGL configuration");
+            eglReleaseThread();
+            eglTerminate(mEglDisplay);
+            return false;
+        }
+
+        /* These objects are initialized below but the default "null" values are
+         * used to cleanup properly at any point in the initialization sequence */
+        EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+        mEglContext = eglCreateContext(mEglDisplay, configs[0], EGL_NO_CONTEXT,
+                attrs);
+        if (mEglContext == EGL_NO_CONTEXT) {
+            ALOGW("Could not create EGL context");
+            cleanup();
+            return false;
+        }
+
+        EGLint surfaceAttrs[] = { EGL_NONE };
+        mEglSurface = eglCreateWindowSurface(mEglDisplay, configs[0],
+                mSurface.get(), surfaceAttrs);
+        if (mEglSurface == EGL_NO_SURFACE) {
+            ALOGW("Could not create EGL surface");
+            cleanup();
+            return false;
+        }
+
+        if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+            ALOGW("Could not change current EGL context");
+            cleanup();
+            return false;
+        }
+
+        return true;
+    }
+
+    void makeCurrent() const
+    {
+        eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
+    }
+
+    void present() const
+    {
+        eglSwapBuffers(mEglDisplay, mEglSurface);
+    }
+
+private:
+    void cleanup()
+    {
+        if (mEglDisplay == EGL_NO_DISPLAY)
+            return;
+        if (mEglSurface != EGL_NO_SURFACE)
+            eglDestroySurface(mEglDisplay, mEglSurface);
+        if (mEglContext != EGL_NO_CONTEXT)
+            eglDestroyContext(mEglDisplay, mEglContext);
+
+        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+                EGL_NO_CONTEXT);
+        eglReleaseThread();
+        eglTerminate(mEglDisplay);
+    }
+
+    sp<Surface> mSurface;
+    EGLDisplay mEglDisplay;
+    EGLSurface mEglSurface;
+    EGLContext mEglContext;
+};
+
+
+static const std::array<vec2, 4> triangles = {{
+    {  1.0f,  1.0f },
+    { -1.0f,  1.0f },
+    {  1.0f, -1.0f },
+    { -1.0f, -1.0f },
+}};
+
+class Hwc2TestFenceGenerator {
+public:
+
+    Hwc2TestFenceGenerator()
+    {
+        mSurfaceManager.initialize({1, 1}, HAL_PIXEL_FORMAT_RGBA_8888,
+                setFence, this);
+
+        if (!mEglManager.initialize(mSurfaceManager.getSurface()))
+            return;
+
+        mEglManager.makeCurrent();
+
+        glClearColor(0.0, 0.0, 0.0, 1.0);
+        glEnableVertexAttribArray(0);
+    }
+
+    ~Hwc2TestFenceGenerator()
+    {
+        if (mFence >= 0)
+            close(mFence);
+        mFence = -1;
+
+        mEglManager.makeCurrent();
+    }
+
+    /* It is not possible to simply generate a fence. The easiest way is to
+     * generate a buffer using egl and use the associated fence. The buffer
+     * cannot be guaranteed to be a certain format across all devices using this
+     * method. Instead the buffer is generated using the CPU */
+    int32_t get()
+    {
+        if (mFence >= 0) {
+            return dup(mFence);
+        }
+
+        std::unique_lock<std::mutex> lock(mMutex);
+
+        /* If the pending is still set to false and times out, we cannot recover.
+         * Set an error and return */
+        while (mPending != false) {
+            if (mCv.wait_for(lock, std::chrono::seconds(2)) == std::cv_status::timeout)
+                return -ETIME;
+        }
+
+        /* Generate a fence. The fence will be returned through the setFence
+         * callback */
+        mEglManager.makeCurrent();
+
+        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, triangles.data());
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        mEglManager.present();
+
+        /* Wait for the setFence callback */
+        while (mPending != true) {
+            if (mCv.wait_for(lock, std::chrono::seconds(2)) == std::cv_status::timeout)
+                return -ETIME;
+        }
+
+        mPending = false;
+
+        return dup(mFence);
+    }
+
+    /* Callback that sets the fence */
+    void set(int32_t fence)
+    {
+        mFence = fence;
+        mPending = true;
+
+        mCv.notify_all();
+    }
+
+private:
+
+    Hwc2TestSurfaceManager mSurfaceManager;
+    Hwc2TestEglManager mEglManager;
+
+    std::mutex mMutex;
+    std::condition_variable mCv;
+
+    int32_t mFence = -1;
+    bool mPending = false;
+};
+
+
+static void setFence(int32_t fence, void* fenceGenerator)
+{
+    static_cast<Hwc2TestFenceGenerator*>(fenceGenerator)->set(fence);
+}
+
+
+/* Sets the pixel of a buffer given the location, format, stride and color.
+ * Currently only supports RGBA_8888 */
+static void setColor(int32_t x, int32_t y,
+        android_pixel_format_t format, uint32_t stride, uint8_t* img, uint8_t r,
+        uint8_t g, uint8_t b, uint8_t a)
+{
+       switch (format) {
+       case HAL_PIXEL_FORMAT_RGBA_8888:
+           img[(y * stride + x) * 4 + 0] = r;
+           img[(y * stride + x) * 4 + 1] = g;
+           img[(y * stride + x) * 4 + 2] = b;
+           img[(y * stride + x) * 4 + 3] = a;
+           break;
+       default:
+           break;
+       }
+}
+
+Hwc2TestBuffer::Hwc2TestBuffer()
+    : mFenceGenerator(new Hwc2TestFenceGenerator()) { }
+
+Hwc2TestBuffer::~Hwc2TestBuffer() = default;
+
+/* When the buffer changes sizes, save the new size and invalidate the current
+ * buffer */
+void Hwc2TestBuffer::updateBufferArea(const Area& bufferArea)
+{
+    if (mBufferArea.width == bufferArea.width
+            && mBufferArea.height == bufferArea.height)
+        return;
+
+    mBufferArea.width = bufferArea.width;
+    mBufferArea.height = bufferArea.height;
+
+    mValidBuffer = false;
+}
+
+/* Returns a valid buffer handle and fence. The handle is filled using the CPU
+ * to ensure the correct format across all devices. The fence is created using
+ * egl. */
+int Hwc2TestBuffer::get(buffer_handle_t* outHandle, int32_t* outFence)
+{
+    if (mBufferArea.width == -1 || mBufferArea.height == -1)
+        return -EINVAL;
+
+    /* If the current buffer is valid, the previous buffer can be reused.
+     * Otherwise, create new buffer */
+    if (!mValidBuffer) {
+        int ret = generateBuffer();
+        if (ret)
+            return ret;
+    }
+
+    *outFence = mFenceGenerator->get();
+    *outHandle = mHandle;
+
+    mValidBuffer = true;
+
+    return 0;
+}
+
+/* CPU fills a buffer to guarantee the correct buffer format across all
+ * devices */
+int Hwc2TestBuffer::generateBuffer()
+{
+    /* Create new graphic buffer with correct dimensions */
+    mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height,
+            mFormat, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER,
+            "hwc2_test_buffer");
+    int ret = mGraphicBuffer->initCheck();
+    if (ret) {
+        return ret;
+    }
+    if (!mGraphicBuffer->handle) {
+        return -EINVAL;
+    }
+
+    /* Locks the buffer for writing */
+    uint8_t* img;
+    mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+
+    uint32_t stride = mGraphicBuffer->getStride();
+
+    /* Iterate from the top row of the buffer to the bottom row */
+    for (int32_t y = 0; y < mBufferArea.height; y++) {
+
+        /* Will be used as R, G and B values for pixel colors */
+        uint8_t max = 255;
+        uint8_t min = 0;
+
+        /* Divide the rows into 3 sections. The first section will contain
+         * the lighest colors. The last section will contain the darkest
+         * colors. */
+        if (y < mBufferArea.height * 1.0 / 3.0) {
+            min = 255 / 2;
+        } else if (y >= mBufferArea.height * 2.0 / 3.0) {
+            max = 255 / 2;
+        }
+
+        /* Divide the columns into 3 sections. The first section is red,
+         * the second is green and the third is blue */
+        int32_t x = 0;
+        for (; x < mBufferArea.width / 3; x++) {
+            setColor(x, y, mFormat, stride, img, max, min, min, 255);
+        }
+
+        for (; x < mBufferArea.width * 2 / 3; x++) {
+            setColor(x, y, mFormat, stride, img, min, max, min, 255);
+        }
+
+        for (; x < mBufferArea.width; x++) {
+            setColor(x, y, mFormat, stride, img, min, min, max, 255);
+        }
+    }
+
+    /* Unlock the buffer for reading */
+    mGraphicBuffer->unlock();
+
+    mHandle = mGraphicBuffer->handle;
+
+    return 0;
+}
+
+
+Hwc2TestClientTargetBuffer::Hwc2TestClientTargetBuffer()
+    : mFenceGenerator(new Hwc2TestFenceGenerator()) { }
+
+Hwc2TestClientTargetBuffer::~Hwc2TestClientTargetBuffer() { }
+
+/* Generates a client target buffer using the layers assigned for client
+ * composition. Takes into account the individual layer properties such as
+ * transform, blend mode, source crop, etc. */
+int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle,
+        int32_t* outFence, const Area& bufferArea,
+        const Hwc2TestLayers* testLayers,
+        const std::set<hwc2_layer_t>* clientLayers,
+        const std::set<hwc2_layer_t>* clearLayers)
+{
+    /* Create new graphic buffer with correct dimensions */
+    mGraphicBuffer = new GraphicBuffer(bufferArea.width, bufferArea.height,
+            mFormat, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER,
+            "hwc2_test_buffer");
+    int ret = mGraphicBuffer->initCheck();
+    if (ret) {
+        return ret;
+    }
+    if (!mGraphicBuffer->handle) {
+        return -EINVAL;
+    }
+
+    uint8_t* img;
+    mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+
+    uint32_t stride = mGraphicBuffer->getStride();
+
+    float bWDiv3 = bufferArea.width / 3;
+    float bW2Div3 = bufferArea.width * 2 / 3;
+    float bHDiv3 = bufferArea.height / 3;
+    float bH2Div3 = bufferArea.height * 2 / 3;
+
+    /* Cycle through every pixel in the buffer and determine what color it
+     * should be. */
+    for (int32_t y = 0; y < bufferArea.height; y++) {
+        for (int32_t x = 0; x < bufferArea.width; x++) {
+
+            uint8_t r = 0, g = 0, b = 0;
+            float a = 0.0f;
+
+            /* Cycle through each client layer from back to front and
+             * update the pixel color. */
+            for (auto layer = clientLayers->rbegin();
+                    layer != clientLayers->rend(); ++layer) {
+
+                const hwc_rect_t df = testLayers->getDisplayFrame(*layer);
+
+                float dfL = df.left;
+                float dfT = df.top;
+                float dfR = df.right;
+                float dfB = df.bottom;
+
+                /* If the pixel location falls outside of the layer display
+                 * frame, skip the layer. */
+                if (x < dfL || x >= dfR || y < dfT || y >= dfB)
+                    continue;
+
+                /* If the device has requested the layer be clear, clear
+                 * the pixel and continue. */
+                if (clearLayers->count(*layer) != 0) {
+                    r = 0;
+                    g = 0;
+                    b = 0;
+                    a = 0.0f;
+                    continue;
+                }
+
+                float planeAlpha = testLayers->getPlaneAlpha(*layer);
+
+                /* If the layer is a solid color, fill the color and
+                 * continue. */
+                if (testLayers->getComposition(*layer)
+                        == HWC2_COMPOSITION_SOLID_COLOR) {
+                    const auto color = testLayers->getColor(*layer);
+                    r = color.r;
+                    g = color.g;
+                    b = color.b;
+                    a = color.a * planeAlpha;
+                    continue;
+                }
+
+                float xPos = x;
+                float yPos = y;
+
+                hwc_transform_t transform = testLayers->getTransform(*layer);
+
+                float dfW = dfR - dfL;
+                float dfH = dfB - dfT;
+
+                /* If a layer has a transform, find which location on the
+                 * layer will end up in the current pixel location. We
+                 * can calculate the color of the current pixel using that
+                 * location. */
+                if (transform > 0) {
+                    /* Change origin to be the center of the layer. */
+                    xPos = xPos - dfL - dfW / 2.0;
+                    yPos = yPos - dfT - dfH / 2.0;
+
+                    /* Flip Horizontal by reflecting across the y axis. */
+                    if (transform & HWC_TRANSFORM_FLIP_H)
+                        xPos = -xPos;
+
+                    /* Flip vertical by reflecting across the x axis. */
+                    if (transform & HWC_TRANSFORM_FLIP_V)
+                        yPos = -yPos;
+
+                    /* Rotate 90 by using a basic linear algebra rotation
+                     * and scaling the result so the display frame remains
+                     * the same. For example, a buffer of size 100x50 should
+                     * rotate 90 degress but remain the same dimension
+                     * (100x50) at the end of the transformation. */
+                    if (transform & HWC_TRANSFORM_ROT_90) {
+                        float tmp = xPos;
+                        xPos = -yPos * dfW / dfH;
+                        yPos = tmp * dfH / dfW;
+                    }
+
+                    /* Change origin back to the top left corner of the
+                     * layer. */
+                    xPos = xPos + dfL + dfW / 2.0;
+                    yPos = yPos + dfT + dfH / 2.0;
+                }
+
+                hwc_frect_t sc = testLayers->getSourceCrop(*layer);
+                float scL = sc.left, scT = sc.top;
+
+                float dfWDivScW = dfW / (sc.right - scL);
+                float dfHDivScH = dfH / (sc.bottom - scT);
+
+                float max = 255, min = 0;
+
+                /* Choose the pixel color. Similar to generateBuffer,
+                 * each layer will be divided into 3x3 colors. Because
+                 * both the source crop and display frame must be taken into
+                 * account, the formulas are more complicated.
+                 *
+                 * If the source crop and display frame were not taken into
+                 * account, we would simply divide the buffer into three
+                 * sections by height. Each section would get one color.
+                 * For example the formula for the first section would be:
+                 *
+                 * if (yPos < bufferArea.height / 3)
+                 *        //Select first section color
+                 *
+                 * However the pixel color is chosen based on the source
+                 * crop and displayed based on the display frame.
+                 *
+                 * If the display frame top was 0 and the source crop height
+                 * and display frame height were the same. The only factor
+                 * would be the source crop top. To calculate the new
+                 * section boundary, the section boundary would be moved up
+                 * by the height of the source crop top. The formula would
+                 * be:
+                 * if (yPos < (bufferArea.height / 3 - sourceCrop.top)
+                 *        //Select first section color
+                 *
+                 * If the display frame top could also vary but source crop
+                 * and display frame heights were the same, the formula
+                 * would be:
+                 * if (yPos < (bufferArea.height / 3 - sourceCrop.top
+                 *              + displayFrameTop)
+                 *        //Select first section color
+                 *
+                 * If the heights were not the same, the conversion between
+                 * the source crop and display frame dimensions must be
+                 * taken into account. The formula would be:
+                 * if (yPos < ((bufferArea.height / 3) - sourceCrop.top)
+                 *              * displayFrameHeight / sourceCropHeight
+                 *              + displayFrameTop)
+                 *        //Select first section color
+                 */
+                if (yPos < ((bHDiv3) - scT) * dfHDivScH + dfT) {
+                    min = 255 / 2;
+                } else if (yPos >= ((bH2Div3) - scT) * dfHDivScH + dfT) {
+                    max = 255 / 2;
+                }
+
+                uint8_t rCur = min, gCur = min, bCur = min;
+                float aCur = 1.0f;
+
+                /* This further divides the color sections from 3 to 3x3.
+                 * The math behind it follows the same logic as the previous
+                 * comment */
+                if (xPos < ((bWDiv3) - scL) * (dfWDivScW) + dfL) {
+                    rCur = max;
+                } else if (xPos < ((bW2Div3) - scL) * (dfWDivScW) + dfL) {
+                    gCur = max;
+                } else {
+                    bCur = max;
+                }
+
+
+                /* Blend the pixel color with the previous layers' pixel
+                 * colors using the plane alpha and blend mode. The final
+                 * pixel color is chosen using the plane alpha and blend
+                 * mode formulas found in hwcomposer2.h */
+                hwc2_blend_mode_t blendMode = testLayers->getBlendMode(*layer);
+
+                if (blendMode == HWC2_BLEND_MODE_PREMULTIPLIED) {
+                    rCur *= planeAlpha;
+                    gCur *= planeAlpha;
+                    bCur *= planeAlpha;
+                }
+
+                aCur *= planeAlpha;
+
+                if (blendMode == HWC2_BLEND_MODE_PREMULTIPLIED) {
+                    r = rCur + r * (1.0 - aCur);
+                    g = gCur + g * (1.0 - aCur);
+                    b = bCur + b * (1.0 - aCur);
+                    a = aCur + a * (1.0 - aCur);
+                } else if (blendMode == HWC2_BLEND_MODE_COVERAGE) {
+                    r = rCur * aCur + r * (1.0 - aCur);
+                    g = gCur * aCur + g * (1.0 - aCur);
+                    b = bCur * aCur + b * (1.0 - aCur);
+                    a = aCur * aCur + a * (1.0 - aCur);
+                } else {
+                    r = rCur;
+                    g = gCur;
+                    b = bCur;
+                    a = aCur;
+                }
+            }
+
+            /* Set the pixel color */
+            setColor(x, y, mFormat, stride, img, r, g, b, a * 255);
+        }
+    }
+
+    mGraphicBuffer->unlock();
+
+    *outFence = mFenceGenerator->get();
+    *outHandle = mGraphicBuffer->handle;
+
+    return 0;
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h
new file mode 100644
index 0000000..b2b3a66
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 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 _HWC2_TEST_BUFFER_H
+#define _HWC2_TEST_BUFFER_H
+
+#include <android-base/unique_fd.h>
+#include <set>
+
+#include <hardware/hwcomposer2.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include "Hwc2TestProperties.h"
+
+class Hwc2TestFenceGenerator;
+class Hwc2TestLayers;
+
+class Hwc2TestBuffer {
+public:
+    Hwc2TestBuffer();
+    ~Hwc2TestBuffer();
+
+    void updateBufferArea(const Area& bufferArea);
+
+    int  get(buffer_handle_t* outHandle, int32_t* outFence);
+
+protected:
+    int generateBuffer();
+
+    android::sp<android::GraphicBuffer> mGraphicBuffer;
+
+    std::unique_ptr<Hwc2TestFenceGenerator> mFenceGenerator;
+
+    Area mBufferArea = {-1, -1};
+    const android_pixel_format_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+
+    bool mValidBuffer = false;
+    buffer_handle_t mHandle = nullptr;
+};
+
+
+class Hwc2TestClientTargetBuffer {
+public:
+    Hwc2TestClientTargetBuffer();
+    ~Hwc2TestClientTargetBuffer();
+
+    int  get(buffer_handle_t* outHandle, int32_t* outFence,
+            const Area& bufferArea, const Hwc2TestLayers* testLayers,
+            const std::set<hwc2_layer_t>* clientLayers,
+            const std::set<hwc2_layer_t>* clearLayers);
+
+protected:
+    android::sp<android::GraphicBuffer> mGraphicBuffer;
+
+    std::unique_ptr<Hwc2TestFenceGenerator> mFenceGenerator;
+
+    const android_pixel_format_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+};
+
+#endif /* ifndef _HWC2_TEST_BUFFER_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.cpp
new file mode 100644
index 0000000..6925492
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 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 <sstream>
+
+#include <ui/Rect.h>
+
+#include "Hwc2TestClientTarget.h"
+
+int Hwc2TestClientTarget::getBuffer(const Hwc2TestLayers& testLayers,
+        const std::set<hwc2_layer_t>& clientLayers,
+        const std::set<hwc2_layer_t>& clearLayers, bool flipClientTarget,
+        const Area& displayArea, buffer_handle_t* outHandle,
+        int32_t* outAcquireFence)
+{
+    if (!flipClientTarget) {
+        bool needsClientTarget = false;
+
+        for (auto clientLayer : clientLayers) {
+            if (testLayers.getVisibleRegion(clientLayer).numRects > 0) {
+                needsClientTarget = true;
+                break;
+            }
+        }
+
+        if (!needsClientTarget) {
+           *outHandle = nullptr;
+           *outAcquireFence = -1;
+           return 0;
+        }
+    }
+
+    return mBuffer.get(outHandle, outAcquireFence, displayArea,
+            &testLayers, &clientLayers, &clearLayers);
+}
+
+
+Hwc2TestClientTargetSupport::Hwc2TestClientTargetSupport(
+        Hwc2TestCoverage coverage, const Area& displayArea)
+    : mBufferArea(coverage, displayArea),
+      mDataspace(coverage),
+      mSurfaceDamage(coverage)
+{
+    mBufferArea.setDependent(&mSurfaceDamage);
+}
+
+std::string Hwc2TestClientTargetSupport::dump() const
+{
+    std::stringstream dmp;
+
+    dmp << "client target: \n";
+
+    for (auto property : properties) {
+        dmp << property->dump();
+    }
+
+    return dmp.str();
+}
+
+void Hwc2TestClientTargetSupport::reset()
+{
+    for (auto property : properties) {
+        property->reset();
+    }
+}
+
+bool Hwc2TestClientTargetSupport::advance()
+{
+    for (auto property : properties) {
+        if (property->advance())
+            return true;
+    }
+    return false;
+}
+
+Area Hwc2TestClientTargetSupport::getBufferArea() const
+{
+    return mBufferArea.get();
+}
+
+android_dataspace_t Hwc2TestClientTargetSupport::getDataspace() const
+{
+    return mDataspace.get();
+}
+
+const hwc_region_t Hwc2TestClientTargetSupport::getSurfaceDamage() const
+{
+    return mSurfaceDamage.get();
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.h b/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.h
new file mode 100644
index 0000000..3b47978
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 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 _HWC2_TEST_CLIENT_TARGET_H
+#define _HWC2_TEST_CLIENT_TARGET_H
+
+#include <set>
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include "Hwc2TestProperties.h"
+#include "Hwc2TestLayers.h"
+
+/* Generates client target buffers from client composition layers */
+class Hwc2TestClientTarget {
+public:
+    int getBuffer(const Hwc2TestLayers& layers,
+            const std::set<hwc2_layer_t>& clientLayers,
+            const std::set<hwc2_layer_t>& clearLayers,
+            bool clearClientTarget, const Area& displayArea,
+            buffer_handle_t* outHandle, int32_t* outAcquireFence);
+
+private:
+    Hwc2TestClientTargetBuffer mBuffer;
+};
+
+/* Generates valid client targets to test which ones the device will support */
+class Hwc2TestClientTargetSupport {
+public:
+    Hwc2TestClientTargetSupport(Hwc2TestCoverage coverage,
+            const Area& displayArea);
+
+    std::string dump() const;
+
+    void reset();
+    bool advance();
+
+    Area getBufferArea() const;
+    android_dataspace_t getDataspace() const;
+    const hwc_region_t getSurfaceDamage() const;
+
+private:
+    std::array<Hwc2TestContainer*, 3> properties = {{
+        &mDataspace, &mSurfaceDamage, &mBufferArea
+    }};
+
+    Hwc2TestBufferArea mBufferArea;
+    Hwc2TestDataspace mDataspace;
+    Hwc2TestSurfaceDamage mSurfaceDamage;
+};
+
+#endif /* ifndef _HWC2_TEST_CLIENT_TARGET_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.cpp
new file mode 100644
index 0000000..937fce2
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2016 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 <sstream>
+
+#include "Hwc2TestLayer.h"
+
+Hwc2TestCoverage getCoverage(Hwc2TestPropertyName property,
+        Hwc2TestCoverage coverage, const std::unordered_map<Hwc2TestPropertyName,
+        Hwc2TestCoverage>& coverageExceptions) {
+    auto exception = coverageExceptions.find(property);
+    return (exception != coverageExceptions.end())? exception->second : coverage;
+}
+
+Hwc2TestLayer::Hwc2TestLayer(Hwc2TestCoverage coverage,
+        const Area& displayArea)
+    : Hwc2TestLayer(coverage, displayArea,
+            std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>()) { }
+
+Hwc2TestLayer::Hwc2TestLayer(Hwc2TestCoverage coverage,
+        const Area& displayArea, const std::unordered_map<Hwc2TestPropertyName,
+        Hwc2TestCoverage>& coverageExceptions)
+    : mBlendMode(getCoverage(Hwc2TestPropertyName::BlendMode, coverage,
+           coverageExceptions)),
+      mBufferArea(getCoverage(Hwc2TestPropertyName::BufferArea, coverage,
+           coverageExceptions), displayArea),
+      mColor(getCoverage(Hwc2TestPropertyName::Color, coverage,
+           coverageExceptions)),
+      mComposition(getCoverage(Hwc2TestPropertyName::Composition, coverage,
+           coverageExceptions)),
+      mDataspace(getCoverage(Hwc2TestPropertyName::Dataspace, coverage,
+           coverageExceptions)),
+      mDisplayFrame(getCoverage(Hwc2TestPropertyName::DisplayFrame, coverage,
+           coverageExceptions), displayArea),
+      mPlaneAlpha(getCoverage(Hwc2TestPropertyName::PlaneAlpha, coverage,
+           coverageExceptions)),
+      mSourceCrop(getCoverage(Hwc2TestPropertyName::SourceCrop, coverage,
+           coverageExceptions)),
+      mSurfaceDamage(getCoverage(Hwc2TestPropertyName::SurfaceDamage, coverage,
+           coverageExceptions)),
+      mTransform(getCoverage(Hwc2TestPropertyName::Transform, coverage,
+           coverageExceptions))
+{
+    mBufferArea.setDependent(&mBuffer);
+    mBufferArea.setDependent(&mSourceCrop);
+    mBufferArea.setDependent(&mSurfaceDamage);
+    mBlendMode.setDependent(&mColor);
+}
+
+std::string Hwc2TestLayer::dump() const
+{
+    std::stringstream dmp;
+
+    dmp << "layer: \n";
+
+    for (auto property : mProperties) {
+        dmp << property->dump();
+    }
+
+    dmp << mVisibleRegion.dump();
+    dmp << "\tz order: " << mZOrder << "\n";
+
+    return dmp.str();
+}
+
+int Hwc2TestLayer::getBuffer(buffer_handle_t* outHandle,
+        android::base::unique_fd* outAcquireFence)
+{
+    int32_t acquireFence;
+    int ret = mBuffer.get(outHandle, &acquireFence);
+    outAcquireFence->reset(acquireFence);
+    return ret;
+}
+
+int Hwc2TestLayer::getBuffer(buffer_handle_t* outHandle,
+        int32_t* outAcquireFence)
+{
+    return mBuffer.get(outHandle, outAcquireFence);
+}
+
+void Hwc2TestLayer::setZOrder(uint32_t zOrder)
+{
+    mZOrder = zOrder;
+}
+
+void Hwc2TestLayer::setVisibleRegion(const android::Region& region)
+{
+    return mVisibleRegion.set(region);
+}
+
+void Hwc2TestLayer::reset()
+{
+    mVisibleRegion.release();
+
+    for (auto property : mProperties) {
+        property->reset();
+    }
+}
+
+bool Hwc2TestLayer::advance()
+{
+    for (auto property : mProperties) {
+        if (property->isSupported(mComposition.get()))
+            if (property->advance())
+                return true;
+    }
+    return false;
+}
+
+hwc2_blend_mode_t Hwc2TestLayer::getBlendMode() const
+{
+    return mBlendMode.get();
+}
+
+Area Hwc2TestLayer::getBufferArea() const
+{
+    return mBufferArea.get();
+}
+
+hwc_color_t Hwc2TestLayer::getColor() const
+{
+    return mColor.get();
+}
+
+hwc2_composition_t Hwc2TestLayer::getComposition() const
+{
+    return mComposition.get();
+}
+
+/* The cursor position corresponds to {displayFrame.left, displayFrame.top} */
+hwc_rect_t Hwc2TestLayer::getCursorPosition() const
+{
+    return mDisplayFrame.get();
+}
+
+android_dataspace_t Hwc2TestLayer::getDataspace() const
+{
+    return mDataspace.get();
+}
+
+hwc_rect_t Hwc2TestLayer::getDisplayFrame() const
+{
+    return mDisplayFrame.get();
+}
+
+float Hwc2TestLayer::getPlaneAlpha() const
+{
+    return mPlaneAlpha.get();
+}
+
+hwc_frect_t Hwc2TestLayer::getSourceCrop() const
+{
+    return mSourceCrop.get();
+}
+
+hwc_region_t Hwc2TestLayer::getSurfaceDamage() const
+{
+    return mSurfaceDamage.get();
+}
+
+hwc_transform_t Hwc2TestLayer::getTransform() const
+{
+    return mTransform.get();
+}
+
+hwc_region_t Hwc2TestLayer::getVisibleRegion() const
+{
+    return mVisibleRegion.get();
+}
+
+uint32_t Hwc2TestLayer::getZOrder() const
+{
+    return mZOrder;
+}
+
+bool Hwc2TestLayer::advanceBlendMode()
+{
+    return mBlendMode.advance();
+}
+
+bool Hwc2TestLayer::advanceBufferArea()
+{
+    return mBufferArea.advance();
+}
+
+bool Hwc2TestLayer::advanceColor()
+{
+    return mColor.advance();
+}
+
+bool Hwc2TestLayer::advanceComposition()
+{
+    return mComposition.advance();
+}
+
+bool Hwc2TestLayer::advanceCursorPosition()
+{
+    return mDisplayFrame.advance();
+}
+
+bool Hwc2TestLayer::advanceDataspace()
+{
+    return mDataspace.advance();
+}
+
+bool Hwc2TestLayer::advanceDisplayFrame()
+{
+    return mDisplayFrame.advance();
+}
+
+bool Hwc2TestLayer::advancePlaneAlpha()
+{
+    return mPlaneAlpha.advance();
+}
+
+bool Hwc2TestLayer::advanceSourceCrop()
+{
+    return mSourceCrop.advance();
+}
+
+bool Hwc2TestLayer::advanceSurfaceDamage()
+{
+    return mSurfaceDamage.advance();
+}
+
+bool Hwc2TestLayer::advanceTransform()
+{
+    return mTransform.advance();
+}
+
+bool Hwc2TestLayer::advanceVisibleRegion()
+{
+    if (mPlaneAlpha.advance())
+        return true;
+    return mDisplayFrame.advance();
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.h b/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.h
new file mode 100644
index 0000000..0e7dd22
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 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 _HWC2_TEST_LAYER_H
+#define _HWC2_TEST_LAYER_H
+
+#include <android-base/unique_fd.h>
+#include <unordered_map>
+
+#include "Hwc2TestBuffer.h"
+#include "Hwc2TestProperties.h"
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+class Hwc2TestLayer {
+public:
+    Hwc2TestLayer(Hwc2TestCoverage coverage, const Area& displayArea);
+
+    Hwc2TestLayer(Hwc2TestCoverage coverage, const Area& displayArea,
+            const std::unordered_map<Hwc2TestPropertyName,
+            Hwc2TestCoverage>& coverage_exceptions);
+
+    std::string dump() const;
+
+    int getBuffer(buffer_handle_t* outHandle,
+            android::base::unique_fd* outAcquireFence);
+    int getBuffer(buffer_handle_t* outHandle, int32_t* outAcquireFence);
+
+    void setZOrder(uint32_t zOrder);
+    void setVisibleRegion(const android::Region& region);
+
+    void reset();
+    bool advance();
+
+    hwc2_blend_mode_t      getBlendMode() const;
+    Area                   getBufferArea() const;
+    hwc_color_t            getColor() const;
+    hwc2_composition_t     getComposition() const;
+    hwc_rect_t             getCursorPosition() const;
+    android_dataspace_t    getDataspace() const;
+    hwc_rect_t             getDisplayFrame() const;
+    float                  getPlaneAlpha() const;
+    hwc_frect_t            getSourceCrop() const;
+    hwc_region_t           getSurfaceDamage() const;
+    hwc_transform_t        getTransform() const;
+    hwc_region_t           getVisibleRegion() const;
+    uint32_t               getZOrder() const;
+
+    bool advanceBlendMode();
+    bool advanceBufferArea();
+    bool advanceColor();
+    bool advanceComposition();
+    bool advanceCursorPosition();
+    bool advanceDataspace();
+    bool advanceDisplayFrame();
+    bool advancePlaneAlpha();
+    bool advanceSourceCrop();
+    bool advanceSurfaceDamage();
+    bool advanceTransform();
+    bool advanceVisibleRegion();
+
+private:
+    std::array<Hwc2TestContainer*, 10> mProperties = {{
+        &mTransform, &mColor, &mDataspace, &mPlaneAlpha, &mSourceCrop,
+        &mSurfaceDamage, &mBlendMode, &mBufferArea, &mDisplayFrame,
+        &mComposition
+    }};
+
+    Hwc2TestBuffer mBuffer;
+
+    Hwc2TestBlendMode mBlendMode;
+    Hwc2TestBufferArea mBufferArea;
+    Hwc2TestColor mColor;
+    Hwc2TestComposition mComposition;
+    Hwc2TestDataspace mDataspace;
+    Hwc2TestDisplayFrame mDisplayFrame;
+    Hwc2TestPlaneAlpha mPlaneAlpha;
+    Hwc2TestSourceCrop mSourceCrop;
+    Hwc2TestSurfaceDamage mSurfaceDamage;
+    Hwc2TestTransform mTransform;
+    Hwc2TestVisibleRegion mVisibleRegion;
+
+    uint32_t mZOrder = UINT32_MAX;
+};
+
+#endif /* ifndef _HWC2_TEST_LAYER_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp
new file mode 100644
index 0000000..495ef79
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp
@@ -0,0 +1,281 @@
+/* * Copyright (C) 2016 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 <sstream>
+#include <gtest/gtest.h>
+
+#include "Hwc2TestLayers.h"
+
+Hwc2TestLayers::Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
+        Hwc2TestCoverage coverage, const Area& displayArea)
+    : Hwc2TestLayers(layers, coverage, displayArea,
+            std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>()) { }
+
+Hwc2TestLayers::Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
+        Hwc2TestCoverage coverage, const Area& displayArea,
+        const std::unordered_map<Hwc2TestPropertyName,
+        Hwc2TestCoverage>& coverageExceptions)
+    : mDisplayArea(displayArea)
+{
+    for (auto layer : layers) {
+        mTestLayers.emplace(std::piecewise_construct,
+                std::forward_as_tuple(layer),
+                std::forward_as_tuple(coverage, displayArea, coverageExceptions));
+    }
+
+    /* Iterate over the layers in order and assign z orders in the same order.
+     * This allows us to iterate over z orders in the same way when computing
+     * visible regions */
+    uint32_t nextZOrder = layers.size();
+
+    for (auto& testLayer : mTestLayers) {
+        testLayer.second.setZOrder(nextZOrder--);
+    }
+
+    setVisibleRegions();
+}
+
+std::string Hwc2TestLayers::dump() const
+{
+    std::stringstream dmp;
+    for (auto& testLayer : mTestLayers) {
+        dmp << testLayer.second.dump();
+    }
+    return dmp.str();
+}
+
+void Hwc2TestLayers::reset()
+{
+    for (auto& testLayer : mTestLayers) {
+        testLayer.second.reset();
+    }
+
+    setVisibleRegions();
+}
+
+bool Hwc2TestLayers::advance()
+{
+    auto itr = mTestLayers.begin();
+    bool optimized;
+
+    while (itr != mTestLayers.end()) {
+        if (itr->second.advance()) {
+            optimized = setVisibleRegions();
+            if (!mOptimize || optimized)
+                return true;
+            itr = mTestLayers.begin();
+        } else {
+            itr->second.reset();
+            ++itr;
+        }
+    }
+    return false;
+}
+
+bool Hwc2TestLayers::advanceVisibleRegions()
+{
+    auto itr = mTestLayers.begin();
+    bool optimized;
+
+    while (itr != mTestLayers.end()) {
+        if (itr->second.advanceVisibleRegion()) {
+            optimized = setVisibleRegions();
+            if (!mOptimize || optimized)
+                return true;
+            itr = mTestLayers.begin();
+        } else {
+            itr->second.reset();
+            ++itr;
+        }
+    }
+    return false;
+}
+
+/* Removes layouts that do not cover the entire display.
+ * Also removes layouts where a layer is completely blocked from view.
+ */
+bool Hwc2TestLayers::optimizeLayouts()
+{
+    mOptimize = true;
+
+    if (setVisibleRegions())
+        return true;
+    return advance();
+}
+
+bool Hwc2TestLayers::contains(hwc2_layer_t layer) const
+{
+    return mTestLayers.count(layer) != 0;
+}
+
+int Hwc2TestLayers::getBuffer(hwc2_layer_t layer, buffer_handle_t* outHandle,
+        int32_t* outAcquireFence)
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getBuffer(outHandle, outAcquireFence);
+}
+
+hwc2_blend_mode_t Hwc2TestLayers::getBlendMode(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getBlendMode();
+}
+
+Area Hwc2TestLayers::getBufferArea(hwc2_layer_t layer) const
+{
+    auto testLayer = mTestLayers.find(layer);
+    if (testLayer == mTestLayers.end())
+        [] () { GTEST_FAIL(); }();
+    return testLayer->second.getBufferArea();
+}
+
+hwc_color_t Hwc2TestLayers::getColor(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getColor();
+}
+
+hwc2_composition_t Hwc2TestLayers::getComposition(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getComposition();
+}
+
+hwc_rect_t Hwc2TestLayers::getCursorPosition(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getCursorPosition();
+}
+
+android_dataspace_t Hwc2TestLayers::getDataspace(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getDataspace();
+}
+
+hwc_rect_t Hwc2TestLayers::getDisplayFrame(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getDisplayFrame();
+}
+
+float Hwc2TestLayers::getPlaneAlpha(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getPlaneAlpha();
+}
+
+hwc_frect_t Hwc2TestLayers::getSourceCrop(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getSourceCrop();
+}
+
+hwc_region_t Hwc2TestLayers::getSurfaceDamage(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getSurfaceDamage();
+}
+
+hwc_transform_t Hwc2TestLayers::getTransform(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getTransform();
+}
+
+hwc_region_t Hwc2TestLayers::getVisibleRegion(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getVisibleRegion();
+}
+
+uint32_t Hwc2TestLayers::getZOrder(hwc2_layer_t layer) const
+{
+    if (mTestLayers.count(layer) == 0) {
+        []() { GTEST_FAIL(); }();
+    }
+    return mTestLayers.at(layer).getZOrder();
+}
+
+/* Sets the visible regions for a display. Returns false if the layers do not
+ * cover the entire display or if a layer is not visible */
+bool Hwc2TestLayers::setVisibleRegions()
+{
+    /* The region of the display that is covered by layers above the current
+     * layer */
+    android::Region aboveOpaqueLayers;
+
+    bool optimized = true;
+
+    /* Iterate over test layers from max z order to min z order. */
+    for (auto& testLayer : mTestLayers) {
+        android::Region visibleRegion;
+
+        /* Set the visible region of this layer */
+        const hwc_rect_t displayFrame = testLayer.second.getDisplayFrame();
+
+        visibleRegion.set(android::Rect(displayFrame.left, displayFrame.top,
+                displayFrame.right, displayFrame.bottom));
+
+        /* Remove the area covered by opaque layers above this layer
+         * from this layer's visible region */
+        visibleRegion.subtractSelf(aboveOpaqueLayers);
+
+        testLayer.second.setVisibleRegion(visibleRegion);
+
+        /* If a layer is not visible, return false */
+        if (visibleRegion.isEmpty())
+            optimized = false;
+
+        /* If this layer is opaque, store the region it covers */
+        if (testLayer.second.getPlaneAlpha() == 1.0f)
+            aboveOpaqueLayers.orSelf(visibleRegion);
+    }
+
+    /* If the opaque region does not cover the entire display return false */
+    if (!aboveOpaqueLayers.isRect())
+        return false;
+
+    const auto rect = aboveOpaqueLayers.begin();
+    if (rect->left != 0 || rect->top != 0 || rect->right != mDisplayArea.width
+            || rect->bottom != mDisplayArea.height)
+        return false;
+
+    return optimized;
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.h b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.h
new file mode 100644
index 0000000..d95a91f
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 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 _HWC2_TEST_LAYERS_H
+#define _HWC2_TEST_LAYERS_H
+
+#include <map>
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include "Hwc2TestProperties.h"
+#include "Hwc2TestLayer.h"
+
+class Hwc2TestLayers {
+public:
+    Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
+            Hwc2TestCoverage coverage, const Area& displayArea);
+
+    Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
+            Hwc2TestCoverage coverage, const Area& displayArea,
+            const std::unordered_map<Hwc2TestPropertyName,
+            Hwc2TestCoverage>& coverageExceptions);
+
+    std::string dump() const;
+
+    void reset();
+
+    bool advance();
+    bool advanceVisibleRegions();
+
+    /* Test cases with multiple layers and property values can take quite some
+     * time to run. A significant amount of time can be spent on test cases
+     * where one layer is changing property values but is not visible. To
+     * decrease runtime, this function can be called. Removes layouts where a
+     * layer is completely blocked from view. It also removes layouts that do
+     * not cover the entire display.*/
+    bool optimizeLayouts();
+
+    bool contains(hwc2_layer_t layer) const;
+
+    int  getBuffer(hwc2_layer_t layer, buffer_handle_t* outHandle,
+            int32_t* outAcquireFence);
+
+    hwc2_blend_mode_t      getBlendMode(hwc2_layer_t layer) const;
+    Area                   getBufferArea(hwc2_layer_t layer) const;
+    hwc_color_t            getColor(hwc2_layer_t layer) const;
+    hwc2_composition_t     getComposition(hwc2_layer_t layer) const;
+    hwc_rect_t             getCursorPosition(hwc2_layer_t layer) const;
+    android_dataspace_t    getDataspace(hwc2_layer_t layer) const;
+    hwc_rect_t             getDisplayFrame(hwc2_layer_t layer) const;
+    android_pixel_format_t getFormat(hwc2_layer_t layer) const;
+    float                  getPlaneAlpha(hwc2_layer_t layer) const;
+    hwc_frect_t            getSourceCrop(hwc2_layer_t layer) const;
+    hwc_region_t           getSurfaceDamage(hwc2_layer_t layer) const;
+    hwc_transform_t        getTransform(hwc2_layer_t layer) const;
+    hwc_region_t           getVisibleRegion(hwc2_layer_t layer) const;
+    uint32_t               getZOrder(hwc2_layer_t layer) const;
+
+private:
+    bool setVisibleRegions();
+
+    std::map<hwc2_layer_t, Hwc2TestLayer> mTestLayers;
+
+    Area mDisplayArea;
+
+    bool mOptimize = false;
+};
+
+#endif /* ifndef _HWC2_TEST_LAYERS_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
new file mode 100644
index 0000000..b5522de
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
@@ -0,0 +1,782 @@
+/*
+ * Copyright (C) 2016 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 <sstream>
+#include <cutils/log.h>
+#include <ui/Rect.h>
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include "Hwc2TestBuffer.h"
+#include "Hwc2TestProperties.h"
+
+Hwc2TestBufferArea::Hwc2TestBufferArea(Hwc2TestCoverage coverage,
+        const Area& displayArea)
+    : Hwc2TestProperty(mBufferAreas, mCompositionSupport),
+      mScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteScalars:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicScalars:
+            mDefaultScalars),
+      mDisplayArea(displayArea)
+{
+    update();
+}
+
+std::string Hwc2TestBufferArea::dump() const
+{
+    std::stringstream dmp;
+    const Area& curr = get();
+    dmp << "\tbuffer area: width " << curr.width << ", height " << curr.height
+            << "\n";
+    return dmp.str();
+}
+
+void Hwc2TestBufferArea::setDependent(Hwc2TestBuffer* buffer)
+{
+    mBuffer = buffer;
+    if (buffer) {
+        buffer->updateBufferArea(get());
+    }
+}
+
+void Hwc2TestBufferArea::setDependent(Hwc2TestSourceCrop* sourceCrop)
+{
+    mSourceCrop = sourceCrop;
+    if (mSourceCrop) {
+        mSourceCrop->updateBufferArea(get());
+    }
+}
+
+void Hwc2TestBufferArea::setDependent(Hwc2TestSurfaceDamage* surfaceDamage)
+{
+    mSurfaceDamage = surfaceDamage;
+    if (mSurfaceDamage) {
+        mSurfaceDamage->updateBufferArea(get());
+    }
+}
+
+void Hwc2TestBufferArea::update()
+{
+    mBufferAreas.clear();
+
+    if (mDisplayArea.width == 0 && mDisplayArea.height == 0) {
+        mBufferAreas.push_back({0, 0});
+        return;
+    }
+
+    for (auto scalar : mScalars) {
+        mBufferAreas.push_back({static_cast<int32_t>(scalar * mDisplayArea.width),
+                static_cast<int32_t>(scalar * mDisplayArea.height)});
+    }
+
+    updateDependents();
+}
+
+void Hwc2TestBufferArea::updateDependents()
+{
+    const Area& curr = get();
+
+    if (mBuffer)
+        mBuffer->updateBufferArea(curr);
+    if (mSourceCrop)
+        mSourceCrop->updateBufferArea(curr);
+    if (mSurfaceDamage)
+        mSurfaceDamage->updateBufferArea(curr);
+}
+
+const std::vector<float> Hwc2TestBufferArea::mDefaultScalars = {
+    1.0f,
+};
+
+const std::vector<float> Hwc2TestBufferArea::mBasicScalars = {
+    1.0f, 0.5f,
+};
+
+const std::vector<float> Hwc2TestBufferArea::mCompleteScalars = {
+    1.0f, 0.75f, 0.5f
+};
+
+
+Hwc2TestBlendMode::Hwc2TestBlendMode(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(coverage, mCompleteBlendModes, mBasicBlendModes,
+            mDefaultBlendModes, mCompositionSupport) { }
+
+std::string Hwc2TestBlendMode::dump() const
+{
+    std::stringstream dmp;
+    dmp << "\tblend mode: " << getBlendModeName(get()) << "\n";
+    return dmp.str();
+}
+
+void Hwc2TestBlendMode::setDependent(Hwc2TestColor* color)
+{
+    mColor = color;
+    updateDependents();
+}
+
+void Hwc2TestBlendMode::updateDependents()
+{
+    if (mColor)
+        mColor->updateBlendMode(get());
+}
+
+const std::vector<hwc2_blend_mode_t> Hwc2TestBlendMode::mDefaultBlendModes = {
+    HWC2_BLEND_MODE_NONE,
+};
+
+const std::vector<hwc2_blend_mode_t> Hwc2TestBlendMode::mBasicBlendModes = {
+    HWC2_BLEND_MODE_NONE,
+    HWC2_BLEND_MODE_PREMULTIPLIED,
+};
+
+const std::vector<hwc2_blend_mode_t> Hwc2TestBlendMode::mCompleteBlendModes = {
+    HWC2_BLEND_MODE_NONE,
+    HWC2_BLEND_MODE_PREMULTIPLIED,
+    HWC2_BLEND_MODE_COVERAGE,
+};
+
+
+Hwc2TestColor::Hwc2TestColor(Hwc2TestCoverage coverage,
+        hwc2_blend_mode_t blendMode)
+    : Hwc2TestProperty(mColors, mCompositionSupport),
+      mBaseColors((coverage == Hwc2TestCoverage::Complete)? mCompleteBaseColors:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicBaseColors:
+            mDefaultBaseColors),
+      mBlendMode(blendMode)
+{
+    update();
+}
+
+std::string Hwc2TestColor::dump() const
+{
+    std::stringstream dmp;
+    const hwc_color_t& color = get();
+    dmp << "\tcolor: r " << std::to_string(color.r) << ", g "
+            << std::to_string(color.g) << ", b " << std::to_string(color.b)
+            << ", a " << std::to_string(color.a) << "\n";
+    return dmp.str();
+}
+
+void Hwc2TestColor::updateBlendMode(hwc2_blend_mode_t blendMode)
+{
+    mBlendMode = blendMode;
+    update();
+}
+
+void Hwc2TestColor::update()
+{
+    if (mBlendMode != HWC2_BLEND_MODE_PREMULTIPLIED) {
+        mColors = mBaseColors;
+        return;
+    }
+
+    mColors.clear();
+
+    for (const hwc_color_t& baseColor : mBaseColors) {
+        if (baseColor.a >= baseColor.r && baseColor.a >= baseColor.g
+                && baseColor.a >= baseColor.b) {
+            mColors.push_back(baseColor);
+        }
+    }
+
+}
+
+const std::vector<hwc_color_t> Hwc2TestColor::mDefaultBaseColors = {
+    {UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX},
+};
+
+const std::vector<hwc_color_t> Hwc2TestColor::mBasicBaseColors = {
+    {UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX},
+    {        0,         0,         0,         0},
+};
+
+const std::vector<hwc_color_t> Hwc2TestColor::mCompleteBaseColors = {
+    {UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX},
+    {UINT8_MAX, UINT8_MAX, UINT8_MAX,         0},
+    {UINT8_MAX, UINT8_MAX,         0, UINT8_MAX},
+    {UINT8_MAX, UINT8_MAX,         0,         0},
+    {UINT8_MAX,         0, UINT8_MAX, UINT8_MAX},
+    {UINT8_MAX,         0, UINT8_MAX,         0},
+    {UINT8_MAX,         0,         0, UINT8_MAX},
+    {UINT8_MAX,         0,         0,         0},
+    {        0, UINT8_MAX, UINT8_MAX, UINT8_MAX},
+    {        0, UINT8_MAX, UINT8_MAX,         0},
+    {        0, UINT8_MAX,         0, UINT8_MAX},
+    {        0, UINT8_MAX,         0,         0},
+    {        0,         0, UINT8_MAX, UINT8_MAX},
+    {        0,         0, UINT8_MAX,         0},
+    {        0,         0,         0, UINT8_MAX},
+    {        0,         0,         0,         0},
+};
+
+
+Hwc2TestComposition::Hwc2TestComposition(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(coverage, mCompleteCompositions, mBasicCompositions,
+            mDefaultCompositions, mCompositionSupport) { }
+
+std::string Hwc2TestComposition::dump() const
+{
+    std::stringstream dmp;
+    dmp << "\tcomposition: " << getCompositionName(get()) << "\n";
+    return dmp.str();
+}
+
+const std::vector<hwc2_composition_t> Hwc2TestComposition::mDefaultCompositions = {
+    HWC2_COMPOSITION_DEVICE,
+};
+
+const std::vector<hwc2_composition_t> Hwc2TestComposition::mBasicCompositions = {
+    HWC2_COMPOSITION_CLIENT,
+    HWC2_COMPOSITION_DEVICE,
+};
+
+const std::vector<hwc2_composition_t> Hwc2TestComposition::mCompleteCompositions = {
+    HWC2_COMPOSITION_CLIENT,
+    HWC2_COMPOSITION_DEVICE,
+    HWC2_COMPOSITION_SOLID_COLOR,
+    HWC2_COMPOSITION_CURSOR,
+    HWC2_COMPOSITION_SIDEBAND,
+};
+
+
+Hwc2TestDataspace::Hwc2TestDataspace(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(coverage, completeDataspaces, basicDataspaces,
+            defaultDataspaces, mCompositionSupport) { }
+
+std::string Hwc2TestDataspace::dump() const
+{
+    std::stringstream dmp;
+    dmp << "\tdataspace: " << get() << "\n";
+    return dmp.str();
+}
+
+const std::vector<android_dataspace_t> Hwc2TestDataspace::defaultDataspaces = {
+    HAL_DATASPACE_UNKNOWN,
+};
+
+const std::vector<android_dataspace_t> Hwc2TestDataspace::basicDataspaces = {
+    HAL_DATASPACE_UNKNOWN,
+    HAL_DATASPACE_V0_SRGB,
+};
+
+const std::vector<android_dataspace_t> Hwc2TestDataspace::completeDataspaces = {
+    HAL_DATASPACE_UNKNOWN,
+    HAL_DATASPACE_ARBITRARY,
+    HAL_DATASPACE_STANDARD_SHIFT,
+    HAL_DATASPACE_STANDARD_MASK,
+    HAL_DATASPACE_STANDARD_UNSPECIFIED,
+    HAL_DATASPACE_STANDARD_BT709,
+    HAL_DATASPACE_STANDARD_BT601_625,
+    HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED,
+    HAL_DATASPACE_STANDARD_BT601_525,
+    HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED,
+    HAL_DATASPACE_STANDARD_BT2020,
+    HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE,
+    HAL_DATASPACE_STANDARD_BT470M,
+    HAL_DATASPACE_STANDARD_FILM,
+    HAL_DATASPACE_TRANSFER_SHIFT,
+    HAL_DATASPACE_TRANSFER_MASK,
+    HAL_DATASPACE_TRANSFER_UNSPECIFIED,
+    HAL_DATASPACE_TRANSFER_LINEAR,
+    HAL_DATASPACE_TRANSFER_SRGB,
+    HAL_DATASPACE_TRANSFER_SMPTE_170M,
+    HAL_DATASPACE_TRANSFER_GAMMA2_2,
+    HAL_DATASPACE_TRANSFER_GAMMA2_8,
+    HAL_DATASPACE_TRANSFER_ST2084,
+    HAL_DATASPACE_TRANSFER_HLG,
+    HAL_DATASPACE_RANGE_SHIFT,
+    HAL_DATASPACE_RANGE_MASK,
+    HAL_DATASPACE_RANGE_UNSPECIFIED,
+    HAL_DATASPACE_RANGE_FULL,
+    HAL_DATASPACE_RANGE_LIMITED,
+    HAL_DATASPACE_SRGB_LINEAR,
+    HAL_DATASPACE_V0_SRGB_LINEAR,
+    HAL_DATASPACE_SRGB,
+    HAL_DATASPACE_V0_SRGB,
+    HAL_DATASPACE_JFIF,
+    HAL_DATASPACE_V0_JFIF,
+    HAL_DATASPACE_BT601_625,
+    HAL_DATASPACE_V0_BT601_625,
+    HAL_DATASPACE_BT601_525,
+    HAL_DATASPACE_V0_BT601_525,
+    HAL_DATASPACE_BT709,
+    HAL_DATASPACE_V0_BT709,
+    HAL_DATASPACE_DEPTH,
+};
+
+
+Hwc2TestDisplayDimension::Hwc2TestDisplayDimension(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(
+            (coverage == Hwc2TestCoverage::Complete)? mCompleteDisplayDimensions:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicDisplayDimensions:
+            mDefaultDisplayDimensions, mCompositionSupport) { }
+
+std::string Hwc2TestDisplayDimension::dump() const
+{
+    std::stringstream dmp;
+    const UnsignedArea& curr = get();
+    dmp << "\tdisplay dimension: " << curr.width<< " x " << curr.height<< "\n";
+    return dmp.str();
+}
+
+void Hwc2TestDisplayDimension::setDependent(Hwc2TestBuffer* buffer)
+{
+    mBuffer = buffer;
+    updateDependents();
+}
+
+void Hwc2TestDisplayDimension::updateDependents()
+{
+    const UnsignedArea& curr = get();
+
+    if (mBuffer)
+        mBuffer->updateBufferArea({static_cast<int32_t>(curr.width),
+                static_cast<int32_t>(curr.height)});
+}
+
+const std::vector<UnsignedArea>
+        Hwc2TestDisplayDimension::mDefaultDisplayDimensions = {
+    {1920, 1080},
+};
+
+const std::vector<UnsignedArea>
+        Hwc2TestDisplayDimension::mBasicDisplayDimensions = {
+    {640, 480},
+    {1280, 720},
+    {1920, 1080},
+    {1920, 1200},
+};
+
+const std::vector<UnsignedArea>
+        Hwc2TestDisplayDimension::mCompleteDisplayDimensions = {
+    {320, 240},
+    {480, 320},
+    {640, 480},
+    {1280, 720},
+    {1920, 1080},
+    {1920, 1200},
+    {2560, 1440},
+    {2560, 1600},
+    {3840, 2160},
+    {4096, 2160},
+};
+
+
+Hwc2TestDisplayFrame::Hwc2TestDisplayFrame(Hwc2TestCoverage coverage,
+        const Area& displayArea)
+    : Hwc2TestProperty(mDisplayFrames, mCompositionSupport),
+      mFrectScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteFrectScalars:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicFrectScalars:
+            mDefaultFrectScalars),
+      mDisplayArea(displayArea)
+{
+    update();
+}
+
+std::string Hwc2TestDisplayFrame::dump() const
+{
+    std::stringstream dmp;
+    const hwc_rect_t& displayFrame = get();
+    dmp << "\tdisplay frame: left " << displayFrame.left << ", top "
+            << displayFrame.top << ", right " << displayFrame.right
+            << ", bottom " << displayFrame.bottom << "\n";
+    return dmp.str();
+}
+
+void Hwc2TestDisplayFrame::update()
+{
+    mDisplayFrames.clear();
+
+    if (mDisplayArea.width == 0 && mDisplayArea.height == 0) {
+        mDisplayFrames.push_back({0, 0, 0, 0});
+        return;
+    }
+
+    for (const auto& frectScalar : mFrectScalars) {
+        mDisplayFrames.push_back({
+                static_cast<int>(frectScalar.left * mDisplayArea.width),
+                static_cast<int>(frectScalar.top * mDisplayArea.height),
+                static_cast<int>(frectScalar.right * mDisplayArea.width),
+                static_cast<int>(frectScalar.bottom * mDisplayArea.height)});
+    }
+}
+
+const std::vector<hwc_frect_t> Hwc2TestDisplayFrame::mDefaultFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+};
+
+const std::vector<hwc_frect_t> Hwc2TestDisplayFrame::mBasicFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+    {0.0, 0.0, 1.0, 0.05},
+    {0.0, 0.95, 1.0, 1.0},
+};
+
+const std::vector<hwc_frect_t> Hwc2TestDisplayFrame::mCompleteFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+    {0.0, 0.05, 1.0, 0.95},
+    {0.0, 0.05, 1.0, 1.0},
+    {0.0, 0.0, 1.0, 0.05},
+    {0.0, 0.95, 1.0, 1.0},
+    {0.25, 0.0, 0.75, 0.35},
+    {0.25, 0.25, 0.75, 0.75},
+};
+
+
+Hwc2TestPlaneAlpha::Hwc2TestPlaneAlpha(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(coverage, mCompletePlaneAlphas, mBasicPlaneAlphas,
+            mDefaultPlaneAlphas, mCompositionSupport) { }
+
+std::string Hwc2TestPlaneAlpha::dump() const
+{
+    std::stringstream dmp;
+    dmp << "\tplane alpha: " << get() << "\n";
+    return dmp.str();
+}
+
+const std::vector<float> Hwc2TestPlaneAlpha::mDefaultPlaneAlphas = {
+    1.0f,
+};
+
+const std::vector<float> Hwc2TestPlaneAlpha::mBasicPlaneAlphas = {
+    1.0f, 0.0f,
+};
+
+const std::vector<float> Hwc2TestPlaneAlpha::mCompletePlaneAlphas = {
+    1.0f, 0.75f, 0.5f, 0.25f, 0.0f,
+};
+
+
+Hwc2TestSourceCrop::Hwc2TestSourceCrop(Hwc2TestCoverage coverage,
+        const Area& bufferArea)
+    : Hwc2TestProperty(mSourceCrops, mCompositionSupport),
+      mFrectScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteFrectScalars:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicFrectScalars:
+            mDefaultFrectScalars),
+      mBufferArea(bufferArea)
+{
+    update();
+}
+
+std::string Hwc2TestSourceCrop::dump() const
+{
+    std::stringstream dmp;
+    const hwc_frect_t& sourceCrop = get();
+    dmp << "\tsource crop: left " << sourceCrop.left << ", top "
+            << sourceCrop.top << ", right " << sourceCrop.right << ", bottom "
+            << sourceCrop.bottom << "\n";
+    return dmp.str();
+}
+
+void Hwc2TestSourceCrop::updateBufferArea(const Area& bufferArea)
+{
+    mBufferArea = bufferArea;
+    update();
+}
+
+void Hwc2TestSourceCrop::update()
+{
+    mSourceCrops.clear();
+
+    if (mBufferArea.width == 0 && mBufferArea.height == 0) {
+        mSourceCrops.push_back({0, 0, 0, 0});
+        return;
+    }
+
+    for (const auto& frectScalar : mFrectScalars) {
+        mSourceCrops.push_back({
+                frectScalar.left * mBufferArea.width,
+                frectScalar.top * mBufferArea.height,
+                frectScalar.right * mBufferArea.width,
+                frectScalar.bottom * mBufferArea.height});
+    }
+}
+
+const std::vector<hwc_frect_t> Hwc2TestSourceCrop::mDefaultFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+};
+
+const std::vector<hwc_frect_t> Hwc2TestSourceCrop::mBasicFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+    {0.0, 0.0, 0.5, 0.5},
+    {0.5, 0.5, 1.0, 1.0},
+};
+
+const std::vector<hwc_frect_t> Hwc2TestSourceCrop::mCompleteFrectScalars = {
+    {0.0, 0.0, 1.0, 1.0},
+    {0.0, 0.0, 0.5, 0.5},
+    {0.5, 0.5, 1.0, 1.0},
+    {0.0, 0.0, 0.25, 0.25},
+    {0.25, 0.25, 0.75, 0.75},
+};
+
+
+Hwc2TestSurfaceDamage::Hwc2TestSurfaceDamage(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(mSurfaceDamages, mCompositionSupport),
+      mRegionScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteRegionScalars:
+            (coverage == Hwc2TestCoverage::Basic)? mBasicRegionScalars:
+            mDefaultRegionScalars)
+{
+    update();
+}
+
+Hwc2TestSurfaceDamage::~Hwc2TestSurfaceDamage()
+{
+    freeSurfaceDamages();
+}
+
+std::string Hwc2TestSurfaceDamage::dump() const
+{
+    std::stringstream dmp;
+
+    const hwc_region_t& curr = get();
+    dmp << "\tsurface damage: region count " << curr.numRects << "\n";
+    for (size_t i = 0; i < curr.numRects; i++) {
+        const hwc_rect_t& rect = curr.rects[i];
+        dmp << "\t\trect: left " << rect.left << ", top " << rect.top
+                << ", right " << rect.right << ", bottom " << rect.bottom << "\n";
+    }
+
+    return dmp.str();
+}
+
+void Hwc2TestSurfaceDamage::updateBufferArea(const Area& bufferArea)
+{
+    mBufferArea = bufferArea;
+    update();
+}
+
+void Hwc2TestSurfaceDamage::update()
+{
+    freeSurfaceDamages();
+
+    if (mBufferArea.width == 0 && mBufferArea.height == 0) {
+        mSurfaceDamages.push_back({0, nullptr});
+        return;
+    }
+
+    hwc_region_t damage;
+
+    for (const auto& regionScalar : mRegionScalars) {
+        damage.numRects = regionScalar.size();
+
+        if (damage.numRects > 0) {
+            hwc_rect_t* rects = new hwc_rect_t[damage.numRects];
+            if (!rects) {
+                ALOGW("failed to allocate new hwc_rect_t array");
+                continue;
+            }
+
+            for (size_t i = 0; i < damage.numRects; i++) {
+                rects[i].left = regionScalar[i].left * mBufferArea.width;
+                rects[i].top = regionScalar[i].top * mBufferArea.height;
+                rects[i].right = regionScalar[i].right * mBufferArea.width;
+                rects[i].bottom = regionScalar[i].bottom * mBufferArea.height;
+            }
+
+            damage.rects = static_cast<hwc_rect_t const*>(rects);
+        } else {
+            damage.rects = nullptr;
+        }
+
+        mSurfaceDamages.push_back(damage);
+    }
+}
+
+void Hwc2TestSurfaceDamage::freeSurfaceDamages()
+{
+    for (const auto& surfaceDamage : mSurfaceDamages) {
+        if (surfaceDamage.numRects > 0 && surfaceDamage.rects)
+            delete[] surfaceDamage.rects;
+    }
+    mSurfaceDamages.clear();
+}
+
+const std::vector<std::vector<hwc_frect_t>> Hwc2TestSurfaceDamage::mDefaultRegionScalars = {
+    {{}},
+};
+
+const std::vector<std::vector<hwc_frect_t>> Hwc2TestSurfaceDamage::mBasicRegionScalars = {
+    {{}},
+    {{0.0, 0.0, 1.0, 1.0}},
+};
+
+const std::vector<std::vector<hwc_frect_t>> Hwc2TestSurfaceDamage::mCompleteRegionScalars = {
+    {{}},
+    {{0.0, 0.0, 1.0, 1.0}},
+    {{0.0, 0.0, 0.5, 0.5}, {0.5, 0.5, 1.0, 1.0}},
+};
+
+
+Hwc2TestTransform::Hwc2TestTransform(Hwc2TestCoverage coverage)
+    : Hwc2TestProperty(coverage, mCompleteTransforms, mBasicTransforms,
+            mDefaultTransforms, mCompositionSupport) { }
+
+std::string Hwc2TestTransform::dump() const
+{
+    std::stringstream dmp;
+    dmp << "\ttransform: " << getTransformName(get()) << "\n";
+    return dmp.str();
+}
+
+const std::vector<hwc_transform_t> Hwc2TestTransform::mDefaultTransforms = {
+    static_cast<hwc_transform_t>(0),
+};
+
+const std::vector<hwc_transform_t> Hwc2TestTransform::mBasicTransforms = {
+    static_cast<hwc_transform_t>(0),
+    HWC_TRANSFORM_FLIP_H,
+    HWC_TRANSFORM_FLIP_V,
+    HWC_TRANSFORM_ROT_90,
+};
+
+const std::vector<hwc_transform_t> Hwc2TestTransform::mCompleteTransforms = {
+    static_cast<hwc_transform_t>(0),
+    HWC_TRANSFORM_FLIP_H,
+    HWC_TRANSFORM_FLIP_V,
+    HWC_TRANSFORM_ROT_90,
+    HWC_TRANSFORM_ROT_180,
+    HWC_TRANSFORM_ROT_270,
+    HWC_TRANSFORM_FLIP_H_ROT_90,
+    HWC_TRANSFORM_FLIP_V_ROT_90,
+};
+
+
+Hwc2TestVisibleRegion::~Hwc2TestVisibleRegion()
+{
+    release();
+}
+
+std::string Hwc2TestVisibleRegion::dump() const
+{
+    std::stringstream dmp;
+
+    const hwc_region_t& curr = get();
+    dmp << "\tvisible region: region count " << curr.numRects << "\n";
+    for (size_t i = 0; i < curr.numRects; i++) {
+        const hwc_rect_t& rect = curr.rects[i];
+        dmp << "\t\trect: left " << rect.left << ", top " << rect.top
+                << ", right " << rect.right << ", bottom " << rect.bottom << "\n";
+    }
+
+    return dmp.str();
+}
+
+void Hwc2TestVisibleRegion::set(const android::Region& visibleRegion)
+{
+    release();
+
+    size_t size = 0;
+    const android::Rect* rects = visibleRegion.getArray(&size);
+
+    mVisibleRegion.numRects = size;
+    mVisibleRegion.rects = nullptr;
+
+    if (size > 0) {
+        hwc_rect_t* hwcRects = new hwc_rect_t[size];
+        for (size_t i = 0; i < size; i++) {
+            hwcRects[i].left = rects[i].left;
+            hwcRects[i].top = rects[i].top;
+            hwcRects[i].right = rects[i].right;
+            hwcRects[i].bottom = rects[i].bottom;
+        }
+        mVisibleRegion.rects = hwcRects;
+    }
+}
+
+hwc_region_t Hwc2TestVisibleRegion::get() const
+{
+    return mVisibleRegion;
+}
+
+void Hwc2TestVisibleRegion::release()
+{
+    if (mVisibleRegion.numRects > 0 && mVisibleRegion.rects)
+        delete[] mVisibleRegion.rects;
+    mVisibleRegion.rects = nullptr;
+    mVisibleRegion.numRects = 0;
+}
+
+/* Identifies which layer properties are supported by each composition type.
+ * hwc2_composition_t values range from:
+ *  HWC2_COMPOSITION_INVALID = 0,
+ *  HWC2_COMPOSITION_CLIENT = 1,
+ *  HWC2_COMPOSITION_DEVICE = 2,
+ *  HWC2_COMPOSITION_SOLID_COLOR = 3,
+ *  HWC2_COMPOSITION_CURSOR = 4,
+ *  HWC2_COMPOSITION_SIDEBAND = 5,
+ *
+ * Each property array can be indexed by a hwc2_composition_t value.
+ * By using an array instead of a more complex data structure, runtimes for
+ * some test cases showed a noticeable improvement.
+ */
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestBufferArea::mCompositionSupport = {{
+    false,   true,    true,    false,   true,    true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestBlendMode::mCompositionSupport = {{
+    false,   true,    true,    false,   true,    true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestColor::mCompositionSupport = {{
+    false,   false,   false,   true,    false,   false,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestComposition::mCompositionSupport = {{
+    false,   true,    true,    true,    true,    true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestDataspace::mCompositionSupport = {{
+    false,   true,    true,    true,    true,    false,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestDisplayDimension::mCompositionSupport = {{
+    false,   true,    true,    true,    true,    true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestDisplayFrame::mCompositionSupport = {{
+    false,   true,    true,    true,    false,   true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestPlaneAlpha::mCompositionSupport = {{
+    false,   true,    true,    true,    true,    true,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestSourceCrop::mCompositionSupport = {{
+    false,   true,    true,    false,   true,    false,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestSurfaceDamage::mCompositionSupport = {{
+    false,   false,   true,    false,   true,    false,
+}};
+
+/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
+const std::array<bool, 6> Hwc2TestTransform::mCompositionSupport = {{
+    false,   true,    true,    false,   true,    true,
+}};
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
new file mode 100644
index 0000000..c2029ab
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2016 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 _HWC2_TEST_PROPERTIES_H
+#define _HWC2_TEST_PROPERTIES_H
+
+#include <array>
+#include <vector>
+
+#include <ui/Region.h>
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+enum class Hwc2TestCoverage {
+    Default = 0,
+    Basic,
+    Complete,
+};
+
+enum class Hwc2TestPropertyName {
+    BlendMode = 1,
+    BufferArea,
+    Color,
+    Composition,
+    CursorPosition,
+    Dataspace,
+    DisplayFrame,
+    PlaneAlpha,
+    SourceCrop,
+    SurfaceDamage,
+    Transform,
+};
+
+typedef struct {
+    int32_t width;
+    int32_t height;
+} Area;
+
+
+typedef struct {
+    uint32_t width;
+    uint32_t height;
+} UnsignedArea;
+
+
+class Hwc2TestContainer {
+public:
+    virtual ~Hwc2TestContainer() = default;
+
+    /* Resets the container */
+    virtual void reset() = 0;
+
+    /* Attempts to advance to the next valid value. Returns true if one can be
+     * found */
+    virtual bool advance() = 0;
+
+    virtual std::string dump() const = 0;
+
+    /* Returns true if the container supports the given composition type */
+    virtual bool isSupported(hwc2_composition_t composition) = 0;
+};
+
+
+template <class T>
+class Hwc2TestProperty : public Hwc2TestContainer {
+public:
+    Hwc2TestProperty(Hwc2TestCoverage coverage,
+            const std::vector<T>& completeList, const std::vector<T>& basicList,
+            const std::vector<T>& defaultList,
+            const std::array<bool, 6>& compositionSupport)
+        : Hwc2TestProperty((coverage == Hwc2TestCoverage::Complete)? completeList:
+                (coverage == Hwc2TestCoverage::Basic)? basicList : defaultList,
+                compositionSupport) { }
+
+    Hwc2TestProperty(const std::vector<T>& list,
+            const std::array<bool, 6>& compositionSupport)
+        : mList(list),
+          mCompositionSupport(compositionSupport) { }
+
+    void reset() override
+    {
+        mListIdx = 0;
+    }
+
+    bool advance() override
+    {
+        if (mListIdx + 1 < mList.size()) {
+            mListIdx++;
+            updateDependents();
+            return true;
+        }
+        reset();
+        updateDependents();
+        return false;
+    }
+
+    T get() const
+    {
+        return mList.at(mListIdx);
+    }
+
+    virtual bool isSupported(hwc2_composition_t composition)
+    {
+        return mCompositionSupport.at(composition);
+    }
+
+protected:
+    /* If a derived class has dependents, override this function */
+    virtual void updateDependents() { }
+
+    const std::vector<T>& mList;
+    size_t mListIdx = 0;
+
+    const std::array<bool, 6>& mCompositionSupport;
+};
+
+class Hwc2TestBuffer;
+class Hwc2TestSourceCrop;
+class Hwc2TestSurfaceDamage;
+
+class Hwc2TestBufferArea : public Hwc2TestProperty<Area> {
+public:
+    Hwc2TestBufferArea(Hwc2TestCoverage coverage, const Area& displayArea);
+
+    std::string dump() const override;
+
+    void setDependent(Hwc2TestBuffer* buffer);
+    void setDependent(Hwc2TestSourceCrop* sourceCrop);
+    void setDependent(Hwc2TestSurfaceDamage* surfaceDamage);
+
+protected:
+    void update();
+    void updateDependents() override;
+
+    const std::vector<float>& mScalars;
+    static const std::vector<float> mDefaultScalars;
+    static const std::vector<float> mBasicScalars;
+    static const std::vector<float> mCompleteScalars;
+
+    Area mDisplayArea;
+
+    Hwc2TestBuffer* mBuffer = nullptr;
+    Hwc2TestSourceCrop* mSourceCrop = nullptr;
+    Hwc2TestSurfaceDamage* mSurfaceDamage = nullptr;
+
+    std::vector<Area> mBufferAreas;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestColor;
+
+class Hwc2TestBlendMode : public Hwc2TestProperty<hwc2_blend_mode_t> {
+public:
+    Hwc2TestBlendMode(Hwc2TestCoverage coverage);
+
+    std::string dump() const override;
+
+    void setDependent(Hwc2TestColor* color);
+
+protected:
+    void updateDependents() override;
+
+    Hwc2TestColor* mColor = nullptr;
+
+    static const std::vector<hwc2_blend_mode_t> mDefaultBlendModes;
+    static const std::vector<hwc2_blend_mode_t> mBasicBlendModes;
+    static const std::vector<hwc2_blend_mode_t> mCompleteBlendModes;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestColor : public Hwc2TestProperty<hwc_color_t> {
+public:
+    Hwc2TestColor(Hwc2TestCoverage coverage,
+            hwc2_blend_mode_t blendMode = HWC2_BLEND_MODE_NONE);
+
+    std::string dump() const override;
+
+    void updateBlendMode(hwc2_blend_mode_t blendMode);
+
+protected:
+    void update();
+
+    std::vector<hwc_color_t> mBaseColors;
+    static const std::vector<hwc_color_t> mDefaultBaseColors;
+    static const std::vector<hwc_color_t> mBasicBaseColors;
+    static const std::vector<hwc_color_t> mCompleteBaseColors;
+
+    hwc2_blend_mode_t mBlendMode;
+
+    std::vector<hwc_color_t> mColors;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestComposition : public Hwc2TestProperty<hwc2_composition_t> {
+public:
+    Hwc2TestComposition(Hwc2TestCoverage coverage);
+
+    std::string dump() const override;
+
+protected:
+    static const std::vector<hwc2_composition_t> mDefaultCompositions;
+    static const std::vector<hwc2_composition_t> mBasicCompositions;
+    static const std::vector<hwc2_composition_t> mCompleteCompositions;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestDataspace : public Hwc2TestProperty<android_dataspace_t> {
+public:
+    Hwc2TestDataspace(Hwc2TestCoverage coverage);
+
+    std::string dump() const override;
+
+protected:
+    static const std::vector<android_dataspace_t> defaultDataspaces;
+    static const std::vector<android_dataspace_t> basicDataspaces;
+    static const std::vector<android_dataspace_t> completeDataspaces;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestDisplayDimension : public Hwc2TestProperty<UnsignedArea> {
+public:
+    Hwc2TestDisplayDimension(Hwc2TestCoverage coverage);
+
+    std::string dump() const;
+
+    void setDependent(Hwc2TestBuffer* buffer);
+
+private:
+    void updateDependents();
+
+    Hwc2TestBuffer* mBuffer;
+
+    static const std::vector<UnsignedArea> mDefaultDisplayDimensions;
+    static const std::vector<UnsignedArea> mBasicDisplayDimensions;
+    static const std::vector<UnsignedArea> mCompleteDisplayDimensions;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestDisplayFrame : public Hwc2TestProperty<hwc_rect_t> {
+public:
+    Hwc2TestDisplayFrame(Hwc2TestCoverage coverage, const Area& displayArea);
+
+    std::string dump() const override;
+
+protected:
+    void update();
+
+    const std::vector<hwc_frect_t>& mFrectScalars;
+    const static std::vector<hwc_frect_t> mDefaultFrectScalars;
+    const static std::vector<hwc_frect_t> mBasicFrectScalars;
+    const static std::vector<hwc_frect_t> mCompleteFrectScalars;
+
+    Area mDisplayArea;
+
+    std::vector<hwc_rect_t> mDisplayFrames;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestPlaneAlpha : public Hwc2TestProperty<float> {
+public:
+    Hwc2TestPlaneAlpha(Hwc2TestCoverage coverage);
+
+    std::string dump() const override;
+
+protected:
+    static const std::vector<float> mDefaultPlaneAlphas;
+    static const std::vector<float> mBasicPlaneAlphas;
+    static const std::vector<float> mCompletePlaneAlphas;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestSourceCrop : public Hwc2TestProperty<hwc_frect_t> {
+public:
+    Hwc2TestSourceCrop(Hwc2TestCoverage coverage, const Area& bufferArea = {0, 0});
+
+    std::string dump() const override;
+
+    void updateBufferArea(const Area& bufferArea);
+
+protected:
+    void update();
+
+    const std::vector<hwc_frect_t>& mFrectScalars;
+    const static std::vector<hwc_frect_t> mDefaultFrectScalars;
+    const static std::vector<hwc_frect_t> mBasicFrectScalars;
+    const static std::vector<hwc_frect_t> mCompleteFrectScalars;
+
+    Area mBufferArea;
+
+    std::vector<hwc_frect_t> mSourceCrops;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestSurfaceDamage : public Hwc2TestProperty<hwc_region_t> {
+public:
+    Hwc2TestSurfaceDamage(Hwc2TestCoverage coverage);
+    ~Hwc2TestSurfaceDamage();
+
+    std::string dump() const override;
+
+    void updateBufferArea(const Area& bufferArea);
+
+protected:
+    void update();
+    void freeSurfaceDamages();
+
+    const std::vector<std::vector<hwc_frect_t>> &mRegionScalars;
+    const static std::vector<std::vector<hwc_frect_t>> mDefaultRegionScalars;
+    const static std::vector<std::vector<hwc_frect_t>> mBasicRegionScalars;
+    const static std::vector<std::vector<hwc_frect_t>> mCompleteRegionScalars;
+
+    Area mBufferArea = {0, 0};
+
+    std::vector<hwc_region_t> mSurfaceDamages;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestTransform : public Hwc2TestProperty<hwc_transform_t> {
+public:
+    Hwc2TestTransform(Hwc2TestCoverage coverage);
+
+    std::string dump() const override;
+
+protected:
+    static const std::vector<hwc_transform_t> mDefaultTransforms;
+    static const std::vector<hwc_transform_t> mBasicTransforms;
+    static const std::vector<hwc_transform_t> mCompleteTransforms;
+
+    static const std::array<bool, 6> mCompositionSupport;
+};
+
+
+class Hwc2TestVisibleRegion {
+public:
+    ~Hwc2TestVisibleRegion();
+
+    std::string dump() const;
+
+    void set(const android::Region& visibleRegion);
+    hwc_region_t get() const;
+    void release();
+
+protected:
+    hwc_region_t mVisibleRegion = {0, nullptr};
+};
+
+#endif /* ifndef _HWC2_TEST_PROPERTIES_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp
new file mode 100644
index 0000000..d0fbc0b
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 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 <sstream>
+
+#include "Hwc2TestVirtualDisplay.h"
+
+Hwc2TestVirtualDisplay::Hwc2TestVirtualDisplay(
+        Hwc2TestCoverage coverage)
+    : mDisplayDimension(coverage)
+{
+    mDisplayDimension.setDependent(&mBuffer);
+}
+
+std::string Hwc2TestVirtualDisplay::dump() const
+{
+    std::stringstream dmp;
+
+    dmp << "virtual display: \n";
+
+    mDisplayDimension.dump();
+
+    return dmp.str();
+}
+
+int Hwc2TestVirtualDisplay::getBuffer(buffer_handle_t* outHandle,
+        android::base::unique_fd* outAcquireFence)
+{
+    int32_t acquireFence;
+    int ret = mBuffer.get(outHandle, &acquireFence);
+    outAcquireFence->reset(acquireFence);
+    return ret;
+}
+
+void Hwc2TestVirtualDisplay::reset()
+{
+    return mDisplayDimension.reset();
+}
+
+bool Hwc2TestVirtualDisplay::advance()
+{
+    return mDisplayDimension.advance();
+}
+
+UnsignedArea Hwc2TestVirtualDisplay::getDisplayDimension() const
+{
+    return mDisplayDimension.get();
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
new file mode 100644
index 0000000..09420ef
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 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 _HWC2_TEST_VIRTUAL_DISPLAY_H
+#define _HWC2_TEST_VIRTUAL_DISPLAY_H
+
+#include "Hwc2TestBuffer.h"
+#include "Hwc2TestProperties.h"
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+class Hwc2TestVirtualDisplay {
+public:
+    Hwc2TestVirtualDisplay(Hwc2TestCoverage coverage);
+
+    std::string dump() const;
+
+    int getBuffer(buffer_handle_t* outHandle,
+            android::base::unique_fd* outAcquireFence);
+
+    void reset();
+    bool advance();
+
+    UnsignedArea getDisplayDimension() const;
+
+private:
+    Hwc2TestBuffer mBuffer;
+
+    Hwc2TestDisplayDimension mDisplayDimension;
+};
+
+#endif /* ifndef _HWC2_TEST_VIRTUAL_DISPLAY_H */
diff --git a/services/vr/bufferhubd/consumer_queue_channel.cpp b/services/vr/bufferhubd/consumer_queue_channel.cpp
index 39d6bc8..ae87acd 100644
--- a/services/vr/bufferhubd/consumer_queue_channel.cpp
+++ b/services/vr/bufferhubd/consumer_queue_channel.cpp
@@ -92,29 +92,39 @@
     size_t producer_slot = pending_buffer_slots_.front().second;
     pending_buffer_slots_.pop();
 
-    // It's possible that the producer channel has expired.
+    // It's possible that the producer channel has expired. When this occurs,
+    // ignore the producer channel.
     if (producer_channel == nullptr) {
-      ALOGE(
+      ALOGW(
           "ConsumerQueueChannel::OnConsumerQueueImportBuffers: producer "
           "channel has already been expired.");
-      REPLY_ERROR_RETURN(message, ENOENT, {});
+      continue;
     }
 
     RemoteChannelHandle consumer_handle(
         producer_channel->CreateConsumer(message));
 
-    // All buffer imports should succeed together.
+    // If no buffers are imported successfully, clear available and return an
+    // error. Otherwise, return all consumer handles already imported
+    // successfully, but keep available bits on, so that the client can retry
+    // importing remaining consumer buffers.
     if (!consumer_handle.valid()) {
       ALOGE(
           "ConsumerQueueChannel::OnConsumerQueueImportBuffers: imported "
           "consumer handle is invalid.");
-      REPLY_ERROR_RETURN(message, EIO, {});
+      if (buffer_handles.empty()) {
+        ClearAvailable();
+        REPLY_ERROR_RETURN(message, EIO, {});
+      } else {
+        return buffer_handles;
+      }
     }
 
     // Move consumer_handle into buffer_handles.
     buffer_handles.emplace_back(std::move(consumer_handle), producer_slot);
   }
 
+  ClearAvailable();
   return buffer_handles;
 }
 
diff --git a/services/vr/sensord/Android.mk b/services/vr/sensord/Android.mk
index e213bd6..f86664e 100644
--- a/services/vr/sensord/Android.mk
+++ b/services/vr/sensord/Android.mk
@@ -14,6 +14,8 @@
 
 LOCAL_PATH := $(call my-dir)
 
+SENSORD_EXTEND ?= libsensordextensionstub
+
 sourceFiles := \
 	pose_service.cpp \
 	sensord.cpp \
@@ -42,6 +44,7 @@
 	liblog \
 	libhardware \
 	libutils \
+	$(SENSORD_EXTEND) \
 
 cFlags := -DLOG_TAG=\"sensord\" \
           -DTRACE=0
@@ -67,3 +70,8 @@
 LOCAL_SRC_FILES := test/poselatencytest.cpp
 LOCAL_MODULE := poselatencytest
 include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libsensordextensionstub
+LOCAL_SRC_FILES := sensord_extension.cpp
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/vr/sensord/sensord.cpp b/services/vr/sensord/sensord.cpp
index 0a75318..db39152 100644
--- a/services/vr/sensord/sensord.cpp
+++ b/services/vr/sensord/sensord.cpp
@@ -14,6 +14,7 @@
 #include "sensor_ndk_thread.h"
 #include "sensor_service.h"
 #include "sensor_thread.h"
+#include "sensord_extension.h"
 
 using android::dvr::PoseService;
 using android::dvr::SensorHalThread;
@@ -22,10 +23,13 @@
 using android::dvr::SensorThread;
 using android::pdx::Service;
 using android::pdx::ServiceDispatcher;
+using android::dvr::SensordExtension;
 
 int main(int, char**) {
   ALOGI("Starting up...");
 
+  SensordExtension::run();
+
   // We need to be able to create endpoints with full perms.
   umask(0000);
 
diff --git a/services/vr/sensord/sensord_extension.cpp b/services/vr/sensord/sensord_extension.cpp
new file mode 100644
index 0000000..6cd7db3
--- /dev/null
+++ b/services/vr/sensord/sensord_extension.cpp
@@ -0,0 +1,4 @@
+#include "sensord_extension.h"
+
+void android::dvr::SensordExtension::run() {
+}
diff --git a/services/vr/sensord/sensord_extension.h b/services/vr/sensord/sensord_extension.h
new file mode 100644
index 0000000..e553eed
--- /dev/null
+++ b/services/vr/sensord/sensord_extension.h
@@ -0,0 +1,16 @@
+#ifndef ANDROID_DVR_SENSORD_EXTENSION_H_
+#define ANDROID_DVR_SENSORD_EXTENSION_H_
+
+namespace android {
+namespace dvr {
+
+// Allows sensord to be extended with additional code.
+class SensordExtension {
+ public:
+  static void run();
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSORD_EXTENSION_H_
diff --git a/services/vr/virtual_touchpad/Android.mk b/services/vr/virtual_touchpad/Android.mk
index b78eb99..88b2dd9 100644
--- a/services/vr/virtual_touchpad/Android.mk
+++ b/services/vr/virtual_touchpad/Android.mk
@@ -9,7 +9,8 @@
   VirtualTouchpadEvdev.cpp
 
 shared_libs := \
-  libbase
+  libbase \
+  libutils
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(src)
@@ -24,21 +25,23 @@
 
 # Touchpad unit tests.
 
-test_src_files := \
-  tests/VirtualTouchpad_test.cpp
-
-static_libs := \
+test_static_libs := \
   libbase \
   libcutils \
-  libutils \
   libvirtualtouchpad
 
+test_shared_libs := \
+  libutils
+
+test_src_files := \
+  tests/VirtualTouchpad_test.cpp
+
 $(foreach file,$(test_src_files), \
     $(eval include $(CLEAR_VARS)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
     $(eval LOCAL_C_INCLUDES := $(LOCAL_PATH)/include) \
-    $(eval LOCAL_STATIC_LIBRARIES := $(static_libs)) \
-    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libs)) \
+    $(eval LOCAL_STATIC_LIBRARIES := $(test_static_libs)) \
+    $(eval LOCAL_SHARED_LIBRARIES := $(test_shared_libs)) \
     $(eval LOCAL_CPPFLAGS += -std=c++11) \
     $(eval LOCAL_LDLIBS := -llog) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
@@ -70,7 +73,7 @@
 LOCAL_STATIC_LIBRARIES := $(static_libs)
 LOCAL_SHARED_LIBRARIES := $(shared_libs)
 LOCAL_CPPFLAGS += -std=c++11
-LOCAL_CFLAGS += -DLOG_TAG=\"VrVirtualTouchpad\"
+LOCAL_CFLAGS += -DLOG_TAG=\"VrVirtualTouchpad\" -DSELINUX_ACCESS_CONTROL
 LOCAL_LDLIBS := -llog
 LOCAL_MODULE := virtual_touchpad
 LOCAL_MODULE_TAGS := optional
diff --git a/services/vr/virtual_touchpad/EvdevInjector.cpp b/services/vr/virtual_touchpad/EvdevInjector.cpp
index d8a1dfa..a4ccdd0 100644
--- a/services/vr/virtual_touchpad/EvdevInjector.cpp
+++ b/services/vr/virtual_touchpad/EvdevInjector.cpp
@@ -307,5 +307,10 @@
   return 0;
 }
 
+void EvdevInjector::dumpInternal(String8& result) {
+  result.appendFormat("injector_state = %d\n", static_cast<int>(state_));
+  result.appendFormat("injector_error = %d\n", error_);
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/virtual_touchpad/EvdevInjector.h b/services/vr/virtual_touchpad/EvdevInjector.h
index 1b1c4da..c69dbef 100644
--- a/services/vr/virtual_touchpad/EvdevInjector.h
+++ b/services/vr/virtual_touchpad/EvdevInjector.h
@@ -3,6 +3,7 @@
 
 #include <android-base/unique_fd.h>
 #include <linux/uinput.h>
+#include <utils/String8.h>
 
 #include <cstdint>
 #include <memory>
@@ -99,6 +100,8 @@
   int SendMultiTouchXY(int32_t slot, int32_t id, int32_t x, int32_t y);
   int SendMultiTouchLift(int32_t slot);
 
+  void dumpInternal(String8& result);
+
  protected:
   // Must be called only between construction and ConfigureBegin().
   inline void SetUInputForTesting(UInput* uinput) { uinput_ = uinput; }
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp b/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
index 175173f..782b19c 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
@@ -10,17 +10,49 @@
 
 class VirtualTouchpadClientImpl : public VirtualTouchpadClient {
  public:
-  VirtualTouchpadClientImpl(sp<IVirtualTouchpadService> service)
-      : service_(service) {}
-  ~VirtualTouchpadClientImpl() override {}
+  VirtualTouchpadClientImpl() {}
+  ~VirtualTouchpadClientImpl() override {
+    if (service_ != nullptr) {
+      Detach();
+    }
+  }
 
-  status_t Touch(int touchpad,
-                 float x, float y, float pressure) override {
+  status_t Attach() {
+    if (service_ != nullptr) {
+      return ALREADY_EXISTS;
+    }
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == nullptr) {
+      ALOGE("no service manager");
+      return NO_INIT;
+    }
+    sp<IVirtualTouchpadService> service =
+        interface_cast<IVirtualTouchpadService>(
+            sm->getService(IVirtualTouchpadService::SERVICE_NAME()));
+    if (service == nullptr) {
+      ALOGE("failed to get service");
+      return NAME_NOT_FOUND;
+    }
+    service_ = service;
+    return service_->attach().transactionError();
+  }
+
+  status_t Detach() {
+    if (service_ == nullptr) {
+      return NO_INIT;
+    }
+    status_t status = service_->detach().transactionError();
+    service_ = nullptr;
+    return status;
+  }
+
+  status_t Touch(int touchpad, float x, float y, float pressure) override {
     if (service_ == nullptr) {
       return NO_INIT;
     }
     return service_->touch(touchpad, x, y, pressure).transactionError();
   }
+
   status_t ButtonState(int touchpad, int buttons) override {
     if (service_ == nullptr) {
       return NO_INIT;
@@ -28,6 +60,12 @@
     return service_->buttonState(touchpad, buttons).transactionError();
   }
 
+  void dumpInternal(String8& result) override {
+    result.append("[virtual touchpad]\n");
+    result.appendFormat("connected = %s\n\n",
+                        service_ != nullptr ? "true" : "false");
+  }
+
  private:
   sp<IVirtualTouchpadService> service_;
 };
@@ -35,18 +73,7 @@
 }  // anonymous namespace
 
 sp<VirtualTouchpad> VirtualTouchpadClient::Create() {
-  sp<IServiceManager> sm = defaultServiceManager();
-  if (sm == nullptr) {
-    ALOGE("no service manager");
-    return sp<VirtualTouchpad>();
-  }
-  sp<IVirtualTouchpadService> service = interface_cast<IVirtualTouchpadService>(
-      sm->getService(IVirtualTouchpadService::SERVICE_NAME()));
-  if (service == nullptr) {
-    ALOGE("failed to get service");
-    return sp<VirtualTouchpad>();
-  }
-  return new VirtualTouchpadClientImpl(service);
+  return new VirtualTouchpadClientImpl();
 }
 
 }  // namespace dvr
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
index f25a2ad..92193d3 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
@@ -18,7 +18,7 @@
 // use it to look up device configuration, so it must be unique. Vendor and
 // product values must be 0 to indicate an internal device and prevent a
 // similar lookup that could conflict with a physical device.
-static const char* const kDeviceName = "vr window manager virtual touchpad";
+static const char* const kDeviceNameFormat = "vr virtual touchpad %d";
 static constexpr int16_t kDeviceBusType = BUS_VIRTUAL;
 static constexpr int16_t kDeviceVendor = 0;
 static constexpr int16_t kDeviceProduct = 0;
@@ -32,100 +32,154 @@
 
 sp<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
   VirtualTouchpadEvdev* const touchpad = new VirtualTouchpadEvdev();
-  const status_t status = touchpad->Initialize();
-  if (status) {
-    ALOGE("initialization failed: %d", status);
-    return sp<VirtualTouchpad>();
-  }
+  touchpad->Reset();
   return sp<VirtualTouchpad>(touchpad);
 }
 
-int VirtualTouchpadEvdev::Initialize() {
-  if (!injector_) {
-    owned_injector_.reset(new EvdevInjector());
-    injector_ = owned_injector_.get();
+void VirtualTouchpadEvdev::Reset() {
+  for (auto& touchpad : touchpad_) {
+    if (touchpad.injector) {
+      touchpad.injector->Close();
+    }
+    touchpad.injector = nullptr;
+    touchpad.owned_injector.reset();
+    touchpad.last_device_x = INT32_MIN;
+    touchpad.last_device_y = INT32_MIN;
+    touchpad.touches = 0;
+    touchpad.last_motion_event_buttons = 0;
   }
-  injector_->ConfigureBegin(kDeviceName, kDeviceBusType, kDeviceVendor,
-                            kDeviceProduct, kDeviceVersion);
-  injector_->ConfigureInputProperty(INPUT_PROP_DIRECT);
-  injector_->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
-  injector_->ConfigureAbsSlots(kSlots);
-  injector_->ConfigureKey(BTN_TOUCH);
-  injector_->ConfigureKey(BTN_BACK);
-  injector_->ConfigureEnd();
-  return injector_->GetError();
 }
 
-int VirtualTouchpadEvdev::Touch(int touchpad, float x, float y,
+status_t VirtualTouchpadEvdev::Attach() {
+  status_t status = OK;
+  for (int i = 0; i < kTouchpads; ++i) {
+    Touchpad& touchpad = touchpad_[i];
+    if (!touchpad.injector) {
+      touchpad.owned_injector.reset(new EvdevInjector());
+      touchpad.injector = touchpad.owned_injector.get();
+    }
+    String8 DeviceName;
+    DeviceName.appendFormat(kDeviceNameFormat, i);
+    touchpad.injector->ConfigureBegin(DeviceName, kDeviceBusType,
+                                      kDeviceVendor, kDeviceProduct,
+                                      kDeviceVersion);
+    touchpad.injector->ConfigureInputProperty(INPUT_PROP_DIRECT);
+    touchpad.injector->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
+    touchpad.injector->ConfigureAbsSlots(kSlots);
+    touchpad.injector->ConfigureKey(BTN_TOUCH);
+    touchpad.injector->ConfigureKey(BTN_BACK);
+    touchpad.injector->ConfigureEnd();
+    if (const status_t configuration_status =  touchpad.injector->GetError()) {
+      status = configuration_status;
+    }
+  }
+  return status;
+}
+
+status_t VirtualTouchpadEvdev::Detach() {
+  Reset();
+  return OK;
+}
+
+int VirtualTouchpadEvdev::Touch(int touchpad_id, float x, float y,
                                 float pressure) {
-  (void)touchpad; // TODO(b/35992608) Support multiple virtual touchpad devices.
+  if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
+    return EINVAL;
+  }
   if ((x < 0.0f) || (x >= 1.0f) || (y < 0.0f) || (y >= 1.0f)) {
     return EINVAL;
   }
   int32_t device_x = x * kWidth;
   int32_t device_y = y * kHeight;
-  touches_ = ((touches_ & 1) << 1) | (pressure > 0);
+  Touchpad& touchpad = touchpad_[touchpad_id];
+  touchpad.touches = ((touchpad.touches & 1) << 1) | (pressure > 0);
   ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d", x, y, pressure, device_x,
-        device_y, touches_);
+        device_y, touchpad.touches);
 
-  if (!injector_) {
+  if (!touchpad.injector) {
     return EvdevInjector::ERROR_SEQUENCING;
   }
-  injector_->ResetError();
-  switch (touches_) {
+  touchpad.injector->ResetError();
+  switch (touchpad.touches) {
     case 0b00:  // Hover continues.
-      if (device_x != last_device_x_ || device_y != last_device_y_) {
-        injector_->SendMultiTouchXY(0, 0, device_x, device_y);
-        injector_->SendSynReport();
+      if (device_x != touchpad.last_device_x ||
+          device_y != touchpad.last_device_y) {
+        touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
+        touchpad.injector->SendSynReport();
       }
       break;
     case 0b01:  // Touch begins.
       // Press.
-      injector_->SendMultiTouchXY(0, 0, device_x, device_y);
-      injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS);
-      injector_->SendSynReport();
+      touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
+      touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS);
+      touchpad.injector->SendSynReport();
       break;
     case 0b10:  // Touch ends.
-      injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
-      injector_->SendMultiTouchLift(0);
-      injector_->SendSynReport();
+      touchpad.injector->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
+      touchpad.injector->SendMultiTouchLift(0);
+      touchpad.injector->SendSynReport();
       break;
     case 0b11:  // Touch continues.
-      if (device_x != last_device_x_ || device_y != last_device_y_) {
-        injector_->SendMultiTouchXY(0, 0, device_x, device_y);
-        injector_->SendSynReport();
+      if (device_x != touchpad.last_device_x ||
+          device_y != touchpad.last_device_y) {
+        touchpad.injector->SendMultiTouchXY(0, 0, device_x, device_y);
+        touchpad.injector->SendSynReport();
       }
       break;
   }
-  last_device_x_ = device_x;
-  last_device_y_ = device_y;
+  touchpad.last_device_x = device_x;
+  touchpad.last_device_y = device_y;
 
-  return injector_->GetError();
+  return touchpad.injector->GetError();
 }
 
-int VirtualTouchpadEvdev::ButtonState(int touchpad, int buttons) {
-  (void)touchpad; // TODO(b/35992608) Support multiple virtual touchpad devices.
-  const int changes = last_motion_event_buttons_ ^ buttons;
+int VirtualTouchpadEvdev::ButtonState(int touchpad_id, int buttons) {
+  if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
+    return EINVAL;
+  }
+  Touchpad& touchpad = touchpad_[touchpad_id];
+  const int changes = touchpad.last_motion_event_buttons ^ buttons;
   if (!changes) {
     return 0;
   }
   if (buttons & ~AMOTION_EVENT_BUTTON_BACK) {
     return ENOTSUP;
   }
-  ALOGV("change %X from %X to %X", changes, last_motion_event_buttons_,
+  ALOGV("change %X from %X to %X", changes, touchpad.last_motion_event_buttons,
         buttons);
 
-  if (!injector_) {
+  if (!touchpad.injector) {
     return EvdevInjector::ERROR_SEQUENCING;
   }
-  injector_->ResetError();
+  touchpad.injector->ResetError();
   if (changes & AMOTION_EVENT_BUTTON_BACK) {
-    injector_->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
-                                     ? EvdevInjector::KEY_PRESS
-                                     : EvdevInjector::KEY_RELEASE);
+    touchpad.injector->SendKey(BTN_BACK, (buttons & AMOTION_EVENT_BUTTON_BACK)
+                                             ? EvdevInjector::KEY_PRESS
+                                             : EvdevInjector::KEY_RELEASE);
+    touchpad.injector->SendSynReport();
   }
-  last_motion_event_buttons_ = buttons;
-  return injector_->GetError();
+  touchpad.last_motion_event_buttons = buttons;
+  return touchpad.injector->GetError();
+}
+
+void VirtualTouchpadEvdev::dumpInternal(String8& result) {
+  for (int i = 0; i < kTouchpads; ++i) {
+    const auto& touchpad = touchpad_[i];
+    result.appendFormat("[virtual touchpad %d]\n", i);
+    if (!touchpad.injector) {
+      result.append("injector = none\n");
+      return;
+    }
+    result.appendFormat("injector = %s\n",
+                        touchpad.owned_injector ? "normal" : "test");
+    result.appendFormat("touches = %d\n", touchpad.touches);
+    result.appendFormat("last_position = (%" PRId32 ", %" PRId32 ")\n",
+                        touchpad.last_device_x, touchpad.last_device_y);
+    result.appendFormat("last_buttons = 0x%" PRIX32 "\n",
+                        touchpad.last_motion_event_buttons);
+    touchpad.injector->dumpInternal(result);
+    result.append("\n");
+  }
 }
 
 }  // namespace dvr
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
index ec8006b..dbaca9a 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
@@ -3,8 +3,8 @@
 
 #include <memory>
 
-#include "VirtualTouchpad.h"
 #include "EvdevInjector.h"
+#include "VirtualTouchpad.h"
 
 namespace android {
 namespace dvr {
@@ -18,36 +18,46 @@
   static sp<VirtualTouchpad> Create();
 
   // VirtualTouchpad implementation:
+  status_t Attach() override;
+  status_t Detach() override;
   status_t Touch(int touchpad, float x, float y, float pressure) override;
   status_t ButtonState(int touchpad, int buttons) override;
+  void dumpInternal(String8& result) override;
 
  protected:
+  static constexpr int kTouchpads = 2;
+
   VirtualTouchpadEvdev() {}
   ~VirtualTouchpadEvdev() override {}
-  status_t Initialize();
+  void Reset();
 
-  // Must be called only between construction and Initialize().
-  inline void SetEvdevInjectorForTesting(EvdevInjector* injector) {
-    injector_ = injector;
+  // Must be called only between construction (or Detach()) and Attach().
+  inline void SetEvdevInjectorForTesting(int touchpad,
+                                         EvdevInjector* injector) {
+    touchpad_[touchpad].injector = injector;
   }
 
  private:
-  // Except for testing, the |EvdevInjector| used to inject evdev events.
-  std::unique_ptr<EvdevInjector> owned_injector_;
+  // Per-touchpad state.
+  struct Touchpad {
+    // Except for testing, the |EvdevInjector| used to inject evdev events.
+    std::unique_ptr<EvdevInjector> owned_injector;
 
-  // Active pointer to |owned_injector_| or to a testing injector.
-  EvdevInjector* injector_ = nullptr;
+    // Active pointer to |owned_injector_| or to a testing injector.
+    EvdevInjector* injector = nullptr;
 
-  // Previous (x, y) position in device space, to suppress redundant events.
-  int32_t last_device_x_ = INT32_MIN;
-  int32_t last_device_y_ = INT32_MIN;
+    // Previous (x, y) position in device space, to suppress redundant events.
+    int32_t last_device_x;
+    int32_t last_device_y;
 
-  // Records current touch state (0=up 1=down) in bit 0, and previous state
-  // in bit 1, to track transitions.
-  int touches_ = 0;
+    // Records current touch state (0=up 1=down) in bit 0, and previous state
+    // in bit 1, to track transitions.
+    int touches;
 
-  // Previous injected button state, to detect changes.
-  int32_t last_motion_event_buttons_ = 0;
+    // Previous injected button state, to detect changes.
+    int32_t last_motion_event_buttons;
+  };
+  Touchpad touchpad_[kTouchpads];
 
   VirtualTouchpadEvdev(const VirtualTouchpadEvdev&) = delete;
   void operator=(const VirtualTouchpadEvdev&) = delete;
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
index a1f281c..2e2f622 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -1,22 +1,136 @@
 #include "VirtualTouchpadService.h"
 
+#include <inttypes.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
 #include <binder/Status.h>
+#include <cutils/log.h>
 #include <linux/input.h>
-#include <log/log.h>
+#include <private/android_filesystem_config.h>
 #include <utils/Errors.h>
 
 namespace android {
 namespace dvr {
 
-binder::Status VirtualTouchpadService::touch(int touchpad,
-                                             float x, float y, float pressure) {
-  const status_t error = touchpad_->Touch(touchpad, x, y, pressure);
-  return error ? binder::Status::fromStatusT(error) : binder::Status::ok();
+namespace {
+const String16 kDumpPermission("android.permission.DUMP");
+const String16 kTouchPermission("android.permission.RESTRICTED_VR_ACCESS");
+}  // anonymous namespace
+
+VirtualTouchpadService::~VirtualTouchpadService() {
+  if (client_pid_) {
+    client_pid_ = 0;
+    touchpad_->Detach();
+  }
+}
+
+binder::Status VirtualTouchpadService::attach() {
+  pid_t pid;
+  if (!CheckTouchPermission(&pid)) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  if (client_pid_ == pid) {
+    // The same client has called attach() twice with no intervening detach().
+    // This indicates a problem with the client, so return an error.
+    // However, since the client is already attached, any touchpad actions
+    // it takes will still work.
+    ALOGE("pid=%ld attached twice", static_cast<long>(pid));
+    return binder::Status::fromStatusT(ALREADY_EXISTS);
+  }
+  if (client_pid_ != 0) {
+    // Attach while another client is attached. This can happen if the client
+    // dies without cleaning up after itself, so move ownership to the current
+    // caller. If two actual clients have connected, the problem will be
+    // reported when the previous client performs any touchpad action.
+    ALOGE("pid=%ld replaces %ld", static_cast<long>(pid),
+          static_cast<long>(client_pid_));
+  }
+  client_pid_ = pid;
+  if (const status_t error = touchpad_->Attach()) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
+}
+
+binder::Status VirtualTouchpadService::detach() {
+  if (!CheckPermissions()) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  client_pid_ = 0;
+  if (const status_t error = touchpad_->Detach()) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
+}
+
+binder::Status VirtualTouchpadService::touch(int touchpad, float x, float y,
+                                             float pressure) {
+  if (!CheckPermissions()) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  if (const status_t error = touchpad_->Touch(touchpad, x, y, pressure)) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
 }
 
 binder::Status VirtualTouchpadService::buttonState(int touchpad, int buttons) {
-  const status_t error = touchpad_->ButtonState(touchpad, buttons);
-  return error ? binder::Status::fromStatusT(error) : binder::Status::ok();
+  if (!CheckPermissions()) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  if (const status_t error = touchpad_->ButtonState(touchpad, buttons)) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
+}
+
+status_t VirtualTouchpadService::dump(
+    int fd, const Vector<String16>& args[[gnu::unused]]) {
+  String8 result;
+  const android::IPCThreadState* ipc = android::IPCThreadState::self();
+  const pid_t pid = ipc->getCallingPid();
+  const uid_t uid = ipc->getCallingUid();
+  if ((uid != AID_SHELL) &&
+      !PermissionCache::checkPermission(kDumpPermission, pid, uid)) {
+    result.appendFormat("Permission denial: can't dump " LOG_TAG
+                        " from pid=%ld, uid=%ld\n",
+                        static_cast<long>(pid), static_cast<long>(uid));
+  } else {
+    result.appendFormat("[service]\nclient_pid = %ld\n\n",
+                        static_cast<long>(client_pid_));
+    touchpad_->dumpInternal(result);
+  }
+  write(fd, result.string(), result.size());
+  return OK;
+}
+
+bool VirtualTouchpadService::CheckPermissions() {
+  pid_t pid;
+  if (!CheckTouchPermission(&pid)) {
+    return false;
+  }
+  if (client_pid_ != pid) {
+    ALOGE("pid=%ld is not owner", static_cast<long>(pid));
+    return false;
+  }
+  return true;
+}
+
+bool VirtualTouchpadService::CheckTouchPermission(pid_t* out_pid) {
+  const android::IPCThreadState* ipc = android::IPCThreadState::self();
+  *out_pid = ipc->getCallingPid();
+#ifdef SELINUX_ACCESS_CONTROL
+  return true;
+#else
+  const uid_t uid = ipc->getCallingUid();
+  const bool permission = PermissionCache::checkPermission(kTouchPermission, *out_pid, uid);
+  if (!permission) {
+    ALOGE("permission denied to pid=%ld uid=%ld", static_cast<long>(*out_pid),
+          static_cast<long>(uid));
+  }
+  return permission;
+#endif
 }
 
 }  // namespace dvr
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.h b/services/vr/virtual_touchpad/VirtualTouchpadService.h
index 9b880b2..194d787 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadService.h
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.h
@@ -14,17 +14,28 @@
 class VirtualTouchpadService : public BnVirtualTouchpadService {
  public:
   VirtualTouchpadService(sp<VirtualTouchpad> touchpad)
-      : touchpad_(touchpad) {}
-  ~VirtualTouchpadService() override {}
+      : touchpad_(touchpad), client_pid_(0) {}
+  ~VirtualTouchpadService() override;
 
  protected:
   // Implements IVirtualTouchpadService.
+  binder::Status attach() override;
+  binder::Status detach() override;
   binder::Status touch(int touchpad, float x, float y, float pressure) override;
   binder::Status buttonState(int touchpad, int buttons) override;
 
+  // Implements BBinder::dump().
+  status_t dump(int fd, const Vector<String16>& args) override;
+
  private:
+  bool CheckPermissions();
+  bool CheckTouchPermission(pid_t* out_pid);
+
   sp<VirtualTouchpad> touchpad_;
 
+  // Only one client at a time can use the virtual touchpad.
+  pid_t client_pid_;
+
   VirtualTouchpadService(const VirtualTouchpadService&) = delete;
   void operator=(const VirtualTouchpadService&) = delete;
 };
diff --git a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
index 496c5e2..9cfb186 100644
--- a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
+++ b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
@@ -6,6 +6,16 @@
   const String SERVICE_NAME = "virtual_touchpad";
 
   /**
+   * Initialize the virtual touchpad.
+   */
+  void attach() = 0;
+
+  /**
+   * Shut down the virtual touchpad.
+   */
+  void detach() = 1;
+
+  /**
    * Generate a simulated touch event.
    *
    * @param touchpad Selects touchpad.
@@ -15,7 +25,7 @@
    *
    * Position values in the range [0.0, 1.0) map to the screen.
    */
-  void touch(int touchpad, float x, float y, float pressure);
+  void touch(int touchpad, float x, float y, float pressure) = 2;
 
   /**
    * Generate a simulated touchpad button state event.
@@ -23,5 +33,5 @@
    * @param touchpad Selects touchpad.
    * @param buttons A union of MotionEvent BUTTON_* values.
    */
-  void buttonState(int touchpad, int buttons);
+  void buttonState(int touchpad, int buttons) = 3;
 }
diff --git a/services/vr/virtual_touchpad/include/VirtualTouchpad.h b/services/vr/virtual_touchpad/include/VirtualTouchpad.h
index d24d121..b1ee700 100644
--- a/services/vr/virtual_touchpad/include/VirtualTouchpad.h
+++ b/services/vr/virtual_touchpad/include/VirtualTouchpad.h
@@ -3,6 +3,7 @@
 
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
+#include <utils/String8.h>
 #include <utils/StrongPointer.h>
 
 namespace android {
@@ -21,10 +22,18 @@
   // Implementations should provide this, and hide their constructors.
   // For the user, switching implementations should be as simple as changing
   // the class whose |Create()| is called.
+  // Implementations should be minimial; major resource allocation should
+  // be performed in Attach().
   static sp<VirtualTouchpad> Create() {
     return sp<VirtualTouchpad>();
   }
 
+  // Initialize a virtual touchpad.
+  virtual status_t Attach() = 0;
+
+  // Shut down a virtual touchpad.
+  virtual status_t Detach() = 0;
+
   // Generate a simulated touch event.
   //
   // @param touchpad Touchpad selector index.
@@ -49,6 +58,9 @@
   //
   virtual status_t ButtonState(int touchpad, int buttons) = 0;
 
+  // Report state for 'dumpsys'.
+  virtual void dumpInternal(String8& result) = 0;
+
  protected:
   VirtualTouchpad() {}
   ~VirtualTouchpad() override {}
diff --git a/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h b/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
index dd9c265..471d9e0 100644
--- a/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
+++ b/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
@@ -13,8 +13,11 @@
  public:
   // VirtualTouchpad implementation:
   static sp<VirtualTouchpad> Create();
+  status_t Attach() override;
+  status_t Detach() override;
   status_t Touch(int touchpad, float x, float y, float pressure) override;
   status_t ButtonState(int touchpad, int buttons) override;
+  void dumpInternal(String8& result) override;
 
  protected:
   VirtualTouchpadClient() {}
diff --git a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
index 469a2d0..bc34850 100644
--- a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
+++ b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
@@ -88,18 +88,24 @@
 
 class EvdevInjectorForTesting : public EvdevInjector {
  public:
-  EvdevInjectorForTesting(UInput& uinput) { SetUInputForTesting(&uinput); }
+  EvdevInjectorForTesting() { SetUInputForTesting(&record); }
   const uinput_user_dev* GetUiDev() const { return GetUiDevForTesting(); }
+  UInputRecorder record;
 };
 
 class VirtualTouchpadForTesting : public VirtualTouchpadEvdev {
  public:
-  static sp<VirtualTouchpad> Create(EvdevInjectorForTesting& injector) {
+  static sp<VirtualTouchpad> Create() { return sp<VirtualTouchpad>(New()); }
+  static VirtualTouchpadForTesting* New() {
     VirtualTouchpadForTesting* const touchpad = new VirtualTouchpadForTesting();
-    touchpad->SetEvdevInjectorForTesting(&injector);
-    touchpad->Initialize();
-    return sp<VirtualTouchpad>(touchpad);
+    touchpad->Reset();
+    for (int t = 0; t < kTouchpads; ++t) {
+      touchpad->SetEvdevInjectorForTesting(t, &touchpad->injector[t]);
+    }
+    return touchpad;
   }
+  int GetTouchpadCount() const { return kTouchpads; }
+  EvdevInjectorForTesting injector[kTouchpads];
 };
 
 void DumpDifference(const char* expect, const char* actual) {
@@ -118,129 +124,180 @@
 class VirtualTouchpadTest : public testing::Test {};
 
 TEST_F(VirtualTouchpadTest, Goodness) {
+  sp<VirtualTouchpadForTesting> touchpad(VirtualTouchpadForTesting::New());
   UInputRecorder expect;
-  UInputRecorder record;
-  EvdevInjectorForTesting injector(record);
-  sp<VirtualTouchpad> touchpad(VirtualTouchpadForTesting::Create(injector));
+
+  status_t touch_status = touchpad->Attach();
+  EXPECT_EQ(0, touch_status);
 
   // Check some aspects of uinput_user_dev.
-  const uinput_user_dev* uidev = injector.GetUiDev();
-  for (int i = 0; i < ABS_CNT; ++i) {
-    EXPECT_EQ(0, uidev->absmin[i]);
-    EXPECT_EQ(0, uidev->absfuzz[i]);
-    EXPECT_EQ(0, uidev->absflat[i]);
-    if (i != ABS_MT_POSITION_X && i != ABS_MT_POSITION_Y && i != ABS_MT_SLOT) {
-      EXPECT_EQ(0, uidev->absmax[i]);
+  const uinput_user_dev* uidev;
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    uidev = touchpad->injector[t].GetUiDev();
+    String8 name;
+    name.appendFormat("vr virtual touchpad %d", t);
+    EXPECT_EQ(name, uidev->name);
+    for (int i = 0; i < ABS_CNT; ++i) {
+      EXPECT_EQ(0, uidev->absmin[i]);
+      EXPECT_EQ(0, uidev->absfuzz[i]);
+      EXPECT_EQ(0, uidev->absflat[i]);
+      if (i != ABS_MT_POSITION_X && i != ABS_MT_POSITION_Y &&
+          i != ABS_MT_SLOT) {
+        EXPECT_EQ(0, uidev->absmax[i]);
+      }
     }
   }
   const int32_t width = 1 + uidev->absmax[ABS_MT_POSITION_X];
   const int32_t height = 1 + uidev->absmax[ABS_MT_POSITION_Y];
   const int32_t slots = uidev->absmax[ABS_MT_SLOT];
 
-  // Check the system calls performed by initialization.
-  // From ConfigureBegin():
-  expect.Open();
-  // From ConfigureInputProperty(INPUT_PROP_DIRECT):
-  expect.IoctlSetInt(UI_SET_PROPBIT, INPUT_PROP_DIRECT);
-  // From ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1):
-  expect.IoctlSetInt(UI_SET_EVBIT, EV_ABS);
-  expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_X);
-  expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_Y);
-  // From ConfigureAbsSlots(kSlots):
-  expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_SLOT);
-  // From ConfigureKey(BTN_TOUCH):
-  expect.IoctlSetInt(UI_SET_EVBIT, EV_KEY);
-  expect.IoctlSetInt(UI_SET_KEYBIT, BTN_TOUCH);
-  // From ConfigureEnd():
-  expect.Write(uidev, sizeof(uinput_user_dev));
-  expect.IoctlVoid(UI_DEV_CREATE);
-  EXPECT_EQ(expect.GetString(), record.GetString());
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    // Check the system calls performed by initialization.
+    expect.Reset();
+    // From ConfigureBegin():
+    expect.Open();
+    // From ConfigureInputProperty(INPUT_PROP_DIRECT):
+    expect.IoctlSetInt(UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+    // From ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1):
+    expect.IoctlSetInt(UI_SET_EVBIT, EV_ABS);
+    expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_X);
+    expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_POSITION_Y);
+    // From ConfigureAbsSlots(kSlots):
+    expect.IoctlSetInt(UI_SET_ABSBIT, ABS_MT_SLOT);
+    // From ConfigureKey(BTN_TOUCH):
+    expect.IoctlSetInt(UI_SET_EVBIT, EV_KEY);
+    expect.IoctlSetInt(UI_SET_KEYBIT, BTN_TOUCH);
+    expect.IoctlSetInt(UI_SET_KEYBIT, BTN_BACK);
+    // From ConfigureEnd():
+    expect.Write(touchpad->injector[t].GetUiDev(), sizeof(uinput_user_dev));
+    expect.IoctlVoid(UI_DEV_CREATE);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
 
   expect.Reset();
-  record.Reset();
-  int touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0, 0, 0);
-  EXPECT_EQ(0, touch_status);
   expect.WriteInputEvent(EV_ABS, ABS_MT_SLOT, 0);
   expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
   expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0);
   expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0);
   expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
-  EXPECT_EQ(expect.GetString(), record.GetString());
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->Touch(t, 0, 0, 0);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
 
   expect.Reset();
-  record.Reset();
-  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 0.75f, 0.5f);
-  EXPECT_EQ(0, touch_status);
   expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
   expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.25f * width);
   expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.75f * height);
   expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_PRESS);
   expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
-  EXPECT_EQ(expect.GetString(), record.GetString());
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->Touch(t, 0.25f, 0.75f, 0.5f);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
 
   expect.Reset();
-  record.Reset();
-  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.0f, 1.0f, 1.0f);
-  EXPECT_EQ(0, touch_status);
   expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
-  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, width);
-  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, height);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, 0.99f * width);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, 0.99f * height);
   expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
-  EXPECT_EQ(expect.GetString(), record.GetString());
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->Touch(t, 0.99f, 0.99f, 0.99f);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
 
   expect.Reset();
-  record.Reset();
-  touch_status =
-      touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, 0.75f, -0.01f);
-  EXPECT_EQ(0, touch_status);
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->Touch(t, 1.0f, 1.0f, 1.0f);
+    EXPECT_EQ(EINVAL, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
+
+  expect.Reset();
   expect.WriteInputEvent(EV_KEY, BTN_TOUCH, EvdevInjector::KEY_RELEASE);
   expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
   expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
-  EXPECT_EQ(expect.GetString(), record.GetString());
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->Touch(t, 0.25f, 0.75f, -0.01f);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
 
   expect.Reset();
-  record.Reset();
-  touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY,
-                                       AMOTION_EVENT_BUTTON_BACK);
-  EXPECT_EQ(0, touch_status);
   expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_PRESS);
   expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
-  EXPECT_EQ(expect.GetString(), record.GetString());
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
 
   expect.Reset();
-  record.Reset();
-  touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY,
-                                       AMOTION_EVENT_BUTTON_BACK);
-  EXPECT_EQ(0, touch_status);
-  EXPECT_EQ(expect.GetString(), record.GetString());
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->ButtonState(t, AMOTION_EVENT_BUTTON_BACK);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
 
   expect.Reset();
-  record.Reset();
-  touch_status = touchpad->ButtonState(VirtualTouchpad::PRIMARY, 0);
-  EXPECT_EQ(0, touch_status);
   expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_RELEASE);
   expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
-  EXPECT_EQ(expect.GetString(), record.GetString());
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+    touch_status = touchpad->ButtonState(t, 0);
+    EXPECT_EQ(0, touch_status);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
 
   expect.Reset();
-  record.Reset();
+  expect.Close();
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    touchpad->injector[t].record.Reset();
+  }
+  touch_status = touchpad->Detach();
+  EXPECT_EQ(0, touch_status);
+  for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
+    SCOPED_TRACE(t);
+    EXPECT_EQ(expect.GetString(), touchpad->injector[t].record.GetString());
+  }
 }
 
 TEST_F(VirtualTouchpadTest, Badness) {
+  sp<VirtualTouchpadForTesting> touchpad(VirtualTouchpadForTesting::New());
   UInputRecorder expect;
-  UInputRecorder record;
-  EvdevInjectorForTesting injector(record);
-  sp<VirtualTouchpad> touchpad(VirtualTouchpadForTesting::Create(injector));
+  UInputRecorder& record = touchpad->injector[VirtualTouchpad::PRIMARY].record;
+
+  status_t touch_status = touchpad->Attach();
+  EXPECT_EQ(0, touch_status);
 
   // Touch off-screen should return an error,
   // and should not result in any system calls.
   expect.Reset();
   record.Reset();
-  status_t touch_status =
-      touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f);
+  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, -0.25f, 0.75f, 1.0f);
   EXPECT_NE(OK, touch_status);
-  touch_status =
-      touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, -0.75f, 1.0f);
+  touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 0.25f, -0.75f, 1.0f);
   EXPECT_NE(OK, touch_status);
   touch_status = touchpad->Touch(VirtualTouchpad::PRIMARY, 1.25f, 0.75f, 1.0f);
   EXPECT_NE(OK, touch_status);
@@ -256,6 +313,10 @@
                                        AMOTION_EVENT_BUTTON_FORWARD);
   EXPECT_NE(OK, touch_status);
   EXPECT_EQ(expect.GetString(), record.GetString());
+
+  // Repeated attach is an error.
+  touch_status = touchpad->Attach();
+  EXPECT_NE(0, touch_status);
 }
 
 }  // namespace dvr
diff --git a/services/vr/vr_window_manager/Android.mk b/services/vr/vr_window_manager/Android.mk
index ddb58e9..59ef63c 100644
--- a/services/vr/vr_window_manager/Android.mk
+++ b/services/vr/vr_window_manager/Android.mk
@@ -17,6 +17,7 @@
 native_src := \
   application.cpp \
   controller_mesh.cpp \
+  display_view.cpp \
   elbow_model.cpp \
   hwc_callback.cpp \
   reticle.cpp \
diff --git a/services/vr/vr_window_manager/application.cpp b/services/vr/vr_window_manager/application.cpp
index 24087c9..eb9f407 100644
--- a/services/vr/vr_window_manager/application.cpp
+++ b/services/vr/vr_window_manager/application.cpp
@@ -224,6 +224,8 @@
     DrawEye(kRightEye, fov_[kRightEye].GetProjectionMatrix(0.1f, 500.0f),
             eye_from_head_[kRightEye], head_matrix);
 
+    OnEndFrame();
+
     dvrPresent(graphics_context_);
   }
 }
@@ -294,10 +296,7 @@
   bool changed = is_visible_ != visible;
   if (changed) {
     is_visible_ = visible;
-    // TODO (alexst): b/36036583 Disable vr_wm visibility until we figure out
-    // why it's always on top. Still make it visible in debug mode.
-    if (debug_mode_)
-      dvrGraphicsSurfaceSetVisible(graphics_context_, is_visible_);
+    dvrGraphicsSurfaceSetVisible(graphics_context_, is_visible_);
     OnVisibilityChanged(is_visible_);
   }
 }
diff --git a/services/vr/vr_window_manager/application.h b/services/vr/vr_window_manager/application.h
index 2c60e0a..6215561 100644
--- a/services/vr/vr_window_manager/application.h
+++ b/services/vr/vr_window_manager/application.h
@@ -57,6 +57,7 @@
   virtual void OnDrawFrame() = 0;
   virtual void DrawEye(EyeType eye, const mat4& perspective,
                        const mat4& eye_matrix, const mat4& head_matrix) = 0;
+  virtual void OnEndFrame() = 0;
 
   void SetVisibility(bool visible);
   virtual void OnVisibilityChanged(bool visible);
diff --git a/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp b/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
index 6a78c98..8b50c01 100644
--- a/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
@@ -102,7 +102,8 @@
 
 HwcLayer* HwcDisplay::GetLayer(Layer id) {
   for (size_t i = 0; i < layers_.size(); ++i)
-    if (layers_[i].info.id == id) return &layers_[i];
+    if (layers_[i].info.id == id)
+      return &layers_[i];
 
   return nullptr;
 }
@@ -219,7 +220,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // VrHwcClient
 
-VrHwc::VrHwc() {}
+VrHwc::VrHwc() { displays_[kDefaultDisplayId].reset(new HwcDisplay()); }
 
 VrHwc::~VrHwc() {}
 
@@ -231,7 +232,6 @@
 }
 
 void VrHwc::enableCallback(bool enable) {
-  std::lock_guard<std::mutex> guard(mutex_);
   if (enable && client_ != nullptr) {
     client_.promote()->onHotplug(kDefaultDisplayId,
                                  IComposerCallback::Connection::CONNECTED);
@@ -247,31 +247,43 @@
   return Error::NONE;
 }
 
-Error VrHwc::destroyVirtualDisplay(Display display) { return Error::NONE; }
+Error VrHwc::destroyVirtualDisplay(Display display) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (display == kDefaultDisplayId || displays_.erase(display) == 0)
+    return Error::BAD_DISPLAY;
+  ComposerView::Frame frame;
+  frame.display_id = display;
+  frame.removed = true;
+  if (observer_)
+    observer_->OnNewFrame(frame);
+  return Error::NONE;
+}
 
 Error VrHwc::createLayer(Display display, Layer* outLayer) {
-  if (display != kDefaultDisplayId) {
-    return Error::BAD_DISPLAY;
-  }
-
   std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* layer = display_.CreateLayer();
+  HwcLayer* layer = display_ptr->CreateLayer();
   *outLayer = layer->info.id;
   return Error::NONE;
 }
 
 Error VrHwc::destroyLayer(Display display, Layer layer) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
   std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr) {
+    return Error::BAD_DISPLAY;
+  }
 
-  return display_.DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER;
+  return display_ptr->DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER;
 }
 
 Error VrHwc::getActiveConfig(Display display, Config* outConfig) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   *outConfig = kDefaultConfigId;
   return Error::NONE;
 }
@@ -291,20 +303,19 @@
 Error VrHwc::getDisplayAttribute(Display display, Config config,
                                  IComposerClient::Attribute attribute,
                                  int32_t* outValue) {
-  if (display != kDefaultDisplayId) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
     return Error::BAD_DISPLAY;
-  }
-
   if (config != kDefaultConfigId) {
     return Error::BAD_CONFIG;
   }
 
   switch (attribute) {
     case IComposerClient::Attribute::WIDTH:
-      *outValue = 1920;
+      *outValue = 1080;
       break;
     case IComposerClient::Attribute::HEIGHT:
-      *outValue = 1080;
+      *outValue = 1920;
       break;
     case IComposerClient::Attribute::VSYNC_PERIOD:
       *outValue = 1000 * 1000 * 1000 / 30;  // 30fps
@@ -321,10 +332,9 @@
 }
 
 Error VrHwc::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
-  if (display != kDefaultDisplayId) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
     return Error::BAD_DISPLAY;
-  }
-
   std::vector<Config> configs(1, kDefaultConfigId);
   *outConfigs = hidl_vec<Config>(configs);
   return Error::NONE;
@@ -337,7 +347,9 @@
 
 Error VrHwc::getDisplayType(Display display,
                             IComposerClient::DisplayType* outType) {
-  if (display != kDefaultDisplayId) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr) {
     *outType = IComposerClient::DisplayType::INVALID;
     return Error::BAD_DISPLAY;
   }
@@ -348,10 +360,10 @@
 
 Error VrHwc::getDozeSupport(Display display, bool* outSupport) {
   *outSupport = false;
-  if (display == kDefaultDisplayId)
-    return Error::NONE;
-  else
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
     return Error::BAD_DISPLAY;
+  return Error::NONE;
 }
 
 Error VrHwc::getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes,
@@ -365,35 +377,41 @@
 }
 
 Error VrHwc::setActiveConfig(Display display, Config config) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
-  if (config != kDefaultConfigId) return Error::BAD_CONFIG;
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
+  if (config != kDefaultConfigId)
+    return Error::BAD_CONFIG;
 
   return Error::NONE;
 }
 
 Error VrHwc::setColorMode(Display display, ColorMode mode) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setPowerMode(Display display, IComposerClient::PowerMode mode) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setColorTransform(Display display, const float* matrix,
                                int32_t hint) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
@@ -401,13 +419,15 @@
                              int32_t acquireFence, int32_t dataspace,
                              const std::vector<hwc_rect_t>& damage) {
   base::unique_fd fence(acquireFence);
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
-  if (target == nullptr) return Error::NONE;
-
   std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  if (!display_.SetClientTarget(target, std::move(fence)))
+  if (target == nullptr)
+    return Error::NONE;
+
+  if (!display_ptr->SetClientTarget(target, std::move(fence)))
     return Error::BAD_PARAMETER;
 
   return Error::NONE;
@@ -416,7 +436,10 @@
 Error VrHwc::setOutputBuffer(Display display, buffer_handle_t buffer,
                              int32_t releaseFence) {
   base::unique_fd fence(releaseFence);
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
   ALOGE("Virtual display support not implemented");
   return Error::UNSUPPORTED;
@@ -427,13 +450,13 @@
     std::vector<IComposerClient::Composition>* outCompositionTypes,
     uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
     std::vector<uint32_t>* outRequestMasks) {
-  if (display != kDefaultDisplayId) {
-    return Error::BAD_DISPLAY;
-  }
-
   std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  display_.GetChangedCompositionTypes(outChangedLayers, outCompositionTypes);
+  display_ptr->GetChangedCompositionTypes(outChangedLayers,
+                                          outCompositionTypes);
   return Error::NONE;
 }
 
@@ -446,18 +469,20 @@
   outLayers->clear();
   outReleaseFences->clear();
 
-  if (display != kDefaultDisplayId) {
-    return Error::BAD_DISPLAY;
-  }
-
-  std::vector<ComposerView::ComposerLayer> frame;
-  std::vector<Layer> last_frame_layers;
   std::lock_guard<std::mutex> guard(mutex_);
-  Error status = display_.GetFrame(&frame);
+  auto display_ptr = FindDisplay(display);
+
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  ComposerView::Frame frame;
+  std::vector<Layer> last_frame_layers;
+  Error status = display_ptr->GetFrame(&frame.layers);
+  frame.display_id = display;
   if (status != Error::NONE)
     return status;
 
-  last_frame_layers = display_.UpdateLastFrameAndGetLastFrameLayers();
+  last_frame_layers = display_ptr->UpdateLastFrameAndGetLastFrameLayers();
 
   base::unique_fd fence;
   if (observer_)
@@ -476,18 +501,23 @@
 
 Error VrHwc::setLayerCursorPosition(Display display, Layer layer, int32_t x,
                                     int32_t y) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerBuffer(Display display, Layer layer,
                             buffer_handle_t buffer, int32_t acquireFence) {
   base::unique_fd fence(acquireFence);
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.buffer = GetBufferFromHandle(buffer);
   hwc_layer->info.fence = new Fence(fence.release());
@@ -497,16 +527,21 @@
 
 Error VrHwc::setLayerSurfaceDamage(Display display, Layer layer,
                                    const std::vector<hwc_rect_t>& damage) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerBlendMode(Display display, Layer layer, int32_t mode) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.blend_mode =
       static_cast<ComposerView::ComposerLayer::BlendMode>(mode);
@@ -516,17 +551,22 @@
 
 Error VrHwc::setLayerColor(Display display, Layer layer,
                            IComposerClient::Color color) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerCompositionType(Display display, Layer layer,
                                      int32_t type) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->composition_type = static_cast<HwcLayer::Composition>(type);
 
@@ -535,17 +575,22 @@
 
 Error VrHwc::setLayerDataspace(Display display, Layer layer,
                                int32_t dataspace) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerDisplayFrame(Display display, Layer layer,
                                   const hwc_rect_t& frame) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.display_frame =
       {frame.left, frame.top, frame.right, frame.bottom};
@@ -554,10 +599,14 @@
 }
 
 Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.alpha = alpha;
 
@@ -566,17 +615,22 @@
 
 Error VrHwc::setLayerSidebandStream(Display display, Layer layer,
                                     buffer_handle_t stream) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerSourceCrop(Display display, Layer layer,
                                 const hwc_frect_t& crop) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.crop = {crop.left, crop.top, crop.right, crop.bottom};
 
@@ -585,23 +639,29 @@
 
 Error VrHwc::setLayerTransform(Display display, Layer layer,
                                int32_t transform) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerVisibleRegion(Display display, Layer layer,
                                    const std::vector<hwc_rect_t>& visible) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
-
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
   return Error::NONE;
 }
 
 Error VrHwc::setLayerZOrder(Display display, Layer layer, uint32_t z) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->z_order = z;
 
@@ -610,10 +670,14 @@
 
 Error VrHwc::setLayerInfo(Display display, Layer layer, uint32_t type,
                           uint32_t appId) {
-  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
 
-  HwcLayer* hwc_layer = display_.GetLayer(layer);
-  if (!hwc_layer) return Error::BAD_LAYER;
+  HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
+  if (!hwc_layer)
+    return Error::BAD_LAYER;
 
   hwc_layer->info.type = type;
   hwc_layer->info.app_id = appId;
@@ -665,6 +729,11 @@
     observer_ = nullptr;
 }
 
+HwcDisplay* VrHwc::FindDisplay(Display display) {
+  auto iter = displays_.find(display);
+  return iter == displays_.end() ? nullptr : iter->second.get();
+}
+
 ComposerView* GetComposerViewFromIComposer(
     hardware::graphics::composer::V2_1::IComposer* composer) {
   return static_cast<VrHwc*>(composer);
diff --git a/services/vr/vr_window_manager/composer/impl/vr_hwc.h b/services/vr/vr_window_manager/composer/impl/vr_hwc.h
index df09687..b869d3e 100644
--- a/services/vr/vr_window_manager/composer/impl/vr_hwc.h
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.h
@@ -24,6 +24,7 @@
 #include <utils/StrongPointer.h>
 
 #include <mutex>
+#include <unordered_map>
 
 using namespace android::hardware::graphics::common::V1_0;
 using namespace android::hardware::graphics::composer::V2_1;
@@ -67,7 +68,14 @@
     uint32_t app_id;
   };
 
-  using Frame = std::vector<ComposerLayer>;
+  struct Frame {
+    Display display_id;
+    // This is set to true to notify the upper layer that the display is
+    // being removed, or left false in the case of a normal frame. The upper
+    // layer tracks display IDs and will handle new ones showing up.
+    bool removed = false;
+    std::vector<ComposerLayer> layers;
+  };
 
   class Observer {
    public:
@@ -231,13 +239,15 @@
   void UnregisterObserver(Observer* observer) override;
 
  private:
+  HwcDisplay* FindDisplay(Display display);
+
   wp<VrComposerClient> client_;
   sp<IComposerCallback> callbacks_;
 
   // Guard access to internal state from binder threads.
   std::mutex mutex_;
 
-  HwcDisplay display_;
+  std::unordered_map<Display, std::unique_ptr<HwcDisplay>> displays_;
 
   Observer* observer_ = nullptr;
 
diff --git a/services/vr/vr_window_manager/display_view.cpp b/services/vr/vr_window_manager/display_view.cpp
new file mode 100644
index 0000000..5f1e73e
--- /dev/null
+++ b/services/vr/vr_window_manager/display_view.cpp
@@ -0,0 +1,432 @@
+#include "display_view.h"
+
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+constexpr float kLayerScaleFactor = 3.0f;
+constexpr unsigned int kVRAppLayerCount = 2;
+constexpr unsigned int kMaximumPendingFrames = 8;
+
+// clang-format off
+const GLfloat kVertices[] = {
+  -1, -1, 0,
+   1, -1, 0,
+  -1, 1, 0,
+   1, 1, 0,
+};
+
+const GLfloat kTextureVertices[] = {
+  0, 1,
+  1, 1,
+  0, 0,
+  1, 0,
+};
+// clang-format on
+
+// Returns true if the given point is inside the given rect.
+bool IsInside(const vec2& pt, const vec2& tl, const vec2& br) {
+  return pt.x() >= tl.x() && pt.x() <= br.x() && pt.y() >= tl.y() &&
+         pt.y() <= br.y();
+}
+
+mat4 GetScalingMatrix(float width, float height) {
+  float xscale = 1, yscale = 1;
+  float ar = width / height;
+  if (ar > 1)
+    yscale = 1.0 / ar;
+  else
+    xscale = ar;
+
+  xscale *= kLayerScaleFactor;
+  yscale *= kLayerScaleFactor;
+
+  return mat4(Eigen::Scaling<float>(xscale, yscale, 1.0));
+}
+
+// Helper function that applies the crop transform to the texture layer and
+// positions (and scales) the texture layer in the appropriate location in the
+// display space.
+mat4 GetLayerTransform(const TextureLayer& texture_layer, float display_width,
+                       float display_height) {
+  // Map from vertex coordinates to [0, 1] coordinates:
+  //  1) Flip y since in vertex coordinates (-1, -1) is at the bottom left and
+  //     in texture coordinates (0, 0) is at the top left.
+  //  2) Translate by (1, 1) to map vertex coordinates to [0, 2] on x and y.
+  //  3) Scale by 1 / 2 to map coordinates to [0, 1] on x  and y.
+  mat4 unit_space(Eigen::AlignedScaling3f(0.5f, 0.5f, 1.0f) *
+                  Eigen::Translation3f(1.0f, 1.0f, 0.0f) *
+                  Eigen::AlignedScaling3f(1.0f, -1.0f, 1.0f));
+
+  mat4 texture_space(Eigen::AlignedScaling3f(
+      texture_layer.texture->width(), texture_layer.texture->height(), 1.0f));
+
+  // 1) Translate the layer to crop the left and top edge.
+  // 2) Scale the layer such that the cropped right and bottom edges map outside
+  //    the exture region.
+  float crop_width = texture_layer.crop.right - texture_layer.crop.left;
+  float crop_height = texture_layer.crop.bottom - texture_layer.crop.top;
+  mat4 texture_crop(Eigen::AlignedScaling3f(
+                        texture_layer.texture->width() / crop_width,
+                        texture_layer.texture->height() / crop_height, 1.0f) *
+                    Eigen::Translation3f(-texture_layer.crop.left,
+                                         -texture_layer.crop.top, 0.0f));
+
+  mat4 display_space(
+      Eigen::AlignedScaling3f(display_width, display_height, 1.0f));
+
+  // 1) Scale the texture to fit the display frame.
+  // 2) Translate the texture in the display frame location.
+  float display_frame_width =
+      texture_layer.display_frame.right - texture_layer.display_frame.left;
+  float display_frame_height =
+      texture_layer.display_frame.bottom - texture_layer.display_frame.top;
+  mat4 display_frame(
+      Eigen::Translation3f(texture_layer.display_frame.left,
+                           texture_layer.display_frame.top, 0.0f) *
+      Eigen::AlignedScaling3f(display_frame_width / display_width,
+                              display_frame_height / display_height, 1.0f));
+
+  mat4 layer_transform = unit_space.inverse() * display_space.inverse() *
+                         display_frame * display_space *
+                         texture_space.inverse() * texture_crop *
+                         texture_space * unit_space;
+  return layer_transform;
+}
+
+// Determine if ths frame should be shown or hidden.
+ViewMode CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
+                                            uint32_t vr_app) {
+  auto& layers = frame.layers();
+
+  // TODO(achaulk): Figure out how to identify the current VR app for 2D app
+  // detection.
+
+  size_t index;
+  // Skip all layers that we don't know about.
+  for (index = 0; index < layers.size(); index++) {
+    if (layers[index].type != 0xFFFFFFFF && layers[index].type != 0)
+      break;
+  }
+
+  if (index == layers.size())
+    return ViewMode::Hidden;
+
+  if (layers[index].type != 1) {
+    // We don't have a VR app layer? Abort.
+    return ViewMode::Hidden;
+  }
+
+  // This is the VR app, ignore it.
+  index++;
+
+  // Now, find a dim layer if it exists.
+  // If it does, ignore any layers behind it for visibility determination.
+  for (size_t i = index; i < layers.size(); i++) {
+    if (layers[i].appid == HwcCallback::HwcLayer::kSurfaceFlingerLayer) {
+      index = i + 1;
+    }
+  }
+
+  // If any non-skipped layers exist now then we show, otherwise hide.
+  for (size_t i = index; i < layers.size(); i++) {
+    if (!layers[i].should_skip_layer())
+      return ViewMode::VR;
+  }
+  return ViewMode::Hidden;
+}
+
+}  // namespace
+
+DisplayView::DisplayView(uint32_t id, int touchpad_id)
+    : id_(id), touchpad_id_(touchpad_id) {
+  translate_ = Eigen::Translation3f(0, 0, -2.5f);
+  ime_translate_ = mat4(Eigen::Translation3f(0.0f, -0.5f, 0.25f));
+  ime_top_left_ = vec2(0, 0);
+  ime_size_ = vec2(0, 0);
+}
+
+DisplayView::~DisplayView() {}
+
+void DisplayView::Recenter(const mat4& initial) {
+  initial_head_matrix_ = initial;
+}
+
+void DisplayView::SetPrograms(ShaderProgram* program,
+                              ShaderProgram* overlay_program) {
+  program_ = program;
+  overlay_program_ = overlay_program;
+}
+
+void DisplayView::DrawEye(EyeType /* eye */, const mat4& perspective,
+                          const mat4& eye_matrix, const mat4& head_matrix,
+                          const vec2& size, float fade_value) {
+  size_ = size;
+  scale_ = GetScalingMatrix(size_.x(), size_.y());
+
+  DrawOverlays(perspective, eye_matrix, head_matrix, fade_value);
+}
+
+void DisplayView::AdvanceFrame() {
+  if (!pending_frames_.empty()) {
+    // Check if we should advance the frame.
+    auto& frame = pending_frames_.front();
+    if (frame.visibility == ViewMode::Hidden ||
+        frame.frame->Finish() == HwcCallback::FrameStatus::kFinished) {
+      current_frame_ = std::move(frame);
+      pending_frames_.pop_front();
+    }
+  }
+}
+
+void DisplayView::OnDrawFrame(SurfaceFlingerView* surface_flinger_view,
+                              bool debug_mode) {
+  textures_.clear();
+  has_ime_ = false;
+
+  if (!visible())
+    return;
+
+  surface_flinger_view->GetTextures(*current_frame_.frame.get(), &textures_,
+                                    &ime_texture_, debug_mode,
+                                    current_frame_.visibility == ViewMode::VR);
+  has_ime_ = ime_texture_.texture != nullptr;
+}
+
+base::unique_fd DisplayView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame,
+                                     bool debug_mode, bool* showing) {
+  ViewMode visibility =
+      CalculateVisibilityFromLayerConfig(*frame.get(), current_vr_app_);
+
+  if (visibility == ViewMode::Hidden && debug_mode)
+    visibility = ViewMode::VR;
+
+  if (frame->layers().empty())
+    current_vr_app_ = 0;
+  else
+    current_vr_app_ = frame->layers().front().appid;
+
+  pending_frames_.emplace_back(std::move(frame), visibility);
+
+  if (pending_frames_.size() > kMaximumPendingFrames) {
+    pending_frames_.pop_front();
+  }
+
+  if (visibility == ViewMode::Hidden &&
+      current_frame_.visibility == ViewMode::Hidden) {
+    // Consume all frames while hidden.
+    while (!pending_frames_.empty())
+      AdvanceFrame();
+  }
+
+  // If we are showing ourselves the main thread is not processing anything,
+  // so give it a kick.
+  if (visibility != ViewMode::Hidden &&
+      current_frame_.visibility == ViewMode::Hidden) {
+    *showing = true;
+  }
+
+  return base::unique_fd(dup(release_fence_.get()));
+}
+
+bool DisplayView::IsHit(const vec3& view_location, const vec3& view_direction,
+                        vec3* hit_location, vec2* hit_location_in_window_coord,
+                        bool test_ime) {
+  mat4 m = initial_head_matrix_ * translate_;
+  if (test_ime)
+    m = m * ime_translate_;
+  mat4 inverse = (m * scale_).inverse();
+  vec4 transformed_loc =
+      inverse * vec4(view_location[0], view_location[1], view_location[2], 1);
+  vec4 transformed_dir = inverse * vec4(view_direction[0], view_direction[1],
+                                        view_direction[2], 0);
+
+  if (transformed_dir.z() >= 0 || transformed_loc.z() <= 0)
+    return false;
+
+  float distance = -transformed_loc.z() / transformed_dir.z();
+  vec4 transformed_hit_loc = transformed_loc + transformed_dir * distance;
+  if (transformed_hit_loc.x() < -1 || transformed_hit_loc.x() > 1)
+    return false;
+  if (transformed_hit_loc.y() < -1 || transformed_hit_loc.y() > 1)
+    return false;
+
+  hit_location_in_window_coord->x() =
+      (1 + transformed_hit_loc.x()) / 2 * size_.x();
+  hit_location_in_window_coord->y() =
+      (1 - transformed_hit_loc.y()) / 2 * size_.y();
+
+  *hit_location = view_location + view_direction * distance;
+  return true;
+}
+
+void DisplayView::DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+                               const mat4& head_matrix, float fade_value) {
+  if (textures_.empty())
+    return;
+
+  program_->Use();
+  mat4 mvp = perspective * eye_matrix * head_matrix;
+  GLint view_projection_location =
+      glGetUniformLocation(program_->GetProgram(), "uViewProjection");
+  glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+  GLint alpha_location = glGetUniformLocation(program_->GetProgram(), "uAlpha");
+
+  GLint tex_location = glGetUniformLocation(program_->GetProgram(), "tex");
+  glUniform1i(tex_location, 0);
+  glActiveTexture(GL_TEXTURE0);
+
+  for (const auto& texture_layer : textures_) {
+    switch (texture_layer.blending) {
+      case HWC2_BLEND_MODE_PREMULTIPLIED:
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+        break;
+      case HWC2_BLEND_MODE_COVERAGE:
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        break;
+      default:
+        break;
+    }
+
+    glUniform1f(alpha_location, fade_value * texture_layer.alpha);
+
+    glBindTexture(GL_TEXTURE_2D, texture_layer.texture->id());
+
+    mat4 layer_transform =
+        GetLayerTransform(texture_layer, size_.x(), size_.y());
+
+    mat4 transform =
+        initial_head_matrix_ * translate_ * scale_ * layer_transform;
+    DrawWithTransform(transform, *program_);
+
+    glDisable(GL_BLEND);
+  }
+
+  if (has_ime_) {
+    ime_top_left_ = vec2(static_cast<float>(ime_texture_.display_frame.left),
+                         static_cast<float>(ime_texture_.display_frame.top));
+    ime_size_ = vec2(static_cast<float>(ime_texture_.display_frame.right -
+                                        ime_texture_.display_frame.left),
+                     static_cast<float>(ime_texture_.display_frame.bottom -
+                                        ime_texture_.display_frame.top));
+
+    DrawDimOverlay(mvp, textures_[0], ime_top_left_, ime_top_left_ + ime_size_);
+
+    DrawIme();
+  }
+}
+
+void DisplayView::UpdateReleaseFence() {
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  EGLSyncKHR sync =
+      eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+  if (sync != EGL_NO_SYNC_KHR) {
+    // Need to flush in order to get the fence FD.
+    glFlush();
+    base::unique_fd fence(eglDupNativeFenceFDANDROID(display, sync));
+    eglDestroySyncKHR(display, sync);
+    release_fence_ = std::move(fence);
+  } else {
+    ALOGE("Failed to create sync fence");
+    release_fence_ = base::unique_fd();
+  }
+}
+
+void DisplayView::DrawIme() {
+  program_->Use();
+  glBindTexture(GL_TEXTURE_2D, ime_texture_.texture->id());
+
+  mat4 layer_transform = GetLayerTransform(ime_texture_, size_.x(), size_.y());
+
+  mat4 transform = initial_head_matrix_ * translate_ * ime_translate_ * scale_ *
+                   layer_transform;
+
+  DrawWithTransform(transform, *program_);
+}
+
+void DisplayView::DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
+                                 const vec2& top_left,
+                                 const vec2& bottom_right) {
+  overlay_program_->Use();
+  glUniformMatrix4fv(
+      glGetUniformLocation(overlay_program_->GetProgram(), "uViewProjection"),
+      1, 0, mvp.data());
+  glUniform4f(glGetUniformLocation(overlay_program_->GetProgram(), "uCoords"),
+              top_left.x() / size_.x(), top_left.y() / size_.y(),
+              bottom_right.x() / size_.x(), bottom_right.y() / size_.y());
+  glEnable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  mat4 layer_transform = GetLayerTransform(layer, size_.x(), size_.y());
+
+  mat4 transform = initial_head_matrix_ * translate_ * scale_ * layer_transform;
+  DrawWithTransform(transform, *overlay_program_);
+  glDisable(GL_BLEND);
+}
+
+void DisplayView::DrawWithTransform(const mat4& transform,
+                                    const ShaderProgram& program) {
+  GLint transform_location =
+      glGetUniformLocation(program.GetProgram(), "uTransform");
+  glUniformMatrix4fv(transform_location, 1, 0, transform.data());
+
+  glEnableVertexAttribArray(0);
+  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, kVertices);
+  glEnableVertexAttribArray(1);
+  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, kTextureVertices);
+  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+bool DisplayView::UpdateHitInfo(const vec3& view_location,
+                                const vec3& view_direction,
+                                vec3* hit_location) {
+  bool is_hit = false;
+  if (has_ime_) {
+    // This will set allow_input_ and hit_location_in_window_coord_.
+    is_hit = IsImeHit(view_location, view_direction, hit_location);
+  } else {
+    is_hit = IsHit(view_location, view_direction, hit_location,
+                   &hit_location_in_window_coord_, false);
+    allow_input_ = is_hit;
+  }
+  return is_hit;
+}
+
+bool DisplayView::IsImeHit(const vec3& view_location,
+                           const vec3& view_direction, vec3* hit_location) {
+  // First, check if the IME window is hit.
+  bool is_hit = IsHit(view_location, view_direction, hit_location,
+                      &hit_location_in_window_coord_, true);
+  if (is_hit) {
+    // If it is, check if the window coordinate is in the IME region;
+    // if so then we are done.
+    if (IsInside(hit_location_in_window_coord_, ime_top_left_,
+                 ime_top_left_ + ime_size_)) {
+      allow_input_ = true;
+      return true;
+    }
+  }
+
+  allow_input_ = false;
+  // Check if we have hit the main window.
+  is_hit = IsHit(view_location, view_direction, hit_location,
+                 &hit_location_in_window_coord_, false);
+  if (is_hit) {
+    // Only allow input if we are not hitting the region hidden by the IME.
+    // Allowing input here would cause clicks on the main window to actually
+    // be clicks on the IME.
+    if (!IsInside(hit_location_in_window_coord_, ime_top_left_,
+                  ime_top_left_ + ime_size_)) {
+      allow_input_ = true;
+    }
+  }
+  return is_hit;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/display_view.h b/services/vr/vr_window_manager/display_view.h
new file mode 100644
index 0000000..0a27781
--- /dev/null
+++ b/services/vr/vr_window_manager/display_view.h
@@ -0,0 +1,107 @@
+#ifndef VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
+#define VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
+
+#include <private/dvr/graphics/mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+
+#include "hwc_callback.h"
+#include "surface_flinger_view.h"
+
+namespace android {
+namespace dvr {
+
+enum class ViewMode {
+  Hidden,
+  VR,
+  App,
+};
+
+class DisplayView {
+ public:
+  DisplayView(uint32_t id, int touchpad_id);
+  ~DisplayView();
+
+  // Calls to these 3 functions must be synchronized.
+  base::unique_fd OnFrame(std::unique_ptr<HwcCallback::Frame> frame,
+                          bool debug_mode, bool* showing);
+  void AdvanceFrame();
+  void UpdateReleaseFence();
+
+  void OnDrawFrame(SurfaceFlingerView* surface_flinger_view, bool debug_mode);
+  void DrawEye(EyeType eye, const mat4& perspective, const mat4& eye_matrix,
+               const mat4& head_matrix, const vec2& size, float fade_value);
+
+  void Recenter(const mat4& initial);
+
+  bool UpdateHitInfo(const vec3& view_location, const vec3& view_direction,
+                     vec3* hit_location);
+
+  void SetPrograms(ShaderProgram* program, ShaderProgram* overlay_program);
+
+  bool visible() const { return current_frame_.visibility != ViewMode::Hidden; }
+  bool allow_input() const { return allow_input_; }
+  const vec2& hit_location() const { return hit_location_in_window_coord_; }
+  uint32_t id() const { return id_; }
+  int touchpad_id() const { return touchpad_id_; }
+
+ private:
+  bool IsHit(const vec3& view_location, const vec3& view_direction,
+             vec3* hit_location, vec2* hit_location_in_window_coord,
+             bool test_ime);
+  bool IsImeHit(const vec3& view_location, const vec3& view_direction,
+                vec3* hit_location);
+  void DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+                    const mat4& head_matrix, float fade_value);
+  void DrawIme();
+  void DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
+                      const vec2& top_left, const vec2& bottom_right);
+  void DrawWithTransform(const mat4& transform, const ShaderProgram& program);
+
+  uint32_t id_;
+  int touchpad_id_;
+
+  uint32_t current_vr_app_;
+
+  ShaderProgram* program_;
+  ShaderProgram* overlay_program_;
+
+  mat4 initial_head_matrix_;
+  mat4 scale_;
+  mat4 translate_;
+  mat4 ime_translate_;
+  vec2 size_;
+
+  std::vector<TextureLayer> textures_;
+  TextureLayer ime_texture_;
+
+  bool allow_input_ = false;
+  vec2 hit_location_in_window_coord_;
+  vec2 ime_top_left_;
+  vec2 ime_size_;
+  bool has_ime_ = false;
+
+  struct PendingFrame {
+    PendingFrame() = default;
+    PendingFrame(std::unique_ptr<HwcCallback::Frame>&& frame,
+                 ViewMode visibility)
+        : frame(std::move(frame)), visibility(visibility) {}
+    PendingFrame(PendingFrame&& r)
+        : frame(std::move(r.frame)), visibility(r.visibility) {}
+
+    void operator=(PendingFrame&& r) {
+      frame.reset(r.frame.release());
+      visibility = r.visibility;
+    }
+
+    std::unique_ptr<HwcCallback::Frame> frame;
+    ViewMode visibility = ViewMode::Hidden;
+  };
+  std::deque<PendingFrame> pending_frames_;
+  PendingFrame current_frame_;
+  base::unique_fd release_fence_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
diff --git a/services/vr/vr_window_manager/hwc_callback.cpp b/services/vr/vr_window_manager/hwc_callback.cpp
index 05ec64a..b755c60 100644
--- a/services/vr/vr_window_manager/hwc_callback.cpp
+++ b/services/vr/vr_window_manager/hwc_callback.cpp
@@ -38,7 +38,8 @@
 HwcCallback::~HwcCallback() {
 }
 
-base::unique_fd HwcCallback::OnNewFrame(const ComposerView::Frame& frame) {
+base::unique_fd HwcCallback::OnNewFrame(const ComposerView::Frame& display_frame) {
+  auto& frame = display_frame.layers;
   std::vector<HwcLayer> hwc_frame(frame.size());
   for (size_t i = 0; i < frame.size(); ++i) {
     hwc_frame[i] = HwcLayer{
@@ -53,12 +54,13 @@
     };
   }
 
-  return client_->OnFrame(
-      std::make_unique<Frame>(std::move(hwc_frame)));
+  return client_->OnFrame(std::make_unique<Frame>(
+      std::move(hwc_frame), display_frame.display_id, display_frame.removed));
 }
 
-HwcCallback::Frame::Frame(std::vector<HwcLayer>&& layers)
-    : layers_(std::move(layers)) {}
+HwcCallback::Frame::Frame(std::vector<HwcLayer>&& layers, uint32_t display_id,
+                          bool removed)
+    : display_id_(display_id), removed_(removed), layers_(std::move(layers)) {}
 
 HwcCallback::FrameStatus HwcCallback::Frame::Finish() {
   if (status_ == FrameStatus::kUnfinished)
diff --git a/services/vr/vr_window_manager/hwc_callback.h b/services/vr/vr_window_manager/hwc_callback.h
index be56856..b8aa51b 100644
--- a/services/vr/vr_window_manager/hwc_callback.h
+++ b/services/vr/vr_window_manager/hwc_callback.h
@@ -81,12 +81,16 @@
 
   class Frame {
   public:
-    Frame(std::vector<HwcLayer>&& layers);
+    Frame(std::vector<HwcLayer>&& layers, uint32_t display_id, bool removed);
 
     FrameStatus Finish();
     const std::vector<HwcLayer>& layers() const { return layers_; }
+    uint32_t display_id() const { return display_id_; }
+    bool removed() const { return removed_; }
 
   private:
+    uint32_t display_id_;
+    bool removed_;
     std::vector<HwcLayer> layers_;
     FrameStatus status_ = FrameStatus::kUnfinished;
   };
diff --git a/services/vr/vr_window_manager/shell_view.cpp b/services/vr/vr_window_manager/shell_view.cpp
index fae1dd9..72a2c26 100644
--- a/services/vr/vr_window_manager/shell_view.cpp
+++ b/services/vr/vr_window_manager/shell_view.cpp
@@ -15,12 +15,6 @@
 
 namespace {
 
-constexpr float kLayerScaleFactor = 4.0f;
-
-constexpr unsigned int kVRAppLayerCount = 2;
-
-constexpr unsigned int kMaximumPendingFrames = 8;
-
 const std::string kVertexShader = SHADER0([]() {
   layout(location = 0) in vec4 aPosition;
   layout(location = 1) in vec4 aTexCoord;
@@ -79,40 +73,6 @@
   void main() { fragColor = vec4(0.8, 0.2, 0.2, 1.0); }
 });
 
-const GLfloat kVertices[] = {
-  -1, -1, 0,
-   1, -1, 0,
-  -1,  1, 0,
-   1,  1, 0,
-};
-
-const GLfloat kTextureVertices[] = {
-  0, 1,
-  1, 1,
-  0, 0,
-  1, 0,
-};
-
-// Returns true if the given point is inside the given rect.
-bool IsInside(const vec2& pt, const vec2& tl, const vec2& br) {
-  return pt.x() >= tl.x() && pt.x() <= br.x() &&
-    pt.y() >= tl.y() && pt.y() <= br.y();
-}
-
-mat4 GetScalingMatrix(float width, float height) {
-  float xscale = 1, yscale = 1;
-  float ar = width / height;
-  if (ar > 1)
-    yscale = 1.0 / ar;
-  else
-    xscale = ar;
-
-  xscale *= kLayerScaleFactor;
-  yscale *= kLayerScaleFactor;
-
-  return mat4(Eigen::Scaling<float>(xscale, yscale, 1.0));
-}
-
 mat4 GetHorizontallyAlignedMatrixFromPose(const Posef& pose) {
   vec3 position = pose.GetPosition();
   quat view_quaternion = pose.GetRotation();
@@ -131,115 +91,16 @@
   m(3, 0) = 0.0f; m(3, 1) = 0.0f; m(3, 2) = 0.0f; m(3, 3) = 1.0f;
   // clang-format on
 
-  return m * Eigen::AngleAxisf(M_PI * 0.5f, vec3::UnitZ());
+  return m;
 }
 
-// Helper function that applies the crop transform to the texture layer and
-// positions (and scales) the texture layer in the appropriate location in the
-// display space.
-mat4 GetLayerTransform(const TextureLayer& texture_layer, float display_width,
-                       float display_height) {
-  // Map from vertex coordinates to [0, 1] coordinates:
-  //  1) Flip y since in vertex coordinates (-1, -1) is at the bottom left and
-  //     in texture coordinates (0, 0) is at the top left.
-  //  2) Translate by (1, 1) to map vertex coordinates to [0, 2] on x and y.
-  //  3) Scale by 1 / 2 to map coordinates to [0, 1] on x  and y.
-  mat4 unit_space(
-      Eigen::AlignedScaling3f(0.5f, 0.5f, 1.0f) *
-      Eigen::Translation3f(1.0f, 1.0f, 0.0f) *
-      Eigen::AlignedScaling3f(1.0f, -1.0f, 1.0f));
-
-  mat4 texture_space(Eigen::AlignedScaling3f(
-      texture_layer.texture->width(), texture_layer.texture->height(), 1.0f));
-
-  // 1) Translate the layer to crop the left and top edge.
-  // 2) Scale the layer such that the cropped right and bottom edges map outside
-  //    the exture region.
-  float crop_width = texture_layer.crop.right - texture_layer.crop.left;
-  float crop_height = texture_layer.crop.bottom - texture_layer.crop.top;
-  mat4 texture_crop(
-      Eigen::AlignedScaling3f(
-          texture_layer.texture->width() / crop_width,
-          texture_layer.texture->height() / crop_height,
-          1.0f) *
-      Eigen::Translation3f(
-          -texture_layer.crop.left, -texture_layer.crop.top, 0.0f));
-
-  mat4 display_space(
-      Eigen::AlignedScaling3f(display_width, display_height, 1.0f));
-
-  // 1) Scale the texture to fit the display frame.
-  // 2) Translate the texture in the display frame location.
-  float display_frame_width = texture_layer.display_frame.right -
-      texture_layer.display_frame.left;
-  float display_frame_height = texture_layer.display_frame.bottom -
-      texture_layer.display_frame.top;
-  mat4 display_frame(
-      Eigen::Translation3f(
-          texture_layer.display_frame.left,
-          texture_layer.display_frame.top,
-          0.0f) *
-      Eigen::AlignedScaling3f(
-          display_frame_width / display_width,
-          display_frame_height / display_height,
-          1.0f));
-
-  mat4 layer_transform = unit_space.inverse() * display_space.inverse() *
-      display_frame * display_space * texture_space.inverse() * texture_crop *
-      texture_space * unit_space;
-  return layer_transform;
+int GetTouchIdForDisplay(uint32_t display) {
+  return display == 1 ? VirtualTouchpad::PRIMARY : VirtualTouchpad::VIRTUAL;
 }
 
-// Determine if ths frame should be shown or hidden.
-ViewMode CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
-                                            uint32_t vr_app) {
-  auto& layers = frame.layers();
-
-  // We assume the first two layers are the VR app. In the case of a 2D app,
-  // there will be the app + at least one system layer so this is still safe.
-  if (layers.size() < kVRAppLayerCount)
-    return ViewMode::Hidden;
-
-  if (vr_app != layers[0].appid || layers[0].appid == 0 ||
-      layers[1].appid != layers[0].appid) {
-    if (layers[1].appid != layers[0].appid && layers[0].appid) {
-      // This might be a 2D app.
-      // If a dim layer exists afterwards it is much more likely that this is
-      // actually an app launch artifact.
-      for (size_t i = 2; i < layers.size(); i++) {
-        if (layers[i].is_extra_layer())
-          return ViewMode::Hidden;
-      }
-      return ViewMode::App;
-    }
-    return ViewMode::Hidden;
-  }
-
-  size_t index = kVRAppLayerCount;
-  // Now, find a dim layer if it exists.
-  // If it does, ignore any layers behind it for visibility determination.
-  for (size_t i = index; i < layers.size(); i++) {
-    if (layers[i].appid == HwcCallback::HwcLayer::kSurfaceFlingerLayer) {
-      index = i + 1;
-    }
-  }
-
-  // If any non-skipped layers exist now then we show, otherwise hide.
-  for (size_t i = index; i < layers.size(); i++) {
-    if (!layers[i].should_skip_layer())
-      return ViewMode::VR;
-  }
-  return ViewMode::Hidden;
-}
-
-
 }  // namespace
 
-ShellView::ShellView() {
-  ime_translate_ = mat4(Eigen::Translation3f(0.0f, -0.5f, 0.25f));
-  ime_top_left_ = vec2(0, 0);
-  ime_size_ = vec2(0, 0);
-}
+ShellView::ShellView() {}
 
 ShellView::~ShellView() {}
 
@@ -248,10 +109,12 @@
   if (ret)
     return ret;
 
-  translate_ = Eigen::Translation3f(0, 0, -2.5f);
-
-  if (!InitializeTouch())
-    ALOGE("Failed to initialize virtual touchpad");
+  virtual_touchpad_ = VirtualTouchpadClient::Create();
+  const status_t touchpad_status = virtual_touchpad_->Attach();
+  if (touchpad_status != OK) {
+    ALOGE("Failed to connect to virtual touchpad");
+    return touchpad_status;
+  }
 
   surface_flinger_view_.reset(new SurfaceFlingerView);
   if (!surface_flinger_view_->Initialize(this))
@@ -282,6 +145,9 @@
   controller_mesh_->SetVertices(kNumControllerMeshVertices,
                                 kControllerMeshVertices);
 
+  for (auto& display : displays_)
+    display->SetPrograms(program_.get(), overlay_program_.get());
+
   initialized_ = true;
 
   return 0;
@@ -298,13 +164,13 @@
 }
 
 void ShellView::EnableDebug(bool debug) {
-  ALOGI("EnableDebug(%d)", (int)debug); // XXX TODO delete
+  ALOGI("EnableDebug(%d)", (int)debug);  // XXX TODO delete
   QueueTask(debug ? MainThreadTask::EnableDebugMode
                   : MainThreadTask::DisableDebugMode);
 }
 
 void ShellView::VrMode(bool mode) {
-  ALOGI("VrMode(%d)", (int)mode); // XXX TODO delete
+  ALOGI("VrMode(%d)", (int)mode);  // XXX TODO delete
   QueueTask(mode ? MainThreadTask::EnteringVrMode
                  : MainThreadTask::ExitingVrMode);
 }
@@ -316,63 +182,114 @@
   result.appendFormat("debug_mode = %s\n\n", debug_mode_ ? "true" : "false");
 }
 
-void ShellView::AdvanceFrame() {
-  if (!pending_frames_.empty()) {
-    // Check if we should advance the frame.
-    auto& frame = pending_frames_.front();
-    if (frame.visibility == ViewMode::Hidden ||
-        frame.frame->Finish() == HwcCallback::FrameStatus::kFinished) {
-      current_frame_ = std::move(frame);
-      pending_frames_.pop_front();
-    }
-  }
-}
-
 void ShellView::OnDrawFrame() {
-  textures_.clear();
-  has_ime_ = false;
+  bool visible = false;
 
   {
-    std::unique_lock<std::mutex> l(pending_frame_mutex_);
-    AdvanceFrame();
-  }
+    std::unique_lock<std::mutex> l(display_frame_mutex_);
 
-  bool visible = current_frame_.visibility != ViewMode::Hidden;
+    // Move any new displays into the list.
+    if (!new_displays_.empty()) {
+      for (auto& display : new_displays_) {
+        display->Recenter(GetHorizontallyAlignedMatrixFromPose(last_pose_));
+        display->SetPrograms(program_.get(), overlay_program_.get());
+        displays_.emplace_back(display.release());
+      }
+      new_displays_.clear();
+    }
+
+    // Remove any old displays from the list now.
+    if (!removed_displays_.empty()) {
+      for (auto& display : removed_displays_) {
+        displays_.erase(std::find_if(
+            displays_.begin(), displays_.end(),
+            [display](auto& ptr) { return display == ptr.get(); }));
+      }
+      removed_displays_.clear();
+    }
+
+    for (auto& display : displays_) {
+      display->AdvanceFrame();
+      visible = visible || display->visible();
+    }
+  }
 
   if (!debug_mode_ && visible != is_visible_) {
-    SetVisibility(current_frame_.visibility != ViewMode::Hidden);
+    SetVisibility(visible);
   }
 
-  if (!debug_mode_ && !visible)
-    return;
-
-  ime_texture_ = TextureLayer();
-
-  surface_flinger_view_->GetTextures(*current_frame_.frame.get(), &textures_,
-                                     &ime_texture_, debug_mode_,
-                                     current_frame_.visibility == ViewMode::VR);
-  has_ime_ = ime_texture_.texture != nullptr;
+  for (auto& display : displays_) {
+    display->OnDrawFrame(surface_flinger_view_.get(), debug_mode_);
+  }
 }
 
-void ShellView::DrawEye(EyeType /* eye */, const mat4& perspective,
+void ShellView::OnEndFrame() {
+  std::unique_lock<std::mutex> l(display_frame_mutex_);
+  for (auto& display : displays_) {
+    display->UpdateReleaseFence();
+  }
+}
+
+DisplayView* ShellView::FindOrCreateDisplay(uint32_t id) {
+  for (auto& display : displays_) {
+    if (display->id() == id) {
+      return display.get();
+    }
+  }
+
+  // It might be pending addition.
+  for (auto& display : new_displays_) {
+    if (display->id() == id) {
+      return display.get();
+    }
+  }
+
+  auto display = new DisplayView(id, GetTouchIdForDisplay(id));
+  new_displays_.emplace_back(display);
+  return display;
+}
+
+base::unique_fd ShellView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame) {
+  std::unique_lock<std::mutex> l(display_frame_mutex_);
+  DisplayView* display = FindOrCreateDisplay(frame->display_id());
+
+  if (frame->removed()) {
+    removed_displays_.push_back(display);
+    return base::unique_fd();
+  }
+
+  bool showing = false;
+
+  base::unique_fd fd(display->OnFrame(std::move(frame), debug_mode_, &showing));
+
+  if (showing)
+    QueueTask(MainThreadTask::Show);
+
+  return fd;
+}
+
+void ShellView::DrawEye(EyeType eye, const mat4& perspective,
                         const mat4& eye_matrix, const mat4& head_matrix) {
-  if (should_recenter_) {
+  if (should_recenter_ && !displays_.empty()) {
     // Position the quad horizontally aligned in the direction the user
     // is facing, effectively taking out head roll.
-    initial_head_matrix_ = GetHorizontallyAlignedMatrixFromPose(last_pose_);
+    displays_[0]->Recenter(GetHorizontallyAlignedMatrixFromPose(last_pose_));
     should_recenter_ = false;
   }
 
   size_ = vec2(surface_flinger_view_->width(), surface_flinger_view_->height());
-  scale_ = GetScalingMatrix(size_.x(), size_.y());
-
-  DrawOverlays(perspective, eye_matrix, head_matrix);
 
   // TODO(alexst): Replicate controller rendering from VR Home.
   // Current approach in the function below is a quick visualization.
   DrawController(perspective, eye_matrix, head_matrix);
 
-  // TODO: Make sure reticle is shown only over visible overlays.
+  for (auto& display : displays_) {
+    if (display->visible()) {
+      display->DrawEye(eye, perspective, eye_matrix, head_matrix, size_,
+                       fade_value_);
+    }
+  }
+
   DrawReticle(perspective, eye_matrix, head_matrix);
 }
 
@@ -383,7 +300,7 @@
 
 bool ShellView::OnClick(bool down) {
   if (down) {
-    if (!is_touching_ && allow_input_) {
+    if (!is_touching_ && active_display_ && active_display_->allow_input()) {
       is_touching_ = true;
     }
   } else {
@@ -393,224 +310,6 @@
   return true;
 }
 
-base::unique_fd ShellView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame) {
-  ViewMode visibility =
-      CalculateVisibilityFromLayerConfig(*frame.get(), current_vr_app_);
-
-  if (visibility == ViewMode::Hidden && debug_mode_)
-    visibility = ViewMode::VR;
-
-  if (frame->layers().empty())
-    current_vr_app_ = 0;
-  else
-    current_vr_app_ = frame->layers().front().appid;
-
-  std::unique_lock<std::mutex> l(pending_frame_mutex_);
-
-  pending_frames_.emplace_back(std::move(frame), visibility);
-
-  if (pending_frames_.size() > kMaximumPendingFrames) {
-    pending_frames_.pop_front();
-  }
-
-  if (visibility == ViewMode::Hidden &&
-      current_frame_.visibility == ViewMode::Hidden) {
-    // Consume all frames while hidden.
-    while (!pending_frames_.empty())
-      AdvanceFrame();
-  }
-
-  // If we are showing ourselves the main thread is not processing anything,
-  // so give it a kick.
-  if (visibility != ViewMode::Hidden &&
-      current_frame_.visibility == ViewMode::Hidden) {
-    QueueTask(MainThreadTask::Show);
-  }
-
-  return base::unique_fd(dup(release_fence_.get()));
-}
-
-bool ShellView::IsHit(const vec3& view_location, const vec3& view_direction,
-                      vec3* hit_location, vec2* hit_location_in_window_coord,
-                      bool test_ime) {
-  mat4 m = initial_head_matrix_ * translate_;
-  if (test_ime)
-    m = m * ime_translate_;
-  mat4 inverse = (m * scale_).inverse();
-  vec4 transformed_loc =
-      inverse * vec4(view_location[0], view_location[1], view_location[2], 1);
-  vec4 transformed_dir = inverse * vec4(view_direction[0], view_direction[1],
-                                        view_direction[2], 0);
-
-  if (transformed_dir.z() >= 0 || transformed_loc.z() <= 0)
-    return false;
-
-  float distance = -transformed_loc.z() / transformed_dir.z();
-  vec4 transformed_hit_loc = transformed_loc + transformed_dir * distance;
-  if (transformed_hit_loc.x() < -1 || transformed_hit_loc.x() > 1)
-    return false;
-  if (transformed_hit_loc.y() < -1 || transformed_hit_loc.y() > 1)
-    return false;
-
-  hit_location_in_window_coord->x() =
-      (1 + transformed_hit_loc.x()) / 2 * size_.x();
-  hit_location_in_window_coord->y() =
-      (1 - transformed_hit_loc.y()) / 2 * size_.y();
-
-  *hit_location = view_location + view_direction * distance;
-  return true;
-}
-
-void ShellView::DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
-                             const mat4& head_matrix) {
-  if (textures_.empty())
-    return;
-
-  program_->Use();
-  mat4 mvp = perspective * eye_matrix * head_matrix;
-  GLint view_projection_location =
-      glGetUniformLocation(program_->GetProgram(), "uViewProjection");
-  glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
-
-  GLint alpha_location =
-      glGetUniformLocation(program_->GetProgram(), "uAlpha");
-
-  GLint tex_location = glGetUniformLocation(program_->GetProgram(), "tex");
-  glUniform1i(tex_location, 0);
-  glActiveTexture(GL_TEXTURE0);
-
-  for (const auto& texture_layer : textures_) {
-    switch (texture_layer.blending) {
-      case HWC2_BLEND_MODE_PREMULTIPLIED:
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-        break;
-      case HWC2_BLEND_MODE_COVERAGE:
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-        break;
-      default:
-        break;
-    }
-
-    glUniform1f(alpha_location, fade_value_ * texture_layer.alpha);
-
-    glBindTexture(GL_TEXTURE_2D, texture_layer.texture->id());
-
-    mat4 layer_transform = GetLayerTransform(texture_layer, size_.x(),
-                                             size_.y());
-
-    mat4 transform = initial_head_matrix_ * translate_ * scale_ *
-        layer_transform;
-    DrawWithTransform(transform, *program_);
-
-    glDisable(GL_BLEND);
-  }
-
-  if (has_ime_) {
-    ime_top_left_ = vec2(static_cast<float>(ime_texture_.display_frame.left),
-                         static_cast<float>(ime_texture_.display_frame.top));
-    ime_size_ = vec2(static_cast<float>(ime_texture_.display_frame.right -
-                                        ime_texture_.display_frame.left),
-                     static_cast<float>(ime_texture_.display_frame.bottom -
-                                        ime_texture_.display_frame.top));
-
-    DrawDimOverlay(mvp, textures_[0], ime_top_left_, ime_top_left_ + ime_size_);
-
-    DrawIme();
-  }
-
-  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-  EGLSyncKHR sync = eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID,
-                                     nullptr);
-  if (sync != EGL_NO_SYNC_KHR) {
-    // Need to flush in order to get the fence FD.
-    glFlush();
-    base::unique_fd fence(eglDupNativeFenceFDANDROID(display, sync));
-    eglDestroySyncKHR(display, sync);
-    UpdateReleaseFence(std::move(fence));
-  } else {
-    ALOGE("Failed to create sync fence");
-    UpdateReleaseFence(base::unique_fd());
-  }
-}
-
-void ShellView::DrawIme() {
-  program_->Use();
-  glBindTexture(GL_TEXTURE_2D, ime_texture_.texture->id());
-
-  mat4 layer_transform = GetLayerTransform(ime_texture_, size_.x(), size_.y());
-
-  mat4 transform = initial_head_matrix_ * translate_ * ime_translate_ * scale_ *
-              layer_transform;
-
-  DrawWithTransform(transform, *program_);
-}
-
-void ShellView::DrawDimOverlay(const mat4& mvp, const TextureLayer& layer, const vec2& top_left,
-                    const vec2& bottom_right) {
-  overlay_program_->Use();
-  glUniformMatrix4fv(
-      glGetUniformLocation(overlay_program_->GetProgram(), "uViewProjection"),
-      1, 0, mvp.data());
-  glUniform4f(glGetUniformLocation(overlay_program_->GetProgram(), "uCoords"),
-              top_left.x() / size_.x(), top_left.y() / size_.y(),
-              bottom_right.x() / size_.x(), bottom_right.y() / size_.y());
-  glEnable(GL_BLEND);
-  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-  mat4 layer_transform =
-      GetLayerTransform(layer, size_.x(), size_.y());
-
-  mat4 transform =
-      initial_head_matrix_ * translate_ * scale_ * layer_transform;
-  DrawWithTransform(transform, *overlay_program_);
-  glDisable(GL_BLEND);
-}
-
-void ShellView::DrawWithTransform(const mat4& transform,
-                                  const ShaderProgram& program) {
-  GLint transform_location =
-      glGetUniformLocation(program.GetProgram(), "uTransform");
-  glUniformMatrix4fv(transform_location, 1, 0, transform.data());
-
-  glEnableVertexAttribArray(0);
-  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, kVertices);
-  glEnableVertexAttribArray(1);
-  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, kTextureVertices);
-  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-}
-
-bool ShellView::IsImeHit(const vec3& view_location, const vec3& view_direction,
-                vec3 *hit_location) {
-  // First, check if the IME window is hit.
-  bool is_hit = IsHit(view_location, view_direction, hit_location,
-                      &hit_location_in_window_coord_, true);
-  if (is_hit) {
-    // If it is, check if the window coordinate is in the IME region;
-    // if so then we are done.
-    if (IsInside(hit_location_in_window_coord_, ime_top_left_,
-                 ime_top_left_ + ime_size_)) {
-      allow_input_ = true;
-      return true;
-    }
-  }
-
-  allow_input_ = false;
-  // Check if we have hit the main window.
-  is_hit = IsHit(view_location, view_direction, hit_location,
-                 &hit_location_in_window_coord_, false);
-  if (is_hit) {
-    // Only allow input if we are not hitting the region hidden by the IME.
-    // Allowing input here would cause clicks on the main window to actually
-    // be clicks on the IME.
-    if (!IsInside(hit_location_in_window_coord_, ime_top_left_,
-                  ime_top_left_ + ime_size_)) {
-      allow_input_ = true;
-    }
-  }
-  return is_hit;
-}
-
 void ShellView::DrawReticle(const mat4& perspective, const mat4& eye_matrix,
                             const mat4& head_matrix) {
   reticle_->Hide();
@@ -649,30 +348,47 @@
     }
   }
 
-  vec3 view_direction = vec3(view_quaternion * vec3(0, 0, -1));
-
   vec3 hit_location;
+  active_display_ =
+      FindActiveDisplay(pointer_location, view_quaternion, &hit_location);
 
-  bool is_hit;
-  if(has_ime_) {
-    // This will set allow_input_ and hit_location_in_window_coord_.
-    is_hit = IsImeHit(pointer_location, view_direction, &hit_location);
-  } else {
-    is_hit = IsHit(pointer_location, view_direction, &hit_location,
-                   &hit_location_in_window_coord_, false);
-    allow_input_ = is_hit;
-  }
-
-  if (is_hit) {
+  if (active_display_) {
     reticle_->ShowAt(
         Eigen::Translation3f(hit_location) * view_quaternion.matrix(),
-        allow_input_ ? vec3(1, 0, 0) : vec3(0, 0, 0));
+        active_display_->allow_input() ? vec3(1, 0, 0) : vec3(0, 0, 0));
     Touch();
   }
 
   reticle_->Draw(perspective, eye_matrix, head_matrix);
 }
 
+DisplayView* ShellView::FindActiveDisplay(const vec3& position,
+                                          const quat& quaternion,
+                                          vec3* hit_location) {
+  vec3 direction = vec3(quaternion * vec3(0, 0, -1));
+  vec3 temp_hit;
+
+  DisplayView* best_display = nullptr;
+  vec3 best_hit;
+
+  auto is_better = [&best_hit, &position](DisplayView*, const vec3& hit) {
+    return (hit - position).squaredNorm() < (best_hit - position).squaredNorm();
+  };
+
+  for (auto& display : displays_) {
+    if (display->UpdateHitInfo(position, direction, &temp_hit)) {
+      if (!best_display || is_better(display.get(), temp_hit)) {
+        best_display = display.get();
+        best_hit = temp_hit;
+      }
+    }
+  }
+
+  if (best_display)
+    *hit_location = best_hit;
+  return best_display;
+}
+
 void ShellView::DrawController(const mat4& perspective, const mat4& eye_matrix,
                                const mat4& head_matrix) {
   if (!shmem_controller_active_)
@@ -702,28 +418,22 @@
   controller_mesh_->Draw();
 }
 
-bool ShellView::InitializeTouch() {
-  virtual_touchpad_ = VirtualTouchpadClient::Create();
-  if (!virtual_touchpad_.get()) {
-    ALOGE("Failed to connect to virtual touchpad");
-    return false;
-  }
-  return true;
-}
-
 void ShellView::Touch() {
   if (!virtual_touchpad_.get()) {
     ALOGE("missing virtual touchpad");
-    // Try to reconnect; useful in development.
-    if (!InitializeTouch()) {
-      return;
-    }
+    return;
   }
 
+  if (!active_display_)
+    return;
+
+  const vec2& hit_location = active_display_->hit_location();
+
+  // Device is portrait, but in landscape when in VR.
+  // Rotate touch input appropriately.
   const android::status_t status = virtual_touchpad_->Touch(
-      VirtualTouchpad::PRIMARY,
-      hit_location_in_window_coord_.x() / size_.x(),
-      hit_location_in_window_coord_.y() / size_.y(),
+      active_display_->touchpad_id(),
+      1.0f - hit_location.y() / size_.y(), hit_location.x() / size_.x(),
       is_touching_ ? 1.0f : 0.0f);
   if (status != OK) {
     ALOGE("touch failed: %d", status);
@@ -733,7 +443,7 @@
 bool ShellView::OnTouchpadButton(bool down, int button) {
   int buttons = touchpad_buttons_;
   if (down) {
-    if (allow_input_) {
+    if (active_display_ && active_display_->allow_input()) {
       buttons |= button;
     }
   } else {
@@ -748,18 +458,16 @@
     return false;
   }
 
+  if (!active_display_)
+    return true;
+
   const android::status_t status = virtual_touchpad_->ButtonState(
-      VirtualTouchpad::PRIMARY, touchpad_buttons_);
+      active_display_->touchpad_id(), touchpad_buttons_);
   if (status != OK) {
     ALOGE("touchpad button failed: %d %d", touchpad_buttons_, status);
   }
   return true;
 }
 
-void ShellView::UpdateReleaseFence(base::unique_fd fence) {
-  std::lock_guard<std::mutex> guard(pending_frame_mutex_);
-  release_fence_ = std::move(fence);
-}
-
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/vr_window_manager/shell_view.h b/services/vr/vr_window_manager/shell_view.h
index c477669..c10bd27 100644
--- a/services/vr/vr_window_manager/shell_view.h
+++ b/services/vr/vr_window_manager/shell_view.h
@@ -8,6 +8,7 @@
 
 #include "VirtualTouchpadClient.h"
 #include "application.h"
+#include "display_view.h"
 #include "reticle.h"
 #include "shell_view_binder_interface.h"
 #include "surface_flinger_view.h"
@@ -15,12 +16,6 @@
 namespace android {
 namespace dvr {
 
-enum class ViewMode {
-  Hidden,
-  VR,
-  App,
-};
-
 class ShellView : public Application,
                   public android::dvr::ShellViewBinderInterface,
                   public HwcCallback::Client {
@@ -41,92 +36,54 @@
  protected:
   void DrawEye(EyeType eye, const mat4& perspective, const mat4& eye_matrix,
                const mat4& head_matrix) override;
+  void OnDrawFrame() override;
+  void OnEndFrame() override;
   void OnVisibilityChanged(bool visible) override;
 
-  void DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
-                    const mat4& head_matrix);
   void DrawReticle(const mat4& perspective, const mat4& eye_matrix,
                    const mat4& head_matrix);
-  void DrawIme();
-  void DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
-                      const vec2& top_left, const vec2& bottom_right);
   void DrawController(const mat4& perspective, const mat4& eye_matrix,
                       const mat4& head_matrix);
 
-  bool IsHit(const vec3& view_location, const vec3& view_direction,
-             vec3* hit_location, vec2* hit_location_in_window_coord,
-             bool test_ime);
-  bool IsImeHit(const vec3& view_location, const vec3& view_direction,
-                vec3 *hit_location);
-  bool InitializeTouch();
   void Touch();
   bool OnTouchpadButton(bool down, int button);
 
-  void OnDrawFrame() override;
-  void DrawWithTransform(const mat4& transform, const ShaderProgram& program);
-
   bool OnClick(bool down);
 
-  void AdvanceFrame();
+  DisplayView* FindActiveDisplay(const vec3& position, const quat& quaternion,
+                                 vec3* hit_location);
 
-  void UpdateReleaseFence(base::unique_fd fence);
 
   // HwcCallback::Client:
   base::unique_fd OnFrame(std::unique_ptr<HwcCallback::Frame> frame) override;
+  DisplayView* FindOrCreateDisplay(uint32_t id);
 
   std::unique_ptr<ShaderProgram> program_;
   std::unique_ptr<ShaderProgram> overlay_program_;
   std::unique_ptr<ShaderProgram> controller_program_;
 
-  uint32_t current_vr_app_;
-
-  // Used to center the scene when the shell becomes visible.
-  bool should_recenter_ = true;
-  mat4 initial_head_matrix_;
-  mat4 scale_;
-  mat4 translate_;
-  mat4 ime_translate_;
-  vec2 size_;
-
   std::unique_ptr<SurfaceFlingerView> surface_flinger_view_;
   std::unique_ptr<Reticle> reticle_;
   sp<VirtualTouchpad> virtual_touchpad_;
-  std::vector<TextureLayer> textures_;
-  TextureLayer ime_texture_;
-
-  bool is_touching_ = false;
-  bool allow_input_ = false;
-  int touchpad_buttons_ = 0;
-  vec2 hit_location_in_window_coord_;
-  vec2 ime_top_left_;
-  vec2 ime_size_;
-  bool has_ime_ = false;
 
   std::unique_ptr<Mesh<vec3, vec3, vec2>> controller_mesh_;
 
-  struct PendingFrame {
-    PendingFrame() = default;
-    PendingFrame(std::unique_ptr<HwcCallback::Frame>&& frame, ViewMode visibility)
-        : frame(std::move(frame)), visibility(visibility) {}
-    PendingFrame(PendingFrame&& r)
-        : frame(std::move(r.frame)), visibility(r.visibility) {}
+  bool is_touching_ = false;
+  int touchpad_buttons_ = 0;
+  vec2 size_;
 
-    void operator=(PendingFrame&& r) {
-      frame.reset(r.frame.release());
-      visibility = r.visibility;
-    }
+  // Used to center the scene when the shell becomes visible.
+  bool should_recenter_ = true;
 
-    std::unique_ptr<HwcCallback::Frame> frame;
-    ViewMode visibility = ViewMode::Hidden;
-  };
-  std::deque<PendingFrame> pending_frames_;
-  std::mutex pending_frame_mutex_;
-  PendingFrame current_frame_;
+  std::mutex display_frame_mutex_;
+
+  std::vector<std::unique_ptr<DisplayView>> displays_;
+  std::vector<std::unique_ptr<DisplayView>> new_displays_;
+  std::vector<DisplayView*> removed_displays_;
+  DisplayView* active_display_ = nullptr;
 
   mat4 controller_translate_;
 
-  base::unique_fd release_fence_;
-
   ShellView(const ShellView&) = delete;
   void operator=(const ShellView&) = delete;
 };
diff --git a/services/vr/vr_window_manager/surface_flinger_view.cpp b/services/vr/vr_window_manager/surface_flinger_view.cpp
index 63bc143..2011967 100644
--- a/services/vr/vr_window_manager/surface_flinger_view.cpp
+++ b/services/vr/vr_window_manager/surface_flinger_view.cpp
@@ -54,9 +54,7 @@
   size_t start = 0;
   // Skip the second layer if it is from the VR app.
   if (!debug && skip_first_layer) {
-    start = 1;
-    if (layers[0].appid && layers[0].appid == layers[1].appid)
-      start = 2;
+    start = 2;
   }
 
   for (size_t i = start; i < layers.size(); ++i) {
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder.cpp b/services/vr/vr_window_manager/vr_window_manager_binder.cpp
index c2138b7..bd3f3ee 100644
--- a/services/vr/vr_window_manager/vr_window_manager_binder.cpp
+++ b/services/vr/vr_window_manager/vr_window_manager_binder.cpp
@@ -16,7 +16,8 @@
 
 namespace {
 const String16 kDumpPermission("android.permission.DUMP");
-const String16 kSendMeControllerInputPermission("TODO");  // TODO(kpschoedel)
+const String16 kSendMeControllerInputPermission(
+    "android.permission.RESTRICTED_VR_ACCESS");
 }  // anonymous namespace
 
 constexpr size_t AshmemControllerDataProvider::kRegionLength;
@@ -136,8 +137,8 @@
     int fd, const Vector<String16>& args [[gnu::unused]]) {
   String8 result;
   const android::IPCThreadState* ipc = android::IPCThreadState::self();
-  const int pid = ipc->getCallingPid();
-  const int uid = ipc->getCallingUid();
+  const pid_t pid = ipc->getCallingPid();
+  const uid_t uid = ipc->getCallingUid();
   if ((uid != AID_SHELL) &&
       !PermissionCache::checkPermission(kDumpPermission, pid, uid)) {
     result.appendFormat("Permission denial: can't dump " LOG_TAG
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
index ba3cf79..3f077a2 100644
--- a/vulkan/Android.bp
+++ b/vulkan/Android.bp
@@ -16,7 +16,10 @@
     name: "libvulkan_headers",
     from: "include",
     to: "",
-    srcs: ["include/vulkan/**/*.h"],
+    srcs: [
+        "include/vulkan/vk_platform.h",
+        "include/vulkan/vulkan.h",
+    ],
     license: "include/vulkan/NOTICE",
 }
 
diff --git a/vulkan/api/platform.api b/vulkan/api/platform.api
index b82cbb4..eb0124d 100644
--- a/vulkan/api/platform.api
+++ b/vulkan/api/platform.api
@@ -50,4 +50,8 @@
 @internal type void* HWND
 @internal type void* HANDLE
 @internal type u32   DWORD
+@internal type u16*  LPCWSTR
 @internal class SECURITY_ATTRIBUTES {}
+
+// VK_USE_PLATFORM_XLIB_XRANDR_EXT
+@internal type u64 RROutput
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index cfeeeef..b4708b0 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -28,7 +28,7 @@
 // API version (major.minor.patch)
 define VERSION_MAJOR 1
 define VERSION_MINOR 0
-define VERSION_PATCH 38
+define VERSION_PATCH 43
 
 // API limits
 define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256
@@ -37,6 +37,9 @@
 define VK_MAX_DESCRIPTION_SIZE          256
 define VK_MAX_MEMORY_TYPES              32
 define VK_MAX_MEMORY_HEAPS              16    /// The maximum number of unique memory heaps, each of which supporting 1 or more memory types.
+define VK_MAX_DEVICE_GROUP_SIZE_KHX     32
+define VK_LUID_SIZE_KHX                 8
+define VK_QUEUE_FAMILY_EXTERNAL_KHX     -2
 
 // API keywords
 define VK_TRUE        1
@@ -70,7 +73,7 @@
 @extension("VK_KHR_xcb_surface") define VK_KHR_XCB_SURFACE_NAME                 "VK_KHR_xcb_surface"
 
 // 7
-@extension("VK_KHR_wayland_surface") define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 5
+@extension("VK_KHR_wayland_surface") define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 6
 @extension("VK_KHR_wayland_surface") define VK_KHR_WAYLAND_SURFACE_NAME         "VK_KHR_wayland_surface"
 
 // 8
@@ -90,7 +93,7 @@
 @extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_NAME             "VK_ANDROID_native_buffer"
 
 // 12
-@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION       4
+@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION       5
 @extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_NAME               "VK_EXT_debug_report"
 
 // 13
@@ -118,7 +121,7 @@
 @extension("VK_AMD_shader_explicit_vertex_parameter") define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter"
 
 // 23
-@extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_SPEC_VERSION       3
+@extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_SPEC_VERSION       4
 @extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_NAME               "VK_EXT_debug_marker"
 
 // 26
@@ -149,6 +152,10 @@
 @extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1
 @extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot"
 
+// 54
+@extension("VK_KHX_multiview") define VK_KHX_MULTIVIEW_SPEC_VERSION 1
+@extension("VK_KHX_multiview") define VK_KHX_MULTIVIEW_EXTENSION_NAME "VK_KHX_multiview"
+
 // 56
 @extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
 @extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities"
@@ -169,21 +176,141 @@
 @extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1
 @extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
 
+// 61
+@extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_SPEC_VERSION 1
+@extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group"
+
 // 62
 @extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1
 @extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags"
 
+// 63
+@extension("VK_NN_vi_surface") define VK_NN_VI_SURFACE_SPEC_VERSION 1
+@extension("VK_NN_vi_surface") define VK_NN_VI_SURFACE_EXTENSION_NAME "VK_NN_vi_surface"
+
+// 64
+@extension("VK_KHR_shader_draw_parameters") define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1
+@extension("VK_KHR_shader_draw_parameters") define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters"
+
+// 65
+@extension("VK_EXT_shader_subgroup_ballot") define VK_EXT_SHADER_SUBGROUP_BALLOT_SPEC_VERSION 1
+@extension("VK_EXT_shader_subgroup_ballot") define VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME "VK_EXT_shader_subgroup_ballot"
+
+// 66
+@extension("VK_EXT_shader_subgroup_vote") define VK_EXT_SHADER_SUBGROUP_VOTE_SPEC_VERSION 1
+@extension("VK_EXT_shader_subgroup_vote") define VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME "VK_EXT_shader_subgroup_vote"
+
+// 70
+@extension("VK_KHR_maintenance1") define VK_KHR_MAINTENANCE1_SPEC_VERSION 1
+@extension("VK_KHR_maintenance1") define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1"
+
+// 71
+@extension("VK_KHX_device_group_creation") define VK_KHX_DEVICE_GROUP_CREATION_SPEC_VERSION 1
+@extension("VK_KHX_device_group_creation") define VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME "VK_KHX_device_group_creation"
+
+// 72
+@extension("VK_KHX_external_memory_capabilities") define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+@extension("VK_KHX_external_memory_capabilities") define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_memory_capabilities"
+
+// 73
+@extension("VK_KHX_external_memory") define VK_KHX_EXTERNAL_MEMORY_SPEC_VERSION 1
+@extension("VK_KHX_external_memory") define VK_KHX_EXTERNAL_MEMORY_EXTENSION_NAME "VK_KHX_external_memory"
+
+// 74
+@extension("VK_KHX_external_memory_win32") define VK_KHX_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
+@extension("VK_KHX_external_memory_win32") define VK_KHX_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_KHX_external_memory_win32"
+
+// 75
+@extension("VK_KHX_external_memory_fd") define VK_KHX_EXTERNAL_MEMORY_FD_SPEC_VERSION 1
+@extension("VK_KHX_external_memory_fd") define VK_KHX_EXTERNAL_MEMORY_FD_EXTENSION_NAME "VK_KHX_external_memory_fd"
+
+// 76
+@extension("VK_KHX_win32_keyed_mutex") define VK_KHX_WIN32_KEYED_MUTEX_SPEC_VERSION 1
+@extension("VK_KHX_win32_keyed_mutex") define VK_KHX_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_KHX_win32_keyed_mutex"
+
+// 77
+@extension("VK_KHX_external_semaphore_capabilities") define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_SPEC_VERSION 1
+@extension("VK_KHX_external_semaphore_capabilities") define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_semaphore_capabilities"
+
+// 78
+@extension("VK_KHX_external_semaphore") define VK_KHX_EXTERNAL_SEMAPHORE_SPEC_VERSION 1
+@extension("VK_KHX_external_semaphore") define VK_KHX_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_KHX_external_semaphore"
+
+// 79
+@extension("VK_KHX_external_semaphore_win32") define VK_KHX_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION 1
+@extension("VK_KHX_external_semaphore_win32") define VK_KHX_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME "VK_KHX_external_semaphore_win32"
+
+// 80
+@extension("VK_KHX_external_semaphore_fd") define VK_KHX_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION 1
+@extension("VK_KHX_external_semaphore_fd") define VK_KHX_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME "VK_KHX_external_semaphore_fd"
+
+// 81
+@extension("VK_KHR_push_descriptor") define VK_KHR_PUSH_DESCRIPTOR_SPEC_VERSION 1
+@extension("VK_KHR_push_descriptor") define VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME "VK_KHR_push_descriptor"
+
 // 85
 @extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
 @extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_NAME "VK_KHR_incremental_present"
 
+// 86
+@extension("VK_KHR_descriptor_update_template") define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1
+@extension("VK_KHR_descriptor_update_template") define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template"
+
 // 87
 @extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
 @extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
 
+// 88
+@extension("VK_NV_clip_space_w_scaling") define VK_NV_CLIP_SPACE_W_SCALING_SPEC_VERSION 1
+@extension("VK_NV_clip_space_w_scaling") define VK_NV_CLIP_SPACE_W_SCALING_EXTENSION_NAME "VK_NV_clip_space_w_scaling"
+
+// 89
+@extension("VK_EXT_direct_mode_display") define VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION 1
+@extension("VK_EXT_direct_mode_display") define VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME "VK_EXT_direct_mode_display"
+
+// 90
+@extension("VK_EXT_acquire_xlib_display") define VK_EXT_ACQUIRE_XLIB_DISPLAY_SPEC_VERSION 1
+@extension("VK_EXT_acquire_xlib_display") define VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_xlib_display"
+
+// 91
+@extension("VK_EXT_display_surface_counter") define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1
+@extension("VK_EXT_display_surface_counter") define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter"
+
+// 92
+@extension("VK_EXT_display_control") define VK_EXT_DISPLAY_CONTROL_SPEC_VERSION 1
+@extension("VK_EXT_display_control") define VK_EXT_DISPLAY_CONTROL_COUNTER_EXTENSION_NAME "VK_EXT_display_control"
+
 // 93
 @extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
-@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_NAME "VK_GOOGLE_display_timing"
+@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing"
+
+// 95
+@extension("VK_NV_sample_mask_override_coverage") define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_SPEC_VERSION 1
+@extension("VK_NV_sample_mask_override_coverage") define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME "VK_NV_sample_mask_override_coverage"
+
+// 96
+@extension("VK_NV_geometry_shader_passthrough") define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_SPEC_VERSION 1
+@extension("VK_NV_geometry_shader_passthrough") define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME "VK_NV_geometry_shader_passthrough"
+
+// 97
+@extension("VK_NV_viewport_array2") define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION 1
+@extension("VK_NV_viewport_array2") define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME "VK_NV_viewport_array2"
+
+// 98
+@extension("VK_NVX_multiview_per_view_attributes") define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION 1
+@extension("VK_NVX_multiview_per_view_attributes") define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME "VK_NVX_multiview_per_view_attributes"
+
+// 99
+@extension("VK_NV_viewport_swizzle") define VK_NV_VIEWPORT_SWIZZLE_SPEC_VERSION 1
+@extension("VK_NV_viewport_swizzle") define VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME "VK_NV_viewport_swizzle"
+
+// 100
+@extension("VK_EXT_discard_rectangles") define VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION 1
+@extension("VK_EXT_discard_rectangles") define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles"
+
+// 105
+@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_SPEC_VERSION 1
+@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_COUNTER_EXTENSION_NAME "VK_EXT_swapchain_colorspace"
 
 // 106
 @extension("VK_EXT_hdr_metadata") define VK_EXT_HDR_METADATA_SPEC_VERSION 1
@@ -193,6 +320,14 @@
 @extension("VK_KHR_shared_presentable_image") define VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION 1
 @extension("VK_KHR_shared_presentable_image") define VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME "VK_KHR_shared_presentable_image"
 
+// 123
+@extension("VK_MVK_ios_surface") define VK_MVK_IOS_SURFACE_SPEC_VERSION 1
+@extension("VK_MVK_ios_surface") define VK_MVK_IOS_SURFACE_EXTENSION_NAME "VK_MVK_ios_surface"
+
+// 124
+@extension("VK_MVK_macos_surface") define VK_MVK_MACOS_SURFACE_SPEC_VERSION 1
+@extension("VK_MVK_macos_surface") define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface"
+
 /////////////
 //  Types  //
 /////////////
@@ -231,15 +366,23 @@
 @nonDispatchHandle type u64 VkRenderPass
 @nonDispatchHandle type u64 VkPipelineCache
 
+// 1
 @extension("VK_KHR_surface")    @nonDispatchHandle type u64 VkSurfaceKHR
 
+// 2
 @extension("VK_KHR_swapchain")  @nonDispatchHandle type u64 VkSwapchainKHR
 
+// 3
 @extension("VK_KHR_display")    @nonDispatchHandle type u64 VkDisplayKHR
 @extension("VK_KHR_display")    @nonDispatchHandle type u64 VkDisplayModeKHR
 
+// 12
 @extension("VK_EXT_debug_report") @nonDispatchHandle type u64 VkDebugReportCallbackEXT
 
+// 86
+@extension("VK_KHR_descriptor_update_template") @nonDispatchHandle type u64 VkDescriptorUpdateTemplateKHR
+
+// 87
 @extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkObjectTableNVX
 @extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkIndirectCommandsLayoutNVX
 
@@ -259,7 +402,7 @@
     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL                    = 0x00000007,   /// Optimal layout when image is used only as destination of transfer operations
     VK_IMAGE_LAYOUT_PREINITIALIZED                          = 0x00000008,   /// Initial layout used when the data is populated by the CPU
 
-    //@extension("VK_KHR_swapchain")
+    //@extension("VK_KHR_swapchain") // 2
     VK_IMAGE_LAYOUT_PRESENT_SRC_KHR                         = 1000001002,
 
     //@extension("VK_KHR_shared_presentable_image")
@@ -375,7 +518,7 @@
     VK_FILTER_NEAREST                                       = 0x00000000,
     VK_FILTER_LINEAR                                        = 0x00000001,
 
-    //@extension("VK_IMG_filter_cubic")
+    //@extension("VK_IMG_filter_cubic") // 16
     VK_FILTER_CUBIC_IMG                                     = 1000015000,
 }
 
@@ -687,7 +830,7 @@
     VK_FORMAT_ASTC_12x12_UNORM_BLOCK                        = 183,
     VK_FORMAT_ASTC_12x12_SRGB_BLOCK                         = 184,
 
-    //@extension("VK_IMG_format_pvrtc")
+    //@extension("VK_IMG_format_pvrtc") // 28
     VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG                   = 1000054000,
     VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG                   = 1000054001,
     VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG                   = 1000054002,
@@ -750,74 +893,73 @@
     VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO               = 47,
     VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO                 = 48,
 
-    //@extension("VK_KHR_swapchain")
+    //@extension("VK_KHR_swapchain") // 2
     VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR                 = 1000001000,
     VK_STRUCTURE_TYPE_PRESENT_INFO_KHR                          = 1000001001,
 
-    //@extension("VK_KHR_display")
+    //@extension("VK_KHR_display") // 3
     VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR              = 1000002000,
     VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR           = 1000002001,
 
-    //@extension("VK_KHR_display_swapchain")
+    //@extension("VK_KHR_display_swapchain") // 4
     VK_STRUCTURE_TYPE_DISPLAY_DISPLAY_PRESENT_INFO_KHR          = 1000003000,
 
-    //@extension("VK_KHR_xlib_surface")
+    //@extension("VK_KHR_xlib_surface") // 5
     VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR              = 1000004000,
 
-    //@extension("VK_KHR_xcb_surface")
+    //@extension("VK_KHR_xcb_surface") // 6
     VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR               = 1000005000,
 
-    //@extension("VK_KHR_wayland_surface")
+    //@extension("VK_KHR_wayland_surface") // 7
     VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR           = 1000006000,
 
-    //@extension("VK_KHR_mir_surface")
+    //@extension("VK_KHR_mir_surface") // 8
     VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR               = 1000007000,
 
-    //@extension("VK_KHR_android_surface")
+    //@extension("VK_KHR_android_surface") // 9
     VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR           = 1000008000,
 
-    //@extension("VK_KHR_win32_surface")
+    //@extension("VK_KHR_win32_surface") // 10
     VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR             = 1000009000,
 
-    //@extension("VK_KHR_incremental_present")
-    VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR                       = 1000084000,
-
-    //@extension("VK_ANDROID_native_buffer")
+    //@extension("VK_ANDROID_native_buffer") // 11
     VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID                     = 1000010000,
     VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID       = 1000010001,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID = 1000010002,
 
-    //@extension("VK_GOOGLE_display_timing")
-    VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE                 = 1000092000,
-
-    //@extension("VK_EXT_debug_report")
+    //@extension("VK_EXT_debug_report") // 12
     VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT     = 1000011000,
 
-    //@extension("VK_AMD_rasterization_order")
+    //@extension("VK_AMD_rasterization_order") // 19
     VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000,
 
-    //@extension("VK_EXT_debug_marker")
+    //@extension("VK_EXT_debug_marker") // 23
     VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT         = 1000022000,
     VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT          = 1000022001,
     VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT              = 1000022002,
 
-    //@extension("VK_NV_dedicated_allocation")
+    //@extension("VK_NV_dedicated_allocation") // 27
     VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000,
     VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001,
     VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002,
 
-    //@extension("VK_NV_external_memory")
+    //@extension("VK_KHX_multiview") // 54
+    VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHX     = 1000053000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHX    = 1000053001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHX  = 1000053002,
+
+    //@extension("VK_NV_external_memory") // 57
     VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV      = 1000056000,
     VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV            = 1000056001,
 
-    //@extension("VK_NV_external_memory_win32")
+    //@extension("VK_NV_external_memory_win32") // 58
     VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV        = 1000057000,
     VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV        = 1000057001,
 
-    //@extension("VK_NV_win32_keyed_mutex")
+    //@extension("VK_NV_win32_keyed_mutex") // 59
     VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000,
 
-    //@extension("VK_KHR_get_physical_device_properties2")
+    //@extension("VK_KHR_get_physical_device_properties2") // 60
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR            = 1000059000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR          = 1000059001,
     VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR                   = 1000059002,
@@ -828,19 +970,123 @@
     VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR      = 1000059007,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
 
-    //@extension("VK_EXT_validation_flags")
+    //@extension("VK_KHX_device_group") // 61
+    VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX            = 1000060000,
+    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX               = 1000060001,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX                = 1000060002,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX   = 1000060003,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX              = 1000060005,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHX         = 1000060006,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHX     = 1000060007,
+    VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHX           = 1000060008,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHX      = 1000060009,
+    VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX               = 1000060010,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX             = 1000060011,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX    = 1000060012,
+
+    //@extension("VK_EXT_validation_flags") // 62
     VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT                      = 1000061000,
 
-    //@extension("VK_KHR_incremental_present")
+    //@extension("VK_NN_vi_surface") // 63
+    VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN                 = 1000062000,
+
+    //@extension("VK_KHX_device_group_creation") // 71
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHX      = 1000070000,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHX       = 1000070001,
+
+    //@extension("VK_KHX_external_memory_capabilities") // 72
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHX    = 1000071000,
+    VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHX      = 1000071001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHX  = 1000071002,
+    VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHX            = 1000071003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHX         = 1000071004,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHX          = 1000071005,
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHX             = 1000071006,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHX   = 1000071007,
+
+    //@extension("VK_KHX_external_memory") // 73
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHX    = 1000072000,
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHX     = 1000072001,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHX           = 1000072002,
+
+    //@extension("VK_KHX_external_memory_win32") // 74
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHX       = 1000073000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHX       = 1000073001,
+    VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHX        = 1000073002,
+
+    //@extension("VK_KHX_external_memory_fd") // 75
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHX                 = 1000074000,
+    VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHX                  = 1000074001,
+
+    //@extension("VK_KHX_win32_keyed_mutex") // 76
+    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHX    = 1000075000,
+
+    //@extension("VK_KHX_external_semaphore_capabilities") // 77
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHX   = 1000076000,
+    VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHX         = 1000076001,
+
+    //@extension("VK_KHX_external_semaphore") // 78
+    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHX          = 1000077000,
+
+    //@extension("VK_KHX_external_semaphore_win32") // 79
+    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX    = 1000078000,
+    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX    = 1000078001,
+    VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHX               = 1000078002,
+
+    //@extension("VK_KHX_external_semaphore_fd") // 80
+    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHX              = 1000079000,
+
+    //@extension("VK_KHR_push_descriptor") // 81
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR    = 1000080000,
+
+    //@extension("VK_KHR_incremental_present") // 85
     VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR                       = 1000084000,
 
-    //@extension("VK_NVX_device_generated_commands")
+    //@extension("VK_KHR_descriptor_update_template") // 86
+    VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR    = 1000085000,
+
+    //@extension("VK_NVX_device_generated_commands") // 87
     VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX              = 1000086000,
     VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX  = 1000086001,
     VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX             = 1000086002,
     VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX   = 1000086003,
     VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX      = 1000086004,
     VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX    = 1000086005,
+
+    //@extension("VK_NV_clip_space_w_scaling") // 88
+    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV  = 1000087000,
+
+    //@extension("VK_EXT_display_surface_counter") // 91
+    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT                 = 1000090000,
+
+    //@extension("VK_EXT_display_control") // 92
+    VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT                    = 1000091000,
+    VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT                     = 1000091001,
+    VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT                    = 1000091002,
+    VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT         = 1000091003,
+
+    //@extension("VK_GOOGLE_display_timing") // 93
+    VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE                 = 1000092000,
+
+    //@extension("VK_NVX_multiview_per_view_attributes") // 98
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX  = 1000097000,
+
+    //@extension("VK_NV_viewport_swizzle") // 99
+    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV    = 1000098000,
+
+    //@extension("VK_EXT_discard_rectangles") // 100
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT  = 1000099000,
+    VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT  = 1000099001,
+
+    //@extension("VK_EXT_hdr_metadata") // 106
+    VK_STRUCTURE_TYPE_HDR_METADATA_EXT                          = 1000105000,
+
+    //@extension("VK_MVK_ios_surface") // 123
+    VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK               = 1000122000,
+
+    //@extension("VK_MVK_macos_surface") // 124
+    VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK             = 1000123000,
 }
 
 enum VkSubpassContents {
@@ -863,7 +1109,7 @@
     VK_EVENT_RESET                                          = 4,
     VK_INCOMPLETE                                           = 5,
 
-    //@extension("VK_KHR_swapchain")
+    //@extension("VK_KHR_swapchain") // 2
     VK_SUBOPTIMAL_KHR                                       = 1000001003,
 
     // Error codes (negative values)
@@ -880,23 +1126,27 @@
     VK_ERROR_FORMAT_NOT_SUPPORTED                           = 0xFFFFFFF5, // -11
     VK_ERROR_FRAGMENTED_POOL                                = 0xFFFFFFF4, // -12
 
-    //@extension("VK_KHR_surface")
+    //@extension("VK_KHR_surface") // 1
     VK_ERROR_SURFACE_LOST_KHR                               = 0xC4653600, // -1000000000
+    VK_ERROR_NATIVE_WINDOW_IN_USE_KHR                       = 0xC46535FF, // -1000000001
 
-    //@extension("VK_KHR_surface")
-    VK_ERROR_NATIVE_WINDOW_IN_USE_KHR                       = 0xC46535FF, // -1000008001
-
-    //@extension("VK_KHR_swapchain")
+    //@extension("VK_KHR_swapchain") // 2
     VK_ERROR_OUT_OF_DATE_KHR                                = 0xC4653214, // -1000001004
 
-    //@extension("VK_KHR_display_swapchain")
+    //@extension("VK_KHR_display_swapchain") // 4
     VK_ERROR_INCOMPATIBLE_DISPLAY_KHR                       = 0xC4652A47, // -1000003001
 
-    //@extension("VK_EXT_debug_report")
+    //@extension("VK_EXT_debug_report") // 12
     VK_ERROR_VALIDATION_FAILED_EXT                          = 0xC4650B07, // -1000011001
 
-    //@extension("VK_NV_glsl_shader")
+    //@extension("VK_NV_glsl_shader") // 13
     VK_ERROR_INVALID_SHADER_NV                              = 0xC4650720, // -1000012000
+
+    //@extension("VK_KHR_maintenance1") // 70
+    VK_ERROR_OUT_OF_POOL_MEMORY_KHR                         = 0xC4642878, // -1000069000
+
+    //@extension("VK_KHX_external_memory") // 73
+    VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX                    = 0xC4641CBD, // -1000072003
 }
 
 enum VkDynamicState {
@@ -909,23 +1159,31 @@
     VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK                   = 0x00000006,
     VK_DYNAMIC_STATE_STENCIL_WRITE_MASK                     = 0x00000007,
     VK_DYNAMIC_STATE_STENCIL_REFERENCE                      = 0x00000008,
+
+    //@extension("VK_NV_clip_space_w_scaling") // 88
+    VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV                  = 1000087000,
+
+    //@extension("VK_EXT_discard_rectangles") // 100
+    VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT                  = 1000099000,
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 enum VkPresentModeKHR {
     VK_PRESENT_MODE_IMMEDIATE_KHR                           = 0x00000000,
     VK_PRESENT_MODE_MAILBOX_KHR                             = 0x00000001,
     VK_PRESENT_MODE_FIFO_KHR                                = 0x00000002,
     VK_PRESENT_MODE_FIFO_RELAXED_KHR                        = 0x00000003,
+
     //@extension("VK_KHR_shared_presentable_image")
-    VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR       = 1000111000,
-    //@extension("VK_KHR_shared_presentable_image")
-    VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR   = 1000111001,
+    VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR               = 1000111000,
+    VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR           = 1000111001,
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 enum VkColorSpaceKHR {
     VK_COLORSPACE_SRGB_NONLINEAR_KHR                        = 0x00000000,
+
+    //@extension("VK_EXT_swapchain_colorspace")
     VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT                    = 1000104001,
     VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT                 = 1000104002,
     VK_COLOR_SPACE_SCRGB_LINEAR_EXT                         = 1000104003,
@@ -940,7 +1198,7 @@
     VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT                   = 1000104012,
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 enum VkDebugReportObjectTypeEXT {
     VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT                 = 0,
     VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT                = 1,
@@ -977,24 +1235,30 @@
     VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 enum VkDebugReportErrorEXT {
     VK_DEBUG_REPORT_ERROR_NONE_EXT                          = 0,
     VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT                  = 1,
 }
 
-@extension("VK_AMD_rasterization_order")
+@extension("VK_AMD_rasterization_order") // 19
 enum VkRasterizationOrderAMD {
     VK_RASTERIZATION_ORDER_STRICT_AMD                       = 0,
     VK_RASTERIZATION_ORDER_RELAXED_AMD                      = 1,
 }
 
-@extension("VK_EXT_validation_flags")
+@extension("VK_EXT_validation_flags") // 62
 enum VkValidationCheckEXT {
     VK_VALIDATION_CHECK_ALL_EXT                             = 0,
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_KHR_descriptor_update_template") // 86
+enum VkDescriptorUpdateTemplateTypeKHR {
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR   = 0,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1,
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
 enum VkIndirectCommandsTokenTypeNVX {
     VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX                 = 0,
     VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX           = 1,
@@ -1006,7 +1270,7 @@
     VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX                 = 7,
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 enum VkObjectEntryTypeNVX {
     VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX                      = 0,
     VK_OBJECT_ENTRY_PIPELINE_NVX                            = 1,
@@ -1015,6 +1279,41 @@
     VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX                       = 4,
 }
 
+@extension("VK_EXT_display_control") // 92
+enum VkDisplayPowerStateEXT {
+    VK_DISPLAY_POWER_STATE_OFF_EXT                          = 0,
+    VK_DISPLAY_POWER_STATE_SUSPEND_EXT                      = 1,
+    VK_DISPLAY_POWER_STATE_ON_EXT                           = 2,
+}
+
+@extension("VK_EXT_display_control") // 92
+enum VkDeviceEventTypeEXT {
+    VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT                = 0,
+}
+
+@extension("VK_EXT_display_control") // 92
+enum VkDisplayEventTypeEXT {
+    VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT               = 0,
+}
+
+@extension("VK_NV_viewport_swizzle") // 99
+enum VkViewportCoordinateSwizzleNV {
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV            = 0,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV            = 1,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV            = 2,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Y_NV            = 3,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV            = 4,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV            = 5,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV            = 6,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV            = 7,
+}
+
+@extension("VK_EXT_discard_rectangles") // 100
+enum VkDiscardRectangleModeEXT {
+    VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT = 0,
+    VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1,
+}
+
 /////////////////
 //  Bitfields  //
 /////////////////
@@ -1042,6 +1341,9 @@
 type VkFlags VkMemoryHeapFlags
 bitfield VkMemoryHeapFlagBits {
     VK_MEMORY_HEAP_DEVICE_LOCAL_BIT                         = 0x00000001,
+
+    //@extension("VK_KHX_device_group_creation") // 71
+    VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHX                   = 0x00000002,
 }
 
 /// Access flags
@@ -1065,7 +1367,7 @@
     VK_ACCESS_MEMORY_READ_BIT                               = 0x00008000,
     VK_ACCESS_MEMORY_WRITE_BIT                              = 0x00010000,
 
-    //@extension("VK_NVX_device_generated_commands")
+    //@extension("VK_NVX_device_generated_commands") // 87
     VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX                  = 0x00020000,
     VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX                 = 0x00040000,
 }
@@ -1138,6 +1440,12 @@
     VK_IMAGE_CREATE_SPARSE_ALIASED_BIT                      = 0x00000004,    /// Image should support constent data access to physical memory blocks mapped into multiple locations of sparse images
     VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT                      = 0x00000008,    /// Allows image views to have different format than the base image
     VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT                     = 0x00000010,    /// Allows creating image views with cube type from the created image
+
+    //@extension("VK_KHR_maintenance1") // 70
+    VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR             = 0x00000020,
+
+    //@extension("VK_KHX_device_group") // 61
+    VK_IMAGE_CREATE_BIND_SFR_BIT_KHX                        = 0x00000040,
 }
 
 /// Image view creation flags
@@ -1151,6 +1459,10 @@
     VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT             = 0x00000001,
     VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT                = 0x00000002,
     VK_PIPELINE_CREATE_DERIVATIVE_BIT                       = 0x00000004,
+
+    //@extension("VK_KHX_device_group") // 61
+    VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHX = 0x00000008,
+    VK_PIPELINE_CREATE_DISPATCH_BASE_KHX                    = 0x00000010,
 }
 
 /// Color component flags
@@ -1190,8 +1502,12 @@
     VK_FORMAT_FEATURE_BLIT_DST_BIT                          = 0x00000800,    /// Format can be used as the destination image of blits with vkCommandBlitImage
     VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT       = 0x00001000,
 
-    //@extension("VK_IMG_filter_cubic")
+    //@extension("VK_IMG_filter_cubic") // 16
     VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG    = 0x00002000,
+
+    //@extension("VK_KHR_maintenance1") // 70
+    VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR                  = 0x00004000,
+    VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR                  = 0x00008000,
 }
 
 /// Query control flags
@@ -1293,7 +1609,7 @@
     VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT                      = 0x00008000,  /// All stages of the graphics pipeline
     VK_PIPELINE_STAGE_ALL_COMMANDS_BIT                      = 0x00010000,  /// All graphics, compute, copy, and transition commands
 
-    //@extension("VK_NVX_device_generated_commands")
+    //@extension("VK_NVX_device_generated_commands") // 87
     VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX               = 0x00020000,
 }
 
@@ -1306,6 +1622,9 @@
 /// Subpass description flags
 type VkFlags VkSubpassDescriptionFlags
 bitfield VkSubpassDescriptionFlagBits {
+    //@extension("VK_NVX_multiview_per_view_attributes") // 98
+    VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX      = 0x00000001,
+    VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002,
 }
 
 /// Command pool creation flags
@@ -1381,8 +1700,10 @@
 
 /// Descriptor set layout creation flags
 type VkFlags VkDescriptorSetLayoutCreateFlags
-//bitfield VkDescriptorSetLayoutCreateFlagBits {
-//}
+bitfield VkDescriptorSetLayoutCreateFlagBits {
+    //@extension("VK_KHR_push_descriptor") // 81
+    VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR     = 0x00000001,
+}
 
 /// Pipeline vertex input state creation flags
 type VkFlags VkPipelineVertexInputStateCreateFlags
@@ -1453,6 +1774,12 @@
 type VkFlags VkDependencyFlags
 bitfield VkDependencyFlagBits {
     VK_DEPENDENCY_BY_REGION_BIT                             = 0x00000001,
+
+    //@extension("VK_KHX_multiview") // 54
+    VK_DEPENDENCY_VIEW_LOCAL_BIT_KHX                        = 0x00000002,
+
+    //@extension("VK_KHX_device_group") // 61
+    VK_DEPENDENCY_DEVICE_GROUP_BIT_KHX                      = 0x00000004,
 }
 
 /// Cull mode flags
@@ -1464,9 +1791,9 @@
     VK_CULL_MODE_FRONT_AND_BACK                             = 0x00000003,
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 type VkFlags VkSurfaceTransformFlagsKHR
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 bitfield VkSurfaceTransformFlagBitsKHR {
     VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR                       = 0x00000001,
     VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR                      = 0x00000002,
@@ -1479,9 +1806,9 @@
     VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR                        = 0x00000100,
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 type VkFlags VkCompositeAlphaFlagsKHR
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 bitfield VkCompositeAlphaFlagBitsKHR {
     VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR                       = 0x00000001,
     VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR               = 0x00000002,
@@ -1489,15 +1816,17 @@
     VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR                      = 0x00000008,
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 type VkFlags VkSwapchainCreateFlagsKHR
-//@extension("VK_KHR_swapchain")
-//bitfield VkSwapchainCreateFlagBitsKHR {
-//}
+@extension("VK_KHR_swapchain") // 2
+bitfield VkSwapchainCreateFlagBitsKHR {
+    //@extension("VK_KHX_device_group") // 61
+    VK_SWAPCHAIN_CREATE_BIND_SFR_BIT_KHX                    = 0x00000001,
+}
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 type VkFlags VkDisplayPlaneAlphaFlagsKHR
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 bitfield VkDisplayPlaneAlphaFlagBitsKHR {
     VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR                   = 0x00000001,
     VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR                   = 0x00000002,
@@ -1505,64 +1834,64 @@
     VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR  = 0x00000008,
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 type VkFlags VkDisplaySurfaceCreateFlagsKHR
-//@extension("VK_KHR_display")
+//@extension("VK_KHR_display") // 3
 //bitfield VkDisplaySurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 type VkFlags VkDisplayModeCreateFlagsKHR
-//@extension("VK_KHR_display")
+//@extension("VK_KHR_display") // 3
 //bitfield VkDisplayModeCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_xlib_surface")
+@extension("VK_KHR_xlib_surface") // 5
 type VkFlags VkXlibSurfaceCreateFlagsKHR
-//@extension("VK_KHR_xlib_surface")
+//@extension("VK_KHR_xlib_surface") // 5
 //bitfield VkXlibSurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_xcb_surface")
+@extension("VK_KHR_xcb_surface") // 6
 type VkFlags VkXcbSurfaceCreateFlagsKHR
-//@extension("VK_KHR_xcb_surface")
+//@extension("VK_KHR_xcb_surface") // 6
 //bitfield VkXcbSurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_wayland_surface")
+@extension("VK_KHR_wayland_surface") // 7
 type VkFlags VkWaylandSurfaceCreateFlagsKHR
-//@extension("VK_KHR_wayland_surface")
+//@extension("VK_KHR_wayland_surface") // 7
 //bitfield VkWaylandSurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_mir_surface")
+@extension("VK_KHR_mir_surface") // 8
 type VkFlags VkMirSurfaceCreateFlagsKHR
-//@extension("VK_KHR_mir_surface")
+//@extension("VK_KHR_mir_surface") // 8
 //bitfield VkMirSurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_android_surface")
+@extension("VK_KHR_android_surface") // 9
 type VkFlags VkAndroidSurfaceCreateFlagsKHR
-//@extension("VK_KHR_android_surface")
+//@extension("VK_KHR_android_surface") // 9
 //bitfield VkAndroidSurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_KHR_win32_surface")
+@extension("VK_KHR_win32_surface") // 10
 type VkFlags VkWin32SurfaceCreateFlagsKHR
-//@extension("VK_KHR_win32_surface")
+//@extension("VK_KHR_win32_surface") // 10
 //bitfield VkWin32SurfaceCreateFlagBitsKHR {
 //}
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
 type VkFlags VkSwapchainImageUsageFlagsANDROID
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
 bitfield VkSwapchainImageUsageFlagBitsANDROID {
     VK_SWAPCHAIN_IMAGE_USAGE_FLAGS_SHARED_BIT_ANDROID = 0x00000001,
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 type VkFlags VkDebugReportFlagsEXT
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 bitfield VkDebugReportFlagBitsEXT {
     VK_DEBUG_REPORT_INFORMATION_BIT_EXT                     = 0x00000001,
     VK_DEBUG_REPORT_WARNING_BIT_EXT                         = 0x00000002,
@@ -1571,9 +1900,9 @@
     VK_DEBUG_REPORT_DEBUG_BIT_EXT                           = 0x00000010,
 }
 
-@extension("VK_NV_external_memory_capabilities")
+@extension("VK_NV_external_memory_capabilities") // 56
 type VkFlags VkExternalMemoryHandleTypeFlagsNV
-@extension("VK_NV_external_memory_capabilities")
+@extension("VK_NV_external_memory_capabilities") // 56
 bitfield VkExternalMemoryHandleTypeFlagBitsNV {
     VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV      = 0x00000001,
     VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV  = 0x00000002,
@@ -1581,18 +1910,104 @@
     VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV   = 0x00000008,
 }
 
-@extension("VK_NV_external_memory_capabilities")
+@extension("VK_NV_external_memory_capabilities") // 56
 type VkFlags VkExternalMemoryFeatureFlagsNV
-@extension("VK_NV_external_memory_capabilities")
+@extension("VK_NV_external_memory_capabilities") // 56
 bitfield VkExternalMemoryFeatureFlagBitsNV {
     VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV        = 0x00000001,
     VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV            = 0x00000002,
     VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV            = 0x00000004,
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_KHX_device_group") // 61
+type VkFlags VkPeerMemoryFeatureFlagsKHX
+@extension("VK_KHX_device_group") // 61
+bitfield VkPeerMemoryFeatureFlagBitsKHX {
+    VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT_KHX                 = 0x00000001,
+    VK_PEER_MEMORY_FEATURE_COPY_DST_BIT_KHX                 = 0x00000002,
+    VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT_KHX              = 0x00000004,
+    VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT_KHX              = 0x00000008,
+}
+
+@extension("VK_KHX_device_group") // 61
+type VkFlags VkMemoryAllocateFlagsKHX
+@extension("VK_KHX_device_group") // 61
+bitfield VkMemoryAllocateFlagBitsKHX {
+    VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHX                  = 0x00000001,
+}
+
+@extension("VK_KHX_device_group") // 61
+type VkFlags VkDeviceGroupPresentModeFlagsKHX
+@extension("VK_KHX_device_group") // 61
+bitfield VkDeviceGroupPresentModeFlagBitsKHX {
+    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHX              = 0x00000001,
+    VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHX             = 0x00000002,
+    VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHX                = 0x00000004,
+    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHX = 0x00000008,
+}
+
+@extension("VK_NN_vi_surface") // 63
+type VkFlags VkViSurfaceCreateFlagsNN
+//@extension("VK_NN_vi_surface") // 63
+//bitfield VkViSurfaceCreateFlagBitsNN {
+//}
+
+@extension("VK_KHR_maintenance1") // 70
+type VkFlags VkCommandPoolTrimFlagsKHR
+//@extension("VK_KHR_maintenance1") // 70
+//bitfield VkCommandPoolTrimFlagBitsKHR {
+//}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+type VkFlags VkExternalMemoryHandleTypeFlagsKHX
+@extension("VK_KHX_external_memory_capabilities") // 72
+bitfield VkExternalMemoryHandleTypeFlagBitsKHX {
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHX            = 0x00000001,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX         = 0x00000002,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX     = 0x00000004,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT_KHX        = 0x00000008,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT_KHX    = 0x00000010,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT_KHX           = 0x00000020,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHX       = 0x00000040,
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+type VkFlags VkExternalMemoryFeatureFlagsKHX
+@extension("VK_KHX_external_memory_capabilities") // 72
+bitfield VkExternalMemoryFeatureFlagBitsKHX {
+    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHX           = 0x00000001,
+    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHX               = 0x00000002,
+    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHX               = 0x00000004,
+}
+
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+type VkFlags VkExternalSemaphoreHandleTypeFlagsKHX
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+bitfield VkExternalSemaphoreHandleTypeFlagBitsKHX {
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX         = 0x00000001
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX      = 0x00000002
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX  = 0x00000004
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT_KHX       = 0x00000008
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX          = 0x00000010
+}
+
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+type VkFlags VkExternalSemaphoreFeatureFlagsKHX
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+bitfield VkExternalSemaphoreFeatureFlagBitsKHX {
+    VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHX            = 0x00000001,
+    VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHX            = 0x00000002,
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+type VkFlags VkDescriptorUpdateTemplateCreateFlagsKHR
+//@extension("VK_KHR_descriptor_update_template") // 86
+//bitfield VkDescriptorUpdateTemplateCreateFlagBitsKHR {
+//}
+
+@extension("VK_NVX_device_generated_commands") // 87
 type VkFlags VkIndirectCommandsLayoutUsageFlagsNVX
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 bitfield VkIndirectCommandsLayoutUsageFlagBitsNVX {
     VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX   = 0x00000001,
     VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX      = 0x00000002,
@@ -1600,14 +2015,45 @@
     VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX     = 0x00000008,
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 type VkFlags VkObjectEntryUsageFlagsNVX
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 bitfield VkObjectEntryUsageFlagBitsNVX {
     VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX                  = 0x00000001,
     VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX                   = 0x00000002,
 }
 
+@extension("VK_EXT_display_surface_counter") // 91
+type VkFlags VkSurfaceCounterFlagsEXT
+@extension("VK_EXT_display_surface_counter") // 91
+bitfield VkSurfaceCounterFlagBitsEXT {
+    VK_SURFACE_COUNTER_VBLANK_EXT                           = 0x00000001,
+}
+
+@extension("VK_NV_viewport_swizzle") // 99
+type VkFlags VkPipelineViewportSwizzleStateCreateFlagsNV
+//@extension("VK_NV_viewport_swizzle") // 99
+//bitfield VkPipelineViewportSwizzleStateCreateFlagBitsNV {
+//}
+
+@extension("VK_EXT_discard_rectangles") // 100
+type VkFlags VkPipelineDiscardRectangleStateCreateFlagsEXT
+//@extension("VK_EXT_discard_rectangles") // 100
+//bitfield VkPipelineDiscardRectangleStateCreateFlagBitsEXT {
+//}
+
+@extension("VK_MVK_ios_surface") // 123
+type VkFlags VkIOSSurfaceCreateFlagsMVK
+//@extension("VK_MVK_ios_surface") // 123
+//bitfield VkIOSSurfaceCreateFlagBitsMVK {
+//}
+
+@extension("VK_MVK_macos_surface") // 124
+type VkFlags VkMacOSSurfaceCreateFlagsMVK
+//@extension("VK_MVK_macos_surface") // 124
+//bitfield VkMacOSSurfaceCreateFlagBitsMVK {
+//}
+
 //////////////////
 //  Structures  //
 //////////////////
@@ -2712,7 +3158,7 @@
     u32                                         z
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 class VkSurfaceCapabilitiesKHR {
     u32                                         minImageCount
     u32                                         maxImageCount
@@ -2726,13 +3172,13 @@
     VkImageUsageFlags                           supportedUsageFlags
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 class VkSurfaceFormatKHR {
     VkFormat                                    format
     VkColorSpaceKHR                             colorSpace
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 class VkSwapchainCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2754,7 +3200,7 @@
     VkSwapchainKHR                              oldSwapchain
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 class VkPresentInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2766,7 +3212,7 @@
     VkResult*                                   pResults
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayPropertiesKHR {
     VkDisplayKHR                                display
     const char*                                 displayName
@@ -2777,19 +3223,19 @@
     VkBool32                                    persistentContent
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayModeParametersKHR {
     VkExtent2D                                  visibleRegion
     u32                                         refreshRate
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayModePropertiesKHR {
     VkDisplayModeKHR                            displayMode
     VkDisplayModeParametersKHR                  parameters
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayModeCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2797,13 +3243,13 @@
     VkDisplayModeParametersKHR                  parameters
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayPlanePropertiesKHR {
     VkDisplayKHR                                currentDisplay
     u32                                         currentStackIndex
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplayPlaneCapabilitiesKHR {
     VkDisplayPlaneAlphaFlagsKHR                 supportedAlpha
     VkOffset2D                                  minSrcPosition
@@ -2816,7 +3262,7 @@
     VkExtent2D                                  maxDstExtent
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 class VkDisplaySurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2830,7 +3276,7 @@
     VkExtent2D                                  imageExtent
 }
 
-@extension("VK_KHR_display_swapchain")
+@extension("VK_KHR_display_swapchain") // 4
 class VkDisplayPresentInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2839,7 +3285,7 @@
     VkBool32                                    persistent
 }
 
-@extension("VK_KHR_xlib_surface")
+@extension("VK_KHR_xlib_surface") // 5
 class VkXlibSurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2848,7 +3294,7 @@
     platform.Window                             window
 }
 
-@extension("VK_KHR_xcb_surface")
+@extension("VK_KHR_xcb_surface") // 6
 class VkXcbSurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2857,7 +3303,7 @@
     platform.xcb_window_t                       window
 }
 
-@extension("VK_KHR_wayland_surface")
+@extension("VK_KHR_wayland_surface") // 7
 class VkWaylandSurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2866,7 +3312,7 @@
     platform.wl_surface*                        surface
 }
 
-@extension("VK_KHR_mir_surface")
+@extension("VK_KHR_mir_surface") // 8
 class VkMirSurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2875,7 +3321,7 @@
     platform.MirSurface*                        mirSurface
 }
 
-@extension("VK_KHR_android_surface")
+@extension("VK_KHR_android_surface") // 9
 class VkAndroidSurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2883,7 +3329,7 @@
     platform.ANativeWindow*                     window
 }
 
-@extension("VK_KHR_win32_surface")
+@extension("VK_KHR_win32_surface") // 10
 class VkWin32SurfaceCreateInfoKHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2892,12 +3338,13 @@
     platform.HWND                               hwnd
 }
 
+@extension("VK_ANDROID_native_buffer") // 11
 @internal class Gralloc1Usage {
     u64                                         consumer
     u64                                         producer
 }
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
 class VkNativeBufferANDROID {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2908,50 +3355,21 @@
     Gralloc1Usage                               usage2
 }
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
 class VkSwapchainImageCreateInfoANDROID {
     VkStructureType                             sType
     const void*                                 pNext
     VkSwapchainImageUsageFlagsANDROID           flags
 }
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
 class VkPhysicalDevicePresentationPropertiesANDROID {
     VkStructureType                             sType
     void*                                       pNext
     VkBool32                                    sharedImage
 }
 
-@extension("VK_GOOGLE_display_timing")
-class VkRefreshCycleDurationGOOGLE {
-    u64                                         minRefreshDuration
-    u64                                         maxRefreshDuration
-}
-
-@extension("VK_GOOGLE_display_timing")
-class VkPastPresentationTimingGOOGLE {
-    u32                                         presentID
-    u64                                         desiredPresentTime
-    u64                                         actualPresentTime
-    u64                                         earliestPresentTime
-    u64                                         presentMargin
-}
-
-@extension("VK_GOOGLE_display_timing")
-class VkPresentTimeGOOGLE {
-    u32                                         presentID
-    u64                                         desiredPresentTime
-}
-
-@extension("VK_GOOGLE_display_timing")
-class VkPresentTimesInfoGOOGLE {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         swapchainCount
-    const VkPresentTimeGOOGLE*                  pTimes
-}
-
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 class VkDebugReportCallbackCreateInfoEXT {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2960,14 +3378,14 @@
     void*                                       pUserData
 }
 
-@extension("VK_AMD_rasterization_order")
+@extension("VK_AMD_rasterization_order") // 19
 class VkPipelineRasterizationStateRasterizationOrderAMD {
     VkStructureType                             sType
     const void*                                 pNext
     VkRasterizationOrderAMD                     rasterizationOrder
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 class VkDebugMarkerObjectNameInfoEXT {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2976,7 +3394,7 @@
     const char*                                 pObjectName
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 class VkDebugMarkerObjectTagInfoEXT {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2987,7 +3405,7 @@
     const void*                                 pTag
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 class VkDebugMarkerMarkerInfoEXT {
     VkStructureType                             sType
     const void*                                 pNext
@@ -2995,21 +3413,21 @@
     f32[4]                                      color
 }
 
-@extension("VK_NV_dedicated_allocation")
+@extension("VK_NV_dedicated_allocation") // 27
 class VkDedicatedAllocationImageCreateInfoNV {
     VkStructureType                             sType
     const void*                                 pNext
     VkBool32                                    dedicatedAllocation
 }
 
-@extension("VK_NV_dedicated_allocation")
+@extension("VK_NV_dedicated_allocation") // 27
 class VkDedicatedAllocationBufferCreateInfoNV {
     VkStructureType                             sType
     const void*                                 pNext
     VkBool32                                    dedicatedAllocation
 }
 
-@extension("VK_NV_dedicated_allocation")
+@extension("VK_NV_dedicated_allocation") // 27
 class VkDedicatedAllocationMemoryAllocateInfoNV {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3017,7 +3435,36 @@
     VkBuffer                                    buffer
 }
 
-@extension("VK_NV_external_memory_capabilities")
+@extension("VK_KHX_multiview") // 54
+class VkRenderPassMultiviewCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         subpassCount
+    const u32*                                  pViewMasks
+    u32                                         dependencyCount
+    const s32*                                  pViewOffsets
+    u32                                         correlationMaskCount
+    const u32*                                  pCorrelationMasks
+}
+
+@extension("VK_KHX_multiview") // 54
+class VkPhysicalDeviceMultiviewFeaturesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkBool32                                    multiview
+    VkBool32                                    multiviewGeometryShader
+    VkBool32                                    multiviewTessellationShader
+}
+
+@extension("VK_KHX_multiview") // 54
+class VkPhysicalDeviceMultiviewPropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    u32                                         maxMultiviewViewCount
+    u32                                         maxMultiviewInstanceIndex
+}
+
+@extension("VK_NV_external_memory_capabilities") // 56
 class VkExternalImageFormatPropertiesNV {
     VkImageFormatProperties                     imageFormatProperties
     VkExternalMemoryFeatureFlagsNV              externalMemoryFeatures
@@ -3025,21 +3472,21 @@
     VkExternalMemoryHandleTypeFlagsNV           compatibleHandleTypes
 }
 
-@extension("VK_NV_external_memory")
+@extension("VK_NV_external_memory") // 57
 class VkExternalMemoryImageCreateInfoNV {
     VkStructureType                             sType
     const void*                                 pNext
     VkExternalMemoryHandleTypeFlagsNV           handleTypes
 }
 
-@extension("VK_NV_external_memory")
+@extension("VK_NV_external_memory") // 57
 class VkExportMemoryAllocateInfoNV {
     VkStructureType                             sType
     const void*                                 pNext
     VkExternalMemoryHandleTypeFlagsNV           handleTypes
 }
 
-@extension("VK_NV_external_memory_win32")
+@extension("VK_NV_external_memory_win32") // 58
 class VkImportMemoryWin32HandleInfoNV {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3047,7 +3494,7 @@
     platform.HANDLE                             handle
 }
 
-@extension("VK_NV_external_memory_win32")
+@extension("VK_NV_external_memory_win32") // 58
 class VkExportMemoryWin32HandleInfoNV {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3055,7 +3502,7 @@
     platform.DWORD                              dwAccess
 }
 
-@extension("VK_NV_win32_keyed_mutex")
+@extension("VK_NV_win32_keyed_mutex") // 59
 class VkWin32KeyedMutexAcquireReleaseInfoNV {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3068,35 +3515,35 @@
     const u64*                                  pReleaseKeys
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 class VkPhysicalDeviceFeatures2KHR {
     VkStructureType                             sType
     void*                                       pNext
     VkPhysicalDeviceFeatures                    features
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 class VkPhysicalDeviceProperties2KHR {
     VkStructureType                             sType
     void*                                       pNext
     VkPhysicalDeviceProperties                  properties
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 class VkFormatProperties2KHR {
     VkStructureType                             sType
     void*                                       pNext
     VkFormatProperties                          formatProperties
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 class VkImageFormatProperties2KHR {
     VkStructureType                             sType
     void*                                       pNext
     VkImageFormatProperties                     imageFormatProperties
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 class VkPhysicalDeviceImageFormatInfo2KHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3107,28 +3554,28 @@
     VkImageCreateFlags                          flags
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 class VkQueueFamilyProperties2KHR {
     VkStructureType                             sType
     void*                                       pNext
     VkQueueFamilyProperties                     queueFamilyProperties
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 class VkPhysicalDeviceMemoryProperties2KHR {
     VkStructureType                             sType
     void*                                       pNext
     VkPhysicalDeviceMemoryProperties            memoryProperties
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 class VkSparseImageFormatProperties2KHR {
     VkStructureType                             sType
     void*                                       pNext
     VkSparseImageFormatProperties               properties
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 class VkPhysicalDeviceSparseImageFormatInfo2KHR {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3139,7 +3586,125 @@
     VkImageTiling                               tiling
 }
 
-@extension("VK_EXT_validation_flags")
+@extension("VK_KHX_device_group") // 61
+class VkMemoryAllocateFlagsInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkMemoryAllocateFlagsKHX                    flags
+    u32                                         deviceMask
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkBindBufferMemoryInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkBuffer                                    buffer
+    VkDeviceMemory                              memory
+    VkDeviceSize                                memoryOffset
+    u32                                         deviceIndexCount
+    const u32*                                  pDeviceIndices
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkBindImageMemoryInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkImage                                     image
+    VkDeviceMemory                              memory
+    VkDeviceSize                                memoryOffset
+    u32                                         deviceIndexCount
+    const u32*                                  pDeviceIndices
+    u32                                         SFRRectCount
+    const VkRect2D*                             pSFRRects
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupRenderPassBeginInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         deviceMask
+    u32                                         deviceRenderAreaCount
+    const VkRect2D*                             pDeviceRenderAreas
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupCommandBufferBeginInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         deviceMask
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupSubmitInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         waitSemaphoreCount
+    const u32*                                  pWaitSemaphoreDeviceIndices
+    u32                                         commandBufferCount
+    const u32*                                  pCommandBufferDeviceMasks
+    u32                                         signalSemaphoreCount
+    const u32*                                  pSignalSemaphoreDeviceIndices
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupBindSparseInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         resourceDeviceIndex
+    u32                                         memoryDeviceIndex
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupPresentCapabilitiesKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32[VK_MAX_DEVICE_GROUP_SIZE_KHX]           presentMask
+    VkDeviceGroupPresentModeFlagsKHX            modes
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkImageSwapchainCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSwapchainKHR                              swapchain
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkBindImageMemorySwapchainInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSwapchainKHR                              swapchain
+    u32                                         imageIndex
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkAcquireNextImageInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSwapchainKHR                              swapchain
+    u64                                         timeout
+    VkSemaphore                                 semaphore
+    VkFence                                     fence
+    u32                                         deviceMask
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupPresentInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         swapchainCount
+    const u32*                                  pDeviceMasks
+    VkDeviceGroupPresentModeFlagBitsKHX         mode
+}
+
+@extension("VK_KHX_device_group") // 61
+class VkDeviceGroupSwapchainCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkDeviceGroupPresentModeFlagsKHX            modes
+}
+
+@extension("VK_EXT_validation_flags") // 62
 class VkValidationFlagsEXT {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3147,14 +3712,271 @@
     VkValidationCheckEXT*                       pDisabledValidationChecks
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NN_vi_surface") // 63
+class VkViSurfaceCreateInfoNN {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkViSurfaceCreateFlagsNN                    flags
+    void*                                       window
+}
+
+@extension("VK_KHX_device_group_creation") // 71
+class VkPhysicalDeviceGroupPropertiesKHX {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    u32                                             physicalDeviceCount
+    VkPhysicalDevice[VK_MAX_DEVICE_GROUP_SIZE_KHX]  physicalDevices
+    VkBool32                                        subsetAllocation
+}
+
+@extension("VK_KHX_device_group_creation") // 71
+class VkDeviceGroupDeviceCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         physicalDeviceCount
+    const VkPhysicalDevice*                     pPhysicalDevices
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkExternalMemoryPropertiesKHX {
+    VkExternalMemoryFeatureFlagsKHX             externalMemoryFeatures
+    VkExternalMemoryHandleTypeFlagsKHX          exportFromImportedHandleTypes
+    VkExternalMemoryHandleTypeFlagsKHX          compatibleHandleTypes
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkPhysicalDeviceExternalImageFormatInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkExternalImageFormatPropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkExternalMemoryPropertiesKHX               externalMemoryProperties
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkPhysicalDeviceExternalBufferInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkBufferCreateFlags                         flags
+    VkBufferUsageFlags                          usage
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkExternalBufferPropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkExternalMemoryPropertiesKHX               externalMemoryProperties
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+class VkPhysicalDeviceIDPropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    u8[VK_UUID_SIZE]                            deviceUUID
+    u8[VK_UUID_SIZE]                            driverUUID
+    u8[VK_LUID_SIZE_KHX]                        deviceLUID
+    VkBool32                                    deviceLUIDValid
+}
+
+@extension("VK_KHX_external_memory") // 73
+class VkExternalMemoryImageCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsKHX          handleTypes
+}
+
+@extension("VK_KHX_external_memory") // 73
+class VkExternalMemoryBufferCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsKHX          handleTypes
+}
+
+@extension("VK_KHX_external_memory") // 73
+class VkExportMemoryAllocateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsKHX          handleTypes
+}
+
+@extension("VK_KHX_external_memory_win32") // 74
+class VkImportMemoryWin32HandleInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType
+    platform.HANDLE                             handle
+}
+
+@extension("VK_KHX_external_memory_win32") // 74
+class VkExportMemoryWin32HandleInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    const platform.SECURITY_ATTRIBUTES*         pAttributes
+    platform.DWORD                              dwAccess
+    platform.LPCWSTR                            name
+}
+
+@extension("VK_KHX_external_memory_win32") // 74
+class VkMemoryWin32HandlePropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    u32                                         memoryTypeBits
+}
+
+@extension("VK_KHX_external_memory_fd") // 75
+class VkImportMemoryFdInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType
+    int                                         fd
+}
+
+@extension("VK_KHX_external_memory_fd") // 75
+class VkMemoryFdPropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    u32                                         memoryTypeBits
+}
+
+@extension("VK_KHX_win32_keyed_mutex") // 76
+class VkWin32KeyedMutexAcquireReleaseInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         acquireCount
+    const VkDeviceMemory*                       pAcquireSyncs
+    const u64*                                  pAcquireKeys
+    const u32*                                  pAcquireTimeouts
+    u32                                         releaseCount
+    const VkDeviceMemory*                       pReleaseSyncs
+    const u64*                                  pReleaseKeys
+}
+
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+class VkPhysicalDeviceExternalSemaphoreInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType
+}
+
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+class VkExternalSemaphorePropertiesKHX {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkExternalSemaphoreHandleTypeFlagsKHX       exportFromImportedHandleTypes
+    VkExternalSemaphoreHandleTypeFlagsKHX       compatibleHandleTypes
+    VkExternalSemaphoreFeatureFlagsKHX          externalSemaphoreFeatures
+}
+
+@extension("VK_KHX_external_semaphore") // 78
+class VkExportSemaphoreCreateInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalSemaphoreHandleTypeFlagsKHX       handleTypes
+}
+
+@extension("VK_KHX_external_semaphore_win32") // 79
+class VkImportSemaphoreWin32HandleInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSemaphore                                 semaphore
+    VkExternalSemaphoreHandleTypeFlagsKHX       handleType
+    platform.HANDLE                             handle
+}
+
+@extension("VK_KHX_external_semaphore_win32") // 79
+class VkExportSemaphoreWin32HandleInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    const platform.SECURITY_ATTRIBUTES*         pAttributes
+    platform.DWORD                              dwAccess
+    platform.LPCWSTR                            name
+}
+
+@extension("VK_KHX_external_semaphore_win32") // 79
+class VkD3D12FenceSubmitInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         waitSemaphoreValuesCount
+    const u64*                                  pWaitSemaphoreValues
+    u32                                         signalSemaphoreValuesCount
+    const u64*                                  pSignalSemaphoreValues
+}
+
+@extension("VK_KHX_external_semaphore_fd") // 80
+class VkImportSemaphoreFdInfoKHX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSemaphore                                 semaphore
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType
+    s32                                         fd
+}
+
+@extension("VK_KHR_push_descriptor") // 81
+class VkPhysicalDevicePushDescriptorPropertiesKHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    u32                                         maxPushDescriptors
+}
+
+@extension("VK_KHR_incremental_present") // 85
+class VkRectLayerKHR {
+    VkOffset2D                                  offset
+    VkExtent2D                                  extent
+    u32                                         layer
+}
+
+@extension("VK_KHR_incremental_present") // 85
+class VkPresentRegionKHR {
+    u32                                         rectangleCount
+    const VkRectLayerKHR*                       pRectangles
+}
+
+@extension("VK_KHR_incremental_present") // 85
+class VkPresentRegionsKHR {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         swapchainCount
+    const VkPresentRegionKHR*                   pRegions
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+class VkDescriptorUpdateTemplateEntryKHR {
+    u32                                         dstBinding
+    u32                                         dstArrayElement
+    u32                                         descriptorCount
+    VkDescriptorType                            descriptorType
+    platform.size_t                             offset
+    platform.size_t                             stride
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+class VkDescriptorUpdateTemplateCreateInfoKHR {
+    VkStructureType                              sType
+    void*                                        pNext
+    VkDescriptorUpdateTemplateCreateFlagsKHR     flags
+    u32                                          descriptorUpdateEntryCount
+    const VkDescriptorUpdateTemplateEntryKHR*    pDescriptorUpdateEntries
+    VkDescriptorUpdateTemplateTypeKHR            templateType
+    VkDescriptorSetLayout                        descriptorSetLayout
+    VkPipelineBindPoint                          pipelineBindPoint
+    VkPipelineLayout                             pipelineLayout
+    u32                                          set
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
 class VkDeviceGeneratedCommandsFeaturesNVX {
     VkStructureType                             sType
     const void*                                 pNext
     VkBool32                                    computeBindingPointSupport
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkDeviceGeneratedCommandsLimitsNVX {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3165,14 +3987,14 @@
     u32                                         minCommandsTokenBufferOffsetAlignment
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkIndirectCommandsTokenNVX {
     VkIndirectCommandsTokenTypeNVX              tokenType
     VkBuffer                                    buffer
     VkDeviceSize                                offset
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkIndirectCommandsLayoutTokenNVX {
     VkIndirectCommandsTokenTypeNVX              tokenType
     u32                                         bindingUnit
@@ -3180,7 +4002,7 @@
     u32                                         divisor
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkIndirectCommandsLayoutCreateInfoNVX {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3190,7 +4012,7 @@
     const VkIndirectCommandsLayoutTokenNVX*     pTokens
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkCmdProcessCommandsInfoNVX {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3206,7 +4028,7 @@
     VkDeviceSize                                sequencesIndexOffset
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkCmdReserveSpaceForCommandsInfoNVX {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3215,7 +4037,7 @@
     u32                                         maxSequencesCount
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkObjectTableCreateInfoNVX {
     VkStructureType                             sType
     const void*                                 pNext
@@ -3230,20 +4052,20 @@
     u32                                         maxPipelineLayouts
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkObjectTableEntryNVX {
     VkObjectEntryTypeNVX                        type
     VkObjectEntryUsageFlagsNVX                  flags
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkObjectTablePipelineEntryNVX {
     VkObjectEntryTypeNVX                        type
     VkObjectEntryUsageFlagsNVX                  flags
     VkPipeline                                  pipeline
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkObjectTableDescriptorSetEntryNVX {
     VkObjectEntryTypeNVX                        type
     VkObjectEntryUsageFlagsNVX                  flags
@@ -3251,21 +4073,22 @@
     VkDescriptorSet                             descriptorSet
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkObjectTableVertexBufferEntryNVX {
     VkObjectEntryTypeNVX                        type
     VkObjectEntryUsageFlagsNVX                  flags
     VkBuffer                                    buffer
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkObjectTableIndexBufferEntryNVX {
     VkObjectEntryTypeNVX                        type
     VkObjectEntryUsageFlagsNVX                  flags
     VkBuffer                                    buffer
+    VkIndexType                                 indexType
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 class VkObjectTablePushConstantEntryNVX {
     VkObjectEntryTypeNVX                        type
     VkObjectEntryUsageFlagsNVX                  flags
@@ -3273,43 +4096,169 @@
     VkShaderStageFlags                          stageFlags
 }
 
-@extension("VK_KHR_incremental_present")
-class VkRectLayerKHR {
-    VkOffset2D                                  offset
-    VkExtent2D                                  extent
-    u32                                         layer
+@extension("VK_NV_clip_space_w_scaling") // 88
+class VkViewportWScalingNV {
+    f32                                         xcoeff
+    f32                                         ycoeff
 }
 
-@extension("VK_KHR_incremental_present")
-class VkPresentRegionKHR {
-    u32                                         rectangleCount
-    const VkRectLayerKHR*                       pRectangles
-}
-
-@extension("VK_KHR_incremental_present")
-class VkPresentRegionsKHR {
+@extension("VK_NV_clip_space_w_scaling") // 88
+class VkPipelineViewportWScalingStateCreateInfoNV {
     VkStructureType                             sType
     const void*                                 pNext
-    u32                                         swapchainCount
-    const VkPresentRegionKHR*                   pRegions
+    VkBool32                                    viewportWScalingEnable
+    u32                                         viewportCount
+    const VkViewportWScalingNV*                 pViewportWScalings
 }
 
-@extension("VK_EXT_hdr_metadata")
+@extension("VK_EXT_display_surface_counter") // 91
+class VkSurfaceCapabilities2EXT {
+    VkStructureType                             sType
+    void*                                       pNext
+    u32                                         minImageCount
+    u32                                         maxImageCount
+    VkExtent2D                                  currentExtent
+    VkExtent2D                                  minImageExtent
+    VkExtent2D                                  maxImageExtent
+    u32                                         maxImageArrayLayers
+    VkSurfaceTransformFlagsKHR                  supportedTransforms
+    VkSurfaceTransformFlagBitsKHR               currentTransform
+    VkCompositeAlphaFlagsKHR                    supportedCompositeAlpha
+    VkImageUsageFlags                           supportedUsageFlags
+    VkSurfaceCounterFlagsEXT                    supportedSurfaceCounters
+}
+
+@extension("VK_EXT_display_control") // 92
+class VkDisplayPowerInfoEXT {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkDisplayPowerStateEXT                      powerState
+}
+
+@extension("VK_EXT_display_control") // 92
+class VkDeviceEventInfoEXT {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkDeviceEventTypeEXT                        deviceEvent
+}
+
+@extension("VK_EXT_display_control") // 92
+class VkDisplayEventInfoEXT {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkDisplayEventTypeEXT                       displayEvent
+}
+
+@extension("VK_EXT_display_control") // 92
+class VkSwapchainCounterCreateInfoEXT {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSurfaceCounterFlagsEXT                    surfaceCounters
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+class VkRefreshCycleDurationGOOGLE {
+    u64                                             refreshDuration
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+class VkPastPresentationTimingGOOGLE {
+    u32                                             presentID
+    u64                                             desiredPresentTime
+    u64                                             actualPresentTime
+    u64                                             earliestPresentTime
+    u64                                             presentMargin
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+class VkPresentTimeGOOGLE {
+    u32                                             presentID
+    u64                                             desiredPresentTime
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+class VkPresentTimesInfoGOOGLE {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    u32                                             swapchainCount
+    const VkPresentTimeGOOGLE*                      pTimes
+}
+
+@extension("VK_NVX_multiview_per_view_attributes") // 98
+class VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkBool32                                    perViewPositionAllComponents
+}
+
+@extension("VK_NV_viewport_swizzle") // 99
+class VkViewportSwizzleNV {
+    VkViewportCoordinateSwizzleNV               x
+    VkViewportCoordinateSwizzleNV               y
+    VkViewportCoordinateSwizzleNV               z
+    VkViewportCoordinateSwizzleNV               w
+}
+
+@extension("VK_NV_viewport_swizzle") // 99
+class VkPipelineViewportSwizzleStateCreateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkPipelineViewportSwizzleStateCreateFlagsNV flags
+    u32                                         viewportCount
+    const VkViewportSwizzleNV*                  pViewportSwizzles
+}
+
+@extension("VK_EXT_discard_rectangles") // 100
+class VkPhysicalDeviceDiscardRectanglePropertiesEXT {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         maxDiscardRectangles
+}
+
+@extension("VK_EXT_discard_rectangles") // 100
+class VkPipelineDiscardRectangleStateCreateInfoEXT {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkPipelineDiscardRectangleStateCreateFlagsEXT   flags
+    VkDiscardRectangleModeEXT                       discardRectangleMode
+    u32                                             discardRectangleCount
+    const VkRect2D*                                 pDiscardRectangles
+}
+
+@extension("VK_EXT_hdr_metadata") // 106
 class VkXYColorEXT {
-    f32    x
-    f32    y
+    f32                                             x
+    f32                                             y
 }
 
-@extension("VK_EXT_hdr_metadata")
+@extension("VK_EXT_hdr_metadata") // 106
 class VkHdrMetadataEXT {
-    VkXYColorEXT    displayPrimaryRed
-    VkXYColorEXT    displayPrimaryGreen
-    VkXYColorEXT    displayPrimaryBlue
-    VkXYColorEXT    whitePoint
-    f32             maxLuminance
-    f32             minLuminance
-    f32             maxContentLightLevel
-    f32             maxFrameAverageLightLevel
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkXYColorEXT                                    displayPrimaryRed
+    VkXYColorEXT                                    displayPrimaryGreen
+    VkXYColorEXT                                    displayPrimaryBlue
+    VkXYColorEXT                                    whitePoint
+    f32                                             maxLuminance
+    f32                                             minLuminance
+    f32                                             maxContentLightLevel
+    f32                                             maxFrameAverageLightLevel
+}
+
+@extension("VK_MVK_ios_surface") // 123
+class VkIOSSurfaceCreateInfoMVK {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkIOSSurfaceCreateFlagsMVK                      flags
+    const void*                                     pView
+}
+
+@extension("VK_MVK_macos_surface") // 124
+class VkMacOSSurfaceCreateInfoMVK {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkMacOSSurfaceCreateFlagsMVK                    flags
+    const void*                                     pView
 }
 
 ////////////////
@@ -5033,9 +5982,9 @@
 @threadSafety("app")
 cmd void vkCmdDispatch(
         VkCommandBuffer                             commandBuffer,
-        u32                                         x,
-        u32                                         y,
-        u32                                         z) {
+        u32                                         groupCountX,
+        u32                                         groupCountY,
+        u32                                         groupCountZ) {
     commandBufferObject := GetCommandBuffer(commandBuffer)
 
     commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_COMPUTE_BIT)
@@ -5507,7 +6456,7 @@
     }
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 cmd void vkDestroySurfaceKHR(
         VkInstance                                  instance,
         VkSurfaceKHR                                surface,
@@ -5519,7 +6468,7 @@
     State.Surfaces[surface] = null
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 cmd VkResult vkGetPhysicalDeviceSurfaceSupportKHR(
         VkPhysicalDevice                            physicalDevice,
         u32                                         queueFamilyIndex,
@@ -5530,7 +6479,7 @@
     return ?
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 cmd VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
         VkPhysicalDevice                            physicalDevice,
         VkSurfaceKHR                                surface,
@@ -5543,7 +6492,7 @@
     return ?
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 cmd VkResult vkGetPhysicalDeviceSurfaceFormatsKHR(
         VkPhysicalDevice                            physicalDevice,
         VkSurfaceKHR                                surface,
@@ -5563,7 +6512,7 @@
     return ?
 }
 
-@extension("VK_KHR_surface")
+@extension("VK_KHR_surface") // 1
 cmd VkResult vkGetPhysicalDeviceSurfacePresentModesKHR(
         VkPhysicalDevice                            physicalDevice,
         VkSurfaceKHR                                surface,
@@ -5583,7 +6532,7 @@
     return ?
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 cmd VkResult vkCreateSwapchainKHR(
         VkDevice                                 device,
         const VkSwapchainCreateInfoKHR*          pCreateInfo,
@@ -5599,7 +6548,7 @@
     return ?
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 cmd void vkDestroySwapchainKHR(
         VkDevice                                 device,
         VkSwapchainKHR                           swapchain,
@@ -5611,7 +6560,7 @@
     State.Swapchains[swapchain] = null
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 cmd VkResult vkGetSwapchainImagesKHR(
         VkDevice                                 device,
         VkSwapchainKHR                           swapchain,
@@ -5632,7 +6581,7 @@
     return ?
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 cmd VkResult vkAcquireNextImageKHR(
         VkDevice                                 device,
         VkSwapchainKHR                           swapchain,
@@ -5649,7 +6598,7 @@
     return ?
 }
 
-@extension("VK_KHR_swapchain")
+@extension("VK_KHR_swapchain") // 2
 cmd VkResult vkQueuePresentKHR(
         VkQueue                                  queue,
         const VkPresentInfoKHR*                  pPresentInfo) {
@@ -5661,7 +6610,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkGetPhysicalDeviceDisplayPropertiesKHR(
         VkPhysicalDevice                        physicalDevice,
         u32*                                    pPropertyCount,
@@ -5670,7 +6619,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkGetPhysicalDeviceDisplayPlanePropertiesKHR(
         VkPhysicalDevice                        physicalDevice,
         u32*                                    pPropertyCount,
@@ -5679,7 +6628,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkGetDisplayPlaneSupportedDisplaysKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     planeIndex,
@@ -5689,7 +6638,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkGetDisplayModePropertiesKHR(
         VkPhysicalDevice                        physicalDevice,
         VkDisplayKHR                            display,
@@ -5699,7 +6648,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkCreateDisplayModeKHR(
         VkPhysicalDevice                        physicalDevice,
         VkDisplayKHR                            display,
@@ -5710,7 +6659,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkGetDisplayPlaneCapabilitiesKHR(
         VkPhysicalDevice                        physicalDevice,
         VkDisplayModeKHR                        mode,
@@ -5720,7 +6669,7 @@
     return ?
 }
 
-@extension("VK_KHR_display")
+@extension("VK_KHR_display") // 3
 cmd VkResult vkCreateDisplayPlaneSurfaceKHR(
         VkInstance                              instance,
         const VkDisplaySurfaceCreateInfoKHR*    pCreateInfo,
@@ -5729,7 +6678,7 @@
     return ?
 }
 
-@extension("VK_KHR_display_swapchain")
+@extension("VK_KHR_display_swapchain") // 4
 cmd VkResult vkCreateSharedSwapchainsKHR(
         VkDevice                                device,
         u32                                     swapchainCount,
@@ -5739,7 +6688,7 @@
     return ?
 }
 
-@extension("VK_KHR_xlib_surface")
+@extension("VK_KHR_xlib_surface") // 5
 cmd VkResult vkCreateXlibSurfaceKHR(
         VkInstance                              instance,
         const VkXlibSurfaceCreateInfoKHR*       pCreateInfo,
@@ -5749,7 +6698,7 @@
     return ?
 }
 
-@extension("VK_KHR_xlib_surface")
+@extension("VK_KHR_xlib_surface") // 5
 cmd VkBool32 vkGetPhysicalDeviceXlibPresentationSupportKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     queueFamilyIndex,
@@ -5759,7 +6708,7 @@
     return ?
 }
 
-@extension("VK_KHR_xcb_surface")
+@extension("VK_KHR_xcb_surface") // 6
 cmd VkResult vkCreateXcbSurfaceKHR(
         VkInstance                              instance,
         const VkXcbSurfaceCreateInfoKHR*        pCreateInfo,
@@ -5769,7 +6718,7 @@
     return ?
 }
 
-@extension("VK_KHR_xcb_surface")
+@extension("VK_KHR_xcb_surface") // 6
 cmd VkBool32 vkGetPhysicalDeviceXcbPresentationSupportKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     queueFamilyIndex,
@@ -5779,7 +6728,7 @@
     return ?
 }
 
-@extension("VK_KHR_wayland_surface")
+@extension("VK_KHR_wayland_surface") // 7
 cmd VkResult vkCreateWaylandSurfaceKHR(
         VkInstance                              instance,
         const VkWaylandSurfaceCreateInfoKHR*    pCreateInfo,
@@ -5789,7 +6738,7 @@
     return ?
 }
 
-@extension("VK_KHR_wayland_surface")
+@extension("VK_KHR_wayland_surface") // 7
 cmd VkBool32 vkGetPhysicalDeviceWaylandPresentationSupportKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     queueFamilyIndex,
@@ -5798,7 +6747,7 @@
     return ?
 }
 
-@extension("VK_KHR_mir_surface")
+@extension("VK_KHR_mir_surface") // 8
 cmd VkResult vkCreateMirSurfaceKHR(
         VkInstance                              instance,
         const VkMirSurfaceCreateInfoKHR*        pCreateInfo,
@@ -5808,7 +6757,7 @@
     return ?
 }
 
-@extension("VK_KHR_mir_surface")
+@extension("VK_KHR_mir_surface") // 8
 cmd VkBool32 vkGetPhysicalDeviceMirPresentationSupportKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     queueFamilyIndex,
@@ -5817,7 +6766,7 @@
     return ?
 }
 
-@extension("VK_KHR_android_surface")
+@extension("VK_KHR_android_surface") // 9
 cmd VkResult vkCreateAndroidSurfaceKHR(
         VkInstance                              instance,
         const VkAndroidSurfaceCreateInfoKHR*    pCreateInfo,
@@ -5827,7 +6776,7 @@
     return ?
 }
 
-@extension("VK_KHR_win32_surface")
+@extension("VK_KHR_win32_surface") // 10
 cmd VkResult vkCreateWin32SurfaceKHR(
         VkInstance                              instance,
         const VkWin32SurfaceCreateInfoKHR*      pCreateInfo,
@@ -5837,7 +6786,7 @@
     return ?
 }
 
-@extension("VK_KHR_win32_surface")
+@extension("VK_KHR_win32_surface") // 10
 cmd VkResult vkGetPhysicalDeviceWin32PresentationSupportKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     queueFamilyIndex) {
@@ -5845,7 +6794,7 @@
     return ?
 }
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
 @optional
 cmd VkResult vkGetSwapchainGrallocUsageANDROID(
         VkDevice                                device,
@@ -5855,7 +6804,7 @@
     return ?
 }
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
 @optional
 cmd VkResult vkGetSwapchainGrallocUsage2ANDROID(
         VkDevice                                device,
@@ -5867,7 +6816,7 @@
     return ?
 }
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
 cmd VkResult vkAcquireImageANDROID(
         VkDevice                                device,
         VkImage                                 image,
@@ -5877,7 +6826,7 @@
     return ?
 }
 
-@extension("VK_ANDROID_native_buffer")
+@extension("VK_ANDROID_native_buffer") // 11
 cmd VkResult vkQueueSignalReleaseImageANDROID(
         VkQueue                                 queue,
         u32                                     waitSemaphoreCount,
@@ -5887,32 +6836,9 @@
     return ?
 }
 
-@extension("VK_GOOGLE_display_timing")
-cmd VkResult vkGetRefreshCycleDurationGOOGLE(
-        VkDevice                                 device,
-        VkSwapchainKHR                           swapchain,
-        VkRefreshCycleDurationGOOGLE*            pDisplayTimingProperties) {
-    deviceObject := GetDevice(device)
-    swapchainObject := GetSwapchain(swapchain)
-
-    displayTimingProperties := ?
-    pDisplayTimingProperties[0] = displayTimingProperties
-
-    return ?
-}
-
-@extension("VK_GOOGLE_display_timing")
-cmd VkResult vkGetPastPresentationTimingGOOGLE(
-        VkDevice                                 device,
-        VkSwapchainKHR                           swapchain,
-        u32*                                     pPresentationTimingCount,
-        VkPastPresentationTimingGOOGLE*          pPresentationTimings) {
-    return ?
-}
-
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 @external type void* PFN_vkDebugReportCallbackEXT
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 @pfn cmd VkBool32 vkDebugReportCallbackEXT(
         VkDebugReportFlagsEXT                   flags,
         VkDebugReportObjectTypeEXT              objectType,
@@ -5925,7 +6851,7 @@
     return ?
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 cmd VkResult vkCreateDebugReportCallbackEXT(
         VkInstance                                  instance,
         const VkDebugReportCallbackCreateInfoEXT*   pCreateInfo,
@@ -5934,14 +6860,14 @@
     return ?
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 cmd void vkDestroyDebugReportCallbackEXT(
         VkInstance                                  instance,
         VkDebugReportCallbackEXT                    callback,
         const VkAllocationCallbacks*                pAllocator) {
 }
 
-@extension("VK_EXT_debug_report")
+@extension("VK_EXT_debug_report") // 12
 cmd void vkDebugReportMessageEXT(
         VkInstance                                  instance,
         VkDebugReportFlagsEXT                       flags,
@@ -5953,38 +6879,38 @@
         const char*                                 pMessage) {
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 cmd VkResult vkDebugMarkerSetObjectTagEXT(
         VkDevice                                    device,
         VkDebugMarkerObjectTagInfoEXT*              pTagInfo) {
     return ?
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 cmd VkResult vkDebugMarkerSetObjectNameEXT(
         VkDevice                                    device,
         VkDebugMarkerObjectNameInfoEXT*             pNameInfo) {
     return ?
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 cmd void vkCmdDebugMarkerBeginEXT(
         VkCommandBuffer                             commandBuffer,
         VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo) {
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 cmd void vkCmdDebugMarkerEndEXT(
         VkCommandBuffer                             commandBuffer) {
 }
 
-@extension("VK_EXT_debug_marker")
+@extension("VK_EXT_debug_marker") // 23
 cmd void vkCmdDebugMarkerInsertEXT(
         VkCommandBuffer                             commandBuffer,
         VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo) {
 }
 
-@extension("VK_AMD_draw_indirect_count")
+@extension("VK_AMD_draw_indirect_count") // 34
 cmd void vkCmdDrawIndirectCountAMD(
         VkCommandBuffer                             commandBuffer,
         VkBuffer                                    buffer,
@@ -5995,7 +6921,7 @@
         u32                                         stride) {
 }
 
-@extension("VK_AMD_draw_indirect_count")
+@extension("VK_AMD_draw_indirect_count") // 34
 cmd void vkCmdDrawIndexedIndirectCountAMD(
         VkCommandBuffer                             commandBuffer,
         VkBuffer                                    buffer,
@@ -6006,7 +6932,7 @@
         u32                                         stride) {
 }
 
-@extension("VK_NV_external_memory_capabilities")
+@extension("VK_NV_external_memory_capabilities") // 56
 cmd VkResult vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
         VkPhysicalDevice                            physicalDevice,
         VkFormat                                    format,
@@ -6019,7 +6945,7 @@
     return ?
 }
 
-@extension("VK_NV_external_memory_win32")
+@extension("VK_NV_external_memory_win32") // 58
 cmd VkResult vkGetMemoryWin32HandleNV(
         VkDevice                                    device,
         VkDeviceMemory                              memory,
@@ -6028,26 +6954,26 @@
     return ?
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 cmd void vkGetPhysicalDeviceFeatures2KHR(
         VkPhysicalDevice                            physicalDevice,
         VkPhysicalDeviceFeatures2KHR*               pFeatures) {
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 cmd void vkGetPhysicalDeviceProperties2KHR(
         VkPhysicalDevice                            physicalDevice,
         VkPhysicalDeviceProperties2KHR*             pProperties) {
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 cmd void vkGetPhysicalDeviceFormatProperties2KHR(
         VkPhysicalDevice                            physicalDevice,
         VkFormat                                    format,
         VkFormatProperties2KHR*                     pFormatProperties) {
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 cmd VkResult vkGetPhysicalDeviceImageFormatProperties2KHR(
         VkPhysicalDevice                            physicalDevice,
         const VkPhysicalDeviceImageFormatInfo2KHR*  pImageFormatInfo,
@@ -6055,40 +6981,263 @@
     return ?
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 cmd void vkGetPhysicalDeviceQueueFamilyProperties2KHR(
         VkPhysicalDevice                            physicalDevice,
         u32*                                        pQueueFamilyPropertyCount,
         VkQueueFamilyProperties2KHR*                pQueueFamilyProperties) {
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 cmd void vkGetPhysicalDeviceMemoryProperties2KHR(
         VkPhysicalDevice                            physicalDevice,
         VkPhysicalDeviceMemoryProperties2KHR*       pMemoryProperties) {
 }
 
-@extension("VK_KHR_get_physical_device_properties2")
+@extension("VK_KHR_get_physical_device_properties2") // 60
 cmd void vkGetPhysicalDeviceSparseImageFormatProperties2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo,
-        u32*                                        pPropertyCount,
-        VkSparseImageFormatProperties2KHR*          pProperties) {
+        VkPhysicalDevice                                    physicalDevice,
+        const VkPhysicalDeviceSparseImageFormatInfo2KHR*    pFormatInfo,
+        u32*                                                pPropertyCount,
+        VkSparseImageFormatProperties2KHR*                  pProperties) {
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_KHX_device_group") // 61
+cmd void vkGetDeviceGroupPeerMemoryFeaturesKHX(
+        VkDevice                                    device,
+        u32                                         heapIndex,
+        u32                                         localDeviceIndex,
+        u32                                         remoteDeviceIndex,
+        VkPeerMemoryFeatureFlagsKHX*                pPeerMemoryFeatures) {
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkBindBufferMemory2KHX(
+        VkDevice                                    device,
+        u32                                         bindInfoCount,
+        const VkBindBufferMemoryInfoKHX*            pBindInfos) {
+    return ?
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkBindImageMemory2KHX(
+        VkDevice                                    device,
+        u32                                         bindInfoCount,
+        const VkBindImageMemoryInfoKHX*             pBindInfos) {
+    return ?
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd void vkCmdSetDeviceMaskKHX(
+        VkCommandBuffer                             commandBuffer,
+        u32                                         deviceMask) {
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkGetDeviceGroupPresentCapabilitiesKHX(
+        VkDevice                                    device,
+        VkDeviceGroupPresentCapabilitiesKHX*        pDeviceGroupPresentCapabilities) {
+    return ?
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkGetDeviceGroupSurfacePresentModesKHX(
+        VkDevice                                    device,
+        VkSurfaceKHR                                surface,
+        VkDeviceGroupPresentModeFlagsKHX*           pModes) {
+    return ?
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkAcquireNextImage2KHX(
+        VkDevice                                    device,
+        const VkAcquireNextImageInfoKHX*            pAcquireInfo,
+        u32*                                        pImageIndex) {
+    return ?
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd void vkCmdDispatchBaseKHX(
+        VkCommandBuffer                             commandBuffer,
+        u32                                         baseGroupX,
+        u32                                         baseGroupY,
+        u32                                         baseGroupZ,
+        u32                                         groupCountX,
+        u32                                         groupCountY,
+        u32                                         groupCountZ) {
+}
+
+@extension("VK_KHX_device_group") // 61
+cmd VkResult vkGetPhysicalDevicePresentRectanglesKHX(
+        VkPhysicalDevice                            physicalDevice,
+        VkSurfaceKHR                                surface,
+        u32*                                        pRectCount,
+        VkRect2D*                                   pRects) {
+    return ?
+}
+
+@extension("VK_NN_vi_surface") // 63
+cmd VkResult vkCreateViSurfaceNN(
+        VkInstance                                  instance,
+        const VkViSurfaceCreateInfoNN*              pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkSurfaceKHR*                               pSurface) {
+    return ?
+}
+
+@extension("VK_KHR_maintenance1") // 70
+cmd void vkTrimCommandPoolKHR(
+        VkDevice                                    device,
+        VkCommandPool                               commandPool,
+        VkCommandPoolTrimFlagsKHR                   flags) {
+}
+
+@extension("VK_KHX_device_group_creation") // 71
+cmd VkResult vkEnumeratePhysicalDeviceGroupsKHX(
+        VkInstance                                  instance,
+        u32*                                        pPhysicalDeviceGroupCount,
+        VkPhysicalDeviceGroupPropertiesKHX*         pPhysicalDeviceGroupProperties) {
+    return ?
+}
+
+@extension("VK_KHX_external_memory_capabilities") // 72
+cmd void vkGetPhysicalDeviceExternalBufferPropertiesKHX(
+        VkPhysicalDevice                                physicalDevice,
+        const VkPhysicalDeviceExternalBufferInfoKHX*    pExternalBufferInfo,
+        VkExternalBufferPropertiesKHX*                  pExternalBufferProperties) {
+}
+
+@extension("VK_KHX_external_memory_win32") // 74
+cmd VkResult vkGetMemoryWin32HandleKHX(
+        VkDevice                                    device,
+        VkDeviceMemory                              memory,
+        VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+        platform.HANDLE*                            pHandle) {
+    return ?
+}
+
+@extension("VK_KHX_external_memory_win32") // 74
+cmd VkResult vkGetMemoryWin32HandlePropertiesKHX(
+        VkDevice                                    device,
+        VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+        platform.HANDLE                             handle,
+        VkMemoryWin32HandlePropertiesKHX*           pMemoryWin32HandleProperties) {
+    return ?
+}
+
+@extension("VK_KHX_external_memory_fd") // 75
+cmd VkResult vkGetMemoryFdKHX(
+        VkDevice                                    device,
+        VkDeviceMemory                              memory,
+        VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+        s32*                                        pFd) {
+    return ?
+}
+
+@extension("VK_KHX_external_memory_fd") // 75
+cmd VkResult vkGetMemoryFdPropertiesKHX(
+        VkDevice                                    device,
+        VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+        s32                                         fd,
+        VkMemoryFdPropertiesKHX*                    pMemoryFdProperties) {
+    return ?
+}
+
+@extension("VK_KHX_external_semaphore_capabilities") // 77
+cmd void vkGetPhysicalDeviceExternalSemaphorePropertiesKHX(
+        VkPhysicalDevice                            physicalDevice,
+        const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo,
+        VkExternalSemaphorePropertiesKHX*           pExternalSemaphoreProperties) {
+}
+
+@extension("VK_KHX_external_semaphore_win32") // 79
+cmd VkResult vkImportSemaphoreWin32HandleKHX(
+        VkDevice                                    device,
+        const VkImportSemaphoreWin32HandleInfoKHX*  pImportSemaphoreWin32HandleInfo) {
+    return ?
+}
+
+@extension("VK_KHX_external_semaphore_win32") // 79
+cmd VkResult vkGetSemaphoreWin32HandleKHX(
+        VkDevice                                    device,
+        VkSemaphore                                 semaphore,
+        VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType,
+        platform.HANDLE*                            pHandle) {
+    return ?
+}
+
+@extension("VK_KHX_external_semaphore_fd") // 80
+cmd VkResult vkImportSemaphoreFdKHX(
+        VkDevice                                    device,
+        const VkImportSemaphoreFdInfoKHX*           pImportSemaphoreFdInfo) {
+    return ?
+}
+
+@extension("VK_KHX_external_semaphore_fd") // 80
+cmd VkResult vkGetSemaphoreFdKHX(
+        VkDevice                                    device,
+        VkSemaphore                                 semaphore,
+        VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType,
+        s32*                                        pFd) {
+    return ?
+}
+
+@extension("VK_KHR_push_descriptor") // 81
+cmd void vkCmdPushDescriptorSetKHR(
+        VkCommandBuffer                             commandBuffer,
+        VkPipelineBindPoint                         pipelineBindPoint,
+        VkPipelineLayout                            layout,
+        u32                                         set,
+        u32                                         descriptorWriteCount,
+        const VkWriteDescriptorSet*                 pDescriptorWrites) {
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+cmd VkResult vkCreateDescriptorUpdateTemplateKHR(
+        VkDevice                                    device,
+        const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkDescriptorUpdateTemplateKHR*              pDescriptorUpdateTemplate) {
+    return ?
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+cmd void vkDestroyDescriptorUpdateTemplateKHR(
+        VkDevice                                    device,
+        VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+        const VkAllocationCallbacks*                pAllocator) {
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+cmd void vkUpdateDescriptorSetWithTemplateKHR(
+        VkDevice                                    device,
+        VkDescriptorSet                             descriptorSet,
+        VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+        const void*                                 pData) {
+}
+
+@extension("VK_KHR_descriptor_update_template") // 86
+cmd void vkCmdPushDescriptorSetWithTemplateKHR(
+        VkCommandBuffer                             commandBuffer,
+        VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+        VkPipelineLayout                            layout,
+        u32                                         set,
+        const void*                                 pData) {
+}
+
+@extension("VK_NVX_device_generated_commands") // 87
 cmd void vkCmdProcessCommandsNVX(
         VkCommandBuffer                             commandBuffer,
         const VkCmdProcessCommandsInfoNVX*          pProcessCommandsInfo) {
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 cmd void vkCmdReserveSpaceForCommandsNVX(
         VkCommandBuffer                             commandBuffer,
         const VkCmdReserveSpaceForCommandsInfoNVX*  pReserveSpaceInfo) {
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 cmd VkResult vkCreateIndirectCommandsLayoutNVX(
         VkDevice                                    device,
         const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo,
@@ -6097,14 +7246,14 @@
     return ?
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 cmd void vkDestroyIndirectCommandsLayoutNVX(
         VkDevice                                    device,
         VkIndirectCommandsLayoutNVX                 indirectCommandsLayout,
         const VkAllocationCallbacks*                pAllocator) {
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 cmd VkResult vkCreateObjectTableNVX(
         VkDevice                                    device,
         const VkObjectTableCreateInfoNVX*           pCreateInfo,
@@ -6113,14 +7262,14 @@
     return ?
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 cmd void vkDestroyObjectTableNVX(
         VkDevice                                    device,
         VkObjectTableNVX                            objectTable,
         const VkAllocationCallbacks*                pAllocator) {
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 cmd VkResult vkRegisterObjectsNVX(
         VkDevice                                    device,
         VkObjectTableNVX                            objectTable,
@@ -6130,7 +7279,7 @@
     return ?
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 cmd VkResult vkUnregisterObjectsNVX(
         VkDevice                                    device,
         VkObjectTableNVX                            objectTable,
@@ -6140,28 +7289,153 @@
     return ?
 }
 
-@extension("VK_NVX_device_generated_commands")
+@extension("VK_NVX_device_generated_commands") // 87
 cmd void vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX(
         VkPhysicalDevice                            physicalDevice,
         VkDeviceGeneratedCommandsFeaturesNVX*       pFeatures,
         VkDeviceGeneratedCommandsLimitsNVX*         pLimits) {
 }
 
-@extension("VK_EXT_hdr_metadata")
-cmd void vkSetHdrMetadataEXT(
-    VkDevice                                        device,
-    u32                                             swapchainCount,
-    const VkSwapchainKHR*                           pSwapchains,
-    const VkHdrMetadataEXT*                         pMetadata) {
+@extension("VK_NV_clip_space_w_scaling") // 88
+cmd void vkCmdSetViewportWScalingNV(
+        VkCommandBuffer                             commandBuffer,
+        u32                                         firstViewport,
+        u32                                         viewportCount,
+        const VkViewportWScalingNV*                 pViewportWScalings) {
 }
 
-@extension("VK_KHR_shared_presentable_image")
+@extension("VK_EXT_direct_mode_display") // 89
+cmd VkResult vkReleaseDisplayEXT(
+        VkPhysicalDevice                            physicalDevice,
+        VkDisplayKHR                                display) {
+    return ?
+}
+
+@extension("VK_EXT_acquire_xlib_display") // 90
+cmd VkResult vkAcquireXlibDisplayEXT(
+        VkPhysicalDevice                            physicalDevice,
+        platform.Display*                           dpy,
+        VkDisplayKHR                                display) {
+    return ?
+}
+
+@extension("VK_EXT_acquire_xlib_display") // 90
+cmd VkResult vkGetRandROutputDisplayEXT(
+        VkPhysicalDevice                            physicalDevice,
+        platform.Display*                           dpy,
+        platform.RROutput                           rrOutput,
+        VkDisplayKHR*                               pDisplay) {
+    return ?
+}
+
+@extension("VK_EXT_display_surface_counter") // 91
+cmd VkResult vkGetPhysicalDeviceSurfaceCapabilities2EXT(
+        VkPhysicalDevice                            physicalDevice,
+        VkSurfaceKHR                                surface,
+        VkSurfaceCapabilities2EXT*                  pSurfaceCapabilities) {
+    return ?
+}
+
+@extension("VK_EXT_display_control") // 92
+cmd VkResult vkDisplayPowerControlEXT(
+        VkDevice                                    device,
+        VkDisplayKHR                                display,
+        const VkDisplayPowerInfoEXT*                pDisplayPowerInfo) {
+    return ?
+}
+
+@extension("VK_EXT_display_control") // 92
+cmd VkResult vkRegisterDeviceEventEXT(
+        VkDevice                                    device,
+        const VkDeviceEventInfoEXT*                 pDeviceEventInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkFence*                                    pFence) {
+    return ?
+}
+
+@extension("VK_EXT_display_control") // 92
+cmd VkResult vkRegisterDisplayEventEXT(
+        VkDevice                                    device,
+        VkDisplayKHR                                display,
+        const VkDisplayEventInfoEXT*                pDisplayEventInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkFence*                                    pFence) {
+    return ?
+}
+
+@extension("VK_EXT_display_control") // 92
+cmd VkResult vkGetSwapchainCounterEXT(
+        VkDevice                                    device,
+        VkSwapchainKHR                              swapchain,
+        VkSurfaceCounterFlagBitsEXT                 counter,
+        u64*                                        pCounterValue) {
+    return ?
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+cmd VkResult vkGetRefreshCycleDurationGOOGLE(
+        VkDevice                                    device,
+        VkSwapchainKHR                              swapchain,
+        VkRefreshCycleDurationGOOGLE*               pDisplayTimingProperties) {
+    deviceObject := GetDevice(device)
+    swapchainObject := GetSwapchain(swapchain)
+
+    displayTimingProperties := ?
+    pDisplayTimingProperties[0] = displayTimingProperties
+
+    return ?
+}
+
+@extension("VK_GOOGLE_display_timing") // 93
+cmd VkResult vkGetPastPresentationTimingGOOGLE(
+        VkDevice                                    device,
+        VkSwapchainKHR                              swapchain,
+        u32*                                        pPresentationTimingCount,
+        VkPastPresentationTimingGOOGLE*             pPresentationTimings) {
+    return ?
+}
+
+@extension("VK_EXT_discard_rectangles") // 100
+cmd void vkCmdSetDiscardRectangleEXT(
+        VkCommandBuffer                             commandBuffer,
+        u32                                         firstDiscardRectangle,
+        u32                                         discardRectangleCount,
+        const VkRect2D*                             pDiscardRectangles) {
+}
+
+@extension("VK_EXT_hdr_metadata") // 106
+cmd void vkSetHdrMetadataEXT(
+        VkDevice                                    device,
+        u32                                         swapchainCount,
+        const VkSwapchainKHR*                       pSwapchains,
+        const VkHdrMetadataEXT*                     pMetadata) {
+}
+
+@extension("VK_KHR_shared_presentable_image") // 112
 cmd VkResult vkGetSwapchainStatusKHR(
         VkDevice                                    device,
         VkSwapchainKHR                              swapchain) {
     return ?
 }
 
+@extension("VK_MVK_ios_surface") // 123
+cmd VkResult vkCreateIOSSurfaceMVK(
+        VkInstance                                  instance,
+        const VkIOSSurfaceCreateInfoMVK*            pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkSurfaceKHR*                               pSurface) {
+    return ?
+}
+
+@extension("VK_MVK_macos_surface") // 124
+cmd VkResult vkCreateMacOSSurfaceMVK(
+        VkInstance                                  instance,
+        const VkMacOSSurfaceCreateInfoMVK*          pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkSurfaceKHR*                               pSurface) {
+    return ?
+}
+
 ////////////////
 // Validation //
 ////////////////
diff --git a/vulkan/include/vulkan/vk_platform.h b/vulkan/include/vulkan/vk_platform.h
index 2054447..6dc5eb5 100644
--- a/vulkan/include/vulkan/vk_platform.h
+++ b/vulkan/include/vulkan/vk_platform.h
@@ -2,7 +2,7 @@
 // File: vk_platform.h
 //
 /*
-** Copyright (c) 2014-2015 The Khronos Group Inc.
+** Copyright (c) 2014-2017 The Khronos Group Inc.
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
diff --git a/vulkan/include/vulkan/vulkan.h b/vulkan/include/vulkan/vulkan.h
index 3c8ee1c..8bb5448 100644
--- a/vulkan/include/vulkan/vulkan.h
+++ b/vulkan/include/vulkan/vulkan.h
@@ -43,11 +43,11 @@
 #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff)
 #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff)
 // Version of this file
-#define VK_HEADER_VERSION 38
+#define VK_HEADER_VERSION 43
 
 
 #define VK_NULL_HANDLE 0
-        
+
 
 
 #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
@@ -60,7 +60,7 @@
         #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
 #endif
 #endif
-        
+
 
 
 typedef uint32_t VkFlags;
@@ -145,6 +145,8 @@
     VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001,
     VK_ERROR_VALIDATION_FAILED_EXT = -1000011001,
     VK_ERROR_INVALID_SHADER_NV = -1000012000,
+    VK_ERROR_OUT_OF_POOL_MEMORY_KHR = -1000069000,
+    VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX = -1000072003,
     VK_RESULT_BEGIN_RANGE = VK_ERROR_FRAGMENTED_POOL,
     VK_RESULT_END_RANGE = VK_INCOMPLETE,
     VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FRAGMENTED_POOL + 1),
@@ -220,6 +222,9 @@
     VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000,
     VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001,
     VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002,
+    VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHX = 1000053000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHX = 1000053001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHX = 1000053002,
     VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000,
     VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001,
     VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000,
@@ -234,15 +239,70 @@
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = 1000059006,
     VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
+    VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX = 1000060000,
+    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX = 1000060001,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX = 1000060002,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX = 1000060003,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX = 1000060005,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHX = 1000060006,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHX = 1000060007,
+    VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHX = 1000060008,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHX = 1000060009,
+    VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX = 1000060011,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX = 1000060012,
     VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000,
+    VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN = 1000062000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHX = 1000070000,
+    VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHX = 1000070001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHX = 1000071000,
+    VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHX = 1000071001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHX = 1000071002,
+    VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHX = 1000071003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHX = 1000071004,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHX = 1000071005,
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHX = 1000071006,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHX = 1000071007,
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHX = 1000072000,
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHX = 1000072001,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHX = 1000072002,
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHX = 1000073000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHX = 1000073001,
+    VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHX = 1000073002,
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHX = 1000074000,
+    VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHX = 1000074001,
+    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHX = 1000075000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHX = 1000076000,
+    VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHX = 1000076001,
+    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHX = 1000077000,
+    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX = 1000078000,
+    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX = 1000078001,
+    VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHX = 1000078002,
+    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHX = 1000079000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR = 1000080000,
     VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000,
+    VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = 1000085000,
     VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000,
     VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001,
     VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002,
     VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003,
     VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004,
     VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005,
+    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000,
+    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = 1000090000,
+    VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000,
+    VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT = 1000091001,
+    VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT = 1000091002,
+    VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT = 1000091003,
     VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE = 1000092000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX = 1000097000,
+    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV = 1000098000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT = 1000099000,
+    VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT = 1000099001,
+    VK_STRUCTURE_TYPE_HDR_METADATA_EXT = 1000105000,
+    VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK = 1000122000,
+    VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000,
     VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO,
     VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO,
     VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1),
@@ -712,6 +772,8 @@
     VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK = 6,
     VK_DYNAMIC_STATE_STENCIL_WRITE_MASK = 7,
     VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8,
+    VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV = 1000087000,
+    VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000,
     VK_DYNAMIC_STATE_BEGIN_RANGE = VK_DYNAMIC_STATE_VIEWPORT,
     VK_DYNAMIC_STATE_END_RANGE = VK_DYNAMIC_STATE_STENCIL_REFERENCE,
     VK_DYNAMIC_STATE_RANGE_SIZE = (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1),
@@ -852,6 +914,8 @@
     VK_FORMAT_FEATURE_BLIT_DST_BIT = 0x00000800,
     VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 0x00001000,
     VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG = 0x00002000,
+    VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR = 0x00004000,
+    VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR = 0x00008000,
     VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkFormatFeatureFlagBits;
 typedef VkFlags VkFormatFeatureFlags;
@@ -875,6 +939,8 @@
     VK_IMAGE_CREATE_SPARSE_ALIASED_BIT = 0x00000004,
     VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT = 0x00000008,
     VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010,
+    VK_IMAGE_CREATE_BIND_SFR_BIT_KHX = 0x00000040,
+    VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = 0x00000020,
     VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkImageCreateFlagBits;
 typedef VkFlags VkImageCreateFlags;
@@ -912,6 +978,7 @@
 
 typedef enum VkMemoryHeapFlagBits {
     VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 0x00000001,
+    VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHX = 0x00000002,
     VK_MEMORY_HEAP_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkMemoryHeapFlagBits;
 typedef VkFlags VkMemoryHeapFlags;
@@ -1029,6 +1096,8 @@
     VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT = 0x00000001,
     VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT = 0x00000002,
     VK_PIPELINE_CREATE_DERIVATIVE_BIT = 0x00000004,
+    VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHX = 0x00000008,
+    VK_PIPELINE_CREATE_DISPATCH_BASE_KHX = 0x00000010,
     VK_PIPELINE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkPipelineCreateFlagBits;
 typedef VkFlags VkPipelineCreateFlags;
@@ -1075,6 +1144,11 @@
 typedef VkFlags VkPipelineLayoutCreateFlags;
 typedef VkFlags VkShaderStageFlags;
 typedef VkFlags VkSamplerCreateFlags;
+
+typedef enum VkDescriptorSetLayoutCreateFlagBits {
+    VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR = 0x00000001,
+    VK_DESCRIPTOR_SET_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkDescriptorSetLayoutCreateFlagBits;
 typedef VkFlags VkDescriptorSetLayoutCreateFlags;
 
 typedef enum VkDescriptorPoolCreateFlagBits {
@@ -1091,6 +1165,12 @@
     VK_ATTACHMENT_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkAttachmentDescriptionFlagBits;
 typedef VkFlags VkAttachmentDescriptionFlags;
+
+typedef enum VkSubpassDescriptionFlagBits {
+    VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX = 0x00000001,
+    VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002,
+    VK_SUBPASS_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSubpassDescriptionFlagBits;
 typedef VkFlags VkSubpassDescriptionFlags;
 
 typedef enum VkAccessFlagBits {
@@ -1119,6 +1199,8 @@
 
 typedef enum VkDependencyFlagBits {
     VK_DEPENDENCY_BY_REGION_BIT = 0x00000001,
+    VK_DEPENDENCY_VIEW_LOCAL_BIT_KHX = 0x00000002,
+    VK_DEPENDENCY_DEVICE_GROUP_BIT_KHX = 0x00000004,
     VK_DEPENDENCY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkDependencyFlagBits;
 typedef VkFlags VkDependencyFlags;
@@ -2381,7 +2463,7 @@
 typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexed)(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance);
 typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
 typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
-typedef void (VKAPI_PTR *PFN_vkCmdDispatch)(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z);
+typedef void (VKAPI_PTR *PFN_vkCmdDispatch)(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
 typedef void (VKAPI_PTR *PFN_vkCmdDispatchIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyBuffer)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions);
@@ -3017,9 +3099,9 @@
 
 VKAPI_ATTR void VKAPI_CALL vkCmdDispatch(
     VkCommandBuffer                             commandBuffer,
-    uint32_t                                    x,
-    uint32_t                                    y,
-    uint32_t                                    z);
+    uint32_t                                    groupCountX,
+    uint32_t                                    groupCountY,
+    uint32_t                                    groupCountZ);
 
 VKAPI_ATTR void VKAPI_CALL vkCmdDispatchIndirect(
     VkCommandBuffer                             commandBuffer,
@@ -3335,6 +3417,11 @@
 #define VK_KHR_SWAPCHAIN_SPEC_VERSION     68
 #define VK_KHR_SWAPCHAIN_EXTENSION_NAME   "VK_KHR_swapchain"
 
+
+typedef enum VkSwapchainCreateFlagBitsKHR {
+    VK_SWAPCHAIN_CREATE_BIND_SFR_BIT_KHX = 0x00000001,
+    VK_SWAPCHAIN_CREATE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkSwapchainCreateFlagBitsKHR;
 typedef VkFlags VkSwapchainCreateFlagsKHR;
 
 typedef struct VkSwapchainCreateInfoKHR {
@@ -3635,7 +3722,7 @@
 #define VK_KHR_wayland_surface 1
 #include <wayland-client.h>
 
-#define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 5
+#define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 6
 #define VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME "VK_KHR_wayland_surface"
 
 typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
@@ -3877,44 +3964,125 @@
     VkSparseImageFormatProperties2KHR*          pProperties);
 #endif
 
-#define VK_KHR_incremental_present 1
-#define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
-#define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present"
+#define VK_KHR_shader_draw_parameters 1
+#define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1
+#define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters"
 
-typedef struct VkRectLayerKHR {
-    VkOffset2D offset;
-    VkExtent2D extent;
-    uint32_t layer;
-} VkRectLayerKHR;
 
-typedef struct VkPresentRegionKHR {
-    uint32_t rectangleCount;
-    const VkRectLayerKHR* pRectangles;
-} VkPresentRegionKHR;
+#define VK_KHR_maintenance1 1
+#define VK_KHR_MAINTENANCE1_SPEC_VERSION  1
+#define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1"
 
-typedef struct VkPresentRegionsKHR {
-    VkStructureType sType;
-    const void* pNext;
-    uint32_t swapchainCount;
-    const VkPresentRegionKHR* pRegions;
-} VkPresentRegionsKHR;
+typedef VkFlags VkCommandPoolTrimFlagsKHR;
 
-#define VK_KHR_shared_presentable_image 1
-#define VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION 1
-#define VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME "VK_KHR_shared_presentable_image"
-
-typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainStatusKHR)(VkDevice device, VkSwapchainKHR swapchain);
+typedef void (VKAPI_PTR *PFN_vkTrimCommandPoolKHR)(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlagsKHR flags);
 
 #ifndef VK_NO_PROTOTYPES
-VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainStatusKHR(
+VKAPI_ATTR void VKAPI_CALL vkTrimCommandPoolKHR(
     VkDevice                                    device,
-    VkSwapchainKHR                              swapchain);
+    VkCommandPool                               commandPool,
+    VkCommandPoolTrimFlagsKHR                   flags);
+#endif
+
+#define VK_KHR_push_descriptor 1
+#define VK_KHR_PUSH_DESCRIPTOR_SPEC_VERSION 1
+#define VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME "VK_KHR_push_descriptor"
+
+typedef struct VkPhysicalDevicePushDescriptorPropertiesKHR {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           maxPushDescriptors;
+} VkPhysicalDevicePushDescriptorPropertiesKHR;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetKHR)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetKHR(
+    VkCommandBuffer                             commandBuffer,
+    VkPipelineBindPoint                         pipelineBindPoint,
+    VkPipelineLayout                            layout,
+    uint32_t                                    set,
+    uint32_t                                    descriptorWriteCount,
+    const VkWriteDescriptorSet*                 pDescriptorWrites);
+#endif
+
+#define VK_KHR_descriptor_update_template 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorUpdateTemplateKHR)
+
+#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1
+#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template"
+
+
+typedef enum VkDescriptorUpdateTemplateTypeKHR {
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR = 0,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_BEGIN_RANGE_KHR = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_END_RANGE_KHR = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR,
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_RANGE_SIZE_KHR = (VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR + 1),
+    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkDescriptorUpdateTemplateTypeKHR;
+
+typedef VkFlags VkDescriptorUpdateTemplateCreateFlagsKHR;
+
+typedef struct VkDescriptorUpdateTemplateEntryKHR {
+    uint32_t            dstBinding;
+    uint32_t            dstArrayElement;
+    uint32_t            descriptorCount;
+    VkDescriptorType    descriptorType;
+    size_t              offset;
+    size_t              stride;
+} VkDescriptorUpdateTemplateEntryKHR;
+
+typedef struct VkDescriptorUpdateTemplateCreateInfoKHR {
+    VkStructureType                              sType;
+    void*                                        pNext;
+    VkDescriptorUpdateTemplateCreateFlagsKHR     flags;
+    uint32_t                                     descriptorUpdateEntryCount;
+    const VkDescriptorUpdateTemplateEntryKHR*    pDescriptorUpdateEntries;
+    VkDescriptorUpdateTemplateTypeKHR            templateType;
+    VkDescriptorSetLayout                        descriptorSetLayout;
+    VkPipelineBindPoint                          pipelineBindPoint;
+    VkPipelineLayout                             pipelineLayout;
+    uint32_t                                     set;
+} VkDescriptorUpdateTemplateCreateInfoKHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorUpdateTemplateKHR)(VkDevice device, const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplateKHR* pDescriptorUpdateTemplate);
+typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorUpdateTemplateKHR)(VkDevice device, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
+typedef void (VKAPI_PTR *PFN_vkUpdateDescriptorSetWithTemplateKHR)(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const void* pData);
+typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetWithTemplateKHR)(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorUpdateTemplateKHR(
+    VkDevice                                    device,
+    const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkDescriptorUpdateTemplateKHR*              pDescriptorUpdateTemplate);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorUpdateTemplateKHR(
+    VkDevice                                    device,
+    VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSetWithTemplateKHR(
+    VkDevice                                    device,
+    VkDescriptorSet                             descriptorSet,
+    VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+    const void*                                 pData);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetWithTemplateKHR(
+    VkCommandBuffer                             commandBuffer,
+    VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
+    VkPipelineLayout                            layout,
+    uint32_t                                    set,
+    const void*                                 pData);
 #endif
 
 #define VK_EXT_debug_report 1
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT)
 
-#define VK_EXT_DEBUG_REPORT_SPEC_VERSION  4
+#define VK_EXT_DEBUG_REPORT_SPEC_VERSION  5
 #define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report"
 #define VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT
 
@@ -4069,7 +4237,7 @@
 
 
 #define VK_EXT_debug_marker 1
-#define VK_EXT_DEBUG_MARKER_SPEC_VERSION  3
+#define VK_EXT_DEBUG_MARKER_SPEC_VERSION  4
 #define VK_EXT_DEBUG_MARKER_EXTENSION_NAME "VK_EXT_debug_marker"
 
 typedef struct VkDebugMarkerObjectNameInfoEXT {
@@ -4154,6 +4322,7 @@
 } VkDedicatedAllocationMemoryAllocateInfoNV;
 
 
+
 #define VK_AMD_draw_indirect_count 1
 #define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
 #define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count"
@@ -4196,6 +4365,38 @@
 #define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot"
 
 
+#define VK_KHX_multiview 1
+#define VK_KHX_MULTIVIEW_SPEC_VERSION     1
+#define VK_KHX_MULTIVIEW_EXTENSION_NAME   "VK_KHX_multiview"
+
+typedef struct VkRenderPassMultiviewCreateInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           subpassCount;
+    const uint32_t*    pViewMasks;
+    uint32_t           dependencyCount;
+    const int32_t*     pViewOffsets;
+    uint32_t           correlationMaskCount;
+    const uint32_t*    pCorrelationMasks;
+} VkRenderPassMultiviewCreateInfoKHX;
+
+typedef struct VkPhysicalDeviceMultiviewFeaturesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    VkBool32           multiview;
+    VkBool32           multiviewGeometryShader;
+    VkBool32           multiviewTessellationShader;
+} VkPhysicalDeviceMultiviewFeaturesKHX;
+
+typedef struct VkPhysicalDeviceMultiviewPropertiesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           maxMultiviewViewCount;
+    uint32_t           maxMultiviewInstanceIndex;
+} VkPhysicalDeviceMultiviewPropertiesKHX;
+
+
+
 #define VK_IMG_format_pvrtc 1
 #define VK_IMG_FORMAT_PVRTC_SPEC_VERSION  1
 #define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc"
@@ -4314,6 +4515,204 @@
 
 #endif /* VK_USE_PLATFORM_WIN32_KHR */
 
+#define VK_KHX_device_group 1
+#define VK_MAX_DEVICE_GROUP_SIZE_KHX      32
+#define VK_KHX_DEVICE_GROUP_SPEC_VERSION  1
+#define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group"
+
+
+typedef enum VkPeerMemoryFeatureFlagBitsKHX {
+    VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT_KHX = 0x00000001,
+    VK_PEER_MEMORY_FEATURE_COPY_DST_BIT_KHX = 0x00000002,
+    VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT_KHX = 0x00000004,
+    VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT_KHX = 0x00000008,
+    VK_PEER_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkPeerMemoryFeatureFlagBitsKHX;
+typedef VkFlags VkPeerMemoryFeatureFlagsKHX;
+
+typedef enum VkMemoryAllocateFlagBitsKHX {
+    VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHX = 0x00000001,
+    VK_MEMORY_ALLOCATE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkMemoryAllocateFlagBitsKHX;
+typedef VkFlags VkMemoryAllocateFlagsKHX;
+
+typedef enum VkDeviceGroupPresentModeFlagBitsKHX {
+    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHX = 0x00000001,
+    VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHX = 0x00000002,
+    VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHX = 0x00000004,
+    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHX = 0x00000008,
+    VK_DEVICE_GROUP_PRESENT_MODE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkDeviceGroupPresentModeFlagBitsKHX;
+typedef VkFlags VkDeviceGroupPresentModeFlagsKHX;
+
+typedef struct VkMemoryAllocateFlagsInfoKHX {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkMemoryAllocateFlagsKHX    flags;
+    uint32_t                    deviceMask;
+} VkMemoryAllocateFlagsInfoKHX;
+
+typedef struct VkBindBufferMemoryInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBuffer           buffer;
+    VkDeviceMemory     memory;
+    VkDeviceSize       memoryOffset;
+    uint32_t           deviceIndexCount;
+    const uint32_t*    pDeviceIndices;
+} VkBindBufferMemoryInfoKHX;
+
+typedef struct VkBindImageMemoryInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkImage            image;
+    VkDeviceMemory     memory;
+    VkDeviceSize       memoryOffset;
+    uint32_t           deviceIndexCount;
+    const uint32_t*    pDeviceIndices;
+    uint32_t           SFRRectCount;
+    const VkRect2D*    pSFRRects;
+} VkBindImageMemoryInfoKHX;
+
+typedef struct VkDeviceGroupRenderPassBeginInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           deviceMask;
+    uint32_t           deviceRenderAreaCount;
+    const VkRect2D*    pDeviceRenderAreas;
+} VkDeviceGroupRenderPassBeginInfoKHX;
+
+typedef struct VkDeviceGroupCommandBufferBeginInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           deviceMask;
+} VkDeviceGroupCommandBufferBeginInfoKHX;
+
+typedef struct VkDeviceGroupSubmitInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           waitSemaphoreCount;
+    const uint32_t*    pWaitSemaphoreDeviceIndices;
+    uint32_t           commandBufferCount;
+    const uint32_t*    pCommandBufferDeviceMasks;
+    uint32_t           signalSemaphoreCount;
+    const uint32_t*    pSignalSemaphoreDeviceIndices;
+} VkDeviceGroupSubmitInfoKHX;
+
+typedef struct VkDeviceGroupBindSparseInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           resourceDeviceIndex;
+    uint32_t           memoryDeviceIndex;
+} VkDeviceGroupBindSparseInfoKHX;
+
+typedef struct VkDeviceGroupPresentCapabilitiesKHX {
+    VkStructureType                     sType;
+    const void*                         pNext;
+    uint32_t                            presentMask[VK_MAX_DEVICE_GROUP_SIZE_KHX];
+    VkDeviceGroupPresentModeFlagsKHX    modes;
+} VkDeviceGroupPresentCapabilitiesKHX;
+
+typedef struct VkImageSwapchainCreateInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkSwapchainKHR     swapchain;
+} VkImageSwapchainCreateInfoKHX;
+
+typedef struct VkBindImageMemorySwapchainInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkSwapchainKHR     swapchain;
+    uint32_t           imageIndex;
+} VkBindImageMemorySwapchainInfoKHX;
+
+typedef struct VkAcquireNextImageInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkSwapchainKHR     swapchain;
+    uint64_t           timeout;
+    VkSemaphore        semaphore;
+    VkFence            fence;
+    uint32_t           deviceMask;
+} VkAcquireNextImageInfoKHX;
+
+typedef struct VkDeviceGroupPresentInfoKHX {
+    VkStructureType                        sType;
+    const void*                            pNext;
+    uint32_t                               swapchainCount;
+    const uint32_t*                        pDeviceMasks;
+    VkDeviceGroupPresentModeFlagBitsKHX    mode;
+} VkDeviceGroupPresentInfoKHX;
+
+typedef struct VkDeviceGroupSwapchainCreateInfoKHX {
+    VkStructureType                     sType;
+    const void*                         pNext;
+    VkDeviceGroupPresentModeFlagsKHX    modes;
+} VkDeviceGroupSwapchainCreateInfoKHX;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetDeviceGroupPeerMemoryFeaturesKHX)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures);
+typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfoKHX* pBindInfos);
+typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHX* pBindInfos);
+typedef void (VKAPI_PTR *PFN_vkCmdSetDeviceMaskKHX)(VkCommandBuffer commandBuffer, uint32_t deviceMask);
+typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupPresentCapabilitiesKHX)(VkDevice device, VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities);
+typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModesKHX)(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHX* pModes);
+typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImage2KHX)(VkDevice device, const VkAcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex);
+typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHX)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDevicePresentRectanglesKHX)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetDeviceGroupPeerMemoryFeaturesKHX(
+    VkDevice                                    device,
+    uint32_t                                    heapIndex,
+    uint32_t                                    localDeviceIndex,
+    uint32_t                                    remoteDeviceIndex,
+    VkPeerMemoryFeatureFlagsKHX*                pPeerMemoryFeatures);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2KHX(
+    VkDevice                                    device,
+    uint32_t                                    bindInfoCount,
+    const VkBindBufferMemoryInfoKHX*            pBindInfos);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHX(
+    VkDevice                                    device,
+    uint32_t                                    bindInfoCount,
+    const VkBindImageMemoryInfoKHX*             pBindInfos);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdSetDeviceMaskKHX(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    deviceMask);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupPresentCapabilitiesKHX(
+    VkDevice                                    device,
+    VkDeviceGroupPresentCapabilitiesKHX*        pDeviceGroupPresentCapabilities);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModesKHX(
+    VkDevice                                    device,
+    VkSurfaceKHR                                surface,
+    VkDeviceGroupPresentModeFlagsKHX*           pModes);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHX(
+    VkDevice                                    device,
+    const VkAcquireNextImageInfoKHX*            pAcquireInfo,
+    uint32_t*                                   pImageIndex);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBaseKHX(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    baseGroupX,
+    uint32_t                                    baseGroupY,
+    uint32_t                                    baseGroupZ,
+    uint32_t                                    groupCountX,
+    uint32_t                                    groupCountY,
+    uint32_t                                    groupCountZ);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDevicePresentRectanglesKHX(
+    VkPhysicalDevice                            physicalDevice,
+    VkSurfaceKHR                                surface,
+    uint32_t*                                   pRectCount,
+    VkRect2D*                                   pRects);
+#endif
+
 #define VK_EXT_validation_flags 1
 #define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1
 #define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags"
@@ -4336,6 +4735,428 @@
 
 
 
+#ifdef VK_USE_PLATFORM_VI_NN
+#define VK_NN_vi_surface 1
+#define VK_NN_VI_SURFACE_SPEC_VERSION     1
+#define VK_NN_VI_SURFACE_EXTENSION_NAME   "VK_NN_vi_surface"
+
+typedef VkFlags VkViSurfaceCreateFlagsNN;
+
+typedef struct VkViSurfaceCreateInfoNN {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkViSurfaceCreateFlagsNN    flags;
+    void*                       window;
+} VkViSurfaceCreateInfoNN;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateViSurfaceNN)(VkInstance instance, const VkViSurfaceCreateInfoNN* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateViSurfaceNN(
+    VkInstance                                  instance,
+    const VkViSurfaceCreateInfoNN*              pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface);
+#endif
+#endif /* VK_USE_PLATFORM_VI_NN */
+
+#define VK_EXT_shader_subgroup_ballot 1
+#define VK_EXT_SHADER_SUBGROUP_BALLOT_SPEC_VERSION 1
+#define VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME "VK_EXT_shader_subgroup_ballot"
+
+
+#define VK_EXT_shader_subgroup_vote 1
+#define VK_EXT_SHADER_SUBGROUP_VOTE_SPEC_VERSION 1
+#define VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME "VK_EXT_shader_subgroup_vote"
+
+
+#define VK_KHX_device_group_creation 1
+#define VK_KHX_DEVICE_GROUP_CREATION_SPEC_VERSION 1
+#define VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME "VK_KHX_device_group_creation"
+
+typedef struct VkPhysicalDeviceGroupPropertiesKHX {
+    VkStructureType     sType;
+    const void*         pNext;
+    uint32_t            physicalDeviceCount;
+    VkPhysicalDevice    physicalDevices[VK_MAX_DEVICE_GROUP_SIZE_KHX];
+    VkBool32            subsetAllocation;
+} VkPhysicalDeviceGroupPropertiesKHX;
+
+typedef struct VkDeviceGroupDeviceCreateInfoKHX {
+    VkStructureType            sType;
+    const void*                pNext;
+    uint32_t                   physicalDeviceCount;
+    const VkPhysicalDevice*    pPhysicalDevices;
+} VkDeviceGroupDeviceCreateInfoKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDeviceGroupsKHX)(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupPropertiesKHX* pPhysicalDeviceGroupProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroupsKHX(
+    VkInstance                                  instance,
+    uint32_t*                                   pPhysicalDeviceGroupCount,
+    VkPhysicalDeviceGroupPropertiesKHX*         pPhysicalDeviceGroupProperties);
+#endif
+
+#define VK_KHX_external_memory_capabilities 1
+#define VK_LUID_SIZE_KHX                  8
+#define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_memory_capabilities"
+
+
+typedef enum VkExternalMemoryHandleTypeFlagBitsKHX {
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX = 0x00000004,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT_KHX = 0x00000008,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT_KHX = 0x00000010,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT_KHX = 0x00000020,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHX = 0x00000040,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalMemoryHandleTypeFlagBitsKHX;
+typedef VkFlags VkExternalMemoryHandleTypeFlagsKHX;
+
+typedef enum VkExternalMemoryFeatureFlagBitsKHX {
+    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHX = 0x00000004,
+    VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalMemoryFeatureFlagBitsKHX;
+typedef VkFlags VkExternalMemoryFeatureFlagsKHX;
+
+typedef struct VkExternalMemoryPropertiesKHX {
+    VkExternalMemoryFeatureFlagsKHX       externalMemoryFeatures;
+    VkExternalMemoryHandleTypeFlagsKHX    exportFromImportedHandleTypes;
+    VkExternalMemoryHandleTypeFlagsKHX    compatibleHandleTypes;
+} VkExternalMemoryPropertiesKHX;
+
+typedef struct VkPhysicalDeviceExternalImageFormatInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkExternalMemoryHandleTypeFlagBitsKHX    handleType;
+} VkPhysicalDeviceExternalImageFormatInfoKHX;
+
+typedef struct VkExternalImageFormatPropertiesKHX {
+    VkStructureType                  sType;
+    void*                            pNext;
+    VkExternalMemoryPropertiesKHX    externalMemoryProperties;
+} VkExternalImageFormatPropertiesKHX;
+
+typedef struct VkPhysicalDeviceExternalBufferInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkBufferCreateFlags                      flags;
+    VkBufferUsageFlags                       usage;
+    VkExternalMemoryHandleTypeFlagBitsKHX    handleType;
+} VkPhysicalDeviceExternalBufferInfoKHX;
+
+typedef struct VkExternalBufferPropertiesKHX {
+    VkStructureType                  sType;
+    void*                            pNext;
+    VkExternalMemoryPropertiesKHX    externalMemoryProperties;
+} VkExternalBufferPropertiesKHX;
+
+typedef struct VkPhysicalDeviceIDPropertiesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    uint8_t            deviceUUID[VK_UUID_SIZE];
+    uint8_t            driverUUID[VK_UUID_SIZE];
+    uint8_t            deviceLUID[VK_LUID_SIZE_KHX];
+    VkBool32           deviceLUIDValid;
+} VkPhysicalDeviceIDPropertiesKHX;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHX)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfoKHX* pExternalBufferInfo, VkExternalBufferPropertiesKHX* pExternalBufferProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalBufferPropertiesKHX(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceExternalBufferInfoKHX* pExternalBufferInfo,
+    VkExternalBufferPropertiesKHX*              pExternalBufferProperties);
+#endif
+
+#define VK_KHX_external_memory 1
+#define VK_KHX_EXTERNAL_MEMORY_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_MEMORY_EXTENSION_NAME "VK_KHX_external_memory"
+#define VK_QUEUE_FAMILY_EXTERNAL_KHX      (~0U-1)
+
+typedef struct VkExternalMemoryImageCreateInfoKHX {
+    VkStructureType                       sType;
+    const void*                           pNext;
+    VkExternalMemoryHandleTypeFlagsKHX    handleTypes;
+} VkExternalMemoryImageCreateInfoKHX;
+
+typedef struct VkExternalMemoryBufferCreateInfoKHX {
+    VkStructureType                       sType;
+    const void*                           pNext;
+    VkExternalMemoryHandleTypeFlagsKHX    handleTypes;
+} VkExternalMemoryBufferCreateInfoKHX;
+
+typedef struct VkExportMemoryAllocateInfoKHX {
+    VkStructureType                       sType;
+    const void*                           pNext;
+    VkExternalMemoryHandleTypeFlagsKHX    handleTypes;
+} VkExportMemoryAllocateInfoKHX;
+
+
+
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+#define VK_KHX_external_memory_win32 1
+#define VK_KHX_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_KHX_external_memory_win32"
+
+typedef struct VkImportMemoryWin32HandleInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkExternalMemoryHandleTypeFlagBitsKHX    handleType;
+    HANDLE                                   handle;
+} VkImportMemoryWin32HandleInfoKHX;
+
+typedef struct VkExportMemoryWin32HandleInfoKHX {
+    VkStructureType               sType;
+    const void*                   pNext;
+    const SECURITY_ATTRIBUTES*    pAttributes;
+    DWORD                         dwAccess;
+    LPCWSTR                       name;
+} VkExportMemoryWin32HandleInfoKHX;
+
+typedef struct VkMemoryWin32HandlePropertiesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           memoryTypeBits;
+} VkMemoryWin32HandlePropertiesKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleKHX)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE* pHandle);
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandlePropertiesKHX)(VkDevice device, VkExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHX* pMemoryWin32HandleProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleKHX(
+    VkDevice                                    device,
+    VkDeviceMemory                              memory,
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+    HANDLE*                                     pHandle);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandlePropertiesKHX(
+    VkDevice                                    device,
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+    HANDLE                                      handle,
+    VkMemoryWin32HandlePropertiesKHX*           pMemoryWin32HandleProperties);
+#endif
+#endif /* VK_USE_PLATFORM_WIN32_KHR */
+
+#define VK_KHX_external_memory_fd 1
+#define VK_KHX_EXTERNAL_MEMORY_FD_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_MEMORY_FD_EXTENSION_NAME "VK_KHX_external_memory_fd"
+
+typedef struct VkImportMemoryFdInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkExternalMemoryHandleTypeFlagBitsKHX    handleType;
+    int                                      fd;
+} VkImportMemoryFdInfoKHX;
+
+typedef struct VkMemoryFdPropertiesKHX {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           memoryTypeBits;
+} VkMemoryFdPropertiesKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdKHX)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagBitsKHX handleType, int* pFd);
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdPropertiesKHX)(VkDevice device, VkExternalMemoryHandleTypeFlagBitsKHX handleType, int fd, VkMemoryFdPropertiesKHX* pMemoryFdProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdKHX(
+    VkDevice                                    device,
+    VkDeviceMemory                              memory,
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+    int*                                        pFd);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdPropertiesKHX(
+    VkDevice                                    device,
+    VkExternalMemoryHandleTypeFlagBitsKHX       handleType,
+    int                                         fd,
+    VkMemoryFdPropertiesKHX*                    pMemoryFdProperties);
+#endif
+
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+#define VK_KHX_win32_keyed_mutex 1
+#define VK_KHX_WIN32_KEYED_MUTEX_SPEC_VERSION 1
+#define VK_KHX_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_KHX_win32_keyed_mutex"
+
+typedef struct VkWin32KeyedMutexAcquireReleaseInfoKHX {
+    VkStructureType          sType;
+    const void*              pNext;
+    uint32_t                 acquireCount;
+    const VkDeviceMemory*    pAcquireSyncs;
+    const uint64_t*          pAcquireKeys;
+    const uint32_t*          pAcquireTimeouts;
+    uint32_t                 releaseCount;
+    const VkDeviceMemory*    pReleaseSyncs;
+    const uint64_t*          pReleaseKeys;
+} VkWin32KeyedMutexAcquireReleaseInfoKHX;
+
+
+#endif /* VK_USE_PLATFORM_WIN32_KHR */
+
+#define VK_KHX_external_semaphore_capabilities 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_semaphore_capabilities"
+
+
+typedef enum VkExternalSemaphoreHandleTypeFlagBitsKHX {
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX = 0x00000004,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT_KHX = 0x00000008,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX = 0x00000010,
+    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalSemaphoreHandleTypeFlagBitsKHX;
+typedef VkFlags VkExternalSemaphoreHandleTypeFlagsKHX;
+
+typedef enum VkExternalSemaphoreFeatureFlagBitsKHX {
+    VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHX = 0x00000001,
+    VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHX = 0x00000002,
+    VK_EXTERNAL_SEMAPHORE_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF
+} VkExternalSemaphoreFeatureFlagBitsKHX;
+typedef VkFlags VkExternalSemaphoreFeatureFlagsKHX;
+
+typedef struct VkPhysicalDeviceExternalSemaphoreInfoKHX {
+    VkStructureType                             sType;
+    const void*                                 pNext;
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType;
+} VkPhysicalDeviceExternalSemaphoreInfoKHX;
+
+typedef struct VkExternalSemaphorePropertiesKHX {
+    VkStructureType                          sType;
+    void*                                    pNext;
+    VkExternalSemaphoreHandleTypeFlagsKHX    exportFromImportedHandleTypes;
+    VkExternalSemaphoreHandleTypeFlagsKHX    compatibleHandleTypes;
+    VkExternalSemaphoreFeatureFlagsKHX       externalSemaphoreFeatures;
+} VkExternalSemaphorePropertiesKHX;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHX)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo, VkExternalSemaphorePropertiesKHX* pExternalSemaphoreProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalSemaphorePropertiesKHX(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo,
+    VkExternalSemaphorePropertiesKHX*           pExternalSemaphoreProperties);
+#endif
+
+#define VK_KHX_external_semaphore 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_KHX_external_semaphore"
+
+typedef struct VkExportSemaphoreCreateInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkExternalSemaphoreHandleTypeFlagsKHX    handleTypes;
+} VkExportSemaphoreCreateInfoKHX;
+
+
+
+#ifdef VK_USE_PLATFORM_WIN32_KHX
+#define VK_KHX_external_semaphore_win32 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME "VK_KHX_external_semaphore_win32"
+
+typedef struct VkImportSemaphoreWin32HandleInfoKHX {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    VkSemaphore                              semaphore;
+    VkExternalSemaphoreHandleTypeFlagsKHX    handleType;
+    HANDLE                                   handle;
+} VkImportSemaphoreWin32HandleInfoKHX;
+
+typedef struct VkExportSemaphoreWin32HandleInfoKHX {
+    VkStructureType               sType;
+    const void*                   pNext;
+    const SECURITY_ATTRIBUTES*    pAttributes;
+    DWORD                         dwAccess;
+    LPCWSTR                       name;
+} VkExportSemaphoreWin32HandleInfoKHX;
+
+typedef struct VkD3D12FenceSubmitInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           waitSemaphoreValuesCount;
+    const uint64_t*    pWaitSemaphoreValues;
+    uint32_t           signalSemaphoreValuesCount;
+    const uint64_t*    pSignalSemaphoreValues;
+} VkD3D12FenceSubmitInfoKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreWin32HandleKHX)(VkDevice device, const VkImportSemaphoreWin32HandleInfoKHX* pImportSemaphoreWin32HandleInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreWin32HandleKHX)(VkDevice device, VkSemaphore semaphore, VkExternalSemaphoreHandleTypeFlagBitsKHX handleType, HANDLE* pHandle);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreWin32HandleKHX(
+    VkDevice                                    device,
+    const VkImportSemaphoreWin32HandleInfoKHX*  pImportSemaphoreWin32HandleInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreWin32HandleKHX(
+    VkDevice                                    device,
+    VkSemaphore                                 semaphore,
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType,
+    HANDLE*                                     pHandle);
+#endif
+#endif /* VK_USE_PLATFORM_WIN32_KHX */
+
+#define VK_KHX_external_semaphore_fd 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION 1
+#define VK_KHX_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME "VK_KHX_external_semaphore_fd"
+
+typedef struct VkImportSemaphoreFdInfoKHX {
+    VkStructureType                             sType;
+    const void*                                 pNext;
+    VkSemaphore                                 semaphore;
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType;
+    int                                         fd;
+} VkImportSemaphoreFdInfoKHX;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreFdKHX)(VkDevice device, const VkImportSemaphoreFdInfoKHX* pImportSemaphoreFdInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreFdKHX)(VkDevice device, VkSemaphore semaphore, VkExternalSemaphoreHandleTypeFlagBitsKHX handleType, int* pFd);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreFdKHX(
+    VkDevice                                    device,
+    const VkImportSemaphoreFdInfoKHX*           pImportSemaphoreFdInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreFdKHX(
+    VkDevice                                    device,
+    VkSemaphore                                 semaphore,
+    VkExternalSemaphoreHandleTypeFlagBitsKHX    handleType,
+    int*                                        pFd);
+#endif
+
+#define VK_KHR_incremental_present 1
+#define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
+#define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present"
+
+typedef struct VkRectLayerKHR {
+    VkOffset2D offset;
+    VkExtent2D extent;
+    uint32_t layer;
+} VkRectLayerKHR;
+
+typedef struct VkPresentRegionKHR {
+    uint32_t rectangleCount;
+    const VkRectLayerKHR* pRectangles;
+} VkPresentRegionKHR;
+
+typedef struct VkPresentRegionsKHR {
+    VkStructureType sType;
+    const void* pNext;
+    uint32_t swapchainCount;
+    const VkPresentRegionKHR* pRegions;
+} VkPresentRegionsKHR;
+
 #define VK_NVX_device_generated_commands 1
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX)
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX)
@@ -4491,6 +5312,7 @@
     VkObjectEntryTypeNVX          type;
     VkObjectEntryUsageFlagsNVX    flags;
     VkBuffer                      buffer;
+    VkIndexType                   indexType;
 } VkObjectTableIndexBufferEntryNVX;
 
 typedef struct VkObjectTablePushConstantEntryNVX {
@@ -4562,6 +5384,194 @@
     VkDeviceGeneratedCommandsLimitsNVX*         pLimits);
 #endif
 
+#define VK_NV_clip_space_w_scaling 1
+#define VK_NV_CLIP_SPACE_W_SCALING_SPEC_VERSION 1
+#define VK_NV_CLIP_SPACE_W_SCALING_EXTENSION_NAME "VK_NV_clip_space_w_scaling"
+
+typedef struct VkViewportWScalingNV {
+    float    xcoeff;
+    float    ycoeff;
+} VkViewportWScalingNV;
+
+typedef struct VkPipelineViewportWScalingStateCreateInfoNV {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkBool32                       viewportWScalingEnable;
+    uint32_t                       viewportCount;
+    const VkViewportWScalingNV*    pViewportWScalings;
+} VkPipelineViewportWScalingStateCreateInfoNV;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdSetViewportWScalingNV)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewportWScalingNV* pViewportWScalings);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdSetViewportWScalingNV(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    firstViewport,
+    uint32_t                                    viewportCount,
+    const VkViewportWScalingNV*                 pViewportWScalings);
+#endif
+
+#define VK_EXT_direct_mode_display 1
+#define VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION 1
+#define VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME "VK_EXT_direct_mode_display"
+
+typedef VkResult (VKAPI_PTR *PFN_vkReleaseDisplayEXT)(VkPhysicalDevice physicalDevice, VkDisplayKHR display);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkReleaseDisplayEXT(
+    VkPhysicalDevice                            physicalDevice,
+    VkDisplayKHR                                display);
+#endif
+
+#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
+#define VK_EXT_acquire_xlib_display 1
+#include <X11/extensions/Xrandr.h>
+
+#define VK_EXT_ACQUIRE_XLIB_DISPLAY_SPEC_VERSION 1
+#define VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_xlib_display"
+
+typedef VkResult (VKAPI_PTR *PFN_vkAcquireXlibDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, VkDisplayKHR display);
+typedef VkResult (VKAPI_PTR *PFN_vkGetRandROutputDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, RROutput rrOutput, VkDisplayKHR* pDisplay);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkAcquireXlibDisplayEXT(
+    VkPhysicalDevice                            physicalDevice,
+    Display*                                    dpy,
+    VkDisplayKHR                                display);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetRandROutputDisplayEXT(
+    VkPhysicalDevice                            physicalDevice,
+    Display*                                    dpy,
+    RROutput                                    rrOutput,
+    VkDisplayKHR*                               pDisplay);
+#endif
+#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */
+
+#define VK_EXT_display_surface_counter 1
+#define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1
+#define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter"
+
+
+typedef enum VkSurfaceCounterFlagBitsEXT {
+    VK_SURFACE_COUNTER_VBLANK_EXT = 0x00000001,
+    VK_SURFACE_COUNTER_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkSurfaceCounterFlagBitsEXT;
+typedef VkFlags VkSurfaceCounterFlagsEXT;
+
+typedef struct VkSurfaceCapabilities2EXT {
+    VkStructureType                  sType;
+    void*                            pNext;
+    uint32_t                         minImageCount;
+    uint32_t                         maxImageCount;
+    VkExtent2D                       currentExtent;
+    VkExtent2D                       minImageExtent;
+    VkExtent2D                       maxImageExtent;
+    uint32_t                         maxImageArrayLayers;
+    VkSurfaceTransformFlagsKHR       supportedTransforms;
+    VkSurfaceTransformFlagBitsKHR    currentTransform;
+    VkCompositeAlphaFlagsKHR         supportedCompositeAlpha;
+    VkImageUsageFlags                supportedUsageFlags;
+    VkSurfaceCounterFlagsEXT         supportedSurfaceCounters;
+} VkSurfaceCapabilities2EXT;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilities2EXT* pSurfaceCapabilities);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilities2EXT(
+    VkPhysicalDevice                            physicalDevice,
+    VkSurfaceKHR                                surface,
+    VkSurfaceCapabilities2EXT*                  pSurfaceCapabilities);
+#endif
+
+#define VK_EXT_display_control 1
+#define VK_EXT_DISPLAY_CONTROL_SPEC_VERSION 1
+#define VK_EXT_DISPLAY_CONTROL_EXTENSION_NAME "VK_EXT_display_control"
+
+
+typedef enum VkDisplayPowerStateEXT {
+    VK_DISPLAY_POWER_STATE_OFF_EXT = 0,
+    VK_DISPLAY_POWER_STATE_SUSPEND_EXT = 1,
+    VK_DISPLAY_POWER_STATE_ON_EXT = 2,
+    VK_DISPLAY_POWER_STATE_BEGIN_RANGE_EXT = VK_DISPLAY_POWER_STATE_OFF_EXT,
+    VK_DISPLAY_POWER_STATE_END_RANGE_EXT = VK_DISPLAY_POWER_STATE_ON_EXT,
+    VK_DISPLAY_POWER_STATE_RANGE_SIZE_EXT = (VK_DISPLAY_POWER_STATE_ON_EXT - VK_DISPLAY_POWER_STATE_OFF_EXT + 1),
+    VK_DISPLAY_POWER_STATE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDisplayPowerStateEXT;
+
+typedef enum VkDeviceEventTypeEXT {
+    VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT = 0,
+    VK_DEVICE_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT,
+    VK_DEVICE_EVENT_TYPE_END_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT,
+    VK_DEVICE_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT - VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT + 1),
+    VK_DEVICE_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDeviceEventTypeEXT;
+
+typedef enum VkDisplayEventTypeEXT {
+    VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT = 0,
+    VK_DISPLAY_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT,
+    VK_DISPLAY_EVENT_TYPE_END_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT,
+    VK_DISPLAY_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT - VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT + 1),
+    VK_DISPLAY_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDisplayEventTypeEXT;
+
+typedef struct VkDisplayPowerInfoEXT {
+    VkStructureType           sType;
+    const void*               pNext;
+    VkDisplayPowerStateEXT    powerState;
+} VkDisplayPowerInfoEXT;
+
+typedef struct VkDeviceEventInfoEXT {
+    VkStructureType         sType;
+    const void*             pNext;
+    VkDeviceEventTypeEXT    deviceEvent;
+} VkDeviceEventInfoEXT;
+
+typedef struct VkDisplayEventInfoEXT {
+    VkStructureType          sType;
+    const void*              pNext;
+    VkDisplayEventTypeEXT    displayEvent;
+} VkDisplayEventInfoEXT;
+
+typedef struct VkSwapchainCounterCreateInfoEXT {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkSurfaceCounterFlagsEXT    surfaceCounters;
+} VkSwapchainCounterCreateInfoEXT;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkDisplayPowerControlEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayPowerInfoEXT* pDisplayPowerInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkRegisterDeviceEventEXT)(VkDevice device, const VkDeviceEventInfoEXT* pDeviceEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence);
+typedef VkResult (VKAPI_PTR *PFN_vkRegisterDisplayEventEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT* pDisplayEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence);
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainCounterEXT)(VkDevice device, VkSwapchainKHR swapchain, VkSurfaceCounterFlagBitsEXT counter, uint64_t* pCounterValue);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkDisplayPowerControlEXT(
+    VkDevice                                    device,
+    VkDisplayKHR                                display,
+    const VkDisplayPowerInfoEXT*                pDisplayPowerInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDeviceEventEXT(
+    VkDevice                                    device,
+    const VkDeviceEventInfoEXT*                 pDeviceEventInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkFence*                                    pFence);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDisplayEventEXT(
+    VkDevice                                    device,
+    VkDisplayKHR                                display,
+    const VkDisplayEventInfoEXT*                pDisplayEventInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkFence*                                    pFence);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainCounterEXT(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    VkSurfaceCounterFlagBitsEXT                 counter,
+    uint64_t*                                   pCounterValue);
+#endif
+
 #define VK_GOOGLE_display_timing 1
 #define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
 #define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing"
@@ -4607,12 +5617,120 @@
     VkPastPresentationTimingGOOGLE*             pPresentationTimings);
 #endif
 
+#define VK_NV_sample_mask_override_coverage 1
+#define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_SPEC_VERSION 1
+#define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME "VK_NV_sample_mask_override_coverage"
+
+
+#define VK_NV_geometry_shader_passthrough 1
+#define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_SPEC_VERSION 1
+#define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME "VK_NV_geometry_shader_passthrough"
+
+
+#define VK_NV_viewport_array2 1
+#define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION 1
+#define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME "VK_NV_viewport_array2"
+
+
+#define VK_NVX_multiview_per_view_attributes 1
+#define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION 1
+#define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME "VK_NVX_multiview_per_view_attributes"
+
+typedef struct VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX {
+    VkStructureType    sType;
+    void*              pNext;
+    VkBool32           perViewPositionAllComponents;
+} VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX;
+
+
+
+#define VK_NV_viewport_swizzle 1
+#define VK_NV_VIEWPORT_SWIZZLE_SPEC_VERSION 1
+#define VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME "VK_NV_viewport_swizzle"
+
+
+typedef enum VkViewportCoordinateSwizzleNV {
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV = 0,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV = 1,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV = 2,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Y_NV = 3,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV = 4,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV = 5,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV = 6,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV = 7,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_BEGIN_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_END_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV,
+    VK_VIEWPORT_COORDINATE_SWIZZLE_RANGE_SIZE_NV = (VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV - VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV + 1),
+    VK_VIEWPORT_COORDINATE_SWIZZLE_MAX_ENUM_NV = 0x7FFFFFFF
+} VkViewportCoordinateSwizzleNV;
+
+typedef VkFlags VkPipelineViewportSwizzleStateCreateFlagsNV;
+
+typedef struct VkViewportSwizzleNV {
+    VkViewportCoordinateSwizzleNV    x;
+    VkViewportCoordinateSwizzleNV    y;
+    VkViewportCoordinateSwizzleNV    z;
+    VkViewportCoordinateSwizzleNV    w;
+} VkViewportSwizzleNV;
+
+typedef struct VkPipelineViewportSwizzleStateCreateInfoNV {
+    VkStructureType                                sType;
+    const void*                                    pNext;
+    VkPipelineViewportSwizzleStateCreateFlagsNV    flags;
+    uint32_t                                       viewportCount;
+    const VkViewportSwizzleNV*                     pViewportSwizzles;
+} VkPipelineViewportSwizzleStateCreateInfoNV;
+
+
+
+#define VK_EXT_discard_rectangles 1
+#define VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION 1
+#define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles"
+
+
+typedef enum VkDiscardRectangleModeEXT {
+    VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT = 0,
+    VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1,
+    VK_DISCARD_RECTANGLE_MODE_BEGIN_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT,
+    VK_DISCARD_RECTANGLE_MODE_END_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT,
+    VK_DISCARD_RECTANGLE_MODE_RANGE_SIZE_EXT = (VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT - VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT + 1),
+    VK_DISCARD_RECTANGLE_MODE_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkDiscardRectangleModeEXT;
+
+typedef VkFlags VkPipelineDiscardRectangleStateCreateFlagsEXT;
+
+typedef struct VkPhysicalDeviceDiscardRectanglePropertiesEXT {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           maxDiscardRectangles;
+} VkPhysicalDeviceDiscardRectanglePropertiesEXT;
+
+typedef struct VkPipelineDiscardRectangleStateCreateInfoEXT {
+    VkStructureType                                  sType;
+    const void*                                      pNext;
+    VkPipelineDiscardRectangleStateCreateFlagsEXT    flags;
+    VkDiscardRectangleModeEXT                        discardRectangleMode;
+    uint32_t                                         discardRectangleCount;
+    const VkRect2D*                                  pDiscardRectangles;
+} VkPipelineDiscardRectangleStateCreateInfoEXT;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdSetDiscardRectangleEXT)(VkCommandBuffer commandBuffer, uint32_t firstDiscardRectangle, uint32_t discardRectangleCount, const VkRect2D* pDiscardRectangles);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdSetDiscardRectangleEXT(
+    VkCommandBuffer                             commandBuffer,
+    uint32_t                                    firstDiscardRectangle,
+    uint32_t                                    discardRectangleCount,
+    const VkRect2D*                             pDiscardRectangles);
+#endif
+
 #define VK_EXT_swapchain_colorspace 1
 #define VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION 1
 #define VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace"
 
 #define VK_EXT_hdr_metadata 1
-#define VK_EXT_HDR_METADATA_SPEC_VERSION  0
+#define VK_EXT_HDR_METADATA_SPEC_VERSION  1
 #define VK_EXT_HDR_METADATA_EXTENSION_NAME "VK_EXT_hdr_metadata"
 
 typedef struct VkXYColorEXT {
@@ -4644,6 +5762,71 @@
     const VkHdrMetadataEXT*                     pMetadata);
 #endif
 
+#define VK_KHR_shared_presentable_image 1
+#define VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION 1
+#define VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME "VK_KHR_shared_presentable_image"
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainStatusKHR)(VkDevice device, VkSwapchainKHR swapchain);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainStatusKHR(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain);
+#endif
+
+
+#ifdef VK_USE_PLATFORM_IOS_MVK
+#define VK_MVK_ios_surface 1
+#define VK_MVK_IOS_SURFACE_SPEC_VERSION   2
+#define VK_MVK_IOS_SURFACE_EXTENSION_NAME "VK_MVK_ios_surface"
+
+typedef VkFlags VkIOSSurfaceCreateFlagsMVK;
+
+typedef struct VkIOSSurfaceCreateInfoMVK {
+    VkStructureType               sType;
+    const void*                   pNext;
+    VkIOSSurfaceCreateFlagsMVK    flags;
+    const void*                   pView;
+} VkIOSSurfaceCreateInfoMVK;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateIOSSurfaceMVK)(VkInstance instance, const VkIOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateIOSSurfaceMVK(
+    VkInstance                                  instance,
+    const VkIOSSurfaceCreateInfoMVK*            pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface);
+#endif
+#endif /* VK_USE_PLATFORM_IOS_MVK */
+
+#ifdef VK_USE_PLATFORM_MACOS_MVK
+#define VK_MVK_macos_surface 1
+#define VK_MVK_MACOS_SURFACE_SPEC_VERSION 2
+#define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface"
+
+typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
+
+typedef struct VkMacOSSurfaceCreateInfoMVK {
+    VkStructureType                 sType;
+    const void*                     pNext;
+    VkMacOSSurfaceCreateFlagsMVK    flags;
+    const void*                     pView;
+} VkMacOSSurfaceCreateInfoMVK;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateMacOSSurfaceMVK)(VkInstance instance, const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateMacOSSurfaceMVK(
+    VkInstance                                  instance,
+    const VkMacOSSurfaceCreateInfoMVK*          pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSurfaceKHR*                               pSurface);
+#endif
+#endif /* VK_USE_PLATFORM_MACOS_MVK */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index b8b7e94..69e38de 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -389,7 +389,7 @@
 VKAPI_ATTR void CmdDrawIndexed(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance);
 VKAPI_ATTR void CmdDrawIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
 VKAPI_ATTR void CmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
-VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z);
+VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
 VKAPI_ATTR void CmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset);
 VKAPI_ATTR void CmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions);
 VKAPI_ATTR void CmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions);
@@ -451,9 +451,12 @@
         "vkEnumerateDeviceLayerProperties",
         "vkEnumerateInstanceExtensionProperties",
         "vkEnumerateInstanceLayerProperties",
+        "vkEnumeratePhysicalDeviceGroupsKHX",
         "vkEnumeratePhysicalDevices",
         "vkGetInstanceProcAddr",
+        "vkGetPhysicalDeviceExternalBufferPropertiesKHX",
         "vkGetPhysicalDeviceExternalImageFormatPropertiesNV",
+        "vkGetPhysicalDeviceExternalSemaphorePropertiesKHX",
         "vkGetPhysicalDeviceFeatures",
         "vkGetPhysicalDeviceFeatures2KHR",
         "vkGetPhysicalDeviceFormatProperties",
@@ -463,6 +466,7 @@
         "vkGetPhysicalDeviceImageFormatProperties2KHR",
         "vkGetPhysicalDeviceMemoryProperties",
         "vkGetPhysicalDeviceMemoryProperties2KHR",
+        "vkGetPhysicalDevicePresentRectanglesKHX",
         "vkGetPhysicalDeviceProperties",
         "vkGetPhysicalDeviceProperties2KHR",
         "vkGetPhysicalDeviceQueueFamilyProperties",
@@ -1058,8 +1062,8 @@
     GetData(commandBuffer).dispatch.CmdDrawIndexedIndirect(commandBuffer, buffer, offset, drawCount, stride);
 }
 
-VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z) {
-    GetData(commandBuffer).dispatch.CmdDispatch(commandBuffer, x, y, z);
+VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
+    GetData(commandBuffer).dispatch.CmdDispatch(commandBuffer, groupCountX, groupCountY, groupCountZ);
 }
 
 VKAPI_ATTR void CmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset) {
@@ -1771,8 +1775,8 @@
 }
 
 __attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z) {
-    vulkan::api::CmdDispatch(commandBuffer, x, y, z);
+VKAPI_ATTR void vkCmdDispatch(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
+    vulkan::api::CmdDispatch(commandBuffer, groupCountX, groupCountY, groupCountZ);
 }
 
 __attribute__((visibility("default")))
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index 992c5d1..185121c 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -1129,11 +1129,21 @@
   {{$ext := index $.Arguments 0}}
   {{     if eq $ext "VK_KHR_display"}}true
   {{else if eq $ext "VK_KHR_display_swapchain"}}true
-  {{else if eq $ext "VK_KHR_xlib_surface"}}true
-  {{else if eq $ext "VK_KHR_xcb_surface"}}true
-  {{else if eq $ext "VK_KHR_wayland_surface"}}true
   {{else if eq $ext "VK_KHR_mir_surface"}}true
+  {{else if eq $ext "VK_KHR_xcb_surface"}}true
+  {{else if eq $ext "VK_KHR_xlib_surface"}}true
+  {{else if eq $ext "VK_KHR_wayland_surface"}}true
   {{else if eq $ext "VK_KHR_win32_surface"}}true
+  {{else if eq $ext "VK_KHX_external_memory_win32"}}true
+  {{else if eq $ext "VK_KHX_win32_keyed_mutex"}}true
+  {{else if eq $ext "VK_KHX_external_semaphore_win32"}}true
+  {{else if eq $ext "VK_EXT_acquire_xlib_display"}}true
+  {{else if eq $ext "VK_EXT_direct_mode_display"}}true
+  {{else if eq $ext "VK_EXT_display_surface_counter"}}true
+  {{else if eq $ext "VK_EXT_display_control"}}true
+  {{else if eq $ext "VK_MVK_ios_surface"}}true
+  {{else if eq $ext "VK_MVK_macos_surface"}}true
+  {{else if eq $ext "VK_NN_vi_surface"}}true
   {{else if eq $ext "VK_NV_external_memory_win32"}}true
   {{else if eq $ext "VK_NV_win32_keyed_mutex"}}true
   {{end}}
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index a0ae1f3..7e96b4c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -17,7 +17,7 @@
 #include <algorithm>
 
 #include <log/log.h>
-#include <gui/BufferQueue.h>
+#include <ui/BufferQueueDefs.h>
 #include <sync/sync.h>
 #include <utils/StrongPointer.h>
 #include <utils/Vector.h>
@@ -207,7 +207,7 @@
         // or by passing ownership e.g. to ANativeWindow::cancelBuffer().
         int dequeue_fence;
         bool dequeued;
-    } images[android::BufferQueue::NUM_BUFFER_SLOTS];
+    } images[android::BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     android::Vector<TimingInfo> timing;
 };
@@ -903,8 +903,7 @@
     int gralloc_usage = 0;
     if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
         uint64_t consumer_usage, producer_usage;
-        uint32_t driver_version = GetData(device).driver_version;
-        if (driver_version == 256587285 || driver_version == 96011958) {
+        if (GetData(device).driver_version == 256587285) {
             // HACK workaround for loader/driver mismatch during transition to
             // vkGetSwapchainGrallocUsage2ANDROID.
             typedef VkResult(VKAPI_PTR *
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index d73bf14..8a9a963 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -138,7 +138,7 @@
 VKAPI_ATTR void CmdDrawIndexed(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance);
 VKAPI_ATTR void CmdDrawIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
 VKAPI_ATTR void CmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride);
-VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z);
+VKAPI_ATTR void CmdDispatch(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
 VKAPI_ATTR void CmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset);
 VKAPI_ATTR void CmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions);
 VKAPI_ATTR void CmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions);