Merge "Add "ls -l /data/anr" output to bugreport" am: ec22502dc5 am: 8c70dddf55
am: 70b7e10425

Change-Id: I973b6e93a4a314bbb2fd0acc0d4395d055a4c726
diff --git a/libs/gui/CleanSpec.mk b/CleanSpec.mk
similarity index 81%
rename from libs/gui/CleanSpec.mk
rename to CleanSpec.mk
index 5a5144c..6a9007c 100644
--- a/libs/gui/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 The Android Open Source Project
+# Copyright (C) 2018 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -44,9 +44,11 @@
 #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
 #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
 
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
+$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libdvr.so" -print0 | xargs -0 rm -f)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvr_intermediates)
 $(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libgui*" -print0 | xargs -0 rm -f)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/thermalserviced)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/thermalservice.rc)
+$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "gpuservice*" -print0 | xargs -0 rm -f)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/gpuservice_intermediates)
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 73360a3..1a932c3 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,8 +7,10 @@
                libs/binder/ndk/
                libs/graphicsenv/
                libs/gui/
+               libs/renderengine/
                libs/ui/
                libs/vr/
+               services/bufferhub/
                services/surfaceflinger/
                services/vr/
 
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 53b3a00..bd3d2d8 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -99,30 +99,31 @@
 
 /* Tracing categories */
 static const TracingCategory k_categories[] = {
-    { "gfx",        "Graphics",         ATRACE_TAG_GRAPHICS, { } },
-    { "input",      "Input",            ATRACE_TAG_INPUT, { } },
-    { "view",       "View System",      ATRACE_TAG_VIEW, { } },
-    { "webview",    "WebView",          ATRACE_TAG_WEBVIEW, { } },
-    { "wm",         "Window Manager",   ATRACE_TAG_WINDOW_MANAGER, { } },
-    { "am",         "Activity Manager", ATRACE_TAG_ACTIVITY_MANAGER, { } },
-    { "sm",         "Sync Manager",     ATRACE_TAG_SYNC_MANAGER, { } },
-    { "audio",      "Audio",            ATRACE_TAG_AUDIO, { } },
-    { "video",      "Video",            ATRACE_TAG_VIDEO, { } },
-    { "camera",     "Camera",           ATRACE_TAG_CAMERA, { } },
-    { "hal",        "Hardware Modules", ATRACE_TAG_HAL, { } },
-    { "res",        "Resource Loading", ATRACE_TAG_RESOURCES, { } },
-    { "dalvik",     "Dalvik VM",        ATRACE_TAG_DALVIK, { } },
-    { "rs",         "RenderScript",     ATRACE_TAG_RS, { } },
-    { "bionic",     "Bionic C Library", ATRACE_TAG_BIONIC, { } },
-    { "power",      "Power Management", ATRACE_TAG_POWER, { } },
-    { "pm",         "Package Manager",  ATRACE_TAG_PACKAGE_MANAGER, { } },
-    { "ss",         "System Server",    ATRACE_TAG_SYSTEM_SERVER, { } },
-    { "database",   "Database",         ATRACE_TAG_DATABASE, { } },
-    { "network",    "Network",          ATRACE_TAG_NETWORK, { } },
-    { "adb",        "ADB",              ATRACE_TAG_ADB, { } },
-    { "vibrator",   "Vibrator",         ATRACE_TAG_VIBRATOR, { } },
-    { "aidl",       "AIDL calls",       ATRACE_TAG_AIDL, { } },
-    { "nnapi",      "NNAPI",            ATRACE_TAG_NNAPI, { } },
+    { "gfx",        "Graphics",                 ATRACE_TAG_GRAPHICS, { } },
+    { "input",      "Input",                    ATRACE_TAG_INPUT, { } },
+    { "view",       "View System",              ATRACE_TAG_VIEW, { } },
+    { "webview",    "WebView",                  ATRACE_TAG_WEBVIEW, { } },
+    { "wm",         "Window Manager",           ATRACE_TAG_WINDOW_MANAGER, { } },
+    { "am",         "Activity Manager",         ATRACE_TAG_ACTIVITY_MANAGER, { } },
+    { "sm",         "Sync Manager",             ATRACE_TAG_SYNC_MANAGER, { } },
+    { "audio",      "Audio",                    ATRACE_TAG_AUDIO, { } },
+    { "video",      "Video",                    ATRACE_TAG_VIDEO, { } },
+    { "camera",     "Camera",                   ATRACE_TAG_CAMERA, { } },
+    { "hal",        "Hardware Modules",         ATRACE_TAG_HAL, { } },
+    { "res",        "Resource Loading",         ATRACE_TAG_RESOURCES, { } },
+    { "dalvik",     "Dalvik VM",                ATRACE_TAG_DALVIK, { } },
+    { "rs",         "RenderScript",             ATRACE_TAG_RS, { } },
+    { "bionic",     "Bionic C Library",         ATRACE_TAG_BIONIC, { } },
+    { "power",      "Power Management",         ATRACE_TAG_POWER, { } },
+    { "pm",         "Package Manager",          ATRACE_TAG_PACKAGE_MANAGER, { } },
+    { "ss",         "System Server",            ATRACE_TAG_SYSTEM_SERVER, { } },
+    { "database",   "Database",                 ATRACE_TAG_DATABASE, { } },
+    { "network",    "Network",                  ATRACE_TAG_NETWORK, { } },
+    { "adb",        "ADB",                      ATRACE_TAG_ADB, { } },
+    { "vibrator",   "Vibrator",                 ATRACE_TAG_VIBRATOR, { } },
+    { "aidl",       "AIDL calls",               ATRACE_TAG_AIDL, { } },
+    { "nnapi",      "NNAPI",                    ATRACE_TAG_NNAPI, { } },
+    { "rro",        "Runtime Resource Overlay", ATRACE_TAG_RRO, { } },
     { k_coreServiceCategory, "Core services", 0, { } },
     { k_pdxServiceCategory, "PDX services", 0, { } },
     { "sched",      "CPU Scheduling",   0, {
diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc
index acc62c0..6c86c21 100644
--- a/cmds/atrace/atrace_userdebug.rc
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -15,8 +15,8 @@
     chmod 0666 /sys/kernel/debug/tracing/events/workqueue/enable
     chmod 0666 /sys/kernel/tracing/events/regulator/enable
     chmod 0666 /sys/kernel/debug/tracing/events/regulator/enable
-    chmod 0666 /sys/kernel/tracing/events/pagecache/enable
-    chmod 0666 /sys/kernel/debug/tracing/events/pagecache/enable
+    chmod 0666 /sys/kernel/tracing/events/filemap/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/filemap/enable
 
     # irq
     chmod 0666 /sys/kernel/tracing/events/irq/enable
diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp
index 75855cf..ded0ed3 100644
--- a/cmds/bugreportz/bugreportz.cpp
+++ b/cmds/bugreportz/bugreportz.cpp
@@ -55,7 +55,7 @@
                 errno = ETIMEDOUT;
             }
             printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno));
-            break;
+            return EXIT_FAILURE;
         }
 
         // Writes line by line.
@@ -71,8 +71,5 @@
     // Process final line, in case it didn't finish with newline
     write_line(line, show_progress);
 
-    if (close(s) == -1) {
-        fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno));
-    }
     return EXIT_SUCCESS;
 }
diff --git a/cmds/bugreportz/bugreportz.h b/cmds/bugreportz/bugreportz.h
index 304e4b3..7af289b 100644
--- a/cmds/bugreportz/bugreportz.h
+++ b/cmds/bugreportz/bugreportz.h
@@ -16,6 +16,7 @@
 #define BUGREPORTZ_H
 
 // Calls dumpstate using the given socket and output its result to stdout.
+// Ownership of the socket is not transferred.
 int bugreportz(int s, bool show_progress);
 
 #endif  // BUGREPORTZ_H
diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp
index a3ae1ff..74a95b0 100644
--- a/cmds/bugreportz/main.cpp
+++ b/cmds/bugreportz/main.cpp
@@ -82,7 +82,7 @@
 
     if (s == -1) {
         printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno));
-        return EXIT_SUCCESS;
+        return EXIT_FAILURE;
     }
 
     // Set a timeout so that if nothing is read in 10 minutes, we'll stop
@@ -92,8 +92,16 @@
     tv.tv_sec = 10 * 60;
     tv.tv_usec = 0;
     if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
-        fprintf(stderr, "WARNING: Cannot set socket timeout: %s\n", strerror(errno));
+        fprintf(stderr,
+                "WARNING: Cannot set socket timeout, bugreportz might hang indefinitely: %s\n",
+                strerror(errno));
     }
 
-    bugreportz(s, show_progress);
+    int ret = bugreportz(s, show_progress);
+
+    if (close(s) == -1) {
+        fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno));
+        ret = EXIT_FAILURE;
+    }
+    return ret;
 }
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index a91eafe..db8848a 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1420,6 +1420,12 @@
     printf("========================================================\n");
     printf("== dumpstate: done (id %d)\n", ds.id_);
     printf("========================================================\n");
+
+    printf("========================================================\n");
+    printf("== Obtaining statsd metadata\n");
+    printf("========================================================\n");
+    // This differs from the usual dumpsys stats, which is the stats report data.
+    RunDumpsys("STATSDSTATS", {"stats", "--metadata"});
 }
 
 /* Dumps state for the default case. Returns true if everything went fine. */
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 8fbea8a..4811927 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -65,7 +65,7 @@
             "         -l: only list services, do not dump them\n"
             "         -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n"
             "         -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n"
-            "         --proto: filter services that support dumping data in proto format. Dumps"
+            "         --proto: filter services that support dumping data in proto format. Dumps\n"
             "               will be in proto format.\n"
             "         --priority LEVEL: filter services based on specified priority\n"
             "               LEVEL must be one of CRITICAL | HIGH | NORMAL\n"
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 8f60881..3ada153 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -371,8 +371,8 @@
                                    IServiceManager::DUMP_FLAG_PRIORITY_NORMAL);
     ExpectCheckService("Locksmith");
     ExpectCheckService("Valet");
-    ExpectDumpWithArgs("Locksmith", {"-a", "--dump-priority", "NORMAL"}, "dump1");
-    ExpectDumpWithArgs("Valet", {"-a", "--dump-priority", "NORMAL"}, "dump2");
+    ExpectDumpWithArgs("Locksmith", {"--dump-priority", "NORMAL", "-a"}, "dump1");
+    ExpectDumpWithArgs("Valet", {"--dump-priority", "NORMAL", "-a"}, "dump2");
 
     CallMain({"--priority", "NORMAL"});
 
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 65743d9..488070d 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -18,6 +18,7 @@
         "dexopt.cpp",
         "globals.cpp",
         "utils.cpp",
+        "utils_default.cpp",
         "view_compiler.cpp",
         ":installd_aidl",
     ],
@@ -205,6 +206,7 @@
         "otapreopt.cpp",
         "otapreopt_utils.cpp",
         "utils.cpp",
+        "utils_default.cpp",
         "view_compiler.cpp",
     ],
 
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index ccffd29..49383e5 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -99,15 +99,6 @@
 static constexpr size_t kSha256Size = 32;
 static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
 
-// NOTE: keep in sync with Installer
-static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
-static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
-static constexpr int FLAG_USE_QUOTA = 1 << 12;
-static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
-static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14;
-static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 15;
-static constexpr int FLAG_FORCE = 1 << 16;
-
 namespace {
 
 constexpr const char* kDump = "android.permission.DUMP";
@@ -575,6 +566,35 @@
                 remove_path_xattr(path, kXattrInodeCodeCache);
             }
         }
+
+        auto extPath = findDataMediaPath(uuid, userId);
+        if (flags & FLAG_CLEAR_CACHE_ONLY) {
+            // Clear only cached data from shared storage
+            path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname);
+            if (delete_dir_contents(path, true) != 0) {
+                res = error("Failed to delete contents of " + path);
+            }
+        } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+            // No code cache on shared storage
+        } else {
+            // Clear everything on shared storage
+            path = StringPrintf("%s/Android/sandbox/%s", extPath.c_str(), pkgname);
+            if (delete_dir_contents(path, true) != 0) {
+                res = error("Failed to delete contents of " + path);
+            }
+            path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname);
+            if (delete_dir_contents(path, true) != 0) {
+                res = error("Failed to delete contents of " + path);
+            }
+            path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname);
+            if (delete_dir_contents(path, true) != 0) {
+                res = error("Failed to delete contents of " + path);
+            }
+            path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname);
+            if (delete_dir_contents(path, true) != 0) {
+                res = error("Failed to delete contents of " + path);
+            }
+        }
     }
     if (flags & FLAG_STORAGE_DE) {
         std::string suffix = "";
@@ -643,6 +663,24 @@
         if (delete_dir_contents_and_dir(path) != 0) {
             res = error("Failed to delete " + path);
         }
+
+        auto extPath = findDataMediaPath(uuid, userId);
+        path = StringPrintf("%s/Android/sandbox/%s", extPath.c_str(), pkgname);
+        if (delete_dir_contents_and_dir(path, true) != 0) {
+            res = error("Failed to delete " + path);
+        }
+        path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname);
+        if (delete_dir_contents_and_dir(path, true) != 0) {
+            res = error("Failed to delete " + path);
+        }
+        path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname);
+        if (delete_dir_contents_and_dir(path, true) != 0) {
+            res = error("Failed to delete " + path);
+        }
+        path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname);
+        if (delete_dir_contents_and_dir(path, true) != 0) {
+            res = error("Failed to delete " + path);
+        }
     }
     if (flags & FLAG_STORAGE_DE) {
         auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
@@ -1632,6 +1670,8 @@
             }
 
             ATRACE_BEGIN("external");
+            auto sandboxPath = create_data_media_package_path(uuid_, userId, "sandbox", pkgname);
+            calculate_tree_size(sandboxPath, &extStats.dataSize);
             auto extPath = create_data_media_package_path(uuid_, userId, "data", pkgname);
             collectManualStats(extPath, &extStats);
             auto mediaPath = create_data_media_package_path(uuid_, userId, "media", pkgname);
@@ -2439,7 +2479,7 @@
     if (validate_apk_path(packageDir.c_str())) {
         return error("Invalid path " + packageDir);
     }
-    if (delete_dir_contents_and_dir(packageDir) != 0) {
+    if (rm_package_dir(packageDir) != 0) {
         return error("Failed to delete " + packageDir);
     }
     return ok();
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 377074a..b345210 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -114,4 +114,14 @@
 
     const int FLAG_STORAGE_DE = 0x1;
     const int FLAG_STORAGE_CE = 0x2;
+
+    const int FLAG_CLEAR_CACHE_ONLY = 0x10;
+    const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20;
+
+    const int FLAG_FREE_CACHE_V2 = 0x100;
+    const int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 0x200;
+    const int FLAG_FREE_CACHE_NOOP = 0x400;
+
+    const int FLAG_USE_QUOTA = 0x1000;
+    const int FLAG_FORCE = 0x2000;
 }
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 2d58515..db09070 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -40,8 +40,8 @@
 constexpr int64_t kGbInBytes = 1024 * kMbInBytes;
 constexpr int64_t kTbInBytes = 1024 * kGbInBytes;
 
-static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
-static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14;
+#define FLAG_FREE_CACHE_V2 InstalldNativeService::FLAG_FREE_CACHE_V2
+#define FLAG_FREE_CACHE_V2_DEFY_QUOTA InstalldNativeService::FLAG_FREE_CACHE_V2_DEFY_QUOTA
 
 int get_property(const char *key, char *value, const char *default_value) {
     return property_get(key, value, default_value);
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index cf7f1eb..e2c5907 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -44,7 +44,7 @@
 
 constexpr const char* kTestUuid = "TEST";
 
-static constexpr int FLAG_FORCE = 1 << 16;
+#define FLAG_FORCE InstalldNativeService::FLAG_FORCE
 
 int get_property(const char *key, char *value, const char *default_value) {
     return property_get(key, value, default_value);
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 5d3a97d..0711b34 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -119,6 +119,8 @@
 
 int delete_dir_contents_fd(int dfd, const char *name);
 
+int rm_package_dir(const std::string& package_dir);
+
 int copy_dir_files(const char *srcname, const char *dstname, uid_t owner, gid_t group);
 
 int64_t data_disk_free(const std::string& data_path);
diff --git a/cmds/installd/utils_default.cpp b/cmds/installd/utils_default.cpp
new file mode 100644
index 0000000..a6025e6
--- /dev/null
+++ b/cmds/installd/utils_default.cpp
@@ -0,0 +1,30 @@
+/*
+** Copyright 2019, 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 "utils.h"
+
+namespace android {
+namespace installd {
+
+// In this file are default definitions of the functions that may contain
+// platform dependent logic.
+
+int rm_package_dir(const std::string& package_dir) {
+    return delete_dir_contents_and_dir(package_dir);
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index fade8cf..cf3b172 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -146,7 +146,19 @@
 
 int binder_become_context_manager(struct binder_state *bs)
 {
-    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
+    struct flat_binder_object obj;
+    memset(&obj, 0, sizeof(obj));
+    obj.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
+
+    int result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR_EXT, &obj);
+
+    // fallback to original method
+    if (result != 0) {
+        android_errorWriteLog(0x534e4554, "121035042");
+
+        result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
+    }
+    return result;
 }
 
 int binder_write(struct binder_state *bs, void *data, size_t len)
@@ -240,13 +252,28 @@
 #endif
             ptr += sizeof(struct binder_ptr_cookie);
             break;
+        case BR_TRANSACTION_SEC_CTX:
         case BR_TRANSACTION: {
-            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
-            if ((end - ptr) < sizeof(*txn)) {
-                ALOGE("parse: txn too small!\n");
-                return -1;
+            struct binder_transaction_data_secctx txn;
+            if (cmd == BR_TRANSACTION_SEC_CTX) {
+                if ((end - ptr) < sizeof(struct binder_transaction_data_secctx)) {
+                    ALOGE("parse: txn too small (binder_transaction_data_secctx)!\n");
+                    return -1;
+                }
+                memcpy(&txn, (void*) ptr, sizeof(struct binder_transaction_data_secctx));
+                ptr += sizeof(struct binder_transaction_data_secctx);
+            } else /* BR_TRANSACTION */ {
+                if ((end - ptr) < sizeof(struct binder_transaction_data)) {
+                    ALOGE("parse: txn too small (binder_transaction_data)!\n");
+                    return -1;
+                }
+                memcpy(&txn.transaction_data, (void*) ptr, sizeof(struct binder_transaction_data));
+                ptr += sizeof(struct binder_transaction_data);
+
+                txn.secctx = 0;
             }
-            binder_dump_txn(txn);
+
+            binder_dump_txn(&txn.transaction_data);
             if (func) {
                 unsigned rdata[256/4];
                 struct binder_io msg;
@@ -254,15 +281,14 @@
                 int res;
 
                 bio_init(&reply, rdata, sizeof(rdata), 4);
-                bio_init_from_txn(&msg, txn);
-                res = func(bs, txn, &msg, &reply);
-                if (txn->flags & TF_ONE_WAY) {
-                    binder_free_buffer(bs, txn->data.ptr.buffer);
+                bio_init_from_txn(&msg, &txn.transaction_data);
+                res = func(bs, &txn, &msg, &reply);
+                if (txn.transaction_data.flags & TF_ONE_WAY) {
+                    binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer);
                 } else {
-                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
+                    binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res);
                 }
             }
-            ptr += sizeof(*txn);
             break;
         }
         case BR_REPLY: {
diff --git a/cmds/servicemanager/binder.h b/cmds/servicemanager/binder.h
index c95b33f..70be3b4 100644
--- a/cmds/servicemanager/binder.h
+++ b/cmds/servicemanager/binder.h
@@ -5,7 +5,8 @@
 #define _BINDER_H_
 
 #include <sys/ioctl.h>
-#include <linux/android/binder.h>
+
+#include "binder_kernel.h"
 
 struct binder_state;
 
@@ -42,7 +43,7 @@
 };
 
 typedef int (*binder_handler)(struct binder_state *bs,
-                              struct binder_transaction_data *txn,
+                              struct binder_transaction_data_secctx *txn,
                               struct binder_io *msg,
                               struct binder_io *reply);
 
diff --git a/cmds/servicemanager/binder_kernel.h b/cmds/servicemanager/binder_kernel.h
new file mode 100644
index 0000000..19fd773
--- /dev/null
+++ b/cmds/servicemanager/binder_kernel.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 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_BINDER_KERNEL_H
+#define ANDROID_BINDER_KERNEL_H
+
+#include <linux/android/binder.h>
+
+/**
+ * This file exists because the uapi kernel headers in bionic are built
+ * from upstream kernel headers only, and not all of the hwbinder kernel changes
+ * have made it upstream yet. Therefore, the modifications to the
+ * binder header are added locally in this file.
+ */
+
+enum {
+        FLAT_BINDER_FLAG_TXN_SECURITY_CTX = 0x1000,
+};
+
+#define BINDER_SET_CONTEXT_MGR_EXT      _IOW('b', 13, struct flat_binder_object)
+
+struct binder_transaction_data_secctx {
+        struct binder_transaction_data transaction_data;
+        binder_uintptr_t secctx;
+};
+
+enum {
+        BR_TRANSACTION_SEC_CTX = _IOR('r', 2,
+                              struct binder_transaction_data_secctx),
+};
+
+
+#endif // ANDROID_BINDER_KERNEL_H
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index d776682..ec3fac5 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -61,14 +61,14 @@
 static char *service_manager_context;
 static struct selabel_handle* sehandle;
 
-static bool check_mac_perms(pid_t spid, uid_t uid, const char *tctx, const char *perm, const char *name)
+static bool check_mac_perms(pid_t spid, const char* sid, uid_t uid, const char *tctx, const char *perm, const char *name)
 {
-    char *sctx = NULL;
+    char *lookup_sid = NULL;
     const char *class = "service_manager";
     bool allowed;
     struct audit_data ad;
 
-    if (getpidcon(spid, &sctx) < 0) {
+    if (sid == NULL && getpidcon(spid, &lookup_sid) < 0) {
         ALOGE("SELinux: getpidcon(pid=%d) failed to retrieve pid context.\n", spid);
         return false;
     }
@@ -77,19 +77,23 @@
     ad.uid = uid;
     ad.name = name;
 
-    int result = selinux_check_access(sctx, tctx, class, perm, (void *) &ad);
+    if (sid == NULL) {
+        android_errorWriteLog(0x534e4554, "121035042");
+    }
+
+    int result = selinux_check_access(sid ? sid : lookup_sid, tctx, class, perm, (void *) &ad);
     allowed = (result == 0);
 
-    freecon(sctx);
+    freecon(lookup_sid);
     return allowed;
 }
 
-static bool check_mac_perms_from_getcon(pid_t spid, uid_t uid, const char *perm)
+static bool check_mac_perms_from_getcon(pid_t spid, const char* sid, uid_t uid, const char *perm)
 {
-    return check_mac_perms(spid, uid, service_manager_context, perm, NULL);
+    return check_mac_perms(spid, sid, uid, service_manager_context, perm, NULL);
 }
 
-static bool check_mac_perms_from_lookup(pid_t spid, uid_t uid, const char *perm, const char *name)
+static bool check_mac_perms_from_lookup(pid_t spid, const char* sid, uid_t uid, const char *perm, const char *name)
 {
     bool allowed;
     char *tctx = NULL;
@@ -104,12 +108,12 @@
         return false;
     }
 
-    allowed = check_mac_perms(spid, uid, tctx, perm, name);
+    allowed = check_mac_perms(spid, sid, uid, tctx, perm, name);
     freecon(tctx);
     return allowed;
 }
 
-static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid)
+static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid, const char* sid, uid_t uid)
 {
     const char *perm = "add";
 
@@ -117,19 +121,19 @@
         return 0; /* Don't allow apps to register services */
     }
 
-    return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0;
+    return check_mac_perms_from_lookup(spid, sid, uid, perm, str8(name, name_len)) ? 1 : 0;
 }
 
-static int svc_can_list(pid_t spid, uid_t uid)
+static int svc_can_list(pid_t spid, const char* sid, uid_t uid)
 {
     const char *perm = "list";
-    return check_mac_perms_from_getcon(spid, uid, perm) ? 1 : 0;
+    return check_mac_perms_from_getcon(spid, sid, uid, perm) ? 1 : 0;
 }
 
-static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid)
+static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, const char* sid, uid_t uid)
 {
     const char *perm = "find";
-    return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0;
+    return check_mac_perms_from_lookup(spid, sid, uid, perm, str8(name, name_len)) ? 1 : 0;
 }
 
 struct svcinfo
@@ -175,7 +179,7 @@
 };
 
 
-uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
+uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid, const char* sid)
 {
     struct svcinfo *si = find_svc(s, len);
 
@@ -192,7 +196,7 @@
         }
     }
 
-    if (!svc_can_find(s, len, spid, uid)) {
+    if (!svc_can_find(s, len, spid, sid, uid)) {
         return 0;
     }
 
@@ -200,7 +204,7 @@
 }
 
 int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle,
-                   uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid) {
+                   uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid, const char* sid) {
     struct svcinfo *si;
 
     //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,
@@ -209,7 +213,7 @@
     if (!handle || (len == 0) || (len > 127))
         return -1;
 
-    if (!svc_can_register(s, len, spid, uid)) {
+    if (!svc_can_register(s, len, spid, sid, uid)) {
         ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
              str8(s, len), handle, uid);
         return -1;
@@ -248,7 +252,7 @@
 }
 
 int svcmgr_handler(struct binder_state *bs,
-                   struct binder_transaction_data *txn,
+                   struct binder_transaction_data_secctx *txn_secctx,
                    struct binder_io *msg,
                    struct binder_io *reply)
 {
@@ -260,6 +264,8 @@
     int allow_isolated;
     uint32_t dumpsys_priority;
 
+    struct binder_transaction_data *txn = &txn_secctx->transaction_data;
+
     //ALOGI("target=%p code=%d pid=%d uid=%d\n",
     //      (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);
 
@@ -274,6 +280,7 @@
     // Note that we ignore the strict_policy and don't propagate it
     // further (since we do no outbound RPCs anyway).
     strict_policy = bio_get_uint32(msg);
+    bio_get_uint32(msg);  // Ignore worksource header.
     s = bio_get_string16(msg, &len);
     if (s == NULL) {
         return -1;
@@ -304,7 +311,8 @@
         if (s == NULL) {
             return -1;
         }
-        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
+        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid,
+                                 (const char*) txn_secctx->secctx);
         if (!handle)
             break;
         bio_put_ref(reply, handle);
@@ -319,7 +327,7 @@
         allow_isolated = bio_get_uint32(msg) ? 1 : 0;
         dumpsys_priority = bio_get_uint32(msg);
         if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
-                           txn->sender_pid))
+                           txn->sender_pid, (const char*) txn_secctx->secctx))
             return -1;
         break;
 
@@ -327,7 +335,7 @@
         uint32_t n = bio_get_uint32(msg);
         uint32_t req_dumpsys_priority = bio_get_uint32(msg);
 
-        if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {
+        if (!svc_can_list(txn->sender_pid, (const char*) txn_secctx->secctx, txn->sender_euid)) {
             ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
                     txn->sender_euid);
             return -1;
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 4d93cb4..152ac28 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -13,5 +13,6 @@
     onrestart restart cameraserver
     onrestart restart keystore
     onrestart restart gatekeeperd
+    onrestart restart thermalservice
     writepid /dev/cpuset/system-background/tasks
     shutdown critical
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 0bc08a9..c70bc3e 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -30,14 +30,13 @@
 
 message SurfaceChange {
     required int32 id = 1;
-
+    reserved 7;
     oneof SurfaceChange {
         PositionChange              position                = 2;
         SizeChange                  size                    = 3;
         AlphaChange                 alpha                   = 4;
         LayerChange                 layer                   = 5;
         CropChange                  crop                    = 6;
-        FinalCropChange             final_crop              = 7;
         MatrixChange                matrix                  = 8;
         OverrideScalingModeChange   override_scaling_mode   = 9;
         TransparentRegionHintChange transparent_region_hint = 10;
@@ -46,6 +45,7 @@
         OpaqueFlagChange            opaque_flag             = 13;
         SecureFlagChange            secure_flag             = 14;
         DeferredTransactionChange   deferred_transaction    = 15;
+        CornerRadiusChange          corner_radius           = 16;
     }
 }
 
@@ -63,6 +63,10 @@
     required float alpha = 1;
 }
 
+message CornerRadiusChange {
+    required float corner_radius = 1;
+}
+
 message LayerChange {
     required uint32 layer = 1;
 }
@@ -71,10 +75,6 @@
     required Rectangle rectangle = 1;
 }
 
-message FinalCropChange {
-    required Rectangle rectangle = 1;
-}
-
 message MatrixChange {
     required float dsdx = 1;
     required float dtdx = 2;
@@ -165,7 +165,7 @@
 message DisplayCreation {
     required int32     id                = 1;
     required string    name              = 2;
-    required int32     type              = 3;
+    optional uint64    display_id        = 3;
     required bool      is_secure         = 4;
 }
 
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 4140f40..34886a9 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -286,10 +286,6 @@
             std::thread(&Replayer::createSurfaceControl, this, increment.surface_creation(), event)
                     .detach();
         } break;
-        case increment.kSurfaceDeletion: {
-            std::thread(&Replayer::deleteSurfaceControl, this, increment.surface_deletion(), event)
-                    .detach();
-        } break;
         case increment.kBufferUpdate: {
             std::lock_guard<std::mutex> lock1(mLayerLock);
             std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
@@ -385,12 +381,12 @@
             case SurfaceChange::SurfaceChangeCase::kCrop:
                 setCrop(transaction, change.id(), change.crop());
                 break;
+            case SurfaceChange::SurfaceChangeCase::kCornerRadius:
+                setCornerRadius(transaction, change.id(), change.corner_radius());
+                break;
             case SurfaceChange::SurfaceChangeCase::kMatrix:
                 setMatrix(transaction, change.id(), change.matrix());
                 break;
-            case SurfaceChange::SurfaceChangeCase::kFinalCrop:
-                setFinalCrop(transaction, change.id(), change.final_crop());
-                break;
             case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
                 setOverrideScalingMode(transaction, change.id(),
                         change.override_scaling_mode());
@@ -489,17 +485,14 @@
 
     Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
             cc.rectangle().bottom());
-    t.setCrop(mLayers[id], r);
+    t.setCrop_legacy(mLayers[id], r);
 }
 
-void Replayer::setFinalCrop(SurfaceComposerClient::Transaction& t,
-        layer_id id, const FinalCropChange& fcc) {
-    ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
-            fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
-            fcc.rectangle().bottom());
-    Rect r = Rect(fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
-            fcc.rectangle().bottom());
-    t.setFinalCrop(mLayers[id], r);
+void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t,
+        layer_id id, const CornerRadiusChange& cc) {
+    ALOGV("Layer %d: Setting Corner Radius -- cornerRadius=%d", id, cc.corner_radius());
+
+    t.setCornerRadius(mLayers[id], cc.corner_radius());
 }
 
 void Replayer::setMatrix(SurfaceComposerClient::Transaction& t,
@@ -570,7 +563,7 @@
 
     auto handle = mLayers[dtc.layer_id()]->getHandle();
 
-    t.deferTransactionUntil(mLayers[id], handle, dtc.frame_number());
+    t.deferTransactionUntil_legacy(mLayers[id], handle, dtc.frame_number());
 }
 
 void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t,
@@ -631,47 +624,10 @@
     return NO_ERROR;
 }
 
-status_t Replayer::deleteSurfaceControl(
-        const SurfaceDeletion& delete_, const std::shared_ptr<Event>& event) {
-    ALOGV("Deleting %d Surface Control", delete_.id());
-    event->readyToExecute();
-
-    std::lock_guard<std::mutex> lock1(mPendingLayersLock);
-
-    mLayersPendingRemoval.push_back(delete_.id());
-
-    const auto& iterator = mBufferQueueSchedulers.find(delete_.id());
-    if (iterator != mBufferQueueSchedulers.end()) {
-        (*iterator).second->stopScheduling();
-    }
-
-    std::lock_guard<std::mutex> lock2(mLayerLock);
-    if (mLayers[delete_.id()] != nullptr) {
-        mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle());
-    }
-
-    return NO_ERROR;
-}
-
-void Replayer::doDeleteSurfaceControls() {
-    std::lock_guard<std::mutex> lock1(mPendingLayersLock);
-    std::lock_guard<std::mutex> lock2(mLayerLock);
-    if (!mLayersPendingRemoval.empty()) {
-        for (int id : mLayersPendingRemoval) {
-            mLayers.erase(id);
-            mColors.erase(id);
-            mBufferQueueSchedulers.erase(id);
-        }
-        mLayersPendingRemoval.clear();
-    }
-}
-
 status_t Replayer::injectVSyncEvent(
         const VSyncEvent& vSyncEvent, const std::shared_ptr<Event>& event) {
     ALOGV("Injecting VSync Event");
 
-    doDeleteSurfaceControls();
-
     event->readyToExecute();
 
     SurfaceComposerClient::injectVSync(vSyncEvent.when());
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index 295403e..ad807ee 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -70,8 +70,6 @@
     status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event);
     status_t createSurfaceControl(const SurfaceCreation& create,
             const std::shared_ptr<Event>& event);
-    status_t deleteSurfaceControl(const SurfaceDeletion& delete_,
-            const std::shared_ptr<Event>& event);
     status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event);
     void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event);
     void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event);
@@ -92,8 +90,8 @@
             layer_id id, const LayerChange& lc);
     void setCrop(SurfaceComposerClient::Transaction& t,
             layer_id id, const CropChange& cc);
-    void setFinalCrop(SurfaceComposerClient::Transaction& t,
-            layer_id id, const FinalCropChange& fcc);
+    void setCornerRadius(SurfaceComposerClient::Transaction& t,
+            layer_id id, const CornerRadiusChange& cc);
     void setMatrix(SurfaceComposerClient::Transaction& t,
             layer_id id, const MatrixChange& mc);
     void setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
@@ -120,7 +118,6 @@
     void setDisplayProjection(SurfaceComposerClient::Transaction& t,
             display_id id, const ProjectionChange& pc);
 
-    void doDeleteSurfaceControls();
     void waitUntilTimestamp(int64_t timestamp);
     void waitUntilDeferredTransactionLayerExists(
             const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock);
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
index a892e46..d63d97f 100644
--- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
+++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
@@ -172,7 +172,7 @@
 def display_create(increment):
     increment.display_creation.id = int(input("Enter id: "))
     increment.display_creation.name = str(raw_input("Enter name: "))
-    increment.display_creation.type = int(input("Enter type: "))
+    increment.display_creation.display_id = int(input("Enter display ID: "))
     increment.display_creation.is_secure = bool(input("Enter if secure: "))
 
 def display_delete(increment):
diff --git a/data/etc/android.hardware.biometrics.face.xml b/data/etc/android.hardware.biometrics.face.xml
new file mode 100644
index 0000000..7fa0bf9
--- /dev/null
+++ b/data/etc/android.hardware.biometrics.face.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard set of features for a biometric face authentication sensor. -->
+<permissions>
+    <feature name="android.hardware.biometrics.face" />
+</permissions>
diff --git a/data/etc/android.hardware.biometrics.fingerprint.xml b/data/etc/android.hardware.biometrics.fingerprint.xml
new file mode 100644
index 0000000..e5af541
--- /dev/null
+++ b/data/etc/android.hardware.biometrics.fingerprint.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- This is the standard set of features for a biometric fingerprint sensor. -->
+<permissions>
+    <feature name="android.hardware.biometrics.fingerprint" />
+</permissions>
diff --git a/data/etc/android.software.ipsec_tunnels.xml b/data/etc/android.software.ipsec_tunnels.xml
new file mode 100644
index 0000000..f7ffc02
--- /dev/null
+++ b/data/etc/android.software.ipsec_tunnels.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!--
+     This is the feature indicating that the device has support for multinetworking-capable IPsec
+     tunnels
+-->
+
+<permissions>
+    <feature name="android.software.ipsec_tunnels" />
+</permissions>
diff --git a/data/etc/android.software.secure_lock_screen.xml b/data/etc/android.software.secure_lock_screen.xml
new file mode 100644
index 0000000..3464487
--- /dev/null
+++ b/data/etc/android.software.secure_lock_screen.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<permissions>
+    <feature name="android.software.secure_lock_screen" />
+</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 561f5ba..6cbe4ae 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -36,12 +36,16 @@
     <feature name="android.hardware.type.automotive" />
 
     <!-- basic system services -->
-    <feature name="android.software.app_widgets" />
     <feature name="android.software.connectionservice" />
     <feature name="android.software.voice_recognizers" notLowRam="true" />
     <feature name="android.software.backup" />
     <feature name="android.software.home_screen" />
+    <feature name="android.software.input_methods" />
     <feature name="android.software.print" />
+    <feature name="android.software.companion_device_setup" />
+    <feature name="android.software.autofill" />
+    <feature name="android.software.cant_save_state" />
+    <feature name="android.software.secure_lock_screen" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml
index 8b5a461..915e579 100644
--- a/data/etc/go_handheld_core_hardware.xml
+++ b/data/etc/go_handheld_core_hardware.xml
@@ -43,6 +43,7 @@
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
+    <feature name="android.software.secure_lock_screen" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 060a334..619d017 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -51,6 +51,7 @@
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
+    <feature name="android.software.secure_lock_screen" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 6db2627..52524ca 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -51,6 +51,7 @@
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
+    <feature name="android.software.secure_lock_screen" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml
index e2ab71a..0f364c1 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -35,6 +35,7 @@
 
     <!-- basic system services -->
     <feature name="android.software.home_screen" />
+    <feature name="android.software.secure_lock_screen" />
 
     <!-- input management and third-party input method editors -->
     <feature name="android.software.input_methods" />
diff --git a/headers/media_plugin/media/cas/CasAPI.h b/headers/media_plugin/media/cas/CasAPI.h
index 4de314d..c87ee56 100644
--- a/headers/media_plugin/media/cas/CasAPI.h
+++ b/headers/media_plugin/media/cas/CasAPI.h
@@ -48,6 +48,14 @@
         uint8_t *data,
         size_t size);
 
+typedef void (*CasPluginCallbackExt)(
+        void *appData,
+        int32_t event,
+        int32_t arg,
+        uint8_t *data,
+        size_t size,
+        const CasSessionId *sessionId);
+
 struct CasFactory {
     CasFactory() {}
     virtual ~CasFactory() {}
@@ -67,6 +75,13 @@
             CasPluginCallback callback,
             CasPlugin **plugin) = 0;
 
+    // Construct a new extend instance of a CasPlugin given a CA_system_id
+    virtual status_t createPlugin(
+            int32_t CA_system_id,
+            void *appData,
+            CasPluginCallbackExt callback,
+            CasPlugin **plugin) = 0;
+
 private:
     CasFactory(const CasFactory &);
     CasFactory &operator=(const CasFactory &); /* NOLINT */
@@ -110,7 +125,15 @@
             int32_t arg,
             const CasData &eventData) = 0;
 
-    // Native implementation of the MediaCas Java API provision method.
+    // Deliver an session event to the CasPlugin. The format of the event is
+    // specific to the CA scheme and is opaque to the framework.
+    virtual status_t sendSessionEvent(
+            const CasSessionId &sessionId,
+            int32_t event,
+            int32_t arg,
+            const CasData &eventData) = 0;
+
+   // Native implementation of the MediaCas Java API provision method.
     virtual status_t provision(
             const String8 &provisionString) = 0;
 
diff --git a/headers/media_plugin/media/drm/DrmAPI.h b/headers/media_plugin/media/drm/DrmAPI.h
index c44a1f6..59bd292 100644
--- a/headers/media_plugin/media/drm/DrmAPI.h
+++ b/headers/media_plugin/media/drm/DrmAPI.h
@@ -84,6 +84,7 @@
             kDrmPluginEventSessionReclaimed,
             kDrmPluginEventExpirationUpdate,
             kDrmPluginEventKeysChange,
+            kDrmPluginEventSessionLostState,
         };
 
         // Drm keys can be for offline content or for online streaming.
@@ -139,6 +140,8 @@
             kHdcpV2_1,
             // HDCP version 2.2 Type 1.
             kHdcpV2_2,
+            // HDCP version 2.3 Type 1.
+            kHdcpV2_3,
             // No digital output, implicitly secure
             kHdcpNoOutput = 0x7fff
         };
@@ -167,6 +170,25 @@
             kSecurityLevelHwSecureAll
         };
 
+        // An offline license may be usable or inactive. The keys in a
+        // usable offline license are available for decryption. When
+        // the offline license state is inactive, the keys have been
+        // marked for release using getKeyRequest with
+        // kKeyType_Release but the key response has not been
+        // received. The keys in an inactive offline license are not
+        // usable for decryption.
+
+        enum OfflineLicenseState {
+            // The offline license state is unknown due to an error
+            kOfflineLicenseStateUnknown,
+            // Offline license state is usable, the keys may be used for decryption.
+            kOfflineLicenseStateUsable,
+            // Offline license state is released, the keys have been marked for
+            // release using getKeyRequest() with kKeyType_Release but the
+            // key response has not been received.
+            kOfflineLicenseStateReleased
+        };
+
         DrmPlugin() {}
         virtual ~DrmPlugin() {}
 
diff --git a/headers/media_plugin/media/hardware/HardwareAPI.h b/headers/media_plugin/media/hardware/HardwareAPI.h
index 6c1ba3d..ae0220a 100644
--- a/headers/media_plugin/media/hardware/HardwareAPI.h
+++ b/headers/media_plugin/media/hardware/HardwareAPI.h
@@ -425,7 +425,7 @@
 
 // HDR color description parameters.
 // This is passed via OMX_SetConfig or OMX_GetConfig to video encoders and decoders when the
-// 'OMX.google.android.index.describeHDRColorInfo' extension is given and an HDR stream
+// 'OMX.google.android.index.describeHDRStaticInfo' extension is given and an HDR stream
 // is detected.  Component SHALL behave as described below if it supports this extension.
 //
 // Currently, only Static Metadata Descriptor Type 1 support is required.
@@ -496,6 +496,64 @@
     HDRStaticInfo sInfo;           // IN/OUT
 };
 
+// HDR10+ metadata configuration.
+//
+// nParamSize: size of the storage starting at nValue (must be at least 1 and at most
+//             MAX_HDR10PLUSINFO_SIZE). This field must not be modified by the component.
+// nParamSizeUsed: size of the actual HDR10+ metadata starting at nValue. For OMX_SetConfig,
+//                 it must not be modified by the component. For OMX_GetConfig, the component
+//                 should put the actual size of the retrieved config in this field (and in
+//                 case where nParamSize is smaller than nParamSizeUsed, the component should
+//                 still update nParamSizeUsed without actually copying the metadata to nValue).
+// nValue: storage of the HDR10+ metadata conforming to the user_data_registered_itu_t_t35()
+//         syntax of SEI message for ST 2094-40.
+//
+// This is passed via OMX_SetConfig or OMX_GetConfig to video encoders and decoders when the
+// 'OMX.google.android.index.describeHDR10PlusInfo' extension is given. In general, this config
+// is associated with a particular frame. A typical sequence of usage is as follows:
+//
+// a) OMX_SetConfig associates the config with the next input buffer sent in OMX_EmptyThisBuffer
+//    (input A);
+// b) The component sends OMX_EventConfigUpdate to notify the client that there is a config
+//    update  on the output port that is associated with the next output buffer that's about to
+//    be sent via FillBufferDone callback (output A);
+// c) The client, upon receiving the OMX_EventConfigUpdate, calls OMX_GetConfig to retrieve
+//    the config and associates it with output A.
+//
+// All config updates will be retrieved in the order reported, and the client is required to
+// call OMX_GetConfig for each OMX_EventConfigUpdate for this config. Note that the order of
+// OMX_EventConfigUpdate relative to FillBufferDone callback determines which output frame
+// the config should be associated with, the actual OMX_GetConfig for the config could happen
+// before or after the component calls the FillBufferDone callback.
+//
+// Depending on the video codec type (in particular, whether the codec uses in-band or out-of-
+// band HDR10+ metadata), the component shall behave as detailed below:
+//
+// VIDEO DECODERS:
+// 1) If the codec utilizes out-of-band HDR10+ metadata, the decoder must support the sequence
+//    a) ~ c) outlined above;
+// 2) If the codec utilizes in-band HDR10+ metadata, OMX_SetConfig for this config should be
+//    ignored (as the metadata is embedded in the input buffer), while the notification and
+//    retrieval of the config on the output as outlined in b) & c) must be supported.
+//
+// VIDEO ENCODERS:
+// 1) If the codec utilizes out-of-band HDR10+ metadata, the decoder must support the sequence
+//    a) ~ c) outlined above;
+// 2) If the codec utilizes in-band HDR10+ metadata, OMX_SetConfig for this config outlined in
+//    a) must be supported. The notification as outlined in b) must not be sent, and the
+//    retrieval of the config via OMX_GetConfig should be ignored (as the metadata is embedded
+//    in the output buffer).
+
+#define MAX_HDR10PLUSINFO_SIZE 1024
+struct DescribeHDR10PlusInfoParams {
+    OMX_U32 nSize;                 // IN
+    OMX_VERSIONTYPE nVersion;      // IN
+    OMX_U32 nPortIndex;            // IN
+    OMX_U32 nParamSize;            // IN
+    OMX_U32 nParamSizeUsed;        // IN/OUT
+    OMX_U8 nValue[1];              // IN/OUT
+};
+
 }  // namespace android
 
 extern android::OMXPluginBase *createOMXPlugin();
diff --git a/headers/media_plugin/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h
index dc25ded..ce30b41 100644
--- a/headers/media_plugin/media/openmax/OMX_AsString.h
+++ b/headers/media_plugin/media/openmax/OMX_AsString.h
@@ -188,7 +188,9 @@
 inline static const char *asString(OMX_AUDIO_CODINGEXTTYPE i, const char *def = "??") {
     switch (i) {
         case OMX_AUDIO_CodingAndroidAC3:  return "AndroidAC3";
+        case OMX_AUDIO_CodingAndroidEAC3: return "AndroidEAC3";
         case OMX_AUDIO_CodingAndroidOPUS: return "AndroidOPUS";
+        case OMX_AUDIO_CodingAndroidAC4:  return "AndroidAC4";
         default:                          return asString((OMX_AUDIO_CODINGTYPE)i, def);
     }
 }
@@ -533,9 +535,11 @@
 //      case OMX_IndexConfigCommit:                     return "ConfigCommit";
         case OMX_IndexConfigAndroidVendorExtension:     return "ConfigAndroidVendorExtension";
         case OMX_IndexParamAudioAndroidAc3:             return "ParamAudioAndroidAc3";
+        case OMX_IndexConfigAudioPresentation:          return "ConfigAudioPresentation";
         case OMX_IndexParamAudioAndroidOpus:            return "ParamAudioAndroidOpus";
         case OMX_IndexParamAudioAndroidAacPresentation: return "ParamAudioAndroidAacPresentation";
         case OMX_IndexParamAudioAndroidEac3:            return "ParamAudioAndroidEac3";
+        case OMX_IndexParamAudioAndroidAc4:             return "ParamAudioAndroidAc4";
         case OMX_IndexParamAudioProfileQuerySupported:  return "ParamAudioProfileQuerySupported";
 //      case OMX_IndexParamNalStreamFormatSupported:    return "ParamNalStreamFormatSupported";
 //      case OMX_IndexParamNalStreamFormat:             return "ParamNalStreamFormat";
@@ -907,6 +911,9 @@
         case OMX_VIDEO_AVCLevel5:  return "Level5";
         case OMX_VIDEO_AVCLevel51: return "Level51";
         case OMX_VIDEO_AVCLevel52: return "Level52";
+        case OMX_VIDEO_AVCLevel6:  return "Level6";
+        case OMX_VIDEO_AVCLevel61: return "Level61";
+        case OMX_VIDEO_AVCLevel62: return "Level62";
         default:                   return def;
     }
 }
diff --git a/headers/media_plugin/media/openmax/OMX_AudioExt.h b/headers/media_plugin/media/openmax/OMX_AudioExt.h
index 8409553..477faed 100644
--- a/headers/media_plugin/media/openmax/OMX_AudioExt.h
+++ b/headers/media_plugin/media/openmax/OMX_AudioExt.h
@@ -48,6 +48,7 @@
     OMX_AUDIO_CodingAndroidAC3,         /**< AC3 encoded data */
     OMX_AUDIO_CodingAndroidOPUS,        /**< OPUS encoded data */
     OMX_AUDIO_CodingAndroidEAC3,        /**< EAC3 encoded data */
+    OMX_AUDIO_CodingAndroidAC4,         /**< AC4 encoded data */
 } OMX_AUDIO_CODINGEXTTYPE;
 
 typedef struct OMX_AUDIO_PARAM_ANDROID_AC3TYPE {
@@ -68,6 +69,15 @@
                                         variable or unknown sampling rate. */
 } OMX_AUDIO_PARAM_ANDROID_EAC3TYPE;
 
+typedef struct OMX_AUDIO_PARAM_ANDROID_AC4TYPE {
+    OMX_U32 nSize;                 /**< size of the structure in bytes */
+    OMX_VERSIONTYPE nVersion;      /**< OMX specification version information */
+    OMX_U32 nPortIndex;            /**< port that this structure applies to */
+    OMX_U32 nChannels;             /**< Number of channels */
+    OMX_U32 nSampleRate;           /**< Sampling rate of the source data.  Use 0 for
+                                        variable or unknown sampling rate. */
+} OMX_AUDIO_PARAM_ANDROID_AC4TYPE;
+
 typedef struct OMX_AUDIO_PARAM_ANDROID_OPUSTYPE {
     OMX_U32 nSize;            /**< size of the structure in bytes */
     OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
@@ -117,6 +127,13 @@
    OMX_U32 nProfileIndex; /**< Used to query for individual profile support information */
 } OMX_AUDIO_PARAM_ANDROID_PROFILETYPE;
 
+typedef struct OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION {
+    OMX_U32 nSize;                 /**< size of the structure in bytes */
+    OMX_VERSIONTYPE nVersion;      /**< OMX specification version information */
+    OMX_S32 nPresentationId;       /**< presentation id */
+    OMX_S32 nProgramId;            /**< program id */
+} OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION;
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/headers/media_plugin/media/openmax/OMX_Core.h b/headers/media_plugin/media/openmax/OMX_Core.h
index bb974b3..9ff934e 100644
--- a/headers/media_plugin/media/openmax/OMX_Core.h
+++ b/headers/media_plugin/media/openmax/OMX_Core.h
@@ -542,6 +542,20 @@
      *  fool-proof way to do that for video encoders.
      */
     OMX_EventDataSpaceChanged,
+
+    /**
+     * Event when a component has an updated configuration on output for the client to retrieve.
+     * |arg1| contains the port index (currently only output port is valid). |arg2| contains the
+     * index of the updated config.
+     *
+     * For config updates that's associated with one frame, the update should be applied to the
+     * next output frame that comes in EmptyBufferDone callback.
+     *
+     * Upon receiving this event, the client must call the corresponding OMX_GetConfig to retrieve
+     * the config update.
+     */
+    OMX_EventConfigUpdate,
+
     OMX_EventMax = 0x7FFFFFFF
 } OMX_EVENTTYPE;
 
diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h
index 716d959..479e9b8 100644
--- a/headers/media_plugin/media/openmax/OMX_IndexExt.h
+++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h
@@ -64,6 +64,8 @@
     OMX_IndexParamAudioAndroidEac3,                 /**< reference: OMX_AUDIO_PARAM_ANDROID_EAC3TYPE */
     OMX_IndexParamAudioProfileQuerySupported,       /**< reference: OMX_AUDIO_PARAM_ANDROID_PROFILETYPE */
     OMX_IndexParamAudioAndroidAacDrcPresentation,   /**< reference: OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE */
+    OMX_IndexParamAudioAndroidAc4,                  /**< reference: OMX_AUDIO_PARAM_ANDROID_AC4TYPE */
+    OMX_IndexConfigAudioPresentation,               /**< reference: OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION */
     OMX_IndexExtAudioEndUnused,
 
     /* Image parameters and configurations */
diff --git a/headers/media_plugin/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h
index 9fd2fd2..b6edaa9 100644
--- a/headers/media_plugin/media/openmax/OMX_Video.h
+++ b/headers/media_plugin/media/openmax/OMX_Video.h
@@ -832,6 +832,9 @@
     OMX_VIDEO_AVCLevel5   = 0x4000,   /**< Level 5 */
     OMX_VIDEO_AVCLevel51  = 0x8000,   /**< Level 5.1 */
     OMX_VIDEO_AVCLevel52  = 0x10000,  /**< Level 5.2 */
+    OMX_VIDEO_AVCLevel6   = 0x20000,  /**< Level 6 */
+    OMX_VIDEO_AVCLevel61  = 0x40000,  /**< Level 6.1 */
+    OMX_VIDEO_AVCLevel62  = 0x80000,  /**< Level 6.2 */
     OMX_VIDEO_AVCLevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
     OMX_VIDEO_AVCLevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
     OMX_VIDEO_AVCLevelMax = 0x7FFFFFFF
diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h
index bbf157b..435fcc8 100644
--- a/headers/media_plugin/media/openmax/OMX_VideoExt.h
+++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h
@@ -164,6 +164,8 @@
     // HDR profiles also support passing HDR metadata
     OMX_VIDEO_VP9Profile2HDR = 0x1000,
     OMX_VIDEO_VP9Profile3HDR = 0x2000,
+    OMX_VIDEO_VP9Profile2HDR10Plus = 0x4000,
+    OMX_VIDEO_VP9Profile3HDR10Plus = 0x8000,
     OMX_VIDEO_VP9ProfileUnknown = 0x6EFFFFFF,
     OMX_VIDEO_VP9ProfileMax = 0x7FFFFFFF
 } OMX_VIDEO_VP9PROFILETYPE;
@@ -216,6 +218,7 @@
     OMX_VIDEO_HEVCProfileMainStill    = 0x4,
     // Main10 profile with HDR SEI support.
     OMX_VIDEO_HEVCProfileMain10HDR10  = 0x1000,
+    OMX_VIDEO_HEVCProfileMain10HDR10Plus  = 0x2000,
     OMX_VIDEO_HEVCProfileMax          = 0x7FFFFFFF
 } OMX_VIDEO_HEVCPROFILETYPE;
 
diff --git a/include/android/input.h b/include/android/input.h
index 6810901..cfade6c 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -83,7 +83,7 @@
 };
 
 /**
- * Meta key / modifer state.
+ * Meta key / modifier state.
  */
 enum {
     /** No meta keys are pressed. */
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 59d67f3..214559d 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -769,7 +769,14 @@
     /** all apps */
     AKEYCODE_ALL_APPS = 284,
     /** refresh key */
-    AKEYCODE_REFRESH = 285
+    AKEYCODE_REFRESH = 285,
+    /** Thumbs up key. Apps can use this to let user upvote content. */
+    AKEYCODE_THUMBS_UP = 286,
+    /** Thumbs down key. Apps can use this to let user downvote content. */
+    AKEYCODE_THUMBS_DOWN = 287,
+    /** Used to switch current account that is consuming content.
+     * May be consumed by system to switch current viewer profile. */
+    AKEYCODE_PROFILE_SWITCH = 288
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 005564d..e9d5c16 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -64,6 +64,7 @@
 #define ASENSOR_RESOLUTION_INVALID     (nanf(""))
 #define ASENSOR_FIFO_COUNT_INVALID     (-1)
 #define ASENSOR_DELAY_INVALID          INT32_MIN
+#define ASENSOR_INVALID                (-1)
 
 /* (Keep in sync with hardware/sensors-base.h and Sensor.java.) */
 
@@ -208,6 +209,35 @@
      */
     ASENSOR_TYPE_HEART_BEAT = 31,
     /**
+     * This sensor type is for delivering additional sensor information aside
+     * from sensor event data.
+     *
+     * Additional information may include:
+     *     - {@link ASENSOR_ADDITIONAL_INFO_INTERNAL_TEMPERATURE}
+     *     - {@link ASENSOR_ADDITIONAL_INFO_SAMPLING}
+     *     - {@link ASENSOR_ADDITIONAL_INFO_SENSOR_PLACEMENT}
+     *     - {@link ASENSOR_ADDITIONAL_INFO_UNTRACKED_DELAY}
+     *     - {@link ASENSOR_ADDITIONAL_INFO_VEC3_CALIBRATION}
+     *
+     * This type will never bind to a sensor. In other words, no sensor in the
+     * sensor list can have the type {@link ASENSOR_TYPE_ADDITIONAL_INFO}.
+     *
+     * If a device supports the sensor additional information feature, it will
+     * report additional information events via {@link ASensorEvent} and will
+     * have {@link ASensorEvent#type} set to
+     * {@link ASENSOR_TYPE_ADDITIONAL_INFO} and {@link ASensorEvent#sensor} set
+     * to the handle of the reporting sensor.
+     *
+     * Additional information reports consist of multiple frames ordered by
+     * {@link ASensorEvent#timestamp}. The first frame in the report will have
+     * a {@link AAdditionalInfoEvent#type} of
+     * {@link ASENSOR_ADDITIONAL_INFO_BEGIN}, and the last frame in the report
+     * will have a {@link AAdditionalInfoEvent#type} of
+     * {@link ASENSOR_ADDITIONAL_INFO_END}.
+     *
+     */
+    ASENSOR_TYPE_ADDITIONAL_INFO = 33,
+    /**
      * {@link ASENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT}
      */
     ASENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT = 34,
@@ -273,6 +303,51 @@
     ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER = 2
 };
 
+/**
+ * Sensor Additional Info Types.
+ *
+ * Used to populate {@link AAdditionalInfoEvent#type}.
+ */
+enum {
+    /** Marks the beginning of additional information frames */
+    ASENSOR_ADDITIONAL_INFO_BEGIN = 0,
+
+    /** Marks the end of additional information frames */
+    ASENSOR_ADDITIONAL_INFO_END = 1,
+
+    /**
+     * Estimation of the delay that is not tracked by sensor timestamps. This
+     * includes delay introduced by sensor front-end filtering, data transport,
+     * etc.
+     * float[2]: delay in seconds, standard deviation of estimated value
+     */
+    ASENSOR_ADDITIONAL_INFO_UNTRACKED_DELAY = 0x10000,
+
+    /** float: Celsius temperature */
+    ASENSOR_ADDITIONAL_INFO_INTERNAL_TEMPERATURE,
+
+    /**
+     * First three rows of a homogeneous matrix, which represents calibration to
+     * a three-element vector raw sensor reading.
+     * float[12]: 3x4 matrix in row major order
+     */
+    ASENSOR_ADDITIONAL_INFO_VEC3_CALIBRATION,
+
+    /**
+     * Location and orientation of sensor element in the device frame: origin is
+     * the geometric center of the mobile device screen surface; the axis
+     * definition corresponds to Android sensor definitions.
+     * float[12]: 3x4 matrix in row major order
+     */
+    ASENSOR_ADDITIONAL_INFO_SENSOR_PLACEMENT,
+
+    /**
+     * float[2]: raw sample period in seconds,
+     *           standard deviation of sampling period
+     */
+    ASENSOR_ADDITIONAL_INFO_SAMPLING,
+};
+
 /*
  * A few useful constants
  */
@@ -341,7 +416,7 @@
     int32_t  handle;
 } ADynamicSensorEvent;
 
-typedef struct {
+typedef struct AAdditionalInfoEvent {
     int32_t type;
     int32_t serial;
     union {
@@ -420,6 +495,7 @@
  * - ASensorEventQueue_hasEvents()
  * - ASensorEventQueue_getEvents()
  * - ASensorEventQueue_setEventRate()
+ * - ASensorEventQueue_requestAdditionalInfoEvents()
  */
 typedef struct ASensorEventQueue ASensorEventQueue;
 
@@ -444,6 +520,7 @@
  * - ASensor_getStringType()
  * - ASensor_getReportingMode()
  * - ASensor_isWakeUpSensor()
+ * - ASensor_getHandle()
  */
 typedef struct ASensor ASensor;
 /**
@@ -703,6 +780,29 @@
  */
 ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);
 
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+/**
+ * Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on
+ * the given {@link ASensorEventQueue}.
+ *
+ * Sensor data events are always delivered to the {@ASensorEventQueue}.
+ *
+ * The {@link ASENSOR_TYPE_ADDITIONAL_INFO} events will be returned through
+ * {@link ASensorEventQueue_getEvents}. The client is responsible for checking
+ * {@link ASensorEvent#type} to determine the event type prior to handling of
+ * the event.
+ *
+ * The client must be tolerant of any value for
+ * {@link AAdditionalInfoEvent#type}, as new values may be defined in the future
+ * and may delivered to the client.
+ *
+ * \param queue {@link ASensorEventQueue} to configure
+ * \param enable true to request {@link ASENSOR_TYPE_ADDITIONAL_INFO} events,
+ *        false to stop receiving events
+ * \return 0 on success or a negative error code on failure
+ */
+int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable);
+#endif /* __ANDROID_API__ >= __ANDRDOID_API_Q__ */
 
 /*****************************************************************************/
 
@@ -785,6 +885,24 @@
 int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor) __INTRODUCED_IN(26);
 #endif /* __ANDROID_API__ >= 26 */
 
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+/**
+ * Returns the sensor's handle.
+ *
+ * The handle identifies the sensor within the system and is included in the
+ * {@link ASensorEvent#sensor} field of sensor events, including those sent with type
+ * {@link ASENSOR_TYPE_ADDITIONAL_INFO}.
+ *
+ * A sensor's handle is able to be used to map {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to the
+ * sensor that generated the event.
+ *
+ * It is important to note that the value returned by {@link ASensor_getHandle} is not the same as
+ * the value returned by the Java API {@link android.hardware.Sensor#getId} and no mapping exists
+ * between the values.
+ */
+int ASensor_getHandle(ASensor const* sensor) __INTRODUCED_IN(__ANDROID_API_Q__);
+#endif /* __ANDROID_API__ >= ANDROID_API_Q__ */
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
new file mode 100644
index 0000000..0573187
--- /dev/null
+++ b/include/android/surface_control.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+
+/**
+ * @file surface_control.h
+ */
+
+#ifndef ANDROID_SURFACE_CONTROL_H
+#define ANDROID_SURFACE_CONTROL_H
+
+#include <sys/cdefs.h>
+
+#include <android/hardware_buffer.h>
+#include <android/hdr_metadata.h>
+#include <android/native_window.h>
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= 29
+
+struct ASurfaceControl;
+
+/**
+ * The SurfaceControl API can be used to provide a heirarchy of surfaces for
+ * composition to the system compositor. ASurfaceControl represents a content node in
+ * this heirarchy.
+ */
+typedef struct ASurfaceControl ASurfaceControl;
+
+/*
+ * Creates an ASurfaceControl with either ANativeWindow or an ASurfaceControl as its parent.
+ * |debug_name| is a debug name associated with this surface. It can be used to
+ * identify this surface in the SurfaceFlinger's layer tree. It must not be
+ * null.
+ *
+ * The caller takes ownership of the ASurfaceControl returned and must release it
+ * using ASurfaceControl_release below.
+ */
+ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name)
+                                                  __INTRODUCED_IN(29);
+
+ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name)
+                                        __INTRODUCED_IN(29);
+
+/**
+ * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer
+ * has ownership of the AsurfaceControl. The surface and it's children may remain on display as long
+ * as their parent remains on display.
+ */
+void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29);
+
+struct ASurfaceTransaction;
+
+/**
+ * ASurfaceTransaction is a collection of updates to the surface tree that must
+ * be applied atomically.
+ */
+typedef struct ASurfaceTransaction ASurfaceTransaction;
+
+/**
+ * The caller takes ownership of the transaction and must release it using
+ * ASurfaceControl_delete below.
+ */
+ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29);
+
+/**
+ * Destroys the |transaction| object.
+ */
+void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
+
+/**
+ * Applies the updates accumulated in |transaction|.
+ *
+ * Note that the transaction is guaranteed to be applied atomically. The
+ * transactions which are applied on the same thread are also guaranteed to be
+ * applied in order.
+ */
+void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
+
+/**
+ * An opaque handle returned during a callback that can be used to query general stats and stats for
+ * surfaces which were either removed or for which buffers were updated after this transaction was
+ * applied.
+ */
+typedef struct ASurfaceTransactionStats ASurfaceTransactionStats;
+
+/**
+ * Since the transactions are applied asynchronously, the
+ * ASurfaceTransaction_OnComplete callback can be used to be notified when a frame
+ * including the updates in a transaction was presented.
+ *
+ * |context| is the optional context provided by the client that is passed into
+ * the callback.
+ *
+ * |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query
+ * information about the transaction. The handle is only valid during the the callback.
+ *
+ * THREADING
+ * The transaction completed callback can be invoked on any thread.
+ */
+typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats)
+                                               __INTRODUCED_IN(29);
+
+/**
+ * Returns the timestamp of when the frame was latched by the framework. Once a frame is
+ * latched by the framework, it is presented at the following hardware vsync.
+ */
+int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats)
+                                              __INTRODUCED_IN(29);
+
+/**
+ * Returns a sync fence that signals when the transaction has been presented.
+ * The recipient of the callback takes ownership of the fence and is responsible for closing
+ * it.
+ */
+int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats)
+                                               __INTRODUCED_IN(29);
+
+/**
+ * |outASurfaceControls| returns an array of ASurfaceControl pointers that were updated during the
+ * transaction. Stats for the surfaces can be queried through ASurfaceTransactionStats functions.
+ * When the client is done using the array, it must release it by calling
+ * ASurfaceTransactionStats_releaseASurfaceControls.
+ *
+ * |outASurfaceControlsSize| returns the size of the ASurfaceControls array.
+ */
+void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats,
+                                                  ASurfaceControl*** outASurfaceControls,
+                                                  size_t* outASurfaceControlsSize)
+                                                  __INTRODUCED_IN(29);
+/**
+ * Releases the array of ASurfaceControls that were returned by
+ * ASurfaceTransactionStats_getASurfaceControls.
+ */
+void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls)
+                                                      __INTRODUCED_IN(29);
+
+/**
+ * Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered
+ * acquired when its acquire_fence_fd has signaled. A buffer cannot be latched or presented until
+ * it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1.
+ */
+int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats,
+                                                ASurfaceControl* surface_control)
+                                                __INTRODUCED_IN(29);
+
+/**
+ * The returns the fence used to signal the release of the PREVIOUS buffer set on
+ * this surface. If this fence is valid (>=0), the PREVIOUS buffer has not yet been released and the
+ * fence will signal when the PREVIOUS buffer has been released. If the fence is -1 , the PREVIOUS
+ * buffer is already released. The recipient of the callback takes ownership of the
+ * previousReleaseFenceFd and is responsible for closing it.
+ *
+ * Each time a buffer is set through ASurfaceTransaction_setBuffer()/_setCachedBuffer() on a
+ * transaction which is applied, the framework takes a ref on this buffer. The framework treats the
+ * addition of a buffer to a particular surface as a unique ref. When a transaction updates or
+ * removes a buffer from a surface, or removes the surface itself from the tree, this ref is
+ * guaranteed to be released in the OnComplete callback for this transaction. The
+ * ASurfaceControlStats provided in the callback for this surface may contain an optional fence
+ * which must be signaled before the ref is assumed to be released.
+ *
+ * The client must ensure that all pending refs on a buffer are released before attempting to reuse
+ * this buffer, otherwise synchronization errors may occur.
+ */
+int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
+                                                ASurfaceTransactionStats* surface_transaction_stats,
+                                                ASurfaceControl* surface_control)
+                                                __INTRODUCED_IN(29);
+
+/**
+ * Sets the callback that will be invoked when the updates from this transaction
+ * are presented. For details on the callback semantics and data, see the
+ * comments on the ASurfaceTransaction_OnComplete declaration above.
+ */
+void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context,
+                                       ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
+
+/**
+ * Reparents the |surface_control| from its old parent to the |new_parent| surface control.
+ * Any children of the* reparented |surface_control| will remain children of the |surface_control|.
+ *
+ * The |new_parent| can be null. Surface controls with a null parent do not appear on the display.
+ */
+void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction,
+                                  ASurfaceControl* surface_control, ASurfaceControl* new_parent)
+                                  __INTRODUCED_IN(29);
+
+/* Parameter for ASurfaceTransaction_setVisibility */
+enum {
+    ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0,
+    ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1,
+};
+/**
+ * Updates the visibility of |surface_control|. If show is set to
+ * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the |surface_control| and all surfaces in its subtree will
+ * be hidden.
+ */
+void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction,
+                                       ASurfaceControl* surface_control, int8_t visibility)
+                                       __INTRODUCED_IN(29);
+
+/**
+ * Updates the z order index for |surface_control|. Note that the z order for a surface
+ * is relative to other surfaces which are siblings of this surface. The behavior of sibilings with
+ * the same z order is undefined.
+ *
+ * Z orders may be from MIN_INT32 to MAX_INT32. A layer's default z order index is 0.
+ */
+void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction,
+                                   ASurfaceControl* surface_control, int32_t z_order)
+                                   __INTRODUCED_IN(29);
+
+/**
+ * Updates the AHardwareBuffer displayed for |surface_control|. If not -1, the
+ * acquire_fence_fd should be a file descriptor that is signaled when all pending work
+ * for the buffer is complete and the buffer can be safely read.
+ *
+ * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible
+ * for closing it.
+ */
+void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction,
+                                   ASurfaceControl* surface_control, AHardwareBuffer* buffer,
+                                   int acquire_fence_fd = -1) __INTRODUCED_IN(29);
+
+/**
+ * Updates the color for |surface_control|.  This will make the background color for the
+ * ASurfaceControl visible in transparent regions of the surface.  Colors |r|, |g|,
+ * and |b| must be within the range that is valid for |dataspace|.  |dataspace| and |alpha|
+ * will be the dataspace and alpha set for the background color layer.
+ */
+void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction,
+                                  ASurfaceControl* surface_control, float r, float g, float b,
+                                  float alpha, ADataSpace dataspace)
+                                  __INTRODUCED_IN(29);
+
+/**
+ * |source| the sub-rect within the buffer's content to be rendered inside the surface's area
+ * The surface's source rect is clipped by the bounds of its current buffer. The source rect's width
+ * and height must be > 0.
+ *
+ * |destination| specifies the rect in the parent's space where this surface will be drawn. The post
+ * source rect bounds are scaled to fit the destination rect. The surface's destination rect is
+ * clipped by the bounds of its parent. The destination rect's width and height must be > 0.
+ *
+ * |transform| the transform applied after the source rect is applied to the buffer. This parameter
+ * should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_*
+ * enum.
+ */
+void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction,
+                                     ASurfaceControl* surface_control, const ARect& source,
+                                     const ARect& destination, int32_t transform)
+                                     __INTRODUCED_IN(29);
+
+
+/* Parameter for ASurfaceTransaction_setBufferTransparency */
+enum {
+    ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0,
+    ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1,
+    ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE = 2,
+};
+/**
+ * Updates whether the content for the buffer associated with this surface is
+ * completely opaque. If true, every pixel of content inside the buffer must be
+ * opaque or visual errors can occur.
+ */
+void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction,
+                                               ASurfaceControl* surface_control,
+                                               int8_t transparency)
+                                               __INTRODUCED_IN(29);
+
+/**
+ * Updates the region for the content on this surface updated in this
+ * transaction. If unspecified, the complete surface is assumed to be damaged.
+ */
+void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction,
+                                         ASurfaceControl* surface_control, const ARect rects[],
+                                         uint32_t count) __INTRODUCED_IN(29);
+
+/**
+ * Specifies a desiredPresentTime for the transaction. The framework will try to present
+ * the transaction at or after the time specified.
+ *
+ * Transactions will not be presented until all of their acquire fences have signaled even if the
+ * app requests an earlier present time.
+ *
+ * If an earlier transaction has a desired present time of x, and a later transaction has a desired
+ * present time that is before x, the later transaction will not preempt the earlier transaction.
+ */
+void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction,
+                                               int64_t desiredPresentTime) __INTRODUCED_IN(29);
+
+/**
+ * Sets the alpha for the buffer. It uses a premultiplied blending.
+ *
+ * The |alpha| must be between 0.0 and 1.0.
+ */
+void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction,
+                                        ASurfaceControl* surface_control, float alpha)
+                                        __INTRODUCED_IN(29);
+
+/*
+ * SMPTE ST 2086 "Mastering Display Color Volume" static metadata
+ *
+ * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering
+ * the surface's buffer.
+ */
+void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction,
+                                                  ASurfaceControl* surface_control,
+                                                  struct AHdrMetadata_smpte2086* metadata)
+                                                  __INTRODUCED_IN(29);
+
+/*
+ * Sets the CTA 861.3 "HDR Static Metadata Extension" static metadata on a surface.
+ *
+ * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering
+ * the surface's buffer.
+ */
+void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction,
+                                                 ASurfaceControl* surface_control,
+                                                 struct AHdrMetadata_cta861_3* metadata)
+                                                 __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
+__END_DECLS
+
+#endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h
new file mode 100644
index 0000000..38f036e
--- /dev/null
+++ b/include/android/system_fonts.h
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file system_fonts.h
+ * @brief Provides the system font configurations.
+ *
+ * These APIs provides the list of system installed font files with additional metadata about the
+ * font.
+ *
+ * The ASystemFontIterator_open method will give you an iterator which can iterate all system
+ * installed font files as shown in the following example.
+ *
+ * \code{.cpp}
+ *   ASystemFontIterator* iterator = ASystemFontIterator_open();
+ *   ASystemFont* font = NULL;
+ *
+ *   while ((font = ASystemFontIterator_next(iterator)) != nullptr) {
+ *       // Look if the font is your desired one.
+ *       if (ASystemFont_getWeight(font) == 400 && !ASystemFont_isItalic(font)
+ *           && ASystemFont_getLocale(font) == NULL) {
+ *           break;
+ *       }
+ *       ASystemFont_close(font);
+ *   }
+ *   ASystemFontIterator_close(iterator);
+ *
+ *   int fd = open(ASystemFont_getFontFilePath(font), O_RDONLY);
+ *   int collectionIndex = ASystemFont_getCollectionINdex(font);
+ *   std::vector<std::pair<uint32_t, float>> variationSettings;
+ *   for (size_t i = 0; i < ASystemFont_getAxisCount(font); ++i) {
+ *       variationSettings.push_back(std::make_pair(
+ *           ASystemFont_getAxisTag(font, i),
+ *           ASystemFont_getAxisValue(font, i)));
+ *   }
+ *   ASystemFont_close(font);
+ *
+ *   // Use this font for your text rendering engine.
+ *
+ * \endcode
+ *
+ * Available since API level 29.
+ */
+
+#ifndef ANDROID_SYSTEM_FONTS_H
+#define ANDROID_SYSTEM_FONTS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/cdefs.h>
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ *   This file is part of Android's set of stable system headers
+ *   exposed by the Android NDK (Native Development Kit).
+ *
+ *   Third-party source AND binary code relies on the definitions
+ *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= 29
+
+enum {
+    /** The minimum value fot the font weight value. */
+    ASYSTEM_FONT_WEIGHT_MIN = 0,
+
+    /** A font weight value for the thin weight. */
+    ASYSTEM_FONT_WEIGHT_THIN = 100,
+
+    /** A font weight value for the extra-light weight. */
+    ASYSTEM_FONT_WEIGHT_EXTRA_LIGHT = 200,
+
+    /** A font weight value for the light weight. */
+    ASYSTEM_FONT_WEIGHT_LIGHT = 300,
+
+    /** A font weight value for the normal weight. */
+    ASYSTEM_FONT_WEIGHT_NORMAL = 400,
+
+    /** A font weight value for the medium weight. */
+    ASYSTEM_FONT_WEIGHT_MEDIUM = 500,
+
+    /** A font weight value for the semi-bold weight. */
+    ASYSTEM_FONT_WEIGHT_SEMI_BOLD = 600,
+
+    /** A font weight value for the bold weight. */
+    ASYSTEM_FONT_WEIGHT_BOLD = 700,
+
+    /** A font weight value for the extra-bold weight. */
+    ASYSTEM_FONT_WEIGHT_EXTRA_BOLD = 800,
+
+    /** A font weight value for the black weight. */
+    ASYSTEM_FONT_WEIGHT_BLACK = 900,
+
+    /** The maximum value for the font weight value. */
+    ASYSTEM_FONT_WEIGHT_MAX = 1000
+};
+
+/**
+ * ASystemFontIterator provides access to the system font configuration.
+ *
+ * ASystemFontIterator is an iterator for all available system font settings.
+ * This iterator is not a thread-safe object. Do not pass this iterator to other threads.
+ */
+struct ASystemFontIterator;
+
+/**
+ * ASystemFont provides information of the single system font configuration.
+ */
+struct ASystemFont;
+
+/**
+ * Create a system font iterator.
+ *
+ * Use ASystemFont_close() to close the iterator.
+ *
+ * \return a pointer for a newly allocated iterator, nullptr on failure.
+ */
+ASystemFontIterator* _Nullable ASystemFontIterator_open() __INTRODUCED_IN(29);
+
+/**
+ * Close an opened system font iterator, freeing any related resources.
+ *
+ * \param iterator a pointer of an iterator for the system fonts. Do nothing if NULL is passed.
+ */
+void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTRODUCED_IN(29);
+
+/**
+ * Move to the next system font.
+ *
+ * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \return a font. If no more font is available, returns nullptr. You need to release the returned
+ *         font by ASystemFont_close when it is no longer needed.
+ */
+ASystemFont* _Nullable ASystemFontIterator_next(ASystemFontIterator* _Nonnull iterator) __INTRODUCED_IN(29);
+
+/**
+ * Close an ASystemFont returned by ASystemFontIterator_next.
+ *
+ * \param font a font returned by ASystemFontIterator_next or ASystemFont_matchFamilyStyleCharacter.
+ *        Do nothing if NULL is passed.
+ */
+void ASystemFont_close(ASystemFont* _Nullable font) __INTRODUCED_IN(29);
+
+
+/**
+ * Select the best font from given parameters.
+ *
+ * Only generic font families are supported.
+ * For more information about generic font families, read [W3C spec](https://www.w3.org/TR/css-fonts-4/#generic-font-families)
+ *
+ * Even if no font can render the given text, this function will return a non-null result for
+ * drawing Tofu character.
+ *
+ * Examples:
+ * \code{.cpp}
+ *  // Simple font query for the ASCII character.
+ *  std::vector<uint16_t> text = { 'A' };
+ *  ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
+ *      "sans", 400, false, "en-US", text.data(), text.length(), &runLength);
+ *  // runLength will be 1 and the font will points a valid font file.
+ *
+ *  // Querying font for CJK characters
+ *  std::vector<uint16_t> text = { 0x9AA8 };
+ *  ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
+ *      "sans", 400, false, "zh-CN,ja-JP", text.data(), text.length(), &runLength);
+ *  // runLength will be 1 and the font will points a Simplified Chinese font.
+ *  ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
+ *      "sans", 400, false, "ja-JP,zh-CN", text.data(), text.length(), &runLength);
+ *  // runLength will be 1 and the font will points a Japanese font.
+ *
+ *  // Querying font for text/color emoji
+ *  std::vector<uint16_t> text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 };
+ *  ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
+ *      "sans", 400, false, "en-US", text.data(), text.length(), &runLength);
+ *  // runLength will be 8 and the font will points a color emoji font.
+ *
+ *  // Mixture of multiple script of characters.
+ *  // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character.
+ *  std::vector<uint16_t> text = { 0x05D0, 0x0E01 };
+ *  ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
+ *      "sans", 400, false, "en-US", text.data(), text.length(), &runLength);
+ *  // runLength will be 1 and the font will points a Hebrew font.
+ * \endcode
+ *
+ * \param familyName a null character terminated font family name
+ * \param weight a font weight value. Only from 0 to 1000 value is valid
+ * \param italic true if italic, otherwise false.
+ * \param languageTags a null character terminated comma separated IETF BCP47 compliant language
+ *                     tags.
+ * \param text a UTF-16 encoded text buffer to be rendered. Do not pass empty string.
+ * \param textLength a length of the given text buffer. This must not be zero.
+ * \param runLengthOut if not null, the font run length will be filled.
+ * \return a font to be used for given text and params. You need to release the returned font by
+ *         ASystemFont_close when it is no longer needed.
+ */
+ASystemFont* _Nonnull ASystemFont_matchFamilyStyleCharacter(
+        const char* _Nonnull familyName,
+        uint16_t weight,
+        bool italic,
+        const char* _Nonnull languageTags,
+        const uint16_t* _Nonnull text,
+        uint32_t textLength,
+        uint32_t* _Nullable runLengthOut) __INTRODUCED_IN(29);
+
+/**
+ * Return an absolute path to the current font file.
+ *
+ * Here is a list of font formats returned by this method:
+ * <ul>
+ *   <li>OpenType</li>
+ *   <li>OpenType Font Collection</li>
+ *   <li>TrueType</li>
+ *   <li>TrueType Collection</li>
+ * </ul>
+ * The file extension could be one of *.otf, *.ttf, *.otc or *.ttc.
+ *
+ * The font file returned is guaranteed to be opend with O_RDONLY.
+ * Note that the returned pointer is valid until ASystemFont_close() is called for the given font.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a string of the font file path.
+ */
+const char* _Nonnull ASystemFont_getFontFilePath(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a weight value associated with the current font.
+ *
+ * The weight values are positive and less than or equal to 1000.
+ * Here are pairs of the common names and their values.
+ * <p>
+ *  <table>
+ *  <tr>
+ *  <th align="center">Value</th>
+ *  <th align="center">Name</th>
+ *  <th align="center">NDK Definition</th>
+ *  </tr>
+ *  <tr>
+ *  <td align="center">100</td>
+ *  <td align="center">Thin</td>
+ *  <td align="center">{@link ASYSTEM_FONT_WEIGHT_THIN}</td>
+ *  </tr>
+ *  <tr>
+ *  <td align="center">200</td>
+ *  <td align="center">Extra Light (Ultra Light)</td>
+ *  <td align="center">{@link ASYSTEM_FONT_WEIGHT_EXTRA_LIGHT}</td>
+ *  </tr>
+ *  <tr>
+ *  <td align="center">300</td>
+ *  <td align="center">Light</td>
+ *  <td align="center">{@link ASYSTEM_FONT_WEIGHT_LIGHT}</td>
+ *  </tr>
+ *  <tr>
+ *  <td align="center">400</td>
+ *  <td align="center">Normal (Regular)</td>
+ *  <td align="center">{@link ASYSTEM_FONT_WEIGHT_NORMAL}</td>
+ *  </tr>
+ *  <tr>
+ *  <td align="center">500</td>
+ *  <td align="center">Medium</td>
+ *  <td align="center">{@link ASYSTEM_FONT_WEIGHT_MEDIUM}</td>
+ *  </tr>
+ *  <tr>
+ *  <td align="center">600</td>
+ *  <td align="center">Semi Bold (Demi Bold)</td>
+ *  <td align="center">{@link ASYSTEM_FONT_WEIGHT_SEMI_BOLD}</td>
+ *  </tr>
+ *  <tr>
+ *  <td align="center">700</td>
+ *  <td align="center">Bold</td>
+ *  <td align="center">{@link ASYSTEM_FONT_WEIGHT_BOLD}</td>
+ *  </tr>
+ *  <tr>
+ *  <td align="center">800</td>
+ *  <td align="center">Extra Bold (Ultra Bold)</td>
+ *  <td align="center">{@link ASYSTEM_FONT_WEIGHT_EXTRA_BOLD}</td>
+ *  </tr>
+ *  <tr>
+ *  <td align="center">900</td>
+ *  <td align="center">Black (Heavy)</td>
+ *  <td align="center">{@link ASYSTEM_FONT_WEIGHT_BLACK}</td>
+ *  </tr>
+ *  </table>
+ * </p>
+ * Note that the weight value may fall in between above values, e.g. 250 weight.
+ *
+ * For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass)
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned.
+ */
+uint16_t ASystemFont_getWeight(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return true if the current font is italic, otherwise returns false.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return true if italic, otherwise false.
+ */
+bool ASystemFont_isItalic(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a IETF BCP47 compliant language tag associated with the current font.
+ *
+ * For information about IETF BCP47, read [Locale.forLanguageTag(java.lang.String)](https://developer.android.com/reference/java/util/Locale.html#forLanguageTag(java.lang.String)")
+ *
+ * Note that the returned pointer is valid until ASystemFont_close() is called.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a IETF BCP47 compliant langauge tag or nullptr if not available.
+ */
+const char* _Nullable ASystemFont_getLocale(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a font collection index value associated with the current font.
+ *
+ * In case the target font file is a font collection (e.g. .ttc or .otc), this
+ * returns a non-negative value as an font offset in the collection. This
+ * always returns 0 if the target font file is a regular font.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a font collection index.
+ */
+size_t ASystemFont_getCollectionIndex(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a count of font variation settings associated with the current font
+ *
+ * The font variation settings are provided as multiple tag-values pairs.
+ *
+ * For example, bold italic font may have following font variation settings:
+ *     'wght' 700, 'slnt' -12
+ * In this case, ASystemFont_getAxisCount returns 2 and ASystemFont_getAxisTag
+ * and ASystemFont_getAxisValue will return following values.
+ * \code{.cpp}
+ *     ASystemFont* font = ASystemFontIterator_next(ite);
+ *
+ *     // Returns the number of axes
+ *     ASystemFont_getAxisCount(font);  // Returns 2
+ *
+ *     // Returns the tag-value pair for the first axis.
+ *     ASystemFont_getAxisTag(font, 0);  // Returns 'wght'(0x77676874)
+ *     ASystemFont_getAxisValue(font, 0);  // Returns 700.0
+ *
+ *     // Returns the tag-value pair for the second axis.
+ *     ASystemFont_getAxisTag(font, 1);  // Returns 'slnt'(0x736c6e74)
+ *     ASystemFont_getAxisValue(font, 1);  // Returns -12.0
+ * \endcode
+ *
+ * For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar)
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a number of font variation settings.
+ */
+size_t ASystemFont_getAxisCount(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+
+/**
+ * Return an OpenType axis tag associated with the current font.
+ *
+ * See ASystemFont_getAxisCount for more details.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \param axisIndex an index to the font variation settings. Passing value larger than or
+ *        equal to {@link ASystemFont_getAxisCount} is not allowed.
+ * \return an OpenType axis tag value for the given font variation setting.
+ */
+uint32_t ASystemFont_getAxisTag(const ASystemFont* _Nonnull font, uint32_t axisIndex)
+      __INTRODUCED_IN(29);
+
+/**
+ * Return an OpenType axis value associated with the current font.
+ *
+ * See ASystemFont_getAxisCount for more details.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \param axisIndex an index to the font variation settings. Passing value larger than or
+ *         equal to {@link ASYstemFont_getAxisCount} is not allwed.
+ * \return a float value for the given font variation setting.
+ */
+float ASystemFont_getAxisValue(const ASystemFont* _Nonnull font, uint32_t axisIndex)
+      __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
+__END_DECLS
+
+#endif // ANDROID_SYSTEM_FONTS_H
diff --git a/include/android/trace.h b/include/android/trace.h
index aa24995..bb7ff28 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -33,6 +33,7 @@
 #define ANDROID_NATIVE_TRACE_H
 
 #include <stdbool.h>
+#include <stdint.h>
 #include <sys/cdefs.h>
 
 #ifdef __cplusplus
@@ -73,6 +74,40 @@
 
 #endif /* __ANDROID_API__ >= 23 */
 
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+
+/**
+ * Writes a trace message to indicate that a given section of code has
+ * begun. Must be followed by a call to {@link ATrace_endAsyncSection} with the same
+ * methodName and cookie. Unlike {@link ATrace_beginSection} and {@link ATrace_endSection},
+ * asynchronous events do not need to be nested. The name and cookie used to
+ * begin an event must be used to end it.
+ *
+ * \param sectionName The method name to appear in the trace.
+ * \param cookie Unique identifier for distinguishing simultaneous events
+ */
+void ATrace_beginAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29);
+
+/**
+ * Writes a trace message to indicate that the current method has ended.
+ * Must be called exactly once for each call to {@link ATrace_beginAsyncSection}
+ * using the same name and cookie.
+ *
+ * \param methodName The method name to appear in the trace.
+ * \param cookie Unique identifier for distinguishing simultaneous events
+ */
+void ATrace_endAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29);
+
+/**
+ * Writes trace message to indicate the value of a given counter.
+ *
+ * \param counterName The counter name to appear in the trace.
+ * \param counterValue The counter value.
+ */
+void ATrace_setCounter(const char* counterName, int64_t counterValue) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 86da4d3..fa456bb 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -17,11 +17,39 @@
 #ifndef _LIBINPUT_DISPLAY_VIEWPORT_H
 #define _LIBINPUT_DISPLAY_VIEWPORT_H
 
+#include <android-base/stringprintf.h>
 #include <ui/DisplayInfo.h>
 #include <input/Input.h>
+#include <inttypes.h>
+#include <optional>
+
+using android::base::StringPrintf;
 
 namespace android {
 
+/**
+ * Describes the different type of viewports supported by input flinger.
+ * Keep in sync with values in InputManagerService.java.
+ */
+enum class ViewportType : int32_t {
+    VIEWPORT_INTERNAL = 1,
+    VIEWPORT_EXTERNAL = 2,
+    VIEWPORT_VIRTUAL = 3,
+};
+
+static const char* viewportTypeToString(ViewportType type) {
+    switch(type) {
+        case ViewportType::VIEWPORT_INTERNAL:
+            return "INTERNAL";
+        case ViewportType::VIEWPORT_EXTERNAL:
+            return "EXTERNAL";
+        case ViewportType::VIEWPORT_VIRTUAL:
+            return "VIRTUAL";
+        default:
+            return "UNKNOWN";
+    }
+}
+
 /*
  * Describes how coordinates are mapped on a physical display.
  * See com.android.server.display.DisplayViewport.
@@ -39,13 +67,18 @@
     int32_t physicalBottom;
     int32_t deviceWidth;
     int32_t deviceHeight;
-    String8 uniqueId;
+    std::string uniqueId;
+    // The actual (hardware) port that the associated display is connected to.
+    // Not all viewports will have this specified.
+    std::optional<uint8_t> physicalPort;
+    ViewportType type;
 
     DisplayViewport() :
             displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
             logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
             physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
-            deviceWidth(0), deviceHeight(0) {
+            deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt),
+            type(ViewportType::VIEWPORT_INTERNAL) {
     }
 
     bool operator==(const DisplayViewport& other) const {
@@ -61,7 +94,9 @@
                 && physicalBottom == other.physicalBottom
                 && deviceWidth == other.deviceWidth
                 && deviceHeight == other.deviceHeight
-                && uniqueId == other.uniqueId;
+                && uniqueId == other.uniqueId
+                && physicalPort == other.physicalPort
+                && type == other.type;
     }
 
     bool operator!=(const DisplayViewport& other) const {
@@ -86,17 +121,25 @@
         deviceWidth = width;
         deviceHeight = height;
         uniqueId.clear();
+        physicalPort = std::nullopt;
+        type = ViewportType::VIEWPORT_INTERNAL;
     }
-};
 
-/**
- * Describes the different type of viewports supported by input flinger.
- * Keep in sync with values in InputManagerService.java.
- */
-enum class ViewportType : int32_t {
-    VIEWPORT_INTERNAL = 1,
-    VIEWPORT_EXTERNAL = 2,
-    VIEWPORT_VIRTUAL = 3,
+    std::string toString() const {
+        return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, "
+            "logicalFrame=[%d, %d, %d, %d], "
+            "physicalFrame=[%d, %d, %d, %d], "
+            "deviceSize=[%d, %d]",
+            viewportTypeToString(type), displayId,
+            uniqueId.c_str(),
+            physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>",
+            orientation,
+            logicalLeft, logicalTop,
+            logicalRight, logicalBottom,
+            physicalLeft, physicalTop,
+            physicalRight, physicalBottom,
+            deviceWidth, deviceHeight);
+    }
 };
 
 } // namespace android
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
index 11bb721..ff443c6 100644
--- a/include/input/IInputFlinger.h
+++ b/include/input/IInputFlinger.h
@@ -22,6 +22,9 @@
 
 #include <binder/IInterface.h>
 
+#include <utils/Vector.h>
+#include <input/InputWindow.h>
+
 namespace android {
 
 /*
@@ -31,6 +34,11 @@
 class IInputFlinger : public IInterface {
 public:
     DECLARE_META_INTERFACE(InputFlinger)
+
+    virtual void setInputWindows(const Vector<InputWindowInfo>& inputHandles) = 0;
+    virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
+    virtual void registerInputChannel(const sp<InputChannel>& channel) = 0;
+    virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0;
 };
 
 
@@ -40,7 +48,10 @@
 class BnInputFlinger : public BnInterface<IInputFlinger> {
 public:
     enum {
-        DO_SOMETHING_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+        SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+        REGISTER_INPUT_CHANNEL_TRANSACTION,
+        UNREGISTER_INPUT_CHANNEL_TRANSACTION,
+        TRANSFER_TOUCH_FOCUS
     };
 
     virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/input/Input.h b/include/input/Input.h
index 756b337..805957a 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -17,6 +17,8 @@
 #ifndef _LIBINPUT_INPUT_H
 #define _LIBINPUT_INPUT_H
 
+#pragma GCC system_header
+
 /**
  * Native input event structures.
  */
@@ -25,7 +27,6 @@
 #include <utils/BitSet.h>
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
-#include <utils/String8.h>
 #include <utils/Timers.h>
 #include <utils/Vector.h>
 #include <stdint.h>
@@ -58,6 +59,12 @@
      */
     AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2,
 
+    /**
+     * This flag indicates that the event has been generated by a gesture generator. It
+     * provides a hint to the GestureDetector to not apply any touch slop.
+     */
+    AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8,
+
     /* Motion event is inconsistent with previously sent motion events. */
     AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
 };
@@ -213,6 +220,32 @@
     POLICY_FLAG_PASS_TO_USER = 0x40000000,
 };
 
+/**
+ * Classifications of the current gesture, if available.
+ *
+ * The following values must be kept in sync with MotionEvent.java
+ */
+enum class MotionClassification : uint8_t {
+    /**
+     * No classification is available.
+     */
+    NONE = 0,
+    /**
+     * Too early to classify the current gesture. Need more events. Look for changes in the
+     * upcoming motion events.
+     */
+    AMBIGUOUS_GESTURE = 1,
+    /**
+     * The current gesture likely represents a user intentionally exerting force on the touchscreen.
+     */
+    DEEP_PRESS = 2,
+};
+
+/**
+ * String representation of MotionClassification
+ */
+const char* motionClassificationToString(MotionClassification classification);
+
 /*
  * Pointer coordinate data.
  */
@@ -237,7 +270,12 @@
     float getAxisValue(int32_t axis) const;
     status_t setAxisValue(int32_t axis, float value);
 
-    void scale(float scale);
+    void scale(float globalScale);
+
+    // Scale the pointer coordinates according to a global scale and a
+    // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR
+    // axes, however the window scaling will not.
+    void scale(float globalScale, float windowXScale, float windowYScale);
     void applyOffset(float xOffset, float yOffset);
 
     inline float getX() const {
@@ -302,12 +340,18 @@
 
     inline void setSource(int32_t source) { mSource = source; }
 
+    inline int32_t getDisplayId() const { return mDisplayId; }
+
+    inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; }
+
+
 protected:
-    void initialize(int32_t deviceId, int32_t source);
+    void initialize(int32_t deviceId, int32_t source, int32_t displayId);
     void initialize(const InputEvent& from);
 
     int32_t mDeviceId;
     int32_t mSource;
+    int32_t mDisplayId;
 };
 
 /*
@@ -339,10 +383,11 @@
 
     static const char* getLabel(int32_t keyCode);
     static int32_t getKeyCodeFromLabel(const char* label);
-    
+
     void initialize(
             int32_t deviceId,
             int32_t source,
+            int32_t displayId,
             int32_t action,
             int32_t flags,
             int32_t keyCode,
@@ -400,6 +445,8 @@
 
     inline void setButtonState(int32_t buttonState) { mButtonState = buttonState; }
 
+    inline MotionClassification getClassification() const { return mClassification; }
+
     inline int32_t getActionButton() const { return mActionButton; }
 
     inline void setActionButton(int32_t button) { mActionButton = button; }
@@ -556,12 +603,14 @@
     void initialize(
             int32_t deviceId,
             int32_t source,
+            int32_t displayId,
             int32_t action,
             int32_t actionButton,
             int32_t flags,
             int32_t edgeFlags,
             int32_t metaState,
             int32_t buttonState,
+            MotionClassification classification,
             float xOffset,
             float yOffset,
             float xPrecision,
@@ -580,7 +629,7 @@
 
     void offsetLocation(float xOffset, float yOffset);
 
-    void scale(float scaleFactor);
+    void scale(float globalScaleFactor);
 
     // Apply 3x3 perspective matrix transformation.
     // Matrix is in row-major form and compatible with SkMatrix.
@@ -615,6 +664,7 @@
     int32_t mEdgeFlags;
     int32_t mMetaState;
     int32_t mButtonState;
+    MotionClassification mClassification;
     float mXOffset;
     float mYOffset;
     float mXPrecision;
diff --git a/services/inputflinger/InputApplication.h b/include/input/InputApplication.h
similarity index 87%
rename from services/inputflinger/InputApplication.h
rename to include/input/InputApplication.h
index 724fc2c..71a8f20 100644
--- a/services/inputflinger/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -17,6 +17,11 @@
 #ifndef _UI_INPUT_APPLICATION_H
 #define _UI_INPUT_APPLICATION_H
 
+#include <string>
+
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+
 #include <input/Input.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
@@ -27,8 +32,12 @@
  * Describes the properties of an application that can receive input.
  */
 struct InputApplicationInfo {
+    sp<IBinder> token;
     std::string name;
     nsecs_t dispatchingTimeout;
+
+    status_t write(Parcel& output) const;
+    static InputApplicationInfo read(const Parcel& from);
 };
 
 
@@ -52,6 +61,10 @@
         return mInfo ? mInfo->dispatchingTimeout : defaultValue;
     }
 
+    inline sp<IBinder> getApplicationToken() const {
+        return mInfo ? mInfo->token : nullptr;
+    }
+
     /**
      * Requests that the state of this object be updated to reflect
      * the most current available information about the application.
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 1ea69d3..34d164c 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -31,9 +31,9 @@
     }
 
     // Information provided by the kernel.
-    String8 name;
-    String8 location;
-    String8 uniqueId;
+    std::string name;
+    std::string location;
+    std::string uniqueId;
     uint16_t bus;
     uint16_t vendor;
     uint16_t product;
@@ -45,7 +45,7 @@
     // It is hashed from whatever kernel provided information is available.
     // Ideally, the way this value is computed should not change between Android releases
     // because that would invalidate persistent settings that rely on it.
-    String8 descriptor;
+    std::string descriptor;
 
     // A value added to uniquely identify a device in the absence of a unique id. This
     // is intended to be a minimum way to distinguish from other active devices and may
@@ -73,16 +73,16 @@
     };
 
     void initialize(int32_t id, int32_t generation, int32_t controllerNumber,
-            const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal,
+            const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal,
             bool hasMic);
 
     inline int32_t getId() const { return mId; }
     inline int32_t getControllerNumber() const { return mControllerNumber; }
     inline int32_t getGeneration() const { return mGeneration; }
     inline const InputDeviceIdentifier& getIdentifier() const { return mIdentifier; }
-    inline const String8& getAlias() const { return mAlias; }
-    inline const String8& getDisplayName() const {
-        return mAlias.isEmpty() ? mIdentifier.name : mAlias;
+    inline const std::string& getAlias() const { return mAlias; }
+    inline const std::string& getDisplayName() const {
+        return mAlias.empty() ? mIdentifier.name : mAlias;
     }
     inline bool isExternal() const { return mIsExternal; }
     inline bool hasMic() const { return mHasMic; }
@@ -121,7 +121,7 @@
     int32_t mGeneration;
     int32_t mControllerNumber;
     InputDeviceIdentifier mIdentifier;
-    String8 mAlias;
+    std::string mAlias;
     bool mIsExternal;
     bool mHasMic;
     uint32_t mSources;
@@ -149,7 +149,7 @@
  *
  * Returns an empty string if not found.
  */
-extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
+extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
         const InputDeviceIdentifier& deviceIdentifier,
         InputDeviceConfigurationFileType type);
 
@@ -162,8 +162,8 @@
  *
  * Returns an empty string if not found.
  */
-extern String8 getInputDeviceConfigurationFilePathByName(
-        const String8& name, InputDeviceConfigurationFileType type);
+extern std::string getInputDeviceConfigurationFilePathByName(
+        const std::string& name, InputDeviceConfigurationFileType type);
 
 } // namespace android
 
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 4b33a96..59d16d1 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -325,8 +325,11 @@
     DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT),
     DEFINE_KEYCODE(ALL_APPS),
     DEFINE_KEYCODE(REFRESH),
+    DEFINE_KEYCODE(THUMBS_UP),
+    DEFINE_KEYCODE(THUMBS_DOWN),
+    DEFINE_KEYCODE(PROFILE_SWITCH),
 
-    { NULL, 0 }
+    { nullptr, 0 }
 };
 
 static const InputEventLabel AXES[] = {
@@ -375,7 +378,7 @@
 
     // NOTE: If you add a new axis here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
-    { NULL, 0 }
+    { nullptr, 0 }
 };
 
 static const InputEventLabel LEDS[] = {
@@ -396,7 +399,7 @@
     DEFINE_LED(CONTROLLER_4),
 
     // NOTE: If you add new LEDs here, you must also add them to Input.h
-    { NULL, 0 }
+    { nullptr, 0 }
 };
 
 static const InputEventLabel FLAGS[] = {
@@ -404,7 +407,7 @@
     DEFINE_FLAG(FUNCTION),
     DEFINE_FLAG(GESTURE),
 
-    { NULL, 0 }
+    { nullptr, 0 }
 };
 
 static int lookupValueByLabel(const char* literal, const InputEventLabel *list) {
@@ -424,7 +427,7 @@
         }
         list++;
     }
-    return NULL;
+    return nullptr;
 }
 
 static inline int32_t getKeyCodeByLabel(const char* label) {
@@ -435,7 +438,7 @@
     if (keyCode >= 0 && keyCode < static_cast<int32_t>(size(KEYCODES))) {
         return KEYCODES[keyCode].literal;
     }
-    return NULL;
+    return nullptr;
 }
 
 static inline uint32_t getKeyFlagByLabel(const char* label) {
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index ecdc075..63606e5 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -17,6 +17,8 @@
 #ifndef _LIBINPUT_INPUT_TRANSPORT_H
 #define _LIBINPUT_INPUT_TRANSPORT_H
 
+#pragma GCC system_header
+
 /**
  * Native input transport.
  *
@@ -27,6 +29,9 @@
  * The InputConsumer is used by the application to receive events from the input dispatcher.
  */
 
+#include <string>
+
+#include <binder/IBinder.h>
 #include <input/Input.h>
 #include <utils/Errors.h>
 #include <utils/Timers.h>
@@ -35,6 +40,7 @@
 #include <utils/BitSet.h>
 
 namespace android {
+class Parcel;
 
 /*
  * Intermediate representation used to send input events and related signals.
@@ -99,8 +105,9 @@
             int32_t flags;
             int32_t metaState;
             int32_t buttonState;
+            MotionClassification classification; // base type: uint8_t
+            uint8_t empty2[3];
             int32_t edgeFlags;
-            uint32_t empty2;
             nsecs_t downTime __attribute__((aligned(8)));
             float xOffset;
             float yOffset;
@@ -154,6 +161,7 @@
     virtual ~InputChannel();
 
 public:
+    InputChannel() = default;
     InputChannel(const std::string& name, int fd);
 
     /* Creates a pair of input channels.
@@ -194,9 +202,19 @@
     /* Returns a new object that has a duplicate of this channel's fd. */
     sp<InputChannel> dup() const;
 
+    status_t write(Parcel& out) const;
+    status_t read(const Parcel& from);
+
+    sp<IBinder> getToken() const;
+    void setToken(const sp<IBinder>& token);
+
 private:
+    void setFd(int fd);
+
     std::string mName;
-    int mFd;
+    int mFd = -1;
+
+    sp<IBinder> mToken = nullptr;
 };
 
 /*
@@ -225,6 +243,7 @@
             uint32_t seq,
             int32_t deviceId,
             int32_t source,
+            int32_t displayId,
             int32_t action,
             int32_t flags,
             int32_t keyCode,
@@ -253,6 +272,7 @@
             int32_t edgeFlags,
             int32_t metaState,
             int32_t buttonState,
+            MotionClassification classification,
             float xOffset,
             float yOffset,
             float xPrecision,
@@ -318,7 +338,7 @@
      * Other errors probably indicate that the channel is broken.
      */
     status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
-            nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId);
+            nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
 
     /* Sends a finished signal to the publisher to inform it that the message
      * with the specified sequence number has finished being process and whether
@@ -473,10 +493,9 @@
     Vector<SeqChain> mSeqChains;
 
     status_t consumeBatch(InputEventFactoryInterface* factory,
-            nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId);
+            nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
     status_t consumeSamples(InputEventFactoryInterface* factory,
-            Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent,
-            int32_t* displayId);
+            Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent);
 
     void updateTouchState(InputMessage& msg);
     void resampleTouchState(nsecs_t frameTime, MotionEvent* event,
diff --git a/services/inputflinger/InputWindow.h b/include/input/InputWindow.h
similarity index 78%
rename from services/inputflinger/InputWindow.h
rename to include/input/InputWindow.h
index 5a48375..a065a4c 100644
--- a/services/inputflinger/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -27,12 +27,15 @@
 #include "InputApplication.h"
 
 namespace android {
-
+class Parcel;
 
 /*
  * Describes the properties of a window that can receive input.
  */
 struct InputWindowInfo {
+    InputWindowInfo() = default;
+    InputWindowInfo(const Parcel& from);
+
     // Window flags from WindowManager.LayoutParams
     enum {
         FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001,
@@ -113,17 +116,42 @@
         INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
         INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
     };
-
-    sp<InputChannel> inputChannel;
+    
+    /* These values are filled in by the WM and passed through SurfaceFlinger
+     * unless specified otherwise.
+     */
+    sp<IBinder> token;
     std::string name;
     int32_t layoutParamsFlags;
     int32_t layoutParamsType;
     nsecs_t dispatchingTimeout;
+
+    /* These values are filled in by SurfaceFlinger. */
     int32_t frameLeft;
     int32_t frameTop;
     int32_t frameRight;
     int32_t frameBottom;
-    float scaleFactor;
+
+    /*
+     * SurfaceFlinger consumes this value to shrink the computed frame. This is
+     * different from shrinking the touchable region in that it DOES shift the coordinate
+     * space where-as the touchable region does not and is more like "cropping". This
+     * is used for window shadows.
+     */
+    int32_t surfaceInset = 0;
+
+    // A global scaling factor for all windows. Unlike windowScaleX/Y this results
+    // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
+    float globalScaleFactor;
+
+    // Scaling factors applied to individual windows.
+    float windowXScale = 1.0f;
+    float windowYScale = 1.0f;
+
+    /*
+     * This is filled in by the WM relative to the frame and then translated
+     * to absolute coordinates by SurfaceFlinger once the frame is computed.
+     */
     Region touchableRegion;
     bool visible;
     bool canReceiveKeys;
@@ -135,6 +163,8 @@
     int32_t ownerUid;
     int32_t inputFeatures;
     int32_t displayId;
+    int32_t portalToDisplayId = ADISPLAY_ID_NONE;
+    InputApplicationInfo applicationInfo;
 
     void addTouchableRegion(const Rect& region);
 
@@ -151,6 +181,9 @@
     bool supportsSplitTouch() const;
 
     bool overlaps(const InputWindowInfo* other) const;
+
+    status_t write(Parcel& output) const;
+    static InputWindowInfo read(const Parcel& from);
 };
 
 
@@ -162,22 +195,23 @@
  */
 class InputWindowHandle : public RefBase {
 public:
-    const sp<InputApplicationHandle> inputApplicationHandle;
 
     inline const InputWindowInfo* getInfo() const {
-        return mInfo;
+        return &mInfo;
     }
 
-    inline sp<InputChannel> getInputChannel() const {
-        return mInfo ? mInfo->inputChannel : NULL;
+    sp<IBinder> getToken() const;
+
+    sp<IBinder> getApplicationToken() {
+        return mInfo.applicationInfo.token;
     }
 
     inline std::string getName() const {
-        return mInfo ? mInfo->name : "<invalid>";
+        return mInfo.token ? mInfo.name : "<invalid>";
     }
 
     inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
-        return mInfo ? mInfo->dispatchingTimeout : defaultValue;
+        return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
     }
 
     /**
@@ -192,16 +226,21 @@
     virtual bool updateInfo() = 0;
 
     /**
-     * Releases the storage used by the associated information when it is
+     * Updates from another input window handle.
+     */
+    void updateFrom(const sp<InputWindowHandle> handle);
+
+    /**
+     * Releases the channel used by the associated information when it is
      * no longer needed.
      */
-    void releaseInfo();
+    void releaseChannel();
 
 protected:
-    explicit InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle);
+    explicit InputWindowHandle();
     virtual ~InputWindowHandle();
 
-    InputWindowInfo* mInfo;
+    InputWindowInfo mInfo;
 };
 
 } // namespace android
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 5e542b8..a1a32a6 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -27,7 +27,6 @@
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/Tokenizer.h>
-#include <utils/String8.h>
 #include <utils/Unicode.h>
 #include <utils/RefBase.h>
 
@@ -75,10 +74,10 @@
     };
 
     /* Loads a key character map from a file. */
-    static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap);
+    static status_t load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap);
 
     /* Loads a key character map from its string contents. */
-    static status_t loadContents(const String8& filename,
+    static status_t loadContents(const std::string& filename,
             const char* contents, Format format, sp<KeyCharacterMap>* outMap);
 
     /* Combines a base key character map and an overlay. */
@@ -221,7 +220,7 @@
         status_t parseKey();
         status_t parseKeyProperty();
         status_t finishKey(Key* key);
-        status_t parseModifier(const String8& token, int32_t* outMetaState);
+        status_t parseModifier(const std::string& token, int32_t* outMetaState);
         status_t parseCharacterLiteral(char16_t* outCharacter);
     };
 
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 1e8de71..73815fe 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -62,7 +62,7 @@
  */
 class KeyLayoutMap : public RefBase {
 public:
-    static status_t load(const String8& filename, sp<KeyLayoutMap>* outMap);
+    static status_t load(const std::string& filename, sp<KeyLayoutMap>* outMap);
 
     status_t mapKey(int32_t scanCode, int32_t usageCode,
             int32_t* outKeyCode, uint32_t* outFlags) const;
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index d4903e9..8b66f69 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -21,7 +21,6 @@
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
 #include <utils/Errors.h>
-#include <utils/String8.h>
 #include <utils/PropertyMap.h>
 
 namespace android {
@@ -43,10 +42,10 @@
  */
 class KeyMap {
 public:
-    String8 keyLayoutFile;
+    std::string keyLayoutFile;
     sp<KeyLayoutMap> keyLayoutMap;
 
-    String8 keyCharacterMapFile;
+    std::string keyCharacterMapFile;
     sp<KeyCharacterMap> keyCharacterMap;
 
     KeyMap();
@@ -56,11 +55,11 @@
             const PropertyMap* deviceConfiguration);
 
     inline bool haveKeyLayout() const {
-        return !keyLayoutFile.isEmpty();
+        return !keyLayoutFile.empty();
     }
 
     inline bool haveKeyCharacterMap() const {
-        return !keyCharacterMapFile.isEmpty();
+        return !keyCharacterMapFile.empty();
     }
 
     inline bool isComplete() const {
@@ -68,12 +67,12 @@
     }
 
 private:
-    bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
-    status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
+    bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name);
+    status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name);
     status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
-            const String8& name);
-    String8 getPath(const InputDeviceIdentifier& deviceIdentifier,
-            const String8& name, InputDeviceConfigurationFileType type);
+            const std::string& name);
+    std::string getPath(const InputDeviceIdentifier& deviceIdentifier,
+            const std::string& name, InputDeviceConfigurationFileType type);
 };
 
 /**
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
new file mode 100644
index 0000000..640bf1f
--- /dev/null
+++ b/include/input/TouchVideoFrame.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBINPUT_TOUCHVIDEOFRAME_H
+#define _LIBINPUT_TOUCHVIDEOFRAME_H
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <vector>
+
+namespace android {
+
+/**
+ * Represents data from a single scan of the touchscreen device.
+ * Similar in concept to a video frame, but the touch strength is used as
+ * the values instead.
+ */
+class TouchVideoFrame {
+public:
+    TouchVideoFrame(uint32_t width, uint32_t height, std::vector<int16_t> data,
+            const struct timeval& timestamp) :
+            mWidth(width), mHeight(height), mData(std::move(data)), mTimestamp(timestamp) {
+    }
+
+    bool operator==(const TouchVideoFrame& rhs) const {
+        return mWidth == rhs.mWidth
+                && mHeight == rhs.mHeight
+                && mData == rhs.mData
+                && mTimestamp.tv_sec == rhs.mTimestamp.tv_sec
+                && mTimestamp.tv_usec == rhs.mTimestamp.tv_usec;
+    }
+
+    /**
+     * Width of the frame
+     */
+    uint32_t getWidth() const { return mWidth; }
+    /**
+     * Height of the frame
+     */
+    uint32_t getHeight() const { return mHeight; }
+    /**
+     * The touch strength data.
+     * The array is a 2-D row-major matrix, with dimensions (height, width).
+     * Total size of the array should equal getHeight() * getWidth().
+     * Data is allowed to be negative.
+     */
+    const std::vector<int16_t>& getData() const { return mData; }
+    /**
+     * Time at which the heatmap was taken.
+     */
+    const struct timeval& getTimestamp() const { return mTimestamp; }
+
+private:
+    uint32_t mWidth;
+    uint32_t mHeight;
+    std::vector<int16_t> mData;
+    struct timeval mTimestamp;
+};
+
+} // namespace android
+
+#endif // _LIBINPUT_TOUCHVIDEOFRAME_H
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index ffa1614..727865a 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -63,7 +63,7 @@
 
     // Creates a velocity tracker using the specified strategy.
     // If strategy is NULL, uses the default strategy for the platform.
-    VelocityTracker(const char* strategy = NULL);
+    VelocityTracker(const char* strategy = nullptr);
 
     ~VelocityTracker();
 
diff --git a/include/input/VirtualKeyMap.h b/include/input/VirtualKeyMap.h
index e245ead..24e0e0e 100644
--- a/include/input/VirtualKeyMap.h
+++ b/include/input/VirtualKeyMap.h
@@ -23,7 +23,6 @@
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/Tokenizer.h>
-#include <utils/String8.h>
 #include <utils/Unicode.h>
 
 namespace android {
@@ -50,7 +49,7 @@
 public:
     ~VirtualKeyMap();
 
-    static status_t load(const String8& filename, VirtualKeyMap** outMap);
+    static status_t load(const std::string& filename, VirtualKeyMap** outMap);
 
     inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const {
         return mVirtualKeys;
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index a20154f..ad8287c 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -25,4 +25,9 @@
     host_supported: true,
     vendor_available: true,
     export_include_dirs: ["include"],
+    target: {
+        windows: {
+            enabled: true,
+        },
+    },
 }
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 28d0e4f..49a9414 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -89,6 +89,15 @@
     return false;
 }
 
+int32_t ActivityManager::getUidProcessState(const uid_t uid, const String16& callingPackage)
+{
+    sp<IActivityManager> service = getService();
+    if (service != nullptr) {
+        return service->getUidProcessState(uid, callingPackage);
+    }
+    return PROCESS_STATE_UNKNOWN;
+}
+
 status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
     sp<IActivityManager> service = getService();
     if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index da10687..aedf6b0 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -107,6 +107,7 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-Wzero-as-null-pointer-constant",
     ],
     product_variables: {
         binder32bit: {
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index f6cc3af..96ee295 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -86,6 +86,10 @@
 class BBinder::Extras
 {
 public:
+    // unlocked objects
+    bool mRequestingSid = false;
+
+    // for below objects
     Mutex mLock;
     BpBinder::ObjectManager mObjects;
 };
@@ -163,19 +167,8 @@
     const void* objectID, void* object, void* cleanupCookie,
     object_cleanup_func func)
 {
-    Extras* e = mExtras.load(std::memory_order_acquire);
-
-    if (!e) {
-        e = new Extras;
-        Extras* expected = nullptr;
-        if (!mExtras.compare_exchange_strong(expected, e,
-                                             std::memory_order_release,
-                                             std::memory_order_acquire)) {
-            delete e;
-            e = expected;  // Filled in by CAS
-        }
-        if (e == nullptr) return; // out of memory
-    }
+    Extras* e = getOrCreateExtras();
+    if (!e) return; // out of memory
 
     AutoMutex _l(e->mLock);
     e->mObjects.attach(objectID, object, cleanupCookie, func);
@@ -204,6 +197,30 @@
     return this;
 }
 
+bool BBinder::isRequestingSid()
+{
+    Extras* e = mExtras.load(std::memory_order_acquire);
+
+    return e && e->mRequestingSid;
+}
+
+void BBinder::setRequestingSid(bool requestingSid)
+{
+    Extras* e = mExtras.load(std::memory_order_acquire);
+
+    if (!e) {
+        // default is false. Most things don't need sids, so avoiding allocations when possible.
+        if (!requestingSid) {
+            return;
+        }
+
+        e = getOrCreateExtras();
+        if (!e) return; // out of memory
+    }
+
+    e->mRequestingSid = true;
+}
+
 BBinder::~BBinder()
 {
     Extras* e = mExtras.load(std::memory_order_relaxed);
@@ -267,6 +284,25 @@
     }
 }
 
+BBinder::Extras* BBinder::getOrCreateExtras()
+{
+    Extras* e = mExtras.load(std::memory_order_acquire);
+
+    if (!e) {
+        e = new Extras;
+        Extras* expected = nullptr;
+        if (!mExtras.compare_exchange_strong(expected, e,
+                                             std::memory_order_release,
+                                             std::memory_order_acquire)) {
+            delete e;
+            e = expected;  // Filled in by CAS
+        }
+        if (e == nullptr) return nullptr; // out of memory
+    }
+
+    return e;
+}
+
 // ---------------------------------------------------------------------------
 
 enum {
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 428db4d..377f604 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -17,8 +17,8 @@
 #include <unistd.h>
 #include <fcntl.h>
 
+#include <binder/ActivityManager.h>
 #include <binder/IActivityManager.h>
-
 #include <binder/Parcel.h>
 
 namespace android {
@@ -90,6 +90,20 @@
          if (reply.readExceptionCode() != 0) return false;
          return reply.readInt32() == 1;
     }
+
+    virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+        data.writeInt32(uid);
+        data.writeString16(callingPackage);
+        remote()->transact(GET_UID_PROCESS_STATE_TRANSACTION, data, &reply);
+        // fail on exception
+        if (reply.readExceptionCode() != 0) {
+            return ActivityManager::PROCESS_STATE_UNKNOWN;
+        }
+        return reply.readInt32();
+    }
 };
 
 // ------------------------------------------------------------------------------------
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 307bc28..caf2318 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -86,7 +86,7 @@
     virtual void* getBase() const;
     virtual size_t getSize() const;
     virtual uint32_t getFlags() const;
-    virtual uint32_t getOffset() const;
+    off_t getOffset() const override;
 
 private:
     friend class IMemory;
@@ -113,7 +113,7 @@
     mutable void*       mBase;
     mutable size_t      mSize;
     mutable uint32_t    mFlags;
-    mutable uint32_t    mOffset;
+    mutable off_t       mOffset;
     mutable bool        mRealHeap;
     mutable Mutex       mLock;
 };
@@ -189,13 +189,16 @@
         data.writeInterfaceToken(IMemory::getInterfaceDescriptor());
         if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) {
             sp<IBinder> heap = reply.readStrongBinder();
-            ssize_t o = reply.readInt32();
-            size_t s = reply.readInt32();
             if (heap != nullptr) {
                 mHeap = interface_cast<IMemoryHeap>(heap);
                 if (mHeap != nullptr) {
+                    const int64_t offset64 = reply.readInt64();
+                    const uint64_t size64 = reply.readUint64();
+                    const ssize_t o = (ssize_t)offset64;
+                    const size_t s = (size_t)size64;
                     size_t heapSize = mHeap->getSize();
-                    if (s <= heapSize
+                    if (s == size64 && o == offset64 // ILP32 bounds check
+                            && s <= heapSize
                             && o >= 0
                             && (static_cast<size_t>(o) <= heapSize - s)) {
                         mOffset = o;
@@ -236,8 +239,8 @@
             ssize_t offset;
             size_t size;
             reply->writeStrongBinder( IInterface::asBinder(getMemory(&offset, &size)) );
-            reply->writeInt32(offset);
-            reply->writeInt32(size);
+            reply->writeInt64(offset);
+            reply->writeUint64(size);
             return NO_ERROR;
         } break;
         default:
@@ -316,18 +319,23 @@
         data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
         status_t err = remote()->transact(HEAP_ID, data, &reply);
         int parcel_fd = reply.readFileDescriptor();
-        ssize_t size = reply.readInt32();
-        uint32_t flags = reply.readInt32();
-        uint32_t offset = reply.readInt32();
-
-        ALOGE_IF(err, "binder=%p transaction failed fd=%d, size=%zd, err=%d (%s)",
-                IInterface::asBinder(this).get(),
-                parcel_fd, size, err, strerror(-err));
+        const uint64_t size64 = reply.readUint64();
+        const int64_t offset64 = reply.readInt64();
+        const uint32_t flags = reply.readUint32();
+        const size_t size = (size_t)size64;
+        const off_t offset = (off_t)offset64;
+        if (err != NO_ERROR || // failed transaction
+                size != size64 || offset != offset64) { // ILP32 size check
+            ALOGE("binder=%p transaction failed fd=%d, size=%zu, err=%d (%s)",
+                    IInterface::asBinder(this).get(),
+                    parcel_fd, size, err, strerror(-err));
+            return;
+        }
 
         Mutex::Autolock _l(mLock);
         if (mHeapId.load(memory_order_relaxed) == -1) {
             int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0);
-            ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)",
+            ALOGE_IF(fd == -1, "cannot dup fd=%d, size=%zu, err=%d (%s)",
                     parcel_fd, size, err, strerror(errno));
 
             int access = PROT_READ;
@@ -337,7 +345,7 @@
             mRealHeap = true;
             mBase = mmap(nullptr, size, access, MAP_SHARED, fd, offset);
             if (mBase == MAP_FAILED) {
-                ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zd, fd=%d (%s)",
+                ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zu, fd=%d (%s)",
                         IInterface::asBinder(this).get(), size, fd, strerror(errno));
                 close(fd);
             } else {
@@ -371,7 +379,7 @@
     return mFlags;
 }
 
-uint32_t BpMemoryHeap::getOffset() const {
+off_t BpMemoryHeap::getOffset() const {
     assertMapped();
     return mOffset;
 }
@@ -394,9 +402,9 @@
        case HEAP_ID: {
             CHECK_INTERFACE(IMemoryHeap, data, reply);
             reply->writeFileDescriptor(getHeapID());
-            reply->writeInt32(getSize());
-            reply->writeInt32(getFlags());
-            reply->writeInt32(getOffset());
+            reply->writeUint64(getSize());
+            reply->writeInt64(getOffset());
+            reply->writeUint32(getFlags());
             return NO_ERROR;
         } break;
         default:
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 22f6f54..9a561cb 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -88,7 +88,8 @@
     "BR_FINISHED",
     "BR_DEAD_BINDER",
     "BR_CLEAR_DEATH_NOTIFICATION_DONE",
-    "BR_FAILED_REPLY"
+    "BR_FAILED_REPLY",
+    "BR_TRANSACTION_SEC_CTX",
 };
 
 static const char *kCommandStrings[] = {
@@ -111,6 +112,8 @@
     "BC_DEAD_BINDER_DONE"
 };
 
+static const int64_t kWorkSourcePropagatedBitIndex = 32;
+
 static const char* getReturnString(uint32_t cmd)
 {
     size_t idx = cmd & 0xff;
@@ -363,6 +366,11 @@
     return mCallingPid;
 }
 
+const char* IPCThreadState::getCallingSid() const
+{
+    return mCallingSid;
+}
+
 uid_t IPCThreadState::getCallingUid() const
 {
     return mCallingUid;
@@ -370,6 +378,7 @@
 
 int64_t IPCThreadState::clearCallingIdentity()
 {
+    // ignore mCallingSid for legacy reasons
     int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
     clearCaller();
     return token;
@@ -385,6 +394,48 @@
     return mStrictModePolicy;
 }
 
+int64_t IPCThreadState::setCallingWorkSourceUid(uid_t uid)
+{
+    int64_t token = setCallingWorkSourceUidWithoutPropagation(uid);
+    mPropagateWorkSource = true;
+    return token;
+}
+
+int64_t IPCThreadState::setCallingWorkSourceUidWithoutPropagation(uid_t uid)
+{
+    const int64_t propagatedBit = ((int64_t)mPropagateWorkSource) << kWorkSourcePropagatedBitIndex;
+    int64_t token = propagatedBit | mWorkSource;
+    mWorkSource = uid;
+    return token;
+}
+
+void IPCThreadState::clearPropagateWorkSource()
+{
+    mPropagateWorkSource = false;
+}
+
+bool IPCThreadState::shouldPropagateWorkSource() const
+{
+    return mPropagateWorkSource;
+}
+
+uid_t IPCThreadState::getCallingWorkSourceUid() const
+{
+    return mWorkSource;
+}
+
+int64_t IPCThreadState::clearCallingWorkSource()
+{
+    return setCallingWorkSourceUid(kUnsetWorkSource);
+}
+
+void IPCThreadState::restoreCallingWorkSource(int64_t token)
+{
+    uid_t uid = (int)token;
+    setCallingWorkSourceUidWithoutPropagation(uid);
+    mPropagateWorkSource = ((token >> kWorkSourcePropagatedBitIndex) & 1) == 1;
+}
+
 void IPCThreadState::setLastTransactionBinderFlags(int32_t flags)
 {
     mLastTransactionBinderFlags = flags;
@@ -398,12 +449,14 @@
 void IPCThreadState::restoreCallingIdentity(int64_t token)
 {
     mCallingUid = (int)(token>>32);
+    mCallingSid = nullptr;  // not enough data to restore
     mCallingPid = (int)token;
 }
 
 void IPCThreadState::clearCaller()
 {
     mCallingPid = getpid();
+    mCallingSid = nullptr;  // expensive to lookup
     mCallingUid = getuid();
 }
 
@@ -748,6 +801,8 @@
 
 IPCThreadState::IPCThreadState()
     : mProcess(ProcessState::self()),
+      mWorkSource(kUnsetWorkSource),
+      mPropagateWorkSource(false),
       mStrictModePolicy(0),
       mLastTransactionBinderFlags(0),
       mCallRestriction(mProcess->mCallRestriction)
@@ -1089,10 +1144,19 @@
         }
         break;
 
+    case BR_TRANSACTION_SEC_CTX:
     case BR_TRANSACTION:
         {
-            binder_transaction_data tr;
-            result = mIn.read(&tr, sizeof(tr));
+            binder_transaction_data_secctx tr_secctx;
+            binder_transaction_data& tr = tr_secctx.transaction_data;
+
+            if (cmd == (int) BR_TRANSACTION_SEC_CTX) {
+                result = mIn.read(&tr_secctx, sizeof(tr_secctx));
+            } else {
+                result = mIn.read(&tr, sizeof(tr));
+                tr_secctx.secctx = 0;
+            }
+
             ALOG_ASSERT(result == NO_ERROR,
                 "Not enough command data for brTRANSACTION");
             if (result != NO_ERROR) break;
@@ -1108,15 +1172,25 @@
                 tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
 
             const pid_t origPid = mCallingPid;
+            const char* origSid = mCallingSid;
             const uid_t origUid = mCallingUid;
             const int32_t origStrictModePolicy = mStrictModePolicy;
             const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
+            const int32_t origWorkSource = mWorkSource;
+            const bool origPropagateWorkSet = mPropagateWorkSource;
+            // Calling work source will be set by Parcel#enforceInterface. Parcel#enforceInterface
+            // is only guaranteed to be called for AIDL-generated stubs so we reset the work source
+            // here to never propagate it.
+            clearCallingWorkSource();
+            clearPropagateWorkSource();
 
             mCallingPid = tr.sender_pid;
+            mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);
             mCallingUid = tr.sender_euid;
             mLastTransactionBinderFlags = tr.flags;
 
-            //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
+            // ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid,
+            //    (mCallingSid ? mCallingSid : "<N/A>"), mCallingUid);
 
             Parcel reply;
             status_t error;
@@ -1148,8 +1222,8 @@
             }
 
             mIPCThreadStateBase->popCurrentState();
-            //ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",
-            //     mCallingPid, origPid, origUid);
+            //ALOGI("<<<< TRANSACT from pid %d restore pid %d sid %s uid %d\n",
+            //     mCallingPid, origPid, (origSid ? origSid : "<N/A>"), origUid);
 
             if ((tr.flags & TF_ONE_WAY) == 0) {
                 LOG_ONEWAY("Sending reply to %d!", mCallingPid);
@@ -1160,9 +1234,12 @@
             }
 
             mCallingPid = origPid;
+            mCallingSid = origSid;
             mCallingUid = origUid;
             mStrictModePolicy = origStrictModePolicy;
             mLastTransactionBinderFlags = origTransactionBinderFlags;
+            mWorkSource = origWorkSource;
+            mPropagateWorkSource = origPropagateWorkSet;
 
             IF_LOG_TRANSACTIONS() {
                 TextOutput::Bundle _b(alog);
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index 697e948..82f9047 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -55,6 +55,16 @@
         data.writeInt32(disabled ? 1 : 0);
         remote()->transact(ON_UID_IDLE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
     }
+
+    virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor());
+        data.writeInt32((int32_t) uid);
+        data.writeInt32(procState);
+        data.writeInt64(procStateSeq);
+        remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
+    }
 };
 
 // ----------------------------------------------------------------------
@@ -89,6 +99,14 @@
             onUidIdle(uid, disabled);
             return NO_ERROR;
         } break;
+        case ON_UID_STATE_CHANGED_TRANSACTION: {
+            CHECK_INTERFACE(IUidObserver, data, reply);
+            uid_t uid = data.readInt32();
+            int32_t procState = data.readInt32();
+            int64_t procStateSeq = data.readInt64();
+            onUidStateChanged(uid, procState, procStateSeq);
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 9850ad9..4c300b4 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -76,7 +76,7 @@
     }
 }
 
-MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset)
+MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, off_t offset)
     : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
       mDevice(nullptr), mNeedUnmap(false), mOffset(0)
 {
@@ -85,7 +85,7 @@
     mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
 }
 
-status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device)
+status_t MemoryHeapBase::init(int fd, void *base, size_t size, int flags, const char* device)
 {
     if (mFD != -1) {
         return INVALID_OPERATION;
@@ -98,13 +98,20 @@
     return NO_ERROR;
 }
 
-status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)
+status_t MemoryHeapBase::mapfd(int fd, size_t size, off_t offset)
 {
     if (size == 0) {
         // try to figure out the size automatically
         struct stat sb;
-        if (fstat(fd, &sb) == 0)
-            size = sb.st_size;
+        if (fstat(fd, &sb) == 0) {
+            size = (size_t)sb.st_size;
+            // sb.st_size is off_t which on ILP32 may be 64 bits while size_t is 32 bits.
+            if ((off_t)size != sb.st_size) {
+                ALOGE("%s: size of file %lld cannot fit in memory",
+                        __func__, (long long)sb.st_size);
+                return INVALID_OPERATION;
+            }
+        }
         // if it didn't work, let mmap() fail.
     }
 
@@ -112,12 +119,12 @@
         void* base = (uint8_t*)mmap(nullptr, size,
                 PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
         if (base == MAP_FAILED) {
-            ALOGE("mmap(fd=%d, size=%u) failed (%s)",
-                    fd, uint32_t(size), strerror(errno));
+            ALOGE("mmap(fd=%d, size=%zu) failed (%s)",
+                    fd, size, strerror(errno));
             close(fd);
             return -errno;
         }
-        //ALOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size);
+        //ALOGD("mmap(fd=%d, base=%p, size=%zu)", fd, base, size);
         mBase = base;
         mNeedUnmap = true;
     } else  {
@@ -140,7 +147,7 @@
     int fd = android_atomic_or(-1, &mFD);
     if (fd >= 0) {
         if (mNeedUnmap) {
-            //ALOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize);
+            //ALOGD("munmap(fd=%d, base=%p, size=%zu)", fd, mBase, mSize);
             munmap(mBase, mSize);
         }
         mBase = nullptr;
@@ -169,7 +176,7 @@
     return mDevice;
 }
 
-uint32_t MemoryHeapBase::getOffset() const {
+off_t MemoryHeapBase::getOffset() const {
     return mOffset;
 }
 
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index b2db945..f779d6e 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -74,7 +74,7 @@
 }
 
 // Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER
-#define STRICT_MODE_PENALTY_GATHER (0x40 << 16)
+#define STRICT_MODE_PENALTY_GATHER (1 << 31)
 
 // XXX This can be made public if we want to provide
 // support for typed data.
@@ -220,7 +220,7 @@
     }
 
     if (binder != nullptr) {
-        IBinder *local = binder->localBinder();
+        BBinder *local = binder->localBinder();
         if (!local) {
             BpBinder *proxy = binder->remoteBinder();
             if (proxy == nullptr) {
@@ -232,6 +232,9 @@
             obj.handle = handle;
             obj.cookie = 0;
         } else {
+            if (local->isRequestingSid()) {
+                obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
+            }
             obj.hdr.type = BINDER_TYPE_BINDER;
             obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
             obj.cookie = reinterpret_cast<uintptr_t>(local);
@@ -599,8 +602,10 @@
 // Write RPC headers.  (previously just the interface token)
 status_t Parcel::writeInterfaceToken(const String16& interface)
 {
-    writeInt32(IPCThreadState::self()->getStrictModePolicy() |
-               STRICT_MODE_PENALTY_GATHER);
+    const IPCThreadState* threadState = IPCThreadState::self();
+    writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
+    writeInt32(threadState->shouldPropagateWorkSource() ?
+            threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
     // currently the interface identification token is just its name as a string
     return writeString16(interface);
 }
@@ -613,6 +618,7 @@
 bool Parcel::enforceInterface(const String16& interface,
                               IPCThreadState* threadState) const
 {
+    // StrictModePolicy.
     int32_t strictPolicy = readInt32();
     if (threadState == nullptr) {
         threadState = IPCThreadState::self();
@@ -627,6 +633,10 @@
     } else {
       threadState->setStrictModePolicy(strictPolicy);
     }
+    // WorkSource.
+    int32_t workSource = readInt32();
+    threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
+    // Interface descriptor.
     const String16 str(readString16());
     if (str == interface) {
         return true;
@@ -871,6 +881,16 @@
     return writeNullableTypedVector(val, &Parcel::writeInt64);
 }
 
+status_t Parcel::writeUint64Vector(const std::vector<uint64_t>& val)
+{
+    return writeTypedVector(val, &Parcel::writeUint64);
+}
+
+status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val)
+{
+    return writeNullableTypedVector(val, &Parcel::writeUint64);
+}
+
 status_t Parcel::writeFloatVector(const std::vector<float>& val)
 {
     return writeTypedVector(val, &Parcel::writeFloat);
@@ -1732,6 +1752,14 @@
     return readTypedVector(val, &Parcel::readInt64);
 }
 
+status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readUint64);
+}
+
+status_t Parcel::readUint64Vector(std::vector<uint64_t>* val) const {
+    return readTypedVector(val, &Parcel::readUint64);
+}
+
 status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const {
     return readNullableTypedVector(val, &Parcel::readFloat);
 }
@@ -2329,6 +2357,15 @@
     int fd = readFileDescriptor();
     if (fd == int(BAD_TYPE)) return BAD_VALUE;
 
+    if (!ashmem_valid(fd)) {
+        ALOGE("invalid fd");
+        return BAD_VALUE;
+    }
+    int size = ashmem_get_size_region(fd);
+    if (size < 0 || size_t(size) < len) {
+        ALOGE("request size %zu does not match fd size %d", len, size);
+        return BAD_VALUE;
+    }
     void* ptr = ::mmap(nullptr, len, isMutable ? PROT_READ | PROT_WRITE : PROT_READ,
             MAP_SHARED, fd, 0);
     if (ptr == MAP_FAILED) return NO_MEMORY;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 3798b61..79db0cb 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -181,8 +181,20 @@
         mBinderContextCheckFunc = checkFunc;
         mBinderContextUserData = userData;
 
-        int dummy = 0;
-        status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+        flat_binder_object obj {
+            .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
+        };
+
+        status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);
+
+        // fallback to original method
+        if (result != 0) {
+            android_errorWriteLog(0x534e4554, "121035042");
+
+            int dummy = 0;
+            result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+        }
+
         if (result == 0) {
             mManagesContexts = true;
         } else if (result == -1) {
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index b8db091..7b086d0 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -31,6 +31,8 @@
 public:
 
     enum {
+        // Flag for registerUidObserver: report uid state changed
+        UID_OBSERVER_PROCSTATE = 1<<0,
         // Flag for registerUidObserver: report uid gone
         UID_OBSERVER_GONE = 1<<1,
         // Flag for registerUidObserver: report uid has become idle
@@ -40,8 +42,28 @@
     };
 
     enum {
-        // Not a real process state
-        PROCESS_STATE_UNKNOWN = -1
+        PROCESS_STATE_UNKNOWN = -1,
+        PROCESS_STATE_PERSISTENT = 0,
+        PROCESS_STATE_PERSISTENT_UI = 1,
+        PROCESS_STATE_TOP = 2,
+        PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
+        PROCESS_STATE_FOREGROUND_SERVICE = 4,
+        PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5,
+        PROCESS_STATE_IMPORTANT_FOREGROUND = 6,
+        PROCESS_STATE_IMPORTANT_BACKGROUND = 7,
+        PROCESS_STATE_TRANSIENT_BACKGROUND = 8,
+        PROCESS_STATE_BACKUP = 9,
+        PROCESS_STATE_SERVICE = 10,
+        PROCESS_STATE_RECEIVER = 11,
+        PROCESS_STATE_TOP_SLEEPING = 12,
+        PROCESS_STATE_HEAVY_WEIGHT = 13,
+        PROCESS_STATE_HOME = 14,
+        PROCESS_STATE_LAST_ACTIVITY = 15,
+        PROCESS_STATE_CACHED_ACTIVITY = 16,
+        PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17,
+        PROCESS_STATE_CACHED_RECENT = 18,
+        PROCESS_STATE_CACHED_EMPTY = 19,
+        PROCESS_STATE_NONEXISTENT = 20,
     };
 
     ActivityManager();
@@ -53,8 +75,10 @@
                              const String16& callingPackage);
     void unregisterUidObserver(const sp<IUidObserver>& observer);
     bool isUidActive(const uid_t uid, const String16& callingPackage);
+    int getUidProcessState(const uid_t uid, const String16& callingPackage);
 
-    status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+
+  status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
     status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
 
 private:
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index c5b57c7..37237df 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -95,6 +95,20 @@
         OP_USE_FINGERPRINT = 55,
         OP_BODY_SENSORS = 56,
         OP_AUDIO_ACCESSIBILITY_VOLUME = 64,
+        OP_READ_PHONE_NUMBERS = 65,
+        OP_REQUEST_INSTALL_PACKAGES = 66,
+        OP_PICTURE_IN_PICTURE = 67,
+        OP_INSTANT_APP_START_FOREGROUND = 68,
+        OP_ANSWER_PHONE_CALLS = 69,
+        OP_RUN_ANY_IN_BACKGROUND = 70,
+        OP_CHANGE_WIFI_STATE = 71,
+        OP_REQUEST_DELETE_PACKAGES = 72,
+        OP_BIND_ACCESSIBILITY_SERVICE = 73,
+        OP_ACCEPT_HANDOVER = 74,
+        OP_MANAGE_IPSEC_TUNNELS = 75,
+        OP_START_FOREGROUND = 76,
+        OP_BLUETOOTH_SCAN = 77,
+        OP_USE_BIOMETRIC = 78,
     };
 
     AppOpsManager();
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index c251468..cf3ef84 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -60,6 +60,10 @@
 
     virtual BBinder*    localBinder();
 
+    bool                isRequestingSid();
+    // This must be called before the object is sent to another process. Not thread safe.
+    void                setRequestingSid(bool requestSid);
+
 protected:
     virtual             ~BBinder();
 
@@ -75,6 +79,8 @@
 
     class Extras;
 
+    Extras*             getOrCreateExtras();
+
     std::atomic<Extras*> mExtras;
             void*       mReserved0;
 };
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index f34969b..6abc071 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -38,12 +38,14 @@
                                      const String16& callingPackage) = 0;
     virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
     virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
+    virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
 
     enum {
         OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
         REGISTER_UID_OBSERVER_TRANSACTION,
         UNREGISTER_UID_OBSERVER_TRANSACTION,
-        IS_UID_ACTIVE_TRANSACTION
+        IS_UID_ACTIVE_TRANSACTION,
+        GET_UID_PROCESS_STATE_TRANSACTION
     };
 };
 
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 14edcbe..1674516 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -47,7 +47,7 @@
  * (method calls, property get and set) is down through a low-level
  * protocol implemented on top of the transact() API.
  */
-class IBinder : public virtual RefBase
+class [[clang::lto_visibility_public]] IBinder : public virtual RefBase
 {
 public:
     enum {
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
index db9f53a..071946f 100644
--- a/libs/binder/include/binder/IMemory.h
+++ b/libs/binder/include/binder/IMemory.h
@@ -43,7 +43,7 @@
     virtual void*       getBase() const = 0;
     virtual size_t      getSize() const = 0;
     virtual uint32_t    getFlags() const = 0;
-    virtual uint32_t    getOffset() const = 0;
+    virtual off_t       getOffset() const = 0;
 
     // these are there just for backward source compatibility
     int32_t heapID() const { return getHeapID(); }
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 745f618..614b0b3 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -42,15 +42,34 @@
             status_t            clearLastError();
 
             pid_t               getCallingPid() const;
+            // nullptr if unavailable
+            //
+            // this can't be restored once it's cleared, and it does not return the
+            // context of the current process when not in a binder call.
+            const char*         getCallingSid() const;
             uid_t               getCallingUid() const;
 
             void                setStrictModePolicy(int32_t policy);
             int32_t             getStrictModePolicy() const;
 
+            // See Binder#setCallingWorkSourceUid in Binder.java.
+            int64_t             setCallingWorkSourceUid(uid_t uid);
+            // Internal only. Use setCallingWorkSourceUid(uid) instead.
+            int64_t             setCallingWorkSourceUidWithoutPropagation(uid_t uid);
+            // See Binder#getCallingWorkSourceUid in Binder.java.
+            uid_t               getCallingWorkSourceUid() const;
+            // See Binder#clearCallingWorkSource in Binder.java.
+            int64_t             clearCallingWorkSource();
+            // See Binder#restoreCallingWorkSource in Binder.java.
+            void                restoreCallingWorkSource(int64_t token);
+            void                clearPropagateWorkSource();
+            bool                shouldPropagateWorkSource() const;
+
             void                setLastTransactionBinderFlags(int32_t flags);
             int32_t             getLastTransactionBinderFlags() const;
 
             int64_t             clearCallingIdentity();
+            // Restores PID/UID (not SID)
             void                restoreCallingIdentity(int64_t token);
             
             int                 setupPolling(int* fd);
@@ -118,6 +137,13 @@
             // infer information about thread state.
             bool                isServingCall() const;
 
+            // The work source represents the UID of the process we should attribute the transaction
+            // to. We use -1 to specify that the work source was not set using #setWorkSource.
+            //
+            // This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java
+            // side.
+            static const int32_t kUnsetWorkSource = -1;
+
 private:
                                 IPCThreadState();
                                 ~IPCThreadState();
@@ -154,7 +180,13 @@
             Parcel              mOut;
             status_t            mLastError;
             pid_t               mCallingPid;
+            const char*         mCallingSid;
             uid_t               mCallingUid;
+            // The UID of the process who is responsible for this transaction.
+            // This is used for resource attribution.
+            int32_t             mWorkSource;
+            // Whether the work source should be propagated.
+            bool                mPropagateWorkSource;
             int32_t             mStrictModePolicy;
             int32_t             mLastTransactionBinderFlags;
             IPCThreadStateBase  *mIPCThreadStateBase;
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index e5d8ea6..aeea1a2 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -88,7 +88,7 @@
     const sp<IServiceManager> sm = defaultServiceManager();
     if (sm != nullptr) {
         *outService = interface_cast<INTERFACE>(sm->getService(name));
-        if ((*outService) != NULL) return NO_ERROR;
+        if ((*outService) != nullptr) return NO_ERROR;
     }
     return NAME_NOT_FOUND;
 }
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h
index 9937ad6..a1f530d 100644
--- a/libs/binder/include/binder/IUidObserver.h
+++ b/libs/binder/include/binder/IUidObserver.h
@@ -34,11 +34,13 @@
     virtual void onUidGone(uid_t uid, bool disabled) = 0;
     virtual void onUidActive(uid_t uid) = 0;
     virtual void onUidIdle(uid_t uid, bool disabled) = 0;
+    virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq) = 0;
 
     enum {
         ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
         ON_UID_ACTIVE_TRANSACTION,
-        ON_UID_IDLE_TRANSACTION
+        ON_UID_IDLE_TRANSACTION,
+        ON_UID_STATE_CHANGED_TRANSACTION
     };
 };
 
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index 4be20a0..100d784 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -42,7 +42,7 @@
      * maps the memory referenced by fd. but DOESN'T take ownership
      * of the filedescriptor (it makes a copy with dup()
      */
-    MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0);
+    MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, off_t offset = 0);
 
     /*
      * maps memory from the given device
@@ -64,7 +64,7 @@
 
     virtual size_t      getSize() const;
     virtual uint32_t    getFlags() const;
-    virtual uint32_t    getOffset() const;
+            off_t       getOffset() const override;
 
     const char*         getDevice() const;
 
@@ -82,11 +82,11 @@
 protected:
             MemoryHeapBase();
     // init() takes ownership of fd
-    status_t init(int fd, void *base, int size,
+    status_t init(int fd, void *base, size_t size,
             int flags = 0, const char* device = nullptr);
 
 private:
-    status_t mapfd(int fd, size_t size, uint32_t offset = 0);
+    status_t mapfd(int fd, size_t size, off_t offset = 0);
 
     int         mFD;
     size_t      mSize;
@@ -94,7 +94,7 @@
     uint32_t    mFlags;
     const char* mDevice;
     bool        mNeedUnmap;
-    uint32_t    mOffset;
+    off_t       mOffset;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index c9c273a..240b708 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -27,8 +27,8 @@
 #include <utils/String16.h>
 #include <utils/Vector.h>
 #include <utils/Flattenable.h>
-#include <linux/android/binder.h>
 
+#include <binder/binder_kernel.h>
 #include <binder/IInterface.h>
 #include <binder/Parcelable.h>
 #include <binder/Map.h>
@@ -139,6 +139,8 @@
     status_t            writeInt32Vector(const std::vector<int32_t>& val);
     status_t            writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val);
     status_t            writeInt64Vector(const std::vector<int64_t>& val);
+    status_t            writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val);
+    status_t            writeUint64Vector(const std::vector<uint64_t>& val);
     status_t            writeFloatVector(const std::unique_ptr<std::vector<float>>& val);
     status_t            writeFloatVector(const std::vector<float>& val);
     status_t            writeDoubleVector(const std::unique_ptr<std::vector<double>>& val);
@@ -313,6 +315,8 @@
     status_t            readInt32Vector(std::vector<int32_t>* val) const;
     status_t            readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const;
     status_t            readInt64Vector(std::vector<int64_t>* val) const;
+    status_t            readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const;
+    status_t            readUint64Vector(std::vector<uint64_t>* val) const;
     status_t            readFloatVector(std::unique_ptr<std::vector<float>>* val) const;
     status_t            readFloatVector(std::vector<float>* val) const;
     status_t            readDoubleVector(std::unique_ptr<std::vector<double>>* val) const;
diff --git a/libs/binder/include/binder/binder_kernel.h b/libs/binder/include/binder/binder_kernel.h
new file mode 100644
index 0000000..c7f6b4b
--- /dev/null
+++ b/libs/binder/include/binder/binder_kernel.h
@@ -0,0 +1,45 @@
+/*
+ * 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 ANDROID_BINDER_KERNEL_H
+#define ANDROID_BINDER_KERNEL_H
+
+#include <linux/android/binder.h>
+
+/**
+ * This file exists because the uapi kernel headers in bionic are built
+ * from upstream kernel headers only, and not all of the hwbinder kernel changes
+ * have made it upstream yet. Therefore, the modifications to the
+ * binder header are added locally in this file.
+ */
+
+enum {
+        FLAT_BINDER_FLAG_TXN_SECURITY_CTX = 0x1000,
+};
+
+#define BINDER_SET_CONTEXT_MGR_EXT      _IOW('b', 13, struct flat_binder_object)
+
+struct binder_transaction_data_secctx {
+        struct binder_transaction_data transaction_data;
+        binder_uintptr_t secctx;
+};
+
+enum {
+        BR_TRANSACTION_SEC_CTX = _IOR('r', 2,
+                              struct binder_transaction_data_secctx),
+};
+
+#endif // ANDROID_BINDER_KERNEL_H
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 99a71bd..78f1159 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -71,6 +71,8 @@
     BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
     BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
     BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
+    BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION,
+    BINDER_LIB_TEST_ECHO_VECTOR,
 };
 
 pid_t start_server_process(int arg2, bool usePoll = false)
@@ -179,6 +181,7 @@
     public:
         virtual void SetUp() {
             m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
+            IPCThreadState::self()->restoreCallingWorkSource(0); 
         }
         virtual void TearDown() {
         }
@@ -938,6 +941,141 @@
     EXPECT_EQ(NO_ERROR, ret);
 }
 
+TEST_F(BinderLibTest, WorkSourceUnsetByDefault)
+{
+    status_t ret;
+    Parcel data, reply;
+    data.writeInterfaceToken(binderLibTestServiceName);
+    ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+    EXPECT_EQ(-1, reply.readInt32());
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceSet)
+{
+    status_t ret;
+    Parcel data, reply;
+    IPCThreadState::self()->clearCallingWorkSource();
+    int64_t previousWorkSource = IPCThreadState::self()->setCallingWorkSourceUid(100);
+    data.writeInterfaceToken(binderLibTestServiceName);
+    ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+    EXPECT_EQ(100, reply.readInt32());
+    EXPECT_EQ(-1, previousWorkSource);
+    EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceSetWithoutPropagation)
+{
+    status_t ret;
+    Parcel data, reply;
+
+    IPCThreadState::self()->setCallingWorkSourceUidWithoutPropagation(100);
+    EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+
+    data.writeInterfaceToken(binderLibTestServiceName);
+    ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+    EXPECT_EQ(-1, reply.readInt32());
+    EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceCleared)
+{
+    status_t ret;
+    Parcel data, reply;
+
+    IPCThreadState::self()->setCallingWorkSourceUid(100);
+    int64_t token = IPCThreadState::self()->clearCallingWorkSource();
+    int32_t previousWorkSource = (int32_t)token;
+    data.writeInterfaceToken(binderLibTestServiceName);
+    ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+
+    EXPECT_EQ(-1, reply.readInt32());
+    EXPECT_EQ(100, previousWorkSource);
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceRestored)
+{
+    status_t ret;
+    Parcel data, reply;
+
+    IPCThreadState::self()->setCallingWorkSourceUid(100);
+    int64_t token = IPCThreadState::self()->clearCallingWorkSource();
+    IPCThreadState::self()->restoreCallingWorkSource(token);
+
+    data.writeInterfaceToken(binderLibTestServiceName);
+    ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+
+    EXPECT_EQ(100, reply.readInt32());
+    EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, PropagateFlagSet)
+{
+    status_t ret;
+    Parcel data, reply;
+
+    IPCThreadState::self()->clearPropagateWorkSource();
+    IPCThreadState::self()->setCallingWorkSourceUid(100);
+    EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, PropagateFlagCleared)
+{
+    status_t ret;
+    Parcel data, reply;
+
+    IPCThreadState::self()->setCallingWorkSourceUid(100);
+    IPCThreadState::self()->clearPropagateWorkSource();
+    EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, PropagateFlagRestored)
+{
+    status_t ret;
+    Parcel data, reply;
+
+    int token = IPCThreadState::self()->setCallingWorkSourceUid(100);
+    IPCThreadState::self()->restoreCallingWorkSource(token);
+
+    EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, WorkSourcePropagatedForAllFollowingBinderCalls)
+{
+    IPCThreadState::self()->setCallingWorkSourceUid(100);
+
+    Parcel data, reply;
+    status_t ret;
+    data.writeInterfaceToken(binderLibTestServiceName);
+    ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+
+    Parcel data2, reply2;
+    status_t ret2;
+    data2.writeInterfaceToken(binderLibTestServiceName);
+    ret2 = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data2, &reply2);
+    EXPECT_EQ(100, reply2.readInt32());
+    EXPECT_EQ(NO_ERROR, ret2);
+}
+
+TEST_F(BinderLibTest, VectorSent) {
+    Parcel data, reply;
+    sp<IBinder> server = addServer();
+    ASSERT_TRUE(server != nullptr);
+
+    std::vector<uint64_t> const testValue = { std::numeric_limits<uint64_t>::max(), 0, 200 };
+    data.writeUint64Vector(testValue);
+
+    status_t ret = server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply);
+    EXPECT_EQ(NO_ERROR, ret);
+    std::vector<uint64_t> readValue;
+    ret = reply.readUint64Vector(&readValue);
+    EXPECT_EQ(readValue, testValue);
+}
+
 class BinderLibTestService : public BBinder
 {
     public:
@@ -1236,6 +1374,19 @@
                 }
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION: {
+                data.enforceInterface(binderLibTestServiceName);
+                reply->writeInt32(IPCThreadState::self()->getCallingWorkSourceUid());
+                return NO_ERROR;
+            }
+            case BINDER_LIB_TEST_ECHO_VECTOR: {
+                std::vector<uint64_t> vector;
+                auto err = data.readUint64Vector(&vector);
+                if (err != NO_ERROR)
+                    return err;
+                reply->writeUint64Vector(vector);
+                return NO_ERROR;
+            }
             default:
                 return UNKNOWN_TRANSACTION;
             };
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
new file mode 100644
index 0000000..28cb138
--- /dev/null
+++ b/libs/cputimeinstate/Android.bp
@@ -0,0 +1,30 @@
+cc_library {
+    name: "libtimeinstate",
+    srcs: ["cputimeinstate.cpp"],
+    shared_libs: [
+        "libbase",
+        "libbpf",
+        "libbpf_android",
+        "liblog",
+        "libnetdutils"
+    ],
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+}
+
+cc_test {
+    name: "libtimeinstate_test",
+    srcs: ["testtimeinstate.cpp"],
+    shared_libs: [
+        "libtimeinstate",
+    ],
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+}
+
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
new file mode 100644
index 0000000..4cddf94
--- /dev/null
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2019 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 "libtimeinstate"
+
+#include "cputimeinstate.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <mutex>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <bpf/BpfMap.h>
+#include <libbpf.h>
+#include <log/log.h>
+
+#define BPF_FS_PATH "/sys/fs/bpf/"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace bpf {
+
+typedef struct {
+    uint32_t uid;
+    uint32_t freq;
+} time_key_t;
+
+typedef struct {
+    uint64_t ar[100];
+} val_t;
+
+static std::mutex gInitializedMutex;
+static bool gInitialized = false;
+static uint32_t gNPolicies = 0;
+static std::vector<std::vector<uint32_t>> gPolicyFreqs;
+static std::vector<std::vector<uint32_t>> gPolicyCpus;
+static std::set<uint32_t> gAllFreqs;
+static unique_fd gMapFd;
+
+static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) {
+    std::string data;
+
+    if (!android::base::ReadFileToString(path, &data)) {
+        ALOGD("Failed to read file %s", path.c_str());
+        return false;
+    }
+
+    auto strings = android::base::Split(data, " \n");
+    for (const auto &s : strings) {
+        if (s.empty()) continue;
+        uint32_t n;
+        if (!android::base::ParseUint(s, &n)) {
+            ALOGD("Failed to parse file %s", path.c_str());
+            return false;
+        }
+        out->emplace_back(n);
+    }
+    return true;
+}
+
+static int isPolicyFile(const struct dirent *d) {
+    return android::base::StartsWith(d->d_name, "policy");
+}
+
+static int comparePolicyFiles(const struct dirent **d1, const struct dirent **d2) {
+    uint32_t policyN1, policyN2;
+    if (sscanf((*d1)->d_name, "policy%" SCNu32 "", &policyN1) != 1 ||
+        sscanf((*d2)->d_name, "policy%" SCNu32 "", &policyN2) != 1)
+        return 0;
+    return policyN1 - policyN2;
+}
+
+static bool initGlobals() {
+    std::lock_guard<std::mutex> guard(gInitializedMutex);
+    if (gInitialized) return true;
+
+    struct dirent **dirlist;
+    const char basepath[] = "/sys/devices/system/cpu/cpufreq";
+    int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles);
+    if (ret == -1) return false;
+    gNPolicies = ret;
+
+    std::vector<std::string> policyFileNames;
+    for (uint32_t i = 0; i < gNPolicies; ++i) {
+        policyFileNames.emplace_back(dirlist[i]->d_name);
+        free(dirlist[i]);
+    }
+    free(dirlist);
+
+    for (const auto &policy : policyFileNames) {
+        std::vector<uint32_t> freqs;
+        for (const auto &name : {"available", "boost"}) {
+            std::string path =
+                    StringPrintf("%s/%s/scaling_%s_frequencies", basepath, policy.c_str(), name);
+            if (!readNumbersFromFile(path, &freqs)) return false;
+        }
+        std::sort(freqs.begin(), freqs.end());
+        gPolicyFreqs.emplace_back(freqs);
+
+        for (auto freq : freqs) gAllFreqs.insert(freq);
+
+        std::vector<uint32_t> cpus;
+        std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus");
+        if (!readNumbersFromFile(path, &cpus)) return false;
+        gPolicyCpus.emplace_back(cpus);
+    }
+
+    gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times")};
+    if (gMapFd < 0) return false;
+
+    gInitialized = true;
+    return true;
+}
+
+static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) {
+    std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s",
+                                    eventType.c_str(), eventName.c_str());
+    int prog_fd = bpf_obj_get(path.c_str());
+    if (prog_fd < 0) {
+        ALOGD("bpf_obj_get() failed for program %s", path.c_str());
+        return false;
+    }
+    if (bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) < 0) {
+        ALOGD("Failed to attach bpf program to tracepoint %s/%s", eventType.c_str(),
+              eventName.c_str());
+        return false;
+    }
+    return true;
+}
+
+// Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes.
+// Returns true on success, false otherwise.
+// Tracking is active only once a live process has successfully called this function; if the calling
+// process dies then it must be called again to resume tracking.
+// This function should *not* be called while tracking is already active; doing so is unnecessary
+// and can lead to accounting errors.
+bool startTrackingUidCpuFreqTimes() {
+    return attachTracepointProgram("sched", "sched_switch") &&
+            attachTracepointProgram("power", "cpu_frequency");
+}
+
+// Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes.
+// Returns false on error. Otherwise, returns true and populates freqTimes with a vector of vectors
+// using the format:
+// [[t0_0, t0_1, ...],
+//  [t1_0, t1_1, ...], ...]
+// where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq.
+bool getUidCpuFreqTimes(uint32_t uid, std::vector<std::vector<uint64_t>> *freqTimes) {
+    if (!gInitialized && !initGlobals()) return false;
+    time_key_t key = {.uid = uid, .freq = 0};
+
+    freqTimes->clear();
+    freqTimes->resize(gNPolicies);
+    std::vector<uint32_t> idxs(gNPolicies, 0);
+
+    val_t value;
+    for (uint32_t freq : gAllFreqs) {
+        key.freq = freq;
+        int ret = findMapEntry(gMapFd, &key, &value);
+        if (ret) {
+            if (errno == ENOENT)
+                memset(&value.ar, 0, sizeof(value.ar));
+            else
+                return false;
+        }
+        for (uint32_t i = 0; i < gNPolicies; ++i) {
+            if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) continue;
+            uint64_t time = 0;
+            for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu];
+            idxs[i] += 1;
+            (*freqTimes)[i].emplace_back(time);
+        }
+    }
+
+    return true;
+}
+
+// Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap.
+// Returns false on error. Otherwise, returns true and populates freqTimeMap with a map from uids to
+// vectors of vectors using the format:
+// { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...],
+//   uid1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... }
+// where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq.
+bool getUidsCpuFreqTimes(
+        std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *freqTimeMap) {
+    if (!gInitialized && !initGlobals()) return false;
+
+    int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times");
+    if (fd < 0) return false;
+    BpfMap<time_key_t, val_t> m(fd);
+
+    std::vector<std::unordered_map<uint32_t, uint32_t>> policyFreqIdxs;
+    for (uint32_t i = 0; i < gNPolicies; ++i) {
+        std::unordered_map<uint32_t, uint32_t> freqIdxs;
+        for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j;
+        policyFreqIdxs.emplace_back(freqIdxs);
+    }
+
+    auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val,
+                                             const BpfMap<time_key_t, val_t> &) {
+        if (freqTimeMap->find(key.uid) == freqTimeMap->end()) {
+            std::vector<std::vector<uint64_t>> v;
+            for (uint32_t i = 0; i < gNPolicies; ++i) {
+                std::vector<uint64_t> v2(gPolicyFreqs[i].size(), 0);
+                v.emplace_back(v2);
+            }
+            (*freqTimeMap)[key.uid] = v;
+        }
+
+        for (size_t policy = 0; policy < gNPolicies; ++policy) {
+            for (const auto &cpu : gPolicyCpus[policy]) {
+                uint32_t cpuTime = val.ar[cpu];
+                if (cpuTime == 0) continue;
+                auto freqIdx = policyFreqIdxs[policy][key.freq];
+                (*freqTimeMap)[key.uid][policy][freqIdx] += cpuTime;
+            }
+        }
+        return android::netdutils::status::ok;
+    };
+    return isOk(m.iterateWithValue(fn));
+}
+
+// Clear all time in state data for a given uid. Returns false on error, true otherwise.
+bool clearUidCpuFreqTimes(uint32_t uid) {
+    if (!gInitialized && !initGlobals()) return false;
+    time_key_t key = {.uid = uid, .freq = 0};
+
+    std::vector<uint32_t> idxs(gNPolicies, 0);
+    for (auto freq : gAllFreqs) {
+        key.freq = freq;
+        if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false;
+    }
+    return true;
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/cputimeinstate/cputimeinstate.h
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/cputimeinstate/cputimeinstate.h
index e6ac6bf..0205452 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <unordered_map>
+#include <vector>
 
 namespace android {
-namespace mock {
+namespace bpf {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+bool startTrackingUidCpuFreqTimes();
+bool getUidCpuFreqTimes(unsigned int uid, std::vector<std::vector<uint64_t>> *freqTimes);
+bool getUidsCpuFreqTimes(std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *tisMap);
+bool clearUidCpuFreqTimes(unsigned int uid);
 
-} // namespace mock
+} // namespace bpf
 } // namespace android
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
new file mode 100644
index 0000000..9837865
--- /dev/null
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -0,0 +1,58 @@
+
+#include <unordered_map>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <cputimeinstate.h>
+
+namespace android {
+namespace bpf {
+
+using std::vector;
+
+TEST(TimeInStateTest, SingleUid) {
+    vector<vector<uint64_t>> times;
+    ASSERT_TRUE(getUidCpuFreqTimes(0, &times));
+    EXPECT_FALSE(times.empty());
+}
+
+TEST(TimeInStateTest, AllUid) {
+    vector<size_t> sizes;
+    std::unordered_map<uint32_t, vector<vector<uint64_t>>> map;
+    ASSERT_TRUE(getUidsCpuFreqTimes(&map));
+
+    ASSERT_FALSE(map.empty());
+
+    auto firstEntry = map.begin()->second;
+    for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
+
+    for (const auto &vec : map) {
+        ASSERT_EQ(vec.second.size(), sizes.size());
+        for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
+    }
+}
+
+TEST(TimeInStateTest, RemoveUid) {
+    vector<vector<uint64_t>> times, times2;
+    ASSERT_TRUE(getUidCpuFreqTimes(0, &times));
+    ASSERT_FALSE(times.empty());
+
+    uint64_t sum = 0;
+    for (size_t i = 0; i < times.size(); ++i) {
+        for (auto x : times[i]) sum += x;
+    }
+    ASSERT_GT(sum, (uint64_t)0);
+
+    ASSERT_TRUE(clearUidCpuFreqTimes(0));
+
+    ASSERT_TRUE(getUidCpuFreqTimes(0, &times2));
+    ASSERT_EQ(times2.size(), times.size());
+    for (size_t i = 0; i < times.size(); ++i) {
+        ASSERT_EQ(times2[i].size(), times[i].size());
+        for (size_t j = 0; j < times[i].size(); ++j) ASSERT_LE(times2[i][j], times[i][j]);
+    }
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 35296a9..04884bb 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -46,6 +46,7 @@
         "android.hardware.bluetooth@1.0::IBluetoothHci",
         "android.hardware.camera.provider@2.4::ICameraProvider",
         "android.hardware.drm@1.0::IDrmFactory",
+        "android.hardware.graphics.allocator@2.0::IAllocator",
         "android.hardware.graphics.composer@2.1::IComposer",
         "android.hardware.health@2.0::IHealth",
         "android.hardware.media.omx@1.0::IOmx",
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 4da30e9..280c14a 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -22,6 +22,8 @@
     cflags: ["-Wall", "-Werror"],
 
     shared_libs: [
+        "libbase",
+        "libcutils",
         "liblog",
     ],
 
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index a8ef7a0..b8c6cfe 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -18,51 +18,320 @@
 #define LOG_TAG "GraphicsEnv"
 #include <graphicsenv/GraphicsEnv.h>
 
-#include <mutex>
+#include <dlfcn.h>
+#include <unistd.h>
 
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
 #include <android/dlext.h>
+#include <cutils/properties.h>
 #include <log/log.h>
+#include <sys/prctl.h>
+
+#include <memory>
+#include <string>
+
+#include <dlfcn.h>
 
 // TODO(b/37049319) Get this from a header once one exists
 extern "C" {
-  android_namespace_t* android_get_exported_namespace(const char*);
-  android_namespace_t* android_create_namespace(const char* name,
-                                                const char* ld_library_path,
-                                                const char* default_library_path,
-                                                uint64_t type,
-                                                const char* permitted_when_isolated_path,
-                                                android_namespace_t* parent);
+android_namespace_t* android_get_exported_namespace(const char*);
+android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
+                                              const char* default_library_path, uint64_t type,
+                                              const char* permitted_when_isolated_path,
+                                              android_namespace_t* parent);
+bool android_link_namespaces(android_namespace_t* from, android_namespace_t* to,
+                             const char* shared_libs_sonames);
 
-  enum {
-     ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
-     ANDROID_NAMESPACE_TYPE_SHARED = 2,
-  };
+enum {
+    ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
+    ANDROID_NAMESPACE_TYPE_SHARED = 2,
+};
 }
 
+// TODO(ianelliott@): Get the following from an ANGLE header:
+#define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting
+// Version-2 API:
+typedef bool (*fpANGLEGetFeatureSupportUtilAPIVersion)(unsigned int* versionToUse);
+typedef bool (*fpANGLEAndroidParseRulesString)(const char* rulesString, void** rulesHandle,
+                                               int* rulesVersion);
+typedef bool (*fpANGLEGetSystemInfo)(void** handle);
+typedef bool (*fpANGLEAddDeviceInfoToSystemInfo)(const char* deviceMfr, const char* deviceModel,
+                                                 void* handle);
+typedef bool (*fpANGLEShouldBeUsedForApplication)(void* rulesHandle, int rulesVersion,
+                                                  void* systemInfoHandle, const char* appName);
+typedef bool (*fpANGLEFreeRulesHandle)(void* handle);
+typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle);
+
 namespace android {
 
+enum NativeLibrary {
+    LLNDK = 0,
+    VNDKSP = 1,
+};
+
+static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/etc/llndk.libraries.txt",
+                                                                   "/etc/vndksp.libraries.txt"};
+
+static std::string vndkVersionStr() {
+#ifdef __BIONIC__
+    std::string version = android::base::GetProperty("ro.vndk.version", "");
+    if (version != "" && version != "current") {
+        return "." + version;
+    }
+#endif
+    return "";
+}
+
+static void insertVndkVersionStr(std::string* fileName) {
+    LOG_ALWAYS_FATAL_IF(!fileName, "fileName should never be nullptr");
+    size_t insertPos = fileName->find_last_of(".");
+    if (insertPos == std::string::npos) {
+        insertPos = fileName->length();
+    }
+    fileName->insert(insertPos, vndkVersionStr());
+}
+
+static bool readConfig(const std::string& configFile, std::vector<std::string>* soNames) {
+    // Read list of public native libraries from the config file.
+    std::string fileContent;
+    if (!base::ReadFileToString(configFile, &fileContent)) {
+        return false;
+    }
+
+    std::vector<std::string> lines = base::Split(fileContent, "\n");
+
+    for (auto& line : lines) {
+        auto trimmedLine = base::Trim(line);
+        if (!trimmedLine.empty()) {
+            soNames->push_back(trimmedLine);
+        }
+    }
+
+    return true;
+}
+
+static const std::string getSystemNativeLibraries(NativeLibrary type) {
+    static const char* androidRootEnv = getenv("ANDROID_ROOT");
+    static const std::string rootDir = androidRootEnv != nullptr ? androidRootEnv : "/system";
+
+    std::string nativeLibrariesSystemConfig = rootDir + kNativeLibrariesSystemConfigPath[type];
+
+    insertVndkVersionStr(&nativeLibrariesSystemConfig);
+
+    std::vector<std::string> soNames;
+    if (!readConfig(nativeLibrariesSystemConfig, &soNames)) {
+        ALOGE("Failed to retrieve library names from %s", nativeLibrariesSystemConfig.c_str());
+        return "";
+    }
+
+    return base::Join(soNames, ':');
+}
+
 /*static*/ GraphicsEnv& GraphicsEnv::getInstance() {
     static GraphicsEnv env;
     return env;
 }
 
+int GraphicsEnv::getCanLoadSystemLibraries() {
+    if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+        // Return an integer value since this crosses library boundaries
+        return 1;
+    }
+    return 0;
+}
+
 void GraphicsEnv::setDriverPath(const std::string path) {
     if (!mDriverPath.empty()) {
-        ALOGV("ignoring attempt to change driver path from '%s' to '%s'",
-                mDriverPath.c_str(), path.c_str());
+        ALOGV("ignoring attempt to change driver path from '%s' to '%s'", mDriverPath.c_str(),
+              path.c_str());
         return;
     }
     ALOGV("setting driver path to '%s'", path.c_str());
     mDriverPath = path;
 }
 
+void* GraphicsEnv::loadLibrary(std::string name) {
+    const android_dlextinfo dlextinfo = {
+            .flags = ANDROID_DLEXT_USE_NAMESPACE,
+            .library_namespace = getAngleNamespace(),
+    };
+
+    std::string libName = std::string("lib") + name + "_angle.so";
+
+    void* so = android_dlopen_ext(libName.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+
+    if (so) {
+        ALOGD("dlopen_ext from APK (%s) success at %p", libName.c_str(), so);
+        return so;
+    } else {
+        ALOGE("dlopen_ext(\"%s\") failed: %s", libName.c_str(), dlerror());
+    }
+
+    return nullptr;
+}
+
+bool GraphicsEnv::checkAngleRules(void* so) {
+    char manufacturer[PROPERTY_VALUE_MAX];
+    char model[PROPERTY_VALUE_MAX];
+    property_get("ro.product.manufacturer", manufacturer, "UNSET");
+    property_get("ro.product.model", model, "UNSET");
+
+    auto ANGLEGetFeatureSupportUtilAPIVersion =
+            (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
+                                                          "ANGLEGetFeatureSupportUtilAPIVersion");
+
+    if (!ANGLEGetFeatureSupportUtilAPIVersion) {
+        ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function");
+        return false;
+    }
+
+    // Negotiate the interface version by requesting most recent known to the platform
+    unsigned int versionToUse = CURRENT_ANGLE_API_VERSION;
+    if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) {
+        ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, "
+              "requested version %u",
+              versionToUse);
+        return false;
+    }
+
+    // Add and remove versions below as needed
+    bool useAngle = false;
+    switch (versionToUse) {
+        case 2: {
+            ALOGV("Using version %d of ANGLE feature-support library", versionToUse);
+            void* rulesHandle = nullptr;
+            int rulesVersion = 0;
+            void* systemInfoHandle = nullptr;
+
+            // Get the symbols for the feature-support-utility library:
+#define GET_SYMBOL(symbol)                                                 \
+    fp##symbol symbol = (fp##symbol)dlsym(so, #symbol);                    \
+    if (!symbol) {                                                         \
+        ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \
+        break;                                                             \
+    }
+            GET_SYMBOL(ANGLEAndroidParseRulesString);
+            GET_SYMBOL(ANGLEGetSystemInfo);
+            GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo);
+            GET_SYMBOL(ANGLEShouldBeUsedForApplication);
+            GET_SYMBOL(ANGLEFreeRulesHandle);
+            GET_SYMBOL(ANGLEFreeSystemInfoHandle);
+
+            // Parse the rules, obtain the SystemInfo, and evaluate the
+            // application against the rules:
+            if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) {
+                ALOGW("ANGLE feature-support library cannot parse rules file");
+                break;
+            }
+            if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) {
+                ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
+                break;
+            }
+            if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) {
+                ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
+                break;
+            }
+            useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion,
+                                                         systemInfoHandle, mAngleAppName.c_str());
+            (ANGLEFreeRulesHandle)(rulesHandle);
+            (ANGLEFreeSystemInfoHandle)(systemInfoHandle);
+        } break;
+
+        default:
+            ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse);
+    }
+
+    ALOGV("Close temporarily-loaded ANGLE opt-in/out logic");
+    return useAngle;
+}
+
+bool GraphicsEnv::shouldUseAngle(std::string appName) {
+    if (appName != mAngleAppName) {
+        // Make sure we are checking the app we were init'ed for
+        ALOGE("App name does not match: expected '%s', got '%s'", mAngleAppName.c_str(),
+              appName.c_str());
+        return false;
+    }
+
+    return shouldUseAngle();
+}
+
+bool GraphicsEnv::shouldUseAngle() {
+    // Make sure we are init'ed
+    if (mAngleAppName.empty()) {
+        ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE.");
+        return false;
+    }
+
+    return mUseAngle;
+}
+
+void GraphicsEnv::updateUseAngle() {
+    mUseAngle = false;
+
+    const char* ANGLE_PREFER_ANGLE = "angle";
+    const char* ANGLE_PREFER_NATIVE = "native";
+
+    if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
+        ALOGV("User set \"Developer Options\" to force the use of ANGLE");
+        mUseAngle = true;
+    } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
+        ALOGV("User set \"Developer Options\" to force the use of Native");
+        mUseAngle = false;
+    } else {
+        // The "Developer Options" value wasn't set to force the use of ANGLE.  Need to temporarily
+        // load ANGLE and call the updatable opt-in/out logic:
+        void* featureSo = loadLibrary("feature_support");
+        if (featureSo) {
+            ALOGV("loaded ANGLE's opt-in/out logic from namespace");
+            mUseAngle = checkAngleRules(featureSo);
+            dlclose(featureSo);
+            featureSo = nullptr;
+        } else {
+            ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE.");
+        }
+    }
+}
+
+void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
+                               const std::string developerOptIn, const int rulesFd,
+                               const long rulesOffset, const long rulesLength) {
+    ALOGV("setting ANGLE path to '%s'", path.c_str());
+    mAnglePath = path;
+    ALOGV("setting ANGLE app name to '%s'", appName.c_str());
+    mAngleAppName = appName;
+    ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str());
+    mAngleDeveloperOptIn = developerOptIn;
+
+    lseek(rulesFd, rulesOffset, SEEK_SET);
+    mRulesBuffer = std::vector<char>(rulesLength + 1);
+    ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength);
+    if (numBytesRead < 0) {
+        ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead);
+        numBytesRead = 0;
+    } else if (numBytesRead == 0) {
+        ALOGW("Empty rules file");
+    }
+    if (numBytesRead != rulesLength) {
+        ALOGW("Did not read all of the necessary bytes from the rules file."
+              "expected: %ld, got: %zd",
+              rulesLength, numBytesRead);
+    }
+    mRulesBuffer[numBytesRead] = '\0';
+
+    // Update the current status of whether we should use ANGLE or not
+    updateUseAngle();
+}
+
 void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) {
     if (mLayerPaths.empty()) {
         mLayerPaths = layerPaths;
         mAppNamespace = appNamespace;
     } else {
         ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'",
-                layerPaths.c_str(), appNamespace);
+              layerPaths.c_str(), appNamespace);
     }
 }
 
@@ -70,40 +339,94 @@
     return mAppNamespace;
 }
 
-const std::string GraphicsEnv::getLayerPaths(){
+std::string& GraphicsEnv::getAngleAppName() {
+    return mAngleAppName;
+}
+
+const std::string& GraphicsEnv::getLayerPaths() {
     return mLayerPaths;
 }
 
-const std::string GraphicsEnv::getDebugLayers() {
+const std::string& GraphicsEnv::getDebugLayers() {
     return mDebugLayers;
 }
 
+const std::string& GraphicsEnv::getDebugLayersGLES() {
+    return mDebugLayersGLES;
+}
+
 void GraphicsEnv::setDebugLayers(const std::string layers) {
     mDebugLayers = layers;
 }
 
+void GraphicsEnv::setDebugLayersGLES(const std::string layers) {
+    mDebugLayersGLES = layers;
+}
+
 android_namespace_t* GraphicsEnv::getDriverNamespace() {
     static std::once_flag once;
     std::call_once(once, [this]() {
-        if (mDriverPath.empty())
-            return;
-        // If the sphal namespace isn't configured for a device, don't support updatable drivers.
-        // We need a parent namespace to inherit the default search path from.
-        auto sphalNamespace = android_get_exported_namespace("sphal");
-        if (!sphalNamespace) return;
+        if (mDriverPath.empty()) return;
+
+        auto vndkNamespace = android_get_exported_namespace("vndk");
+        if (!vndkNamespace) return;
+
         mDriverNamespace = android_create_namespace("gfx driver",
-                                                    nullptr,             // ld_library_path
+                                                    mDriverPath.c_str(), // ld_library_path
                                                     mDriverPath.c_str(), // default_library_path
-                                                    ANDROID_NAMESPACE_TYPE_SHARED |
-                                                            ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                                    ANDROID_NAMESPACE_TYPE_ISOLATED,
                                                     nullptr, // permitted_when_isolated_path
-                                                    sphalNamespace);
+                                                    nullptr);
+
+        const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK);
+        if (llndkLibraries.empty()) {
+            mDriverNamespace = nullptr;
+            return;
+        }
+        if (!android_link_namespaces(mDriverNamespace, nullptr, llndkLibraries.c_str())) {
+            ALOGE("Failed to link default namespace[%s]", dlerror());
+            mDriverNamespace = nullptr;
+            return;
+        }
+
+        const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP);
+        if (vndkspLibraries.empty()) {
+            mDriverNamespace = nullptr;
+            return;
+        }
+        if (!android_link_namespaces(mDriverNamespace, vndkNamespace, vndkspLibraries.c_str())) {
+            ALOGE("Failed to link vndk namespace[%s]", dlerror());
+            mDriverNamespace = nullptr;
+            return;
+        }
     });
+
     return mDriverNamespace;
 }
 
-} // namespace android
+android_namespace_t* GraphicsEnv::getAngleNamespace() {
+    std::lock_guard<std::mutex> lock(mNamespaceMutex);
 
-extern "C" android_namespace_t* android_getDriverNamespace() {
-    return android::GraphicsEnv::getInstance().getDriverNamespace();
+    if (mAngleNamespace) {
+        return mAngleNamespace;
+    }
+
+    if (mAnglePath.empty()) {
+        ALOGV("mAnglePath is empty, not creating ANGLE namespace");
+        return nullptr;
+    }
+
+    mAngleNamespace = android_create_namespace("ANGLE",
+                                               nullptr,            // ld_library_path
+                                               mAnglePath.c_str(), // default_library_path
+                                               ANDROID_NAMESPACE_TYPE_SHARED |
+                                                       ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                               nullptr, // permitted_when_isolated_path
+                                               nullptr);
+
+    ALOGD_IF(!mAngleNamespace, "Could not create ANGLE namespace from default");
+
+    return mAngleNamespace;
 }
+
+} // namespace android
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 784e2c8..ed08882 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -17,7 +17,9 @@
 #ifndef ANDROID_UI_GRAPHICS_ENV_H
 #define ANDROID_UI_GRAPHICS_ENV_H 1
 
+#include <mutex>
 #include <string>
+#include <vector>
 
 struct android_namespace_t;
 
@@ -29,6 +31,8 @@
 public:
     static GraphicsEnv& getInstance();
 
+    int getCanLoadSystemLibraries();
+
     // Set a search path for loading graphics drivers. The path is a list of
     // directories separated by ':'. A directory can be contained in a zip file
     // (drivers must be stored uncompressed and page aligned); such elements
@@ -37,35 +41,49 @@
     void setDriverPath(const std::string path);
     android_namespace_t* getDriverNamespace();
 
+    bool shouldUseAngle(std::string appName);
+    bool shouldUseAngle();
+    // Set a search path for loading ANGLE libraries. The path is a list of
+    // directories separated by ':'. A directory can be contained in a zip file
+    // (libraries must be stored uncompressed and page aligned); such elements
+    // in the search path must have a '!' after the zip filename, e.g.
+    //     /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
+    void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
+                      const int rulesFd, const long rulesOffset, const long rulesLength);
+    android_namespace_t* getAngleNamespace();
+    std::string& getAngleAppName();
+
     void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths);
     NativeLoaderNamespace* getAppNamespace();
-    const std::string getLayerPaths();
+
+    const std::string& getLayerPaths();
 
     void setDebugLayers(const std::string layers);
-    const std::string getDebugLayers();
+    void setDebugLayersGLES(const std::string layers);
+    const std::string& getDebugLayers();
+    const std::string& getDebugLayersGLES();
 
 private:
+    void* loadLibrary(std::string name);
+    bool checkAngleRules(void* so);
+    void updateUseAngle();
+
     GraphicsEnv() = default;
     std::string mDriverPath;
+    std::string mAnglePath;
+    std::string mAngleAppName;
+    std::string mAngleDeveloperOptIn;
+    std::vector<char> mRulesBuffer;
+    bool mUseAngle;
     std::string mDebugLayers;
+    std::string mDebugLayersGLES;
     std::string mLayerPaths;
+    std::mutex mNamespaceMutex;
     android_namespace_t* mDriverNamespace = nullptr;
+    android_namespace_t* mAngleNamespace = nullptr;
     NativeLoaderNamespace* mAppNamespace = nullptr;
 };
 
 } // namespace android
 
-/* FIXME
- * Export an un-mangled function that just does
- *     return android::GraphicsEnv::getInstance().getDriverNamespace();
- * This allows libEGL to get the function pointer via dlsym, since it can't
- * directly link against libgui. In a future release, we'll fix this so that
- * libgui does not depend on graphics API libraries, and libEGL can link
- * against it. The current dependencies from libgui -> libEGL are:
- *  - the GLConsumer class, which should be moved to its own library
- *  - the EGLsyncKHR synchronization in BufferQueue, which is deprecated and
- *    will be removed soon.
- */
-extern "C" android_namespace_t* android_getDriverNamespace();
-
 #endif // ANDROID_UI_GRAPHICS_ENV_H
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index b29c1d5..f40eb6c 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -58,7 +58,7 @@
         "-Wno-weak-vtables",
 
         // Allow four-character integer literals
-	"-Wno-four-char-constants",
+        "-Wno-four-char-constants",
 
         // Allow documentation warnings
         "-Wno-documentation",
@@ -106,7 +106,9 @@
         "IProducerListener.cpp",
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
+        "ITransactionCompletedListener.cpp",
         "LayerDebugInfo.cpp",
+        "LayerMetadata.cpp",
         "LayerState.cpp",
         "OccupancyTracker.cpp",
         "StreamSplitter.cpp",
@@ -116,14 +118,17 @@
         "SyncFeatures.cpp",
         "view/Surface.cpp",
         "bufferqueue/1.0/B2HProducerListener.cpp",
-        "bufferqueue/1.0/H2BGraphicBufferProducer.cpp"
+        "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
     ],
 
     shared_libs: [
+        "android.frameworks.bufferhub@1.0",
         "android.hardware.graphics.common@1.1",
+        "libbase",
         "libsync",
         "libbinder",
-        "libbufferhubqueue",  // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
+        "libbufferhub",
+        "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
         "libpdx_default_transport",
         "libcutils",
         "libEGL",
@@ -132,25 +137,27 @@
         "libutils",
         "libnativewindow",
         "liblog",
+        "libinput",
         "libhidlbase",
         "libhidltransport",
         "android.hidl.token@1.0-utils",
         "android.hardware.graphics.bufferqueue@1.0",
-        "android.hardware.configstore@1.0",
-        "android.hardware.configstore-utils",
     ],
 
     // bufferhub is not used when building libgui for vendors
     target: {
         vendor: {
-            cflags: ["-DNO_BUFFERHUB"],
+            cflags: ["-DNO_BUFFERHUB", "-DNO_INPUT"],
             exclude_srcs: [
                 "BufferHubConsumer.cpp",
                 "BufferHubProducer.cpp",
             ],
             exclude_shared_libs: [
+                "android.frameworks.bufferhub@1.0",
+                "libbufferhub",
                 "libbufferhubqueue",
                 "libpdx_default_transport",
+                "libinput"
             ],
         },
     },
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp
index ae5cca2..4be014f 100644
--- a/libs/gui/BufferHubProducer.cpp
+++ b/libs/gui/BufferHubProducer.cpp
@@ -19,6 +19,7 @@
 #include <inttypes.h>
 #include <log/log.h>
 #include <system/window.h>
+#include <ui/BufferHubBuffer.h>
 
 namespace android {
 
@@ -63,13 +64,13 @@
     } else if (buffers_[slot].mGraphicBuffer != nullptr) {
         ALOGE("requestBuffer: slot %d is not empty.", slot);
         return BAD_VALUE;
-    } else if (buffers_[slot].mBufferProducer == nullptr) {
+    } else if (buffers_[slot].mProducerBuffer == nullptr) {
         ALOGE("requestBuffer: slot %d is not dequeued.", slot);
         return BAD_VALUE;
     }
 
-    const auto& buffer_producer = buffers_[slot].mBufferProducer;
-    sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer();
+    const auto& producer_buffer = buffers_[slot].mProducerBuffer;
+    sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
 
     buffers_[slot].mGraphicBuffer = graphic_buffer;
     buffers_[slot].mRequestBufferCalled = true;
@@ -157,19 +158,19 @@
     }
 
     size_t slot = 0;
-    std::shared_ptr<BufferProducer> buffer_producer;
+    std::shared_ptr<ProducerBuffer> producer_buffer;
 
     for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) {
         LocalHandle fence;
         auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence);
         if (!buffer_status) return NO_MEMORY;
 
-        buffer_producer = buffer_status.take();
-        if (!buffer_producer) return NO_MEMORY;
+        producer_buffer = buffer_status.take();
+        if (!producer_buffer) return NO_MEMORY;
 
-        if (width == buffer_producer->width() && height == buffer_producer->height() &&
-            uint32_t(format) == buffer_producer->format()) {
-            // The producer queue returns a buffer producer matches the request.
+        if (width == producer_buffer->width() && height == producer_buffer->height() &&
+            uint32_t(format) == producer_buffer->format()) {
+            // The producer queue returns a producer buffer matches the request.
             break;
         }
 
@@ -178,8 +179,8 @@
         ALOGI("dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different "
               "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need "
               "re-allocattion.",
-              width, height, format, slot, buffer_producer->width(), buffer_producer->height(),
-              buffer_producer->format());
+              width, height, format, slot, producer_buffer->width(), producer_buffer->height(),
+              producer_buffer->format());
         // Mark the slot as reallocating, so that later we can set
         // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
         buffers_[slot].mIsReallocating = true;
@@ -224,23 +225,172 @@
     return ret;
 }
 
-status_t BufferHubProducer::detachBuffer(int /* slot */) {
-    ALOGE("BufferHubProducer::detachBuffer not implemented.");
+status_t BufferHubProducer::detachBuffer(int slot) {
+    ALOGV("detachBuffer: slot=%d", slot);
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    return DetachBufferLocked(static_cast<size_t>(slot));
+}
+
+status_t BufferHubProducer::DetachBufferLocked(size_t slot) {
+    if (connected_api_ == kNoConnectedApi) {
+        ALOGE("detachBuffer: BufferHubProducer is not connected.");
+        return NO_INIT;
+    }
+
+    if (slot >= static_cast<size_t>(max_buffer_count_)) {
+        ALOGE("detachBuffer: slot index %zu out of range [0, %d)", slot, max_buffer_count_);
+        return BAD_VALUE;
+    } else if (!buffers_[slot].mBufferState.isDequeued()) {
+        ALOGE("detachBuffer: slot %zu is not owned by the producer (state = %s)", slot,
+              buffers_[slot].mBufferState.string());
+        return BAD_VALUE;
+    } else if (!buffers_[slot].mRequestBufferCalled) {
+        ALOGE("detachBuffer: buffer in slot %zu has not been requested", slot);
+        return BAD_VALUE;
+    }
+    std::shared_ptr<ProducerBuffer> producer_buffer = queue_->GetBuffer(slot);
+    if (producer_buffer == nullptr || producer_buffer->buffer() == nullptr) {
+        ALOGE("detachBuffer: Invalid ProducerBuffer at slot %zu.", slot);
+        return BAD_VALUE;
+    }
+    sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
+    if (graphic_buffer == nullptr) {
+        ALOGE("detachBuffer: Invalid GraphicBuffer at slot %zu.", slot);
+        return BAD_VALUE;
+    }
+
+    // Remove the ProducerBuffer from the ProducerQueue.
+    status_t error = RemoveBuffer(slot);
+    if (error != NO_ERROR) {
+        ALOGE("detachBuffer: Failed to remove buffer, slot=%zu, error=%d.", slot, error);
+        return error;
+    }
+
+    // Here we need to convert the existing ProducerBuffer into a DetachedBufferHandle and inject
+    // the handle into the GraphicBuffer object at the requested slot.
+    auto status_or_handle = producer_buffer->Detach();
+    if (!status_or_handle.ok()) {
+        ALOGE("detachBuffer: Failed to detach from a ProducerBuffer at slot %zu, error=%d.", slot,
+              status_or_handle.error());
+        return BAD_VALUE;
+    }
+
+    // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can
+    // be directly backed by BufferHub.
     return INVALID_OPERATION;
 }
 
-status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* /* out_buffer */,
-                                             sp<Fence>* /* out_fence */) {
-    ALOGE("BufferHubProducer::detachNextBuffer not implemented.");
-    return INVALID_OPERATION;
+status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) {
+    ALOGV("detachNextBuffer.");
+
+    if (out_buffer == nullptr || out_fence == nullptr) {
+        ALOGE("detachNextBuffer: Invalid parameter: out_buffer=%p, out_fence=%p", out_buffer,
+              out_fence);
+        return BAD_VALUE;
+    }
+
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    if (connected_api_ == kNoConnectedApi) {
+        ALOGE("detachNextBuffer: BufferHubProducer is not connected.");
+        return NO_INIT;
+    }
+
+    // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer, and detachBuffer in
+    // sequence, except for two things:
+    //
+    // 1) It is unnecessary to know the dimensions, format, or usage of the next buffer, i.e. the
+    // function just returns whatever ProducerBuffer is available from the ProducerQueue and no
+    // buffer allocation or re-allocation will happen.
+    // 2) It will not block, since if it cannot find an appropriate buffer to return, it will return
+    // an error instead.
+    size_t slot = 0;
+    LocalHandle fence;
+
+    // First, dequeue a ProducerBuffer from the ProducerQueue with no timeout. Report error
+    // immediately if ProducerQueue::Dequeue() fails.
+    auto status_or_buffer = queue_->Dequeue(/*timeout=*/0, &slot, &fence);
+    if (!status_or_buffer.ok()) {
+        ALOGE("detachNextBuffer: Failed to dequeue buffer, error=%d.", status_or_buffer.error());
+        return NO_MEMORY;
+    }
+
+    std::shared_ptr<ProducerBuffer> producer_buffer = status_or_buffer.take();
+    if (producer_buffer == nullptr) {
+        ALOGE("detachNextBuffer: Dequeued buffer is null.");
+        return NO_MEMORY;
+    }
+
+    // With the BufferHub backed solution, slot returned from |queue_->Dequeue| is guaranteed to
+    // be available for producer's use. It's either in free state (if the buffer has never been used
+    // before) or in queued state (if the buffer has been dequeued and queued back to
+    // BufferHubQueue).
+    if (!buffers_[slot].mBufferState.isFree() && !buffers_[slot].mBufferState.isQueued()) {
+        ALOGE("detachNextBuffer: slot %zu is not free or queued, actual state: %s.", slot,
+              buffers_[slot].mBufferState.string());
+        return BAD_VALUE;
+    }
+    if (buffers_[slot].mProducerBuffer == nullptr) {
+        ALOGE("detachNextBuffer: ProducerBuffer at slot %zu is null.", slot);
+        return BAD_VALUE;
+    }
+    if (buffers_[slot].mProducerBuffer->id() != producer_buffer->id()) {
+        ALOGE("detachNextBuffer: ProducerBuffer at slot %zu has mismatched id, actual: "
+              "%d, expected: %d.",
+              slot, buffers_[slot].mProducerBuffer->id(), producer_buffer->id());
+        return BAD_VALUE;
+    }
+
+    ALOGV("detachNextBuffer: slot=%zu", slot);
+    buffers_[slot].mBufferState.freeQueued();
+    buffers_[slot].mBufferState.dequeue();
+
+    // Second, request the buffer.
+    sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
+    buffers_[slot].mGraphicBuffer = producer_buffer->buffer()->buffer();
+
+    // Finally, detach the buffer and then return.
+    status_t error = DetachBufferLocked(slot);
+    if (error == NO_ERROR) {
+        *out_fence = new Fence(fence.Release());
+        *out_buffer = graphic_buffer;
+    }
+    return error;
 }
 
-status_t BufferHubProducer::attachBuffer(int* /* out_slot */,
-                                         const sp<GraphicBuffer>& /* buffer */) {
-    // With this BufferHub backed implementation, we assume (for now) all buffers
-    // are allocated and owned by the BufferHub. Thus the attempt of transfering
-    // ownership of a buffer to the buffer queue is intentionally unsupported.
-    LOG_ALWAYS_FATAL("BufferHubProducer::attachBuffer not supported.");
+status_t BufferHubProducer::attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) {
+    // In the BufferHub design, all buffers are allocated and owned by the BufferHub. Thus only
+    // GraphicBuffers that are originated from BufferHub can be attached to a BufferHubProducer.
+    ALOGV("queueBuffer: buffer=%p", buffer.get());
+
+    if (out_slot == nullptr) {
+        ALOGE("attachBuffer: out_slot cannot be NULL.");
+        return BAD_VALUE;
+    }
+    if (buffer == nullptr) {
+        ALOGE("attachBuffer: invalid GraphicBuffer.");
+        return BAD_VALUE;
+    }
+
+    std::unique_lock<std::mutex> lock(mutex_);
+
+    if (connected_api_ == kNoConnectedApi) {
+        ALOGE("attachBuffer: BufferQueue has no connected producer");
+        return NO_INIT;
+    }
+
+    // Before attaching the buffer, caller is supposed to call
+    // IGraphicBufferProducer::setGenerationNumber to inform the
+    // BufferHubProducer the next generation number.
+    if (buffer->getGenerationNumber() != generation_number_) {
+        ALOGE("attachBuffer: Mismatched generation number, buffer: %u, queue: %u.",
+              buffer->getGenerationNumber(), generation_number_);
+        return BAD_VALUE;
+    }
+
+    // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can
+    // be directly backed by BufferHub.
     return INVALID_OPERATION;
 }
 
@@ -302,11 +452,11 @@
         return BAD_VALUE;
     }
 
-    // Post the buffer producer with timestamp in the metadata.
-    const auto& buffer_producer = buffers_[slot].mBufferProducer;
+    // Post the producer buffer with timestamp in the metadata.
+    const auto& producer_buffer = buffers_[slot].mProducerBuffer;
 
     // Check input crop is not out of boundary of current buffer.
-    Rect buffer_rect(buffer_producer->width(), buffer_producer->height());
+    Rect buffer_rect(producer_buffer->width(), producer_buffer->height());
     Rect cropped_rect(Rect::EMPTY_RECT);
     crop.intersect(buffer_rect, &cropped_rect);
     if (cropped_rect != crop) {
@@ -327,11 +477,11 @@
     meta_data.scaling_mode = int32_t(scaling_mode);
     meta_data.transform = int32_t(transform);
 
-    buffer_producer->PostAsync(&meta_data, fence_fd);
+    producer_buffer->PostAsync(&meta_data, fence_fd);
     buffers_[slot].mBufferState.queue();
 
-    output->width = buffer_producer->width();
-    output->height = buffer_producer->height();
+    output->width = producer_buffer->width();
+    output->height = producer_buffer->height();
     output->transformHint = 0; // default value, we don't use it yet.
 
     // |numPendingBuffers| counts of the number of buffers that has been enqueued
@@ -369,8 +519,8 @@
         return BAD_VALUE;
     }
 
-    auto buffer_producer = buffers_[slot].mBufferProducer;
-    queue_->Enqueue(buffer_producer, size_t(slot), 0ULL);
+    auto producer_buffer = buffers_[slot].mProducerBuffer;
+    queue_->Enqueue(producer_buffer, size_t(slot), 0U);
     buffers_[slot].mBufferState.cancel();
     buffers_[slot].mFence = fence;
     ALOGV("cancelBuffer: slot %d", slot);
@@ -641,12 +791,12 @@
     }
 
     size_t slot = status.get();
-    auto buffer_producer = queue_->GetBuffer(slot);
+    auto producer_buffer = queue_->GetBuffer(slot);
 
-    LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr, "Failed to get buffer producer at slot: %zu",
-                        slot);
+    LOG_ALWAYS_FATAL_IF(producer_buffer == nullptr,
+                        "Failed to get the producer buffer at slot: %zu", slot);
 
-    buffers_[slot].mBufferProducer = buffer_producer;
+    buffers_[slot].mProducerBuffer = producer_buffer;
 
     return NO_ERROR;
 }
@@ -654,26 +804,28 @@
 status_t BufferHubProducer::RemoveBuffer(size_t slot) {
     auto status = queue_->RemoveBuffer(slot);
     if (!status) {
-        ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer: %s",
-              status.GetErrorMessage().c_str());
+        ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer at slot: %zu, error: %s.",
+              slot, status.GetErrorMessage().c_str());
         return INVALID_OPERATION;
     }
 
     // Reset in memory objects related the the buffer.
-    buffers_[slot].mBufferProducer = nullptr;
-    buffers_[slot].mGraphicBuffer = nullptr;
+    buffers_[slot].mProducerBuffer = nullptr;
     buffers_[slot].mBufferState.detachProducer();
+    buffers_[slot].mFence = Fence::NO_FENCE;
+    buffers_[slot].mGraphicBuffer = nullptr;
+    buffers_[slot].mRequestBufferCalled = false;
     return NO_ERROR;
 }
 
 status_t BufferHubProducer::FreeAllBuffers() {
     for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
         // Reset in memory objects related the the buffer.
-        buffers_[slot].mGraphicBuffer = nullptr;
+        buffers_[slot].mProducerBuffer = nullptr;
         buffers_[slot].mBufferState.reset();
-        buffers_[slot].mRequestBufferCalled = false;
-        buffers_[slot].mBufferProducer = nullptr;
         buffers_[slot].mFence = Fence::NO_FENCE;
+        buffers_[slot].mGraphicBuffer = nullptr;
+        buffers_[slot].mRequestBufferCalled = false;
     }
 
     auto status = queue_->FreeAllBuffers();
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index f50379b..5beba02 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -39,8 +39,8 @@
 }
 
 BufferItem::BufferItem() :
-    mGraphicBuffer(NULL),
-    mFence(NULL),
+    mGraphicBuffer(nullptr),
+    mFence(nullptr),
     mCrop(Rect::INVALID_RECT),
     mTransform(0),
     mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
@@ -91,11 +91,11 @@
 
 size_t BufferItem::getFlattenedSize() const {
     size_t size = sizeof(uint32_t); // Flags
-    if (mGraphicBuffer != 0) {
+    if (mGraphicBuffer != nullptr) {
         size += mGraphicBuffer->getFlattenedSize();
         size = FlattenableUtils::align<4>(size);
     }
-    if (mFence != 0) {
+    if (mFence != nullptr) {
         size += mFence->getFlattenedSize();
         size = FlattenableUtils::align<4>(size);
     }
@@ -107,10 +107,10 @@
 
 size_t BufferItem::getFdCount() const {
     size_t count = 0;
-    if (mGraphicBuffer != 0) {
+    if (mGraphicBuffer != nullptr) {
         count += mGraphicBuffer->getFdCount();
     }
-    if (mFence != 0) {
+    if (mFence != nullptr) {
         count += mFence->getFdCount();
     }
     return count;
@@ -137,13 +137,13 @@
     FlattenableUtils::advance(buffer, size, sizeof(uint32_t));
 
     flags = 0;
-    if (mGraphicBuffer != 0) {
+    if (mGraphicBuffer != nullptr) {
         status_t err = mGraphicBuffer->flatten(buffer, size, fds, count);
         if (err) return err;
         size -= FlattenableUtils::align<4>(buffer);
         flags |= 1;
     }
-    if (mFence != 0) {
+    if (mFence != nullptr) {
         status_t err = mFence->flatten(buffer, size, fds, count);
         if (err) return err;
         size -= FlattenableUtils::align<4>(buffer);
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 89bc0c4..f50bc20 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -107,7 +107,7 @@
 
 void BufferItemConsumer::freeBufferLocked(int slotIndex) {
     sp<BufferFreedListener> listener = mBufferFreedListener.promote();
-    if (listener != NULL && mSlots[slotIndex].mGraphicBuffer != NULL) {
+    if (listener != nullptr && mSlots[slotIndex].mGraphicBuffer != nullptr) {
         // Fire callback if we have a listener registered and the buffer being freed is valid.
         BI_LOGV("actually calling onBufferFreed");
         listener->onBufferFreed(mSlots[slotIndex].mGraphicBuffer);
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index a8da134..5fb3f0b 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -38,7 +38,7 @@
 
 void BufferQueue::ProxyConsumerListener::onDisconnect() {
     sp<ConsumerListener> listener(mConsumerListener.promote());
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onDisconnect();
     }
 }
@@ -46,7 +46,7 @@
 void BufferQueue::ProxyConsumerListener::onFrameAvailable(
         const BufferItem& item) {
     sp<ConsumerListener> listener(mConsumerListener.promote());
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onFrameAvailable(item);
     }
 }
@@ -54,21 +54,21 @@
 void BufferQueue::ProxyConsumerListener::onFrameReplaced(
         const BufferItem& item) {
     sp<ConsumerListener> listener(mConsumerListener.promote());
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onFrameReplaced(item);
     }
 }
 
 void BufferQueue::ProxyConsumerListener::onBuffersReleased() {
     sp<ConsumerListener> listener(mConsumerListener.promote());
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onBuffersReleased();
     }
 }
 
 void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() {
     sp<ConsumerListener> listener(mConsumerListener.promote());
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onSidebandStreamChanged();
     }
 }
@@ -85,21 +85,21 @@
 void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
         sp<IGraphicBufferConsumer>* outConsumer,
         bool consumerIsSurfaceFlinger) {
-    LOG_ALWAYS_FATAL_IF(outProducer == NULL,
+    LOG_ALWAYS_FATAL_IF(outProducer == nullptr,
             "BufferQueue: outProducer must not be NULL");
-    LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
+    LOG_ALWAYS_FATAL_IF(outConsumer == nullptr,
             "BufferQueue: outConsumer must not be NULL");
 
     sp<BufferQueueCore> core(new BufferQueueCore());
-    LOG_ALWAYS_FATAL_IF(core == NULL,
+    LOG_ALWAYS_FATAL_IF(core == nullptr,
             "BufferQueue: failed to create BufferQueueCore");
 
     sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
-    LOG_ALWAYS_FATAL_IF(producer == NULL,
+    LOG_ALWAYS_FATAL_IF(producer == nullptr,
             "BufferQueue: failed to create BufferQueueProducer");
 
     sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
-    LOG_ALWAYS_FATAL_IF(consumer == NULL,
+    LOG_ALWAYS_FATAL_IF(consumer == nullptr,
             "BufferQueue: failed to create BufferQueueConsumer");
 
     *outProducer = producer;
@@ -109,8 +109,8 @@
 #ifndef NO_BUFFERHUB
 void BufferQueue::createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer,
                                        sp<IGraphicBufferConsumer>* outConsumer) {
-    LOG_ALWAYS_FATAL_IF(outProducer == NULL, "BufferQueue: outProducer must not be NULL");
-    LOG_ALWAYS_FATAL_IF(outConsumer == NULL, "BufferQueue: outConsumer must not be NULL");
+    LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL");
+    LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL");
 
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
@@ -118,16 +118,16 @@
     dvr::ProducerQueueConfigBuilder configBuilder;
     std::shared_ptr<dvr::ProducerQueue> producerQueue =
             dvr::ProducerQueue::Create(configBuilder.Build(), dvr::UsagePolicy{});
-    LOG_ALWAYS_FATAL_IF(producerQueue == NULL, "BufferQueue: failed to create ProducerQueue.");
+    LOG_ALWAYS_FATAL_IF(producerQueue == nullptr, "BufferQueue: failed to create ProducerQueue.");
 
     std::shared_ptr<dvr::ConsumerQueue> consumerQueue = producerQueue->CreateConsumerQueue();
-    LOG_ALWAYS_FATAL_IF(consumerQueue == NULL, "BufferQueue: failed to create ConsumerQueue.");
+    LOG_ALWAYS_FATAL_IF(consumerQueue == nullptr, "BufferQueue: failed to create ConsumerQueue.");
 
     producer = BufferHubProducer::Create(producerQueue);
     consumer = BufferHubConsumer::Create(consumerQueue);
 
-    LOG_ALWAYS_FATAL_IF(producer == NULL, "BufferQueue: failed to create BufferQueueProducer");
-    LOG_ALWAYS_FATAL_IF(consumer == NULL, "BufferQueue: failed to create BufferQueueConsumer");
+    LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer");
+    LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer");
 
     *outProducer = producer;
     *outConsumer = consumer;
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index d70e142..3837c3e 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -255,7 +255,7 @@
         // mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer
         // on the consumer side
         if (outBuffer->mAcquireCalled) {
-            outBuffer->mGraphicBuffer = NULL;
+            outBuffer->mGraphicBuffer = nullptr;
         }
 
         mCore->mQueue.erase(front);
@@ -272,7 +272,7 @@
         VALIDATE_CONSISTENCY();
     }
 
-    if (listener != NULL) {
+    if (listener != nullptr) {
         for (int i = 0; i < numDroppedBuffers; ++i) {
             listener->onBufferReleased();
         }
@@ -321,10 +321,10 @@
         const sp<android::GraphicBuffer>& buffer) {
     ATRACE_CALL();
 
-    if (outSlot == NULL) {
+    if (outSlot == nullptr) {
         BQ_LOGE("attachBuffer: outSlot must not be NULL");
         return BAD_VALUE;
-    } else if (buffer == NULL) {
+    } else if (buffer == nullptr) {
         BQ_LOGE("attachBuffer: cannot attach NULL buffer");
         return BAD_VALUE;
     }
@@ -413,7 +413,7 @@
     ATRACE_BUFFER_INDEX(slot);
 
     if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
-            releaseFence == NULL) {
+            releaseFence == nullptr) {
         BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot,
                 releaseFence.get());
         return BAD_VALUE;
@@ -465,7 +465,7 @@
     } // Autolock scope
 
     // Call back without lock held
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onBufferReleased();
     }
 
@@ -476,7 +476,7 @@
         const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
     ATRACE_CALL();
 
-    if (consumerListener == NULL) {
+    if (consumerListener == nullptr) {
         BQ_LOGE("connect: consumerListener may not be NULL");
         return BAD_VALUE;
     }
@@ -504,13 +504,13 @@
 
     Mutex::Autolock lock(mCore->mMutex);
 
-    if (mCore->mConsumerListener == NULL) {
+    if (mCore->mConsumerListener == nullptr) {
         BQ_LOGE("disconnect: no consumer is connected");
         return BAD_VALUE;
     }
 
     mCore->mIsAbandoned = true;
-    mCore->mConsumerListener = NULL;
+    mCore->mConsumerListener = nullptr;
     mCore->mQueue.clear();
     mCore->freeAllBuffersLocked();
     mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
@@ -521,7 +521,7 @@
 status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) {
     ATRACE_CALL();
 
-    if (outSlotMask == NULL) {
+    if (outSlotMask == nullptr) {
         BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL");
         return BAD_VALUE;
     }
@@ -673,7 +673,7 @@
         }
     }
     // Call back without lock held
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onBuffersReleased();
     }
 
@@ -772,7 +772,7 @@
     if (uid != shellUid) {
 #endif
         android_errorWriteWithInfoLog(0x534e4554, "27046057",
-                static_cast<int32_t>(uid), NULL, 0);
+                static_cast<int32_t>(uid), nullptr, 0);
         return PERMISSION_DENIED;
     }
 
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index bb703da..960b194 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -349,7 +349,7 @@
                 BQ_LOGE("Slot %d is in mUnusedSlots but is not FREE", slot);
                 usleep(PAUSE_TIME);
             }
-            if (mSlots[slot].mGraphicBuffer != NULL) {
+            if (mSlots[slot].mGraphicBuffer != nullptr) {
                 BQ_LOGE("Slot %d is in mUnusedSluts but has an active buffer",
                         slot);
                 usleep(PAUSE_TIME);
@@ -371,7 +371,7 @@
                 BQ_LOGE("Slot %d is in mFreeSlots but is not FREE", slot);
                 usleep(PAUSE_TIME);
             }
-            if (mSlots[slot].mGraphicBuffer != NULL) {
+            if (mSlots[slot].mGraphicBuffer != nullptr) {
                 BQ_LOGE("Slot %d is in mFreeSlots but has a buffer",
                         slot);
                 usleep(PAUSE_TIME);
@@ -394,7 +394,7 @@
                 BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE", slot);
                 usleep(PAUSE_TIME);
             }
-            if (mSlots[slot].mGraphicBuffer == NULL) {
+            if (mSlots[slot].mGraphicBuffer == nullptr) {
                 BQ_LOGE("Slot %d is in mFreeBuffers but has no buffer", slot);
                 usleep(PAUSE_TIME);
             }
@@ -418,7 +418,7 @@
                 BQ_LOGE("Slot %d is in mActiveBuffers but is FREE", slot);
                 usleep(PAUSE_TIME);
             }
-            if (mSlots[slot].mGraphicBuffer == NULL && !mIsAllocating) {
+            if (mSlots[slot].mGraphicBuffer == nullptr && !mIsAllocating) {
                 BQ_LOGE("Slot %d is in mActiveBuffers but has no buffer", slot);
                 usleep(PAUSE_TIME);
             }
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index c96a2dd..5e250a4 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -166,7 +166,7 @@
     } // Autolock scope
 
     // Call back without lock held
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onBuffersReleased();
     }
 
@@ -221,7 +221,7 @@
     } // Autolock scope
 
     // Call back without lock held
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onBuffersReleased();
     }
     return NO_ERROR;
@@ -449,11 +449,11 @@
 
         mSlots[found].mBufferState.dequeue();
 
-        if ((buffer == NULL) ||
+        if ((buffer == nullptr) ||
                 buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
         {
             mSlots[found].mAcquireCalled = false;
-            mSlots[found].mGraphicBuffer = NULL;
+            mSlots[found].mGraphicBuffer = nullptr;
             mSlots[found].mRequestBufferCalled = false;
             mSlots[found].mEglDisplay = EGL_NO_DISPLAY;
             mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
@@ -471,7 +471,7 @@
         BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64,
                 mCore->mBufferAge);
 
-        if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {
+        if (CC_UNLIKELY(mSlots[found].mFence == nullptr)) {
             BQ_LOGE("dequeueBuffer: about to return a NULL fence - "
                     "slot=%d w=%d h=%d format=%u",
                     found, buffer->width, buffer->height, buffer->format);
@@ -612,7 +612,7 @@
         listener = mCore->mConsumerListener;
     }
 
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onBuffersReleased();
     }
 
@@ -623,10 +623,10 @@
         sp<Fence>* outFence) {
     ATRACE_CALL();
 
-    if (outBuffer == NULL) {
+    if (outBuffer == nullptr) {
         BQ_LOGE("detachNextBuffer: outBuffer must not be NULL");
         return BAD_VALUE;
-    } else if (outFence == NULL) {
+    } else if (outFence == nullptr) {
         BQ_LOGE("detachNextBuffer: outFence must not be NULL");
         return BAD_VALUE;
     }
@@ -670,7 +670,7 @@
         listener = mCore->mConsumerListener;
     }
 
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onBuffersReleased();
     }
 
@@ -681,10 +681,10 @@
         const sp<android::GraphicBuffer>& buffer) {
     ATRACE_CALL();
 
-    if (outSlot == NULL) {
+    if (outSlot == nullptr) {
         BQ_LOGE("attachBuffer: outSlot must not be NULL");
         return BAD_VALUE;
-    } else if (buffer == NULL) {
+    } else if (buffer == nullptr) {
         BQ_LOGE("attachBuffer: cannot attach NULL buffer");
         return BAD_VALUE;
     }
@@ -766,7 +766,7 @@
     const Region& surfaceDamage = input.getSurfaceDamage();
     const HdrMetadata& hdrMetadata = input.getHdrMetadata();
 
-    if (acquireFence == NULL) {
+    if (acquireFence == nullptr) {
         BQ_LOGE("queueBuffer: fence is NULL");
         return BAD_VALUE;
     }
@@ -972,9 +972,9 @@
             mCallbackCondition.wait(mCallbackMutex);
         }
 
-        if (frameAvailableListener != NULL) {
+        if (frameAvailableListener != nullptr) {
             frameAvailableListener->onFrameAvailable(item);
-        } else if (frameReplacedListener != NULL) {
+        } else if (frameReplacedListener != nullptr) {
             frameReplacedListener->onFrameReplaced(item);
         }
 
@@ -1039,7 +1039,7 @@
         BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
                 "(state = %s)", slot, mSlots[slot].mBufferState.string());
         return BAD_VALUE;
-    } else if (fence == NULL) {
+    } else if (fence == nullptr) {
         BQ_LOGE("cancelBuffer: fence is NULL");
         return BAD_VALUE;
     }
@@ -1069,7 +1069,7 @@
     ATRACE_CALL();
     Mutex::Autolock lock(mCore->mMutex);
 
-    if (outValue == NULL) {
+    if (outValue == nullptr) {
         BQ_LOGE("query: outValue was NULL");
         return BAD_VALUE;
     }
@@ -1145,12 +1145,12 @@
         return NO_INIT;
     }
 
-    if (mCore->mConsumerListener == NULL) {
+    if (mCore->mConsumerListener == nullptr) {
         BQ_LOGE("connect: BufferQueue has no consumer");
         return NO_INIT;
     }
 
-    if (output == NULL) {
+    if (output == nullptr) {
         BQ_LOGE("connect: output was NULL");
         return BAD_VALUE;
     }
@@ -1188,10 +1188,10 @@
             output->nextFrameNumber = mCore->mFrameCounter + 1;
             output->bufferReplaced = false;
 
-            if (listener != NULL) {
+            if (listener != nullptr) {
                 // Set up a death notification so that we can disconnect
                 // automatically if the remote producer dies
-                if (IInterface::asBinder(listener)->remoteBinder() != NULL) {
+                if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
                     status = IInterface::asBinder(listener)->linkToDeath(
                             static_cast<IBinder::DeathRecipient*>(this));
                     if (status != NO_ERROR) {
@@ -1268,7 +1268,7 @@
                     mCore->freeAllBuffersLocked();
 
                     // Remove our death notification callback if we have one
-                    if (mCore->mLinkedToDeath != NULL) {
+                    if (mCore->mLinkedToDeath != nullptr) {
                         sp<IBinder> token =
                                 IInterface::asBinder(mCore->mLinkedToDeath);
                         // This can fail if we're here because of the death
@@ -1278,8 +1278,8 @@
                     }
                     mCore->mSharedBufferSlot =
                             BufferQueueCore::INVALID_BUFFER_SLOT;
-                    mCore->mLinkedToDeath = NULL;
-                    mCore->mConnectedProducerListener = NULL;
+                    mCore->mLinkedToDeath = nullptr;
+                    mCore->mConnectedProducerListener = nullptr;
                     mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
                     mCore->mConnectedPid = -1;
                     mCore->mSidebandStream.clear();
@@ -1302,7 +1302,7 @@
     } // Autolock scope
 
     // Call back without lock held
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onBuffersReleased();
         listener->onDisconnect();
     }
@@ -1318,7 +1318,7 @@
         listener = mCore->mConsumerListener;
     } // Autolock scope
 
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->onSidebandStreamChanged();
     }
     return NO_ERROR;
@@ -1536,7 +1536,7 @@
         Mutex::Autolock lock(mCore->mMutex);
         listener = mCore->mConsumerListener;
     }
-    if (listener != NULL) {
+    if (listener != nullptr) {
         listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
     }
 }
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index f9e292e..abd9921 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -96,7 +96,7 @@
 
 void ConsumerBase::freeBufferLocked(int slotIndex) {
     CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
-    mSlots[slotIndex].mGraphicBuffer = 0;
+    mSlots[slotIndex].mGraphicBuffer = nullptr;
     mSlots[slotIndex].mFence = Fence::NO_FENCE;
     mSlots[slotIndex].mFrameNumber = 0;
 }
@@ -110,7 +110,7 @@
         listener = mFrameAvailableListener.promote();
     }
 
-    if (listener != NULL) {
+    if (listener != nullptr) {
         CB_LOGV("actually calling onFrameAvailable");
         listener->onFrameAvailable(item);
     }
@@ -125,7 +125,7 @@
         listener = mFrameAvailableListener.promote();
     }
 
-    if (listener != NULL) {
+    if (listener != nullptr) {
         CB_LOGV("actually calling onFrameReplaced");
         listener->onFrameReplaced(item);
     }
@@ -352,8 +352,8 @@
         return err;
     }
 
-    if (item->mGraphicBuffer != NULL) {
-        if (mSlots[item->mSlot].mGraphicBuffer != NULL) {
+    if (item->mGraphicBuffer != nullptr) {
+        if (mSlots[item->mSlot].mGraphicBuffer != nullptr) {
             freeBufferLocked(item->mSlot);
         }
         mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer;
@@ -468,7 +468,7 @@
     if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) {
         return false;
     }
-    return (mSlots[slot].mGraphicBuffer != NULL &&
+    return (mSlots[slot].mGraphicBuffer != nullptr &&
             mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle);
 }
 
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 1757ec1..f5cf1c4 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -34,9 +34,9 @@
 
 DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    if (sf != NULL) {
+    if (sf != nullptr) {
         mEventConnection = sf->createDisplayEventConnection(vsyncSource);
-        if (mEventConnection != NULL) {
+        if (mEventConnection != nullptr) {
             mDataChannel = std::make_unique<gui::BitTube>();
             mEventConnection->stealReceiveChannel(mDataChannel.get());
         }
@@ -47,13 +47,13 @@
 }
 
 status_t DisplayEventReceiver::initCheck() const {
-    if (mDataChannel != NULL)
+    if (mDataChannel != nullptr)
         return NO_ERROR;
     return NO_INIT;
 }
 
 int DisplayEventReceiver::getFd() const {
-    if (mDataChannel == NULL)
+    if (mDataChannel == nullptr)
         return NO_INIT;
 
     return mDataChannel->getFd();
@@ -63,7 +63,7 @@
     if (int32_t(count) < 0)
         return BAD_VALUE;
 
-    if (mEventConnection != NULL) {
+    if (mEventConnection != nullptr) {
         mEventConnection->setVsyncRate(count);
         return NO_ERROR;
     }
@@ -71,7 +71,7 @@
 }
 
 status_t DisplayEventReceiver::requestNextVsync() {
-    if (mEventConnection != NULL) {
+    if (mEventConnection != nullptr) {
         mEventConnection->requestNextVsync();
         return NO_ERROR;
     }
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index 8a14359..c04d907 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -18,10 +18,10 @@
 
 #define LOG_TAG "FrameEvents"
 
+#include <android-base/stringprintf.h>
 #include <cutils/compiler.h>  // For CC_[UN]LIKELY
 #include <inttypes.h>
 #include <utils/Log.h>
-#include <utils/String8.h>
 
 #include <algorithm>
 #include <limits>
@@ -29,6 +29,7 @@
 
 namespace android {
 
+using base::StringAppendF;
 
 // ============================================================================
 // FrameEvents
@@ -86,50 +87,49 @@
     releaseFence->getSignalTime();
 }
 
-static void dumpFenceTime(String8& outString, const char* name,
-        bool pending, const FenceTime& fenceTime) {
-    outString.appendFormat("--- %s", name);
+static void dumpFenceTime(std::string& outString, const char* name, bool pending,
+                          const FenceTime& fenceTime) {
+    StringAppendF(&outString, "--- %s", name);
     nsecs_t signalTime = fenceTime.getCachedSignalTime();
     if (Fence::isValidTimestamp(signalTime)) {
-        outString.appendFormat("%" PRId64 "\n", signalTime);
+        StringAppendF(&outString, "%" PRId64 "\n", signalTime);
     } else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) {
-        outString.appendFormat("Pending\n");
+        outString.append("Pending\n");
     } else if (&fenceTime == FenceTime::NO_FENCE.get()){
-        outString.appendFormat("N/A\n");
+        outString.append("N/A\n");
     } else {
-        outString.appendFormat("Error\n");
+        outString.append("Error\n");
     }
 }
 
-void FrameEvents::dump(String8& outString) const
-{
+void FrameEvents::dump(std::string& outString) const {
     if (!valid) {
         return;
     }
 
-    outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber);
-    outString.appendFormat("--- Posted      \t%" PRId64 "\n", postedTime);
-    outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime);
+    StringAppendF(&outString, "-- Frame %" PRIu64 "\n", frameNumber);
+    StringAppendF(&outString, "--- Posted      \t%" PRId64 "\n", postedTime);
+    StringAppendF(&outString, "--- Req. Present\t%" PRId64 "\n", requestedPresentTime);
 
-    outString.appendFormat("--- Latched     \t");
+    outString.append("--- Latched     \t");
     if (FrameEvents::isValidTimestamp(latchTime)) {
-        outString.appendFormat("%" PRId64 "\n", latchTime);
+        StringAppendF(&outString, "%" PRId64 "\n", latchTime);
     } else {
-        outString.appendFormat("Pending\n");
+        outString.append("Pending\n");
     }
 
-    outString.appendFormat("--- Refresh (First)\t");
+    outString.append("--- Refresh (First)\t");
     if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) {
-        outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
+        StringAppendF(&outString, "%" PRId64 "\n", firstRefreshStartTime);
     } else {
-        outString.appendFormat("Pending\n");
+        outString.append("Pending\n");
     }
 
-    outString.appendFormat("--- Refresh (Last)\t");
+    outString.append("--- Refresh (Last)\t");
     if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) {
-        outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime);
+        StringAppendF(&outString, "%" PRId64 "\n", lastRefreshStartTime);
     } else {
-        outString.appendFormat("Pending\n");
+        outString.append("Pending\n");
     }
 
     dumpFenceTime(outString, "Acquire           \t",
@@ -139,11 +139,11 @@
     dumpFenceTime(outString, "Display Present   \t",
             !addPostCompositeCalled, *displayPresentFence);
 
-    outString.appendFormat("--- DequeueReady  \t");
+    outString.append("--- DequeueReady  \t");
     if (FrameEvents::isValidTimestamp(dequeueReadyTime)) {
-        outString.appendFormat("%" PRId64 "\n", dequeueReadyTime);
+        StringAppendF(&outString, "%" PRId64 "\n", dequeueReadyTime);
     } else {
-        outString.appendFormat("Pending\n");
+        outString.append("Pending\n");
     }
 
     dumpFenceTime(outString, "Release           \t",
@@ -206,11 +206,11 @@
     return lhs.valid;
 }
 
-void FrameEventHistory::dump(String8& outString) const {
+void FrameEventHistory::dump(std::string& outString) const {
     auto earliestFrame = std::min_element(
             mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
     if (!earliestFrame->valid) {
-        outString.appendFormat("-- N/A\n");
+        outString.append("-- N/A\n");
         return;
     }
     for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 885efec..faf02f3 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -46,7 +46,6 @@
 #include <utils/Trace.h>
 
 extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-#define CROP_EXT_STR "EGL_ANDROID_image_crop"
 #define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
 #define EGL_PROTECTED_CONTENT_EXT 0x32C0
 
@@ -82,26 +81,6 @@
 Mutex GLConsumer::sStaticInitLock;
 sp<GraphicBuffer> GLConsumer::sReleasedTexImageBuffer;
 
-static bool hasEglAndroidImageCropImpl() {
-    EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
-    size_t cropExtLen = strlen(CROP_EXT_STR);
-    size_t extsLen = strlen(exts);
-    bool equal = !strcmp(CROP_EXT_STR, exts);
-    bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1);
-    bool atEnd = (cropExtLen+1) < extsLen &&
-            !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1));
-    bool inMiddle = strstr(exts, " " CROP_EXT_STR " ");
-    return equal || atStart || atEnd || inMiddle;
-}
-
-static bool hasEglAndroidImageCrop() {
-    // Only compute whether the extension is present once the first time this
-    // function is called.
-    static bool hasIt = hasEglAndroidImageCropImpl();
-    return hasIt;
-}
-
 static bool hasEglProtectedContentImpl() {
     EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
@@ -122,10 +101,6 @@
     return hasIt;
 }
 
-static bool isEglImageCroppable(const Rect& crop) {
-    return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0);
-}
-
 GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
         uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
     ConsumerBase(bq, isControlledByApp),
@@ -291,7 +266,7 @@
             return err;
         }
 
-        if (mReleasedTexImage == NULL) {
+        if (mReleasedTexImage == nullptr) {
             mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
         }
 
@@ -321,7 +296,7 @@
 
 sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() {
     Mutex::Autolock _l(sStaticInitLock);
-    if (CC_UNLIKELY(sReleasedTexImageBuffer == NULL)) {
+    if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) {
         // The first time, create the debug texture in case the application
         // continues to use it.
         sp<GraphicBuffer> buffer = new GraphicBuffer(
@@ -357,7 +332,7 @@
     // If item->mGraphicBuffer is not null, this buffer has not been acquired
     // before, so any prior EglImage created is using a stale buffer. This
     // replaces any old EglImage with a new one (using the new buffer).
-    if (item->mGraphicBuffer != NULL) {
+    if (item->mGraphicBuffer != nullptr) {
         int slot = item->mSlot;
         mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer);
     }
@@ -406,7 +381,7 @@
     // ConsumerBase.
     // We may have to do this even when item.mGraphicBuffer == NULL (which
     // means the buffer was previously acquired).
-    err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay, item.mCrop);
+    err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay);
     if (err != NO_ERROR) {
         GLC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
                 mEglDisplay, slot);
@@ -430,8 +405,8 @@
     }
 
     GLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)",
-            mCurrentTexture, mCurrentTextureImage != NULL ?
-                    mCurrentTextureImage->graphicBufferHandle() : 0,
+            mCurrentTexture, mCurrentTextureImage != nullptr ?
+                    mCurrentTextureImage->graphicBufferHandle() : nullptr,
             slot, mSlots[slot].mGraphicBuffer->handle);
 
     // Hang onto the pointer so that it isn't freed in the call to
@@ -491,13 +466,12 @@
 
     glBindTexture(mTexTarget, mTexName);
     if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT &&
-            mCurrentTextureImage == NULL) {
+            mCurrentTextureImage == nullptr) {
         GLC_LOGE("bindTextureImage: no currently-bound texture");
         return NO_INIT;
     }
 
-    status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay,
-                                                        mCurrentCrop);
+    status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay);
     if (err != NO_ERROR) {
         GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
                 mEglDisplay, mCurrentTexture);
@@ -511,9 +485,7 @@
     // forcing the creation of a new image.
     if ((error = glGetError()) != GL_NO_ERROR) {
         glBindTexture(mTexTarget, mTexName);
-        status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay,
-                                                               mCurrentCrop,
-                                                               true);
+        status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true);
         if (result != NO_ERROR) {
             GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
                     mEglDisplay, mCurrentTexture);
@@ -655,7 +627,7 @@
     mTexName = tex;
     mAttached = true;
 
-    if (mCurrentTextureImage != NULL) {
+    if (mCurrentTextureImage != nullptr) {
         // This may wait for a buffer a second time. This is likely required if
         // this is a different context, since otherwise the wait could be skipped
         // by bouncing through another context. For the same context the extra
@@ -676,7 +648,7 @@
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         if (SyncFeatures::getInstance().useNativeFenceSync()) {
             EGLSyncKHR sync = eglCreateSyncKHR(dpy,
-                    EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+                    EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
             if (sync == EGL_NO_SYNC_KHR) {
                 GLC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x",
                         eglGetError());
@@ -720,7 +692,7 @@
 
             // Create a fence for the outstanding accesses in the current
             // OpenGL ES context.
-            fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
+            fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
             if (fence == EGL_NO_SYNC_KHR) {
                 GLC_LOGE("syncForReleaseLocked: error creating fence: %#x",
                         eglGetError());
@@ -752,11 +724,11 @@
     bool needsRecompute = mFilteringEnabled != enabled;
     mFilteringEnabled = enabled;
 
-    if (needsRecompute && mCurrentTextureImage==NULL) {
+    if (needsRecompute && mCurrentTextureImage==nullptr) {
         GLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL");
     }
 
-    if (needsRecompute && mCurrentTextureImage != NULL) {
+    if (needsRecompute && mCurrentTextureImage != nullptr) {
         computeCurrentTransformMatrixLocked();
     }
 }
@@ -769,8 +741,7 @@
         GLC_LOGD("computeCurrentTransformMatrixLocked: "
                 "mCurrentTextureImage is NULL");
     }
-    computeTransformMatrix(mCurrentTransformMatrix, buf,
-        isEglImageCroppable(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop,
+    computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop,
         mCurrentTransform, mFilteringEnabled);
 }
 
@@ -863,10 +834,10 @@
         xform = crop * xform;
     }
 
-    // SurfaceFlinger expects the top of its window textures to be at a Y
-    // coordinate of 0, so GLConsumer must behave the same way.  We don't
-    // want to expose this to applications, however, so we must add an
-    // additional vertical flip to the transform after all the other transforms.
+    // GLConsumer uses the GL convention where (0, 0) is the bottom-left
+    // corner and (1, 1) is the top-right corner.  Add an additional vertical
+    // flip after all other transforms to map from GL convention to buffer
+    // queue memory layout, where (0, 0) is the top-left corner.
     xform = mtxFlipV * xform;
 
     memcpy(outTransform, xform.asArray(), sizeof(xform));
@@ -938,7 +909,7 @@
     }
 
     return (mCurrentTextureImage == nullptr) ?
-            NULL : mCurrentTextureImage->graphicBuffer();
+            nullptr : mCurrentTextureImage->graphicBuffer();
 }
 
 Rect GLConsumer::getCurrentCrop() const {
@@ -1063,8 +1034,7 @@
 GLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) :
     mGraphicBuffer(graphicBuffer),
     mEglImage(EGL_NO_IMAGE_KHR),
-    mEglDisplay(EGL_NO_DISPLAY),
-    mCropRect(Rect::EMPTY_RECT) {
+    mEglDisplay(EGL_NO_DISPLAY) {
 }
 
 GLConsumer::EglImage::~EglImage() {
@@ -1077,13 +1047,11 @@
 }
 
 status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay,
-                                              const Rect& cropRect,
                                               bool forceCreation) {
     // If there's an image and it's no longer valid, destroy it.
     bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
     bool displayInvalid = mEglDisplay != eglDisplay;
-    bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect;
-    if (haveImage && (displayInvalid || cropInvalid || forceCreation)) {
+    if (haveImage && (displayInvalid || forceCreation)) {
         if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
            ALOGE("createIfNeeded: eglDestroyImageKHR failed");
         }
@@ -1095,14 +1063,12 @@
     // If there's no image, create one.
     if (mEglImage == EGL_NO_IMAGE_KHR) {
         mEglDisplay = eglDisplay;
-        mCropRect = cropRect;
-        mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect);
+        mEglImage = createImage(mEglDisplay, mGraphicBuffer);
     }
 
     // Fail if we can't create a valid image.
     if (mEglImage == EGL_NO_IMAGE_KHR) {
         mEglDisplay = EGL_NO_DISPLAY;
-        mCropRect.makeInvalid();
         const sp<GraphicBuffer>& buffer = mGraphicBuffer;
         ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
             buffer->getWidth(), buffer->getHeight(), buffer->getStride(),
@@ -1119,38 +1085,19 @@
 }
 
 EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy,
-        const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
+        const sp<GraphicBuffer>& graphicBuffer) {
     EGLClientBuffer cbuf =
             static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer());
     const bool createProtectedImage =
             (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) &&
             hasEglProtectedContent();
     EGLint attrs[] = {
-        EGL_IMAGE_PRESERVED_KHR,        EGL_TRUE,
-        EGL_IMAGE_CROP_LEFT_ANDROID,    crop.left,
-        EGL_IMAGE_CROP_TOP_ANDROID,     crop.top,
-        EGL_IMAGE_CROP_RIGHT_ANDROID,   crop.right,
-        EGL_IMAGE_CROP_BOTTOM_ANDROID,  crop.bottom,
+        EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
         createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
         createProtectedImage ? EGL_TRUE : EGL_NONE,
         EGL_NONE,
     };
-    if (!crop.isValid()) {
-        // No crop rect to set, so leave the crop out of the attrib array. Make
-        // sure to propagate the protected content attrs if they are set.
-        attrs[2] = attrs[10];
-        attrs[3] = attrs[11];
-        attrs[4] = EGL_NONE;
-    } else if (!isEglImageCroppable(crop)) {
-        // The crop rect is not at the origin, so we can't set the crop on the
-        // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
-        // extension.  In the future we can add a layered extension that
-        // removes this restriction if there is hardware that can support it.
-        attrs[2] = attrs[10];
-        attrs[3] = attrs[11];
-        attrs[4] = EGL_NONE;
-    }
-    eglInitialize(dpy, 0, 0);
+    eglInitialize(dpy, nullptr, nullptr);
     EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
             EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
     if (image == EGL_NO_IMAGE_KHR) {
diff --git a/libs/gui/GuiConfig.cpp b/libs/gui/GuiConfig.cpp
index bc0c83c..3ec20ee 100644
--- a/libs/gui/GuiConfig.cpp
+++ b/libs/gui/GuiConfig.cpp
@@ -18,8 +18,7 @@
 
 namespace android {
 
-void appendGuiConfigString(String8& configStr)
-{
+void appendGuiConfigString(std::string& configStr) {
     static const char* config =
             " [libgui"
 #ifdef DONT_USE_FENCE_SYNC
diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp
index b715e43..add3ef0 100644
--- a/libs/gui/HdrMetadata.cpp
+++ b/libs/gui/HdrMetadata.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <gui/HdrMetadata.h>
+#include <limits>
 
 namespace android {
 
@@ -26,6 +27,10 @@
     if (validTypes & CTA861_3) {
         size += sizeof(cta8613);
     }
+    if (validTypes & HDR10PLUS) {
+        size += sizeof(size_t);
+        size += hdr10plus.size();
+    }
     return size;
 }
 
@@ -41,6 +46,12 @@
     if (validTypes & CTA861_3) {
         FlattenableUtils::write(buffer, size, cta8613);
     }
+    if (validTypes & HDR10PLUS) {
+        size_t metadataSize = hdr10plus.size();
+        FlattenableUtils::write(buffer, size, metadataSize);
+        memcpy(buffer, hdr10plus.data(), metadataSize);
+        FlattenableUtils::advance(buffer, size, metadataSize);
+    }
 
     return NO_ERROR;
 }
@@ -62,6 +73,22 @@
         }
         FlattenableUtils::read(buffer, size, cta8613);
     }
+    if (validTypes & HDR10PLUS) {
+        if (size < sizeof(size_t)) {
+            return NO_MEMORY;
+        }
+
+        size_t metadataSize;
+        FlattenableUtils::read(buffer, size, metadataSize);
+
+        if (size < metadataSize) {
+            return NO_MEMORY;
+        }
+
+        hdr10plus.resize(metadataSize);
+        memcpy(hdr10plus.data(), buffer, metadataSize);
+        FlattenableUtils::advance(buffer, size, metadataSize);
+    }
 
     return NO_ERROR;
 }
@@ -91,6 +118,10 @@
         }
     }
 
+    if ((validTypes & HDR10PLUS) == HDR10PLUS) {
+        if (hdr10plus != rhs.hdr10plus) return false;
+    }
+
     return true;
 }
 
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 74ab5ac..d997674 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -190,10 +190,10 @@
 
     virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence) {
-        if (outBuffer == NULL) {
+        if (outBuffer == nullptr) {
             ALOGE("detachNextBuffer: outBuffer must not be NULL");
             return BAD_VALUE;
-        } else if (outFence == NULL) {
+        } else if (outFence == nullptr) {
             ALOGE("detachNextBuffer: outFence must not be NULL");
             return BAD_VALUE;
         }
@@ -301,7 +301,7 @@
             int api, bool producerControlledByApp, QueueBufferOutput* output) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
-        if (listener != NULL) {
+        if (listener != nullptr) {
             data.writeInt32(1);
             data.writeStrongBinder(IInterface::asBinder(listener));
         } else {
@@ -738,8 +738,8 @@
             int bufferIdx   = data.readInt32();
             sp<GraphicBuffer> buffer;
             int result = requestBuffer(bufferIdx, &buffer);
-            reply->writeInt32(buffer != 0);
-            if (buffer != 0) {
+            reply->writeInt32(buffer != nullptr);
+            if (buffer != nullptr) {
                 reply->write(*buffer);
             }
             reply->writeInt32(result);
@@ -797,12 +797,12 @@
             int32_t result = detachNextBuffer(&buffer, &fence);
             reply->writeInt32(result);
             if (result == NO_ERROR) {
-                reply->writeInt32(buffer != NULL);
-                if (buffer != NULL) {
+                reply->writeInt32(buffer != nullptr);
+                if (buffer != nullptr) {
                     reply->write(*buffer);
                 }
-                reply->writeInt32(fence != NULL);
-                if (fence != NULL) {
+                reply->writeInt32(fence != nullptr);
+                if (fence != nullptr) {
                     reply->write(*fence);
                 }
             }
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index d2d27e8..bef68ef 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -63,21 +63,11 @@
         return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
     }
 
-    virtual sp<ISurfaceComposerClient> createScopedConnection(
-            const sp<IGraphicBufferProducer>& parent)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        data.writeStrongBinder(IInterface::asBinder(parent));
-        remote()->transact(BnSurfaceComposer::CREATE_SCOPED_CONNECTION, data, &reply);
-        return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
-    }
-
-    virtual void setTransactionState(
-            const Vector<ComposerState>& state,
-            const Vector<DisplayState>& displays,
-            uint32_t flags)
-    {
+    virtual void setTransactionState(const Vector<ComposerState>& state,
+                                     const Vector<DisplayState>& displays, uint32_t flags,
+                                     const sp<IBinder>& applyToken,
+                                     const InputWindowCommands& commands,
+                                     int64_t desiredPresentTime) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
 
@@ -92,6 +82,9 @@
         }
 
         data.writeUint32(flags);
+        data.writeStrongBinder(applyToken);
+        commands.write(data);
+        data.writeInt64(desiredPresentTime);
         remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
     }
 
@@ -103,59 +96,64 @@
     }
 
     virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
-                                   Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-                                   int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform,
+                                   const ui::Dataspace reqDataspace,
+                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
                                    ISurfaceComposer::Rotation rotation) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeStrongBinder(display);
+        data.writeInt32(static_cast<int32_t>(reqDataspace));
+        data.writeInt32(static_cast<int32_t>(reqPixelFormat));
         data.write(sourceCrop);
         data.writeUint32(reqWidth);
         data.writeUint32(reqHeight);
-        data.writeInt32(minLayerZ);
-        data.writeInt32(maxLayerZ);
         data.writeInt32(static_cast<int32_t>(useIdentityTransform));
         data.writeInt32(static_cast<int32_t>(rotation));
-        status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
-
-        if (err != NO_ERROR) {
-            return err;
+        status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("captureScreen failed to transact: %d", result);
+            return result;
         }
-
-        err = reply.readInt32();
-        if (err != NO_ERROR) {
-            return err;
+        result = reply.readInt32();
+        if (result != NO_ERROR) {
+            ALOGE("captureScreen failed to readInt32: %d", result);
+            return result;
         }
 
         *outBuffer = new GraphicBuffer();
         reply.read(**outBuffer);
-        return err;
+
+        return result;
     }
 
     virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder,
-                                   sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
+                                   sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace,
+                                   const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                    float frameScale, bool childrenOnly) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeStrongBinder(layerHandleBinder);
+        data.writeInt32(static_cast<int32_t>(reqDataspace));
+        data.writeInt32(static_cast<int32_t>(reqPixelFormat));
         data.write(sourceCrop);
         data.writeFloat(frameScale);
         data.writeBool(childrenOnly);
-        status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
-
-        if (err != NO_ERROR) {
-            return err;
+        status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("captureLayers failed to transact: %d", result);
+            return result;
         }
-
-        err = reply.readInt32();
-        if (err != NO_ERROR) {
-            return err;
+        result = reply.readInt32();
+        if (result != NO_ERROR) {
+            ALOGE("captureLayers failed to readInt32: %d", result);
+            return result;
         }
 
         *outBuffer = new GraphicBuffer();
         reply.read(**outBuffer);
 
-        return err;
+        return result;
     }
 
     virtual bool authenticateSurfaceTexture(
@@ -332,34 +330,6 @@
         return result;
     }
 
-    virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) {
-        Parcel data, reply;
-        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        if (result != NO_ERROR) {
-            ALOGE("getDisplayViewport failed to writeInterfaceToken: %d", result);
-            return result;
-        }
-        result = data.writeStrongBinder(display);
-        if (result != NO_ERROR) {
-            ALOGE("getDisplayViewport failed to writeStrongBinder: %d", result);
-            return result;
-        }
-        result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_VIEWPORT, data, &reply);
-        if (result != NO_ERROR) {
-            ALOGE("getDisplayViewport failed to transact: %d", result);
-            return result;
-        }
-        result = reply.readInt32();
-        if (result == NO_ERROR) {
-            result = reply.read(*outViewport);
-            if (result != NO_ERROR) {
-                ALOGE("getDisplayViewport failed to read: %d", result);
-                return result;
-            }
-        }
-        return result;
-    }
-
     virtual int getActiveConfig(const sp<IBinder>& display)
     {
         Parcel data, reply;
@@ -372,10 +342,26 @@
     virtual status_t setActiveConfig(const sp<IBinder>& display, int id)
     {
         Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        data.writeStrongBinder(display);
-        data.writeInt32(id);
-        remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply);
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("setActiveConfig failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("setActiveConfig failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = data.writeInt32(id);
+        if (result != NO_ERROR) {
+            ALOGE("setActiveConfig failed to writeInt32: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("setActiveConfig failed to transact: %d", result);
+            return result;
+        }
         return reply.readInt32();
     }
 
@@ -409,6 +395,32 @@
         return result;
     }
 
+    virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+            ui::DisplayPrimaries& primaries) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("getDisplayNativePrimaries failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("getDisplayNativePrimaries failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getDisplayNativePrimaries failed to transact: %d", result);
+            return result;
+        }
+        result = reply.readInt32();
+        if (result == NO_ERROR) {
+            memcpy(&primaries, reply.readInplace(sizeof(ui::DisplayPrimaries)),
+                    sizeof(ui::DisplayPrimaries));
+        }
+        return result;
+    }
+
     virtual ColorMode getActiveColorMode(const sp<IBinder>& display) {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -457,8 +469,16 @@
 
     virtual status_t clearAnimationFrameStats() {
         Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply);
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("clearAnimationFrameStats failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("clearAnimationFrameStats failed to transact: %d", result);
+            return result;
+        }
         return reply.readInt32();
     }
 
@@ -563,6 +583,200 @@
         outLayers->clear();
         return reply.readParcelableVector(outLayers);
     }
+
+    virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace,
+                                              ui::PixelFormat* defaultPixelFormat,
+                                              ui::Dataspace* wideColorGamutDataspace,
+                                              ui::PixelFormat* wideColorGamutPixelFormat) const {
+        Parcel data, reply;
+        status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (error != NO_ERROR) {
+            return error;
+        }
+        error = remote()->transact(BnSurfaceComposer::GET_COMPOSITION_PREFERENCE, data, &reply);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        error = static_cast<status_t>(reply.readInt32());
+        if (error == NO_ERROR) {
+            *defaultDataspace = static_cast<ui::Dataspace>(reply.readInt32());
+            *defaultPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32());
+            *wideColorGamutDataspace = static_cast<ui::Dataspace>(reply.readInt32());
+            *wideColorGamutPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32());
+        }
+        return error;
+    }
+
+    virtual status_t getColorManagement(bool* outGetColorManagement) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        remote()->transact(BnSurfaceComposer::GET_COLOR_MANAGEMENT, data, &reply);
+        bool result;
+        status_t err = reply.readBool(&result);
+        if (err == NO_ERROR) {
+            *outGetColorManagement = result;
+        }
+        return err;
+    }
+
+    virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+                                                           ui::PixelFormat* outFormat,
+                                                           ui::Dataspace* outDataspace,
+                                                           uint8_t* outComponentMask) const {
+        if (!outFormat || !outDataspace || !outComponentMask) return BAD_VALUE;
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeStrongBinder(display);
+
+        status_t error =
+                remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
+                                   data, &reply);
+        if (error != NO_ERROR) {
+            return error;
+        }
+
+        uint32_t value = 0;
+        error = reply.readUint32(&value);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        *outFormat = static_cast<ui::PixelFormat>(value);
+
+        error = reply.readUint32(&value);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        *outDataspace = static_cast<ui::Dataspace>(value);
+
+        error = reply.readUint32(&value);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        *outComponentMask = static_cast<uint8_t>(value);
+        return error;
+    }
+
+    virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
+                                                      uint8_t componentMask,
+                                                      uint64_t maxFrames) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeStrongBinder(display);
+        data.writeBool(enable);
+        data.writeByte(static_cast<int8_t>(componentMask));
+        data.writeUint64(maxFrames);
+        status_t result =
+                remote()->transact(BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED, data,
+                                   &reply);
+        return result;
+    }
+
+    virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+                                               uint64_t timestamp,
+                                               DisplayedFrameStats* outStats) const {
+        if (!outStats) return BAD_VALUE;
+
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeStrongBinder(display);
+        data.writeUint64(maxFrames);
+        data.writeUint64(timestamp);
+
+        status_t result =
+                remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE, data, &reply);
+
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        result = reply.readUint64(&outStats->numFrames);
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        result = reply.readUint64Vector(&outStats->component_0_sample);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readUint64Vector(&outStats->component_1_sample);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readUint64Vector(&outStats->component_2_sample);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readUint64Vector(&outStats->component_3_sample);
+        return result;
+    }
+
+    virtual status_t getProtectedContentSupport(bool* outSupported) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        status_t error =
+                remote()->transact(BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, data, &reply);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        error = reply.readBool(outSupported);
+        return error;
+    }
+
+    virtual status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+                                 int32_t* outBufferId) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+
+        data.writeStrongBinder(token);
+        if (buffer) {
+            data.writeBool(true);
+            data.write(*buffer);
+        } else {
+            data.writeBool(false);
+        }
+
+        status_t result = remote()->transact(BnSurfaceComposer::CACHE_BUFFER, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        int32_t id = -1;
+        result = reply.readInt32(&id);
+        if (result == NO_ERROR) {
+            *outBufferId = id;
+        }
+        return result;
+    }
+
+    virtual status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+
+        data.writeStrongBinder(token);
+        data.writeInt32(bufferId);
+
+        return remote()->transact(BnSurfaceComposer::UNCACHE_BUFFER, data, &reply);
+    }
+
+    virtual status_t isWideColorDisplay(const sp<IBinder>& token,
+                                        bool* outIsWideColorDisplay) const {
+        Parcel data, reply;
+        status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (error != NO_ERROR) {
+            return error;
+        }
+        error = data.writeStrongBinder(token);
+        if (error != NO_ERROR) {
+            return error;
+        }
+
+        error = remote()->transact(BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY, data, &reply);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        error = reply.readBool(outIsWideColorDisplay);
+        return error;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -583,14 +797,6 @@
             reply->writeStrongBinder(b);
             return NO_ERROR;
         }
-        case CREATE_SCOPED_CONNECTION: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IGraphicBufferProducer> bufferProducer =
-                interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
-            sp<IBinder> b = IInterface::asBinder(createScopedConnection(bufferProducer));
-            reply->writeStrongBinder(b);
-            return NO_ERROR;
-        }
         case SET_TRANSACTION_STATE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
 
@@ -598,10 +804,10 @@
             if (count > data.dataSize()) {
                 return BAD_VALUE;
             }
-            ComposerState s;
             Vector<ComposerState> state;
             state.setCapacity(count);
             for (size_t i = 0; i < count; i++) {
+                ComposerState s;
                 if (s.read(data) == BAD_VALUE) {
                     return BAD_VALUE;
                 }
@@ -623,7 +829,13 @@
             }
 
             uint32_t stateFlags = data.readUint32();
-            setTransactionState(state, displays, stateFlags);
+            sp<IBinder> applyToken = data.readStrongBinder();
+            InputWindowCommands inputWindowCommands;
+            inputWindowCommands.read(data);
+
+            int64_t desiredPresentTime = data.readInt64();
+            setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
+                                desiredPresentTime);
             return NO_ERROR;
         }
         case BOOT_FINISHED: {
@@ -634,18 +846,18 @@
         case CAPTURE_SCREEN: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> display = data.readStrongBinder();
+            ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
+            ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
             sp<GraphicBuffer> outBuffer;
             Rect sourceCrop(Rect::EMPTY_RECT);
             data.read(sourceCrop);
             uint32_t reqWidth = data.readUint32();
             uint32_t reqHeight = data.readUint32();
-            int32_t minLayerZ = data.readInt32();
-            int32_t maxLayerZ = data.readInt32();
             bool useIdentityTransform = static_cast<bool>(data.readInt32());
             int32_t rotation = data.readInt32();
 
-            status_t res = captureScreen(display, &outBuffer, sourceCrop, reqWidth, reqHeight,
-                                         minLayerZ, maxLayerZ, useIdentityTransform,
+            status_t res = captureScreen(display, &outBuffer, reqDataspace, reqPixelFormat,
+                                         sourceCrop, reqWidth, reqHeight, useIdentityTransform,
                                          static_cast<ISurfaceComposer::Rotation>(rotation));
             reply->writeInt32(res);
             if (res == NO_ERROR) {
@@ -656,14 +868,16 @@
         case CAPTURE_LAYERS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> layerHandleBinder = data.readStrongBinder();
+            ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
+            ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
             sp<GraphicBuffer> outBuffer;
             Rect sourceCrop(Rect::EMPTY_RECT);
             data.read(sourceCrop);
             float frameScale = data.readFloat();
             bool childrenOnly = data.readBool();
 
-            status_t res = captureLayers(layerHandleBinder, &outBuffer, sourceCrop, frameScale,
-                                         childrenOnly);
+            status_t res = captureLayers(layerHandleBinder, &outBuffer, reqDataspace,
+                                         reqPixelFormat, sourceCrop, frameScale, childrenOnly);
             reply->writeInt32(res);
             if (res == NO_ERROR) {
                 reply->write(*outBuffer);
@@ -752,26 +966,6 @@
             }
             return NO_ERROR;
         }
-        case GET_DISPLAY_VIEWPORT: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            Rect outViewport;
-            sp<IBinder> display = nullptr;
-            status_t result = data.readStrongBinder(&display);
-            if (result != NO_ERROR) {
-                ALOGE("getDisplayViewport failed to readStrongBinder: %d", result);
-                return result;
-            }
-            result = getDisplayViewport(display, &outViewport);
-            result = reply->writeInt32(result);
-            if (result == NO_ERROR) {
-                result = reply->write(outViewport);
-                if (result != NO_ERROR) {
-                    ALOGE("getDisplayViewport failed to write: %d", result);
-                    return result;
-                }
-            }
-            return NO_ERROR;
-        }
         case GET_ACTIVE_CONFIG: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> display = data.readStrongBinder();
@@ -806,6 +1000,26 @@
             }
             return NO_ERROR;
         }
+        case GET_DISPLAY_NATIVE_PRIMARIES: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            ui::DisplayPrimaries primaries;
+            sp<IBinder> display = nullptr;
+
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getDisplayNativePrimaries failed to readStrongBinder: %d", result);
+                return result;
+            }
+
+            result = getDisplayNativePrimaries(display, primaries);
+            reply->writeInt32(result);
+            if (result == NO_ERROR) {
+                memcpy(reply->writeInplace(sizeof(ui::DisplayPrimaries)), &primaries,
+                        sizeof(ui::DisplayPrimaries));
+            }
+
+            return NO_ERROR;
+        }
         case GET_ACTIVE_COLOR_MODE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> display = nullptr;
@@ -906,6 +1120,180 @@
             }
             return result;
         }
+        case GET_COMPOSITION_PREFERENCE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            ui::Dataspace defaultDataspace;
+            ui::PixelFormat defaultPixelFormat;
+            ui::Dataspace wideColorGamutDataspace;
+            ui::PixelFormat wideColorGamutPixelFormat;
+            status_t error =
+                    getCompositionPreference(&defaultDataspace, &defaultPixelFormat,
+                                             &wideColorGamutDataspace, &wideColorGamutPixelFormat);
+            reply->writeInt32(error);
+            if (error == NO_ERROR) {
+                reply->writeInt32(static_cast<int32_t>(defaultDataspace));
+                reply->writeInt32(static_cast<int32_t>(defaultPixelFormat));
+                reply->writeInt32(static_cast<int32_t>(wideColorGamutDataspace));
+                reply->writeInt32(static_cast<int32_t>(wideColorGamutPixelFormat));
+            }
+            return error;
+        }
+        case GET_COLOR_MANAGEMENT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            bool result;
+            status_t error = getColorManagement(&result);
+            if (error == NO_ERROR) {
+                reply->writeBool(result);
+            }
+            return error;
+        }
+        case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+            sp<IBinder> display = data.readStrongBinder();
+            ui::PixelFormat format;
+            ui::Dataspace dataspace;
+            uint8_t component = 0;
+            auto result =
+                    getDisplayedContentSamplingAttributes(display, &format, &dataspace, &component);
+            if (result == NO_ERROR) {
+                reply->writeUint32(static_cast<uint32_t>(format));
+                reply->writeUint32(static_cast<uint32_t>(dataspace));
+                reply->writeUint32(static_cast<uint32_t>(component));
+            }
+            return result;
+        }
+        case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+            sp<IBinder> display = nullptr;
+            bool enable = false;
+            int8_t componentMask = 0;
+            uint64_t maxFrames = 0;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("setDisplayContentSamplingEnabled failure in reading Display token: %d",
+                      result);
+                return result;
+            }
+
+            result = data.readBool(&enable);
+            if (result != NO_ERROR) {
+                ALOGE("setDisplayContentSamplingEnabled failure in reading enable: %d", result);
+                return result;
+            }
+
+            result = data.readByte(static_cast<int8_t*>(&componentMask));
+            if (result != NO_ERROR) {
+                ALOGE("setDisplayContentSamplingEnabled failure in reading component mask: %d",
+                      result);
+                return result;
+            }
+
+            result = data.readUint64(&maxFrames);
+            if (result != NO_ERROR) {
+                ALOGE("setDisplayContentSamplingEnabled failure in reading max frames: %d", result);
+                return result;
+            }
+
+            return setDisplayContentSamplingEnabled(display, enable,
+                                                    static_cast<uint8_t>(componentMask), maxFrames);
+        }
+        case GET_DISPLAYED_CONTENT_SAMPLE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+            sp<IBinder> display = data.readStrongBinder();
+            uint64_t maxFrames = 0;
+            uint64_t timestamp = 0;
+
+            status_t result = data.readUint64(&maxFrames);
+            if (result != NO_ERROR) {
+                ALOGE("getDisplayedContentSample failure in reading max frames: %d", result);
+                return result;
+            }
+
+            result = data.readUint64(&timestamp);
+            if (result != NO_ERROR) {
+                ALOGE("getDisplayedContentSample failure in reading timestamp: %d", result);
+                return result;
+            }
+
+            DisplayedFrameStats stats;
+            result = getDisplayedContentSample(display, maxFrames, timestamp, &stats);
+            if (result == NO_ERROR) {
+                reply->writeUint64(stats.numFrames);
+                reply->writeUint64Vector(stats.component_0_sample);
+                reply->writeUint64Vector(stats.component_1_sample);
+                reply->writeUint64Vector(stats.component_2_sample);
+                reply->writeUint64Vector(stats.component_3_sample);
+            }
+            return result;
+        }
+        case GET_PROTECTED_CONTENT_SUPPORT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            bool result;
+            status_t error = getProtectedContentSupport(&result);
+            if (error == NO_ERROR) {
+                reply->writeBool(result);
+            }
+            return error;
+        }
+        case CACHE_BUFFER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> token;
+            status_t result = data.readStrongBinder(&token);
+            if (result != NO_ERROR) {
+                ALOGE("cache buffer failure in reading token: %d", result);
+                return result;
+            }
+
+            sp<GraphicBuffer> buffer = new GraphicBuffer();
+            if (data.readBool()) {
+                result = data.read(*buffer);
+                if (result != NO_ERROR) {
+                    ALOGE("cache buffer failure in reading buffer: %d", result);
+                    return result;
+                }
+            }
+            int32_t bufferId = -1;
+            status_t error = cacheBuffer(token, buffer, &bufferId);
+            if (error == NO_ERROR) {
+                reply->writeInt32(bufferId);
+            }
+            return error;
+        }
+        case UNCACHE_BUFFER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> token;
+            status_t result = data.readStrongBinder(&token);
+            if (result != NO_ERROR) {
+                ALOGE("uncache buffer failure in reading token: %d", result);
+                return result;
+            }
+
+            int32_t bufferId = -1;
+            result = data.readInt32(&bufferId);
+            if (result != NO_ERROR) {
+                ALOGE("uncache buffer failure in reading buffer id: %d", result);
+                return result;
+            }
+
+            return uncacheBuffer(token, bufferId);
+        }
+        case IS_WIDE_COLOR_DISPLAY: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t error = data.readStrongBinder(&display);
+            if (error != NO_ERROR) {
+                return error;
+            }
+            bool result;
+            error = isWideColorDisplay(display, &result);
+            if (error == NO_ERROR) {
+                reply->writeBool(result);
+            }
+            return error;
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index a6890ee..129558b 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -31,7 +31,7 @@
 
 enum class Tag : uint32_t {
     CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
-    DESTROY_SURFACE,
+    CREATE_WITH_SURFACE_PARENT,
     CLEAR_LAYER_FRAME_STATS,
     GET_LAYER_FRAME_STATS,
     LAST = GET_LAYER_FRAME_STATS,
@@ -47,19 +47,26 @@
     ~BpSurfaceComposerClient() override;
 
     status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
-                           uint32_t flags, const sp<IBinder>& parent, int32_t windowType,
-                           int32_t ownerUid, sp<IBinder>* handle,
-                           sp<IGraphicBufferProducer>* gbp) override {
+                           uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
+                           sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) override {
         return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
                                                                             name, width, height,
                                                                             format, flags, parent,
-                                                                            windowType, ownerUid,
+                                                                            std::move(metadata),
                                                                             handle, gbp);
     }
 
-    status_t destroySurface(const sp<IBinder>& handle) override {
-        return callRemote<decltype(&ISurfaceComposerClient::destroySurface)>(Tag::DESTROY_SURFACE,
-                                                                             handle);
+    status_t createWithSurfaceParent(const String8& name, uint32_t width, uint32_t height,
+                                     PixelFormat format, uint32_t flags,
+                                     const sp<IGraphicBufferProducer>& parent,
+                                     LayerMetadata metadata, sp<IBinder>* handle,
+                                     sp<IGraphicBufferProducer>* gbp) override {
+        return callRemote<decltype(
+                &ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
+                                                                   name, width, height, format,
+                                                                   flags, parent,
+                                                                   std::move(metadata), handle,
+                                                                   gbp);
     }
 
     status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
@@ -92,8 +99,8 @@
     switch (tag) {
         case Tag::CREATE_SURFACE:
             return callLocal(data, reply, &ISurfaceComposerClient::createSurface);
-        case Tag::DESTROY_SURFACE:
-            return callLocal(data, reply, &ISurfaceComposerClient::destroySurface);
+        case Tag::CREATE_WITH_SURFACE_PARENT:
+            return callLocal(data, reply, &ISurfaceComposerClient::createWithSurfaceParent);
         case Tag::CLEAR_LAYER_FRAME_STATS:
             return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats);
         case Tag::GET_LAYER_FRAME_STATS:
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
new file mode 100644
index 0000000..ce88d7b
--- /dev/null
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ITransactionCompletedListener"
+//#define LOG_NDEBUG 0
+
+#include <gui/ITransactionCompletedListener.h>
+
+namespace android {
+
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+    ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
+    LAST = ON_TRANSACTION_COMPLETED,
+};
+
+} // Anonymous namespace
+
+status_t SurfaceStats::writeToParcel(Parcel* output) const {
+    status_t err = output->writeStrongBinder(surfaceControl);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = output->writeInt64(acquireTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    if (previousReleaseFence) {
+        err = output->writeBool(true);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        err = output->write(*previousReleaseFence);
+    } else {
+        err = output->writeBool(false);
+    }
+    return err;
+}
+
+status_t SurfaceStats::readFromParcel(const Parcel* input) {
+    status_t err = input->readStrongBinder(&surfaceControl);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = input->readInt64(&acquireTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    bool hasFence = false;
+    err = input->readBool(&hasFence);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    if (hasFence) {
+        previousReleaseFence = new Fence();
+        err = input->read(*previousReleaseFence);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t TransactionStats::writeToParcel(Parcel* output) const {
+    status_t err = output->writeInt64(latchTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    if (presentFence) {
+        err = output->writeBool(true);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        err = output->write(*presentFence);
+    } else {
+        err = output->writeBool(false);
+    }
+    if (err != NO_ERROR) {
+        return err;
+    }
+    return output->writeParcelableVector(surfaceStats);
+}
+
+status_t TransactionStats::readFromParcel(const Parcel* input) {
+    status_t err = input->readInt64(&latchTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    bool hasFence = false;
+    err = input->readBool(&hasFence);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    if (hasFence) {
+        presentFence = new Fence();
+        err = input->read(*presentFence);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+    return input->readParcelableVector(&surfaceStats);
+}
+
+status_t ListenerStats::writeToParcel(Parcel* output) const {
+    status_t err = output->writeInt32(static_cast<int32_t>(transactionStats.size()));
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    for (const auto& [callbackIds, stats] : transactionStats) {
+        err = output->writeParcelable(stats);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        err = output->writeInt64Vector(callbackIds);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t ListenerStats::readFromParcel(const Parcel* input) {
+    int32_t transactionStats_size = input->readInt32();
+
+    for (int i = 0; i < transactionStats_size; i++) {
+        TransactionStats stats;
+        std::vector<CallbackId> callbackIds;
+
+        status_t err = input->readParcelable(&stats);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        err = input->readInt64Vector(&callbackIds);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        transactionStats.emplace(callbackIds, stats);
+    }
+    return NO_ERROR;
+}
+
+ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener,
+                                         const std::unordered_set<CallbackId>& callbackIds) {
+    ListenerStats listenerStats;
+    listenerStats.listener = listener;
+    TransactionStats transactionStats;
+    listenerStats.transactionStats.emplace(std::piecewise_construct,
+                                           std::forward_as_tuple(callbackIds.begin(),
+                                                                 callbackIds.end()),
+                                           std::forward_as_tuple(transactionStats));
+    return listenerStats;
+}
+
+class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> {
+public:
+    explicit BpTransactionCompletedListener(const sp<IBinder>& impl)
+          : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") {
+    }
+
+    ~BpTransactionCompletedListener() override;
+
+    void onTransactionCompleted(ListenerStats stats) override {
+        callRemoteAsync<decltype(&ITransactionCompletedListener::
+                                         onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
+                                                                  stats);
+    }
+};
+
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpTransactionCompletedListener::~BpTransactionCompletedListener() = default;
+
+IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener");
+
+status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data,
+                                                    Parcel* reply, uint32_t flags) {
+    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+    auto tag = static_cast<Tag>(code);
+    switch (tag) {
+        case Tag::ON_TRANSACTION_COMPLETED:
+            return callLocalAsync(data, reply,
+                                  &ITransactionCompletedListener::onTransactionCompleted);
+    }
+}
+
+}; // namespace android
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
index d3dc16d..cdde9a2 100644
--- a/libs/gui/LayerDebugInfo.cpp
+++ b/libs/gui/LayerDebugInfo.cpp
@@ -16,13 +16,14 @@
 
 #include <gui/LayerDebugInfo.h>
 
+#include <android-base/stringprintf.h>
+
 #include <ui/DebugUtils.h>
 
 #include <binder/Parcel.h>
 
-#include <utils/String8.h>
-
 using namespace android;
+using android::base::StringAppendF;
 
 #define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false)
 
@@ -42,7 +43,6 @@
     RETURN_ON_ERROR(parcel->writeInt32(mWidth));
     RETURN_ON_ERROR(parcel->writeInt32(mHeight));
     RETURN_ON_ERROR(parcel->write(mCrop));
-    RETURN_ON_ERROR(parcel->write(mFinalCrop));
     RETURN_ON_ERROR(parcel->writeFloat(mColor.r));
     RETURN_ON_ERROR(parcel->writeFloat(mColor.g));
     RETURN_ON_ERROR(parcel->writeFloat(mColor.b));
@@ -81,7 +81,6 @@
     RETURN_ON_ERROR(parcel->readInt32(&mWidth));
     RETURN_ON_ERROR(parcel->readInt32(&mHeight));
     RETURN_ON_ERROR(parcel->read(mCrop));
-    RETURN_ON_ERROR(parcel->read(mFinalCrop));
     mColor.r = parcel->readFloat();
     RETURN_ON_ERROR(parcel->errorCheck());
     mColor.g = parcel->readFloat();
@@ -110,39 +109,37 @@
 }
 
 std::string to_string(const LayerDebugInfo& info) {
-    String8 result;
+    std::string result;
 
-    result.appendFormat("+ %s (%s)\n", info.mType.c_str(), info.mName.c_str());
+    StringAppendF(&result, "+ %s (%s)\n", info.mType.c_str(), info.mName.c_str());
     info.mTransparentRegion.dump(result, "TransparentRegion");
     info.mVisibleRegion.dump(result, "VisibleRegion");
     info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
 
-    result.appendFormat("      layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
-            info.mLayerStack, info.mZ, static_cast<double>(info.mX), static_cast<double>(info.mY),
-            info.mWidth, info.mHeight);
+    StringAppendF(&result, "      layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
+                  info.mLayerStack, info.mZ, static_cast<double>(info.mX),
+                  static_cast<double>(info.mY), info.mWidth, info.mHeight);
 
-    result.appendFormat("crop=%s, finalCrop=%s, ",
-            to_string(info.mCrop).c_str(), to_string(info.mFinalCrop).c_str());
-    result.appendFormat("isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty);
-    result.appendFormat("dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str());
-    result.appendFormat("pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str());
-    result.appendFormat("color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
-            static_cast<double>(info.mColor.r), static_cast<double>(info.mColor.g),
-            static_cast<double>(info.mColor.b), static_cast<double>(info.mColor.a),
-            info.mFlags);
-    result.appendFormat("tr=[%.2f, %.2f][%.2f, %.2f]",
-            static_cast<double>(info.mMatrix[0][0]), static_cast<double>(info.mMatrix[0][1]),
-            static_cast<double>(info.mMatrix[1][0]), static_cast<double>(info.mMatrix[1][1]));
+    StringAppendF(&result, "crop=%s, ", to_string(info.mCrop).c_str());
+    StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty);
+    StringAppendF(&result, "dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str());
+    StringAppendF(&result, "pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str());
+    StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
+                  static_cast<double>(info.mColor.r), static_cast<double>(info.mColor.g),
+                  static_cast<double>(info.mColor.b), static_cast<double>(info.mColor.a),
+                  info.mFlags);
+    StringAppendF(&result, "tr=[%.2f, %.2f][%.2f, %.2f]", static_cast<double>(info.mMatrix[0][0]),
+                  static_cast<double>(info.mMatrix[0][1]), static_cast<double>(info.mMatrix[1][0]),
+                  static_cast<double>(info.mMatrix[1][1]));
     result.append("\n");
-    result.appendFormat("      parent=%s\n", info.mParentName.c_str());
-    result.appendFormat("      activeBuffer=[%4ux%4u:%4u,%s],",
-            info.mActiveBufferWidth, info.mActiveBufferHeight,
-            info.mActiveBufferStride,
-            decodePixelFormat(info.mActiveBufferFormat).c_str());
-    result.appendFormat(" queued-frames=%d, mRefreshPending=%d",
-            info.mNumQueuedFrames, info.mRefreshPending);
+    StringAppendF(&result, "      parent=%s\n", info.mParentName.c_str());
+    StringAppendF(&result, "      activeBuffer=[%4ux%4u:%4u,%s],", info.mActiveBufferWidth,
+                  info.mActiveBufferHeight, info.mActiveBufferStride,
+                  decodePixelFormat(info.mActiveBufferFormat).c_str());
+    StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d", info.mNumQueuedFrames,
+                  info.mRefreshPending);
     result.append("\n");
-    return std::string(result.c_str());
+    return result;
 }
 
 } // android
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
new file mode 100644
index 0000000..745433a
--- /dev/null
+++ b/libs/gui/LayerMetadata.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 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 <android-base/stringprintf.h>
+#include <binder/Parcel.h>
+#include <gui/LayerMetadata.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+LayerMetadata::LayerMetadata() = default;
+
+LayerMetadata::LayerMetadata(std::unordered_map<uint32_t, std::vector<uint8_t>> map)
+      : mMap(std::move(map)) {}
+
+LayerMetadata::LayerMetadata(const LayerMetadata& other) = default;
+
+LayerMetadata::LayerMetadata(LayerMetadata&& other) = default;
+
+void LayerMetadata::merge(const LayerMetadata& other) {
+    for (const auto& entry : other.mMap) {
+        mMap[entry.first] = entry.second;
+    }
+}
+
+status_t LayerMetadata::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(static_cast<int>(mMap.size()));
+    status_t status = OK;
+    for (const auto& entry : mMap) {
+        status = parcel->writeUint32(entry.first);
+        if (status != OK) {
+            break;
+        }
+        status = parcel->writeByteVector(entry.second);
+        if (status != OK) {
+            break;
+        }
+    }
+    return status;
+}
+
+status_t LayerMetadata::readFromParcel(const Parcel* parcel) {
+    int size = parcel->readInt32();
+    status_t status = OK;
+    mMap.clear();
+    for (int i = 0; i < size; ++i) {
+        uint32_t key = parcel->readUint32();
+        status = parcel->readByteVector(&mMap[key]);
+        if (status != OK) {
+            break;
+        }
+    }
+    return status;
+}
+
+LayerMetadata& LayerMetadata::operator=(const LayerMetadata& other) {
+    mMap = other.mMap;
+    return *this;
+}
+
+LayerMetadata& LayerMetadata::operator=(LayerMetadata&& other) {
+    mMap = std::move(other.mMap);
+    return *this;
+}
+
+bool LayerMetadata::has(uint32_t key) const {
+    return mMap.count(key);
+}
+
+int32_t LayerMetadata::getInt32(uint32_t key, int32_t fallback) const {
+    if (!has(key)) return fallback;
+    const std::vector<uint8_t>& data = mMap.at(key);
+    if (data.size() < sizeof(uint32_t)) return fallback;
+    Parcel p;
+    p.setData(data.data(), data.size());
+    return p.readInt32();
+}
+
+void LayerMetadata::setInt32(uint32_t key, int32_t value) {
+    std::vector<uint8_t>& data = mMap[key];
+    Parcel p;
+    p.writeInt32(value);
+    data.resize(p.dataSize());
+    memcpy(data.data(), p.data(), p.dataSize());
+}
+
+std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
+    if (!has(key)) return std::string();
+    switch (key) {
+        case METADATA_OWNER_UID:
+            return StringPrintf("ownerUID%s%d", separator, getInt32(key, 0));
+        case METADATA_WINDOW_TYPE:
+            return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
+        case METADATA_TASK_ID:
+            return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
+        default:
+            return StringPrintf("%d%s%dbytes", key, separator,
+                                static_cast<int>(mMap.at(key).size()));
+    }
+}
+
+} // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 01acc2d..6091d3f 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "LayerState"
+
+#include <inttypes.h>
+
 #include <utils/Errors.h>
 #include <binder/Parcel.h>
 #include <gui/ISurfaceComposerClient.h>
@@ -25,7 +29,7 @@
 status_t layer_state_t::write(Parcel& output) const
 {
     output.writeStrongBinder(surface);
-    output.writeUint32(what);
+    output.writeUint64(what);
     output.writeFloat(x);
     output.writeFloat(y);
     output.writeInt32(z);
@@ -37,26 +41,73 @@
     output.writeUint32(mask);
     *reinterpret_cast<layer_state_t::matrix22_t *>(
             output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix;
-    output.write(crop);
-    output.write(finalCrop);
-    output.writeStrongBinder(barrierHandle);
+    output.write(crop_legacy);
+    output.writeStrongBinder(barrierHandle_legacy);
     output.writeStrongBinder(reparentHandle);
-    output.writeUint64(frameNumber);
+    output.writeUint64(frameNumber_legacy);
     output.writeInt32(overrideScalingMode);
-    output.writeStrongBinder(IInterface::asBinder(barrierGbp));
+    output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy));
     output.writeStrongBinder(relativeLayerHandle);
     output.writeStrongBinder(parentHandleForChild);
     output.writeFloat(color.r);
     output.writeFloat(color.g);
     output.writeFloat(color.b);
+#ifndef NO_INPUT
+    inputInfo.write(output);
+#endif
     output.write(transparentRegion);
+    output.writeUint32(transform);
+    output.writeBool(transformToDisplayInverse);
+    output.write(crop);
+    output.write(frame);
+    if (buffer) {
+        output.writeBool(true);
+        output.write(*buffer);
+    } else {
+        output.writeBool(false);
+    }
+    if (acquireFence) {
+        output.writeBool(true);
+        output.write(*acquireFence);
+    } else {
+        output.writeBool(false);
+    }
+    output.writeUint32(static_cast<uint32_t>(dataspace));
+    output.write(hdrMetadata);
+    output.write(surfaceDamageRegion);
+    output.writeInt32(api);
+    if (sidebandStream) {
+        output.writeBool(true);
+        output.writeNativeHandle(sidebandStream->handle());
+    } else {
+        output.writeBool(false);
+    }
+
+    memcpy(output.writeInplace(16 * sizeof(float)),
+           colorTransform.asArray(), 16 * sizeof(float));
+    output.writeFloat(cornerRadius);
+
+    if (output.writeVectorSize(listenerCallbacks) == NO_ERROR) {
+        for (const auto& [listener, callbackIds] : listenerCallbacks) {
+            output.writeStrongBinder(IInterface::asBinder(listener));
+            output.writeInt64Vector(callbackIds);
+        }
+    }
+
+    output.writeStrongBinder(cachedBuffer.token);
+    output.writeInt32(cachedBuffer.bufferId);
+    output.writeParcelable(metadata);
+
+    output.writeFloat(colorAlpha);
+    output.writeUint32(static_cast<uint32_t>(colorDataspace));
+
     return NO_ERROR;
 }
 
 status_t layer_state_t::read(const Parcel& input)
 {
     surface = input.readStrongBinder();
-    what = input.readUint32();
+    what = input.readUint64();
     x = input.readFloat();
     y = input.readFloat();
     z = input.readInt32();
@@ -72,20 +123,61 @@
     } else {
         return BAD_VALUE;
     }
-    input.read(crop);
-    input.read(finalCrop);
-    barrierHandle = input.readStrongBinder();
+    input.read(crop_legacy);
+    barrierHandle_legacy = input.readStrongBinder();
     reparentHandle = input.readStrongBinder();
-    frameNumber = input.readUint64();
+    frameNumber_legacy = input.readUint64();
     overrideScalingMode = input.readInt32();
-    barrierGbp =
-        interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
+    barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
     relativeLayerHandle = input.readStrongBinder();
     parentHandleForChild = input.readStrongBinder();
     color.r = input.readFloat();
     color.g = input.readFloat();
     color.b = input.readFloat();
+
+#ifndef NO_INPUT
+    inputInfo = InputWindowInfo::read(input);
+#endif
+
     input.read(transparentRegion);
+    transform = input.readUint32();
+    transformToDisplayInverse = input.readBool();
+    input.read(crop);
+    input.read(frame);
+    buffer = new GraphicBuffer();
+    if (input.readBool()) {
+        input.read(*buffer);
+    }
+    acquireFence = new Fence();
+    if (input.readBool()) {
+        input.read(*acquireFence);
+    }
+    dataspace = static_cast<ui::Dataspace>(input.readUint32());
+    input.read(hdrMetadata);
+    input.read(surfaceDamageRegion);
+    api = input.readInt32();
+    if (input.readBool()) {
+        sidebandStream = NativeHandle::create(input.readNativeHandle(), true);
+    }
+
+    colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
+    cornerRadius = input.readFloat();
+
+    int32_t listenersSize = input.readInt32();
+    for (int32_t i = 0; i < listenersSize; i++) {
+        auto listener = interface_cast<ITransactionCompletedListener>(input.readStrongBinder());
+        std::vector<CallbackId> callbackIds;
+        input.readInt64Vector(&callbackIds);
+        listenerCallbacks.emplace_back(listener, callbackIds);
+    }
+
+    cachedBuffer.token = input.readStrongBinder();
+    cachedBuffer.bufferId = input.readInt32();
+    input.readParcelable(&metadata);
+
+    colorAlpha = input.readFloat();
+    colorDataspace = static_cast<ui::Dataspace>(input.readUint32());
+
     return NO_ERROR;
 }
 
@@ -194,19 +286,19 @@
         what |= eLayerStackChanged;
         layerStack = other.layerStack;
     }
-    if (other.what & eCropChanged) {
-        what |= eCropChanged;
-        crop = other.crop;
+    if (other.what & eCropChanged_legacy) {
+        what |= eCropChanged_legacy;
+        crop_legacy = other.crop_legacy;
     }
-    if (other.what & eDeferTransaction) {
-        what |= eDeferTransaction;
-        barrierHandle = other.barrierHandle;
-        barrierGbp = other.barrierGbp;
-        frameNumber = other.frameNumber;
+    if (other.what & eCornerRadiusChanged) {
+        what |= eCornerRadiusChanged;
+        cornerRadius = other.cornerRadius;
     }
-    if (other.what & eFinalCropChanged) {
-        what |= eFinalCropChanged;
-        finalCrop = other.finalCrop;
+    if (other.what & eDeferTransaction_legacy) {
+        what |= eDeferTransaction_legacy;
+        barrierHandle_legacy = other.barrierHandle_legacy;
+        barrierGbp_legacy = other.barrierGbp_legacy;
+        frameNumber_legacy = other.frameNumber_legacy;
     }
     if (other.what & eOverrideScalingModeChanged) {
         what |= eOverrideScalingModeChanged;
@@ -234,6 +326,119 @@
     if (other.what & eDestroySurface) {
         what |= eDestroySurface;
     }
+    if (other.what & eTransformChanged) {
+        what |= eTransformChanged;
+        transform = other.transform;
+    }
+    if (other.what & eTransformToDisplayInverseChanged) {
+        what |= eTransformToDisplayInverseChanged;
+        transformToDisplayInverse = other.transformToDisplayInverse;
+    }
+    if (other.what & eCropChanged) {
+        what |= eCropChanged;
+        crop = other.crop;
+    }
+    if (other.what & eFrameChanged) {
+        what |= eFrameChanged;
+        frame = other.frame;
+    }
+    if (other.what & eBufferChanged) {
+        what |= eBufferChanged;
+        buffer = other.buffer;
+    }
+    if (other.what & eAcquireFenceChanged) {
+        what |= eAcquireFenceChanged;
+        acquireFence = other.acquireFence;
+    }
+    if (other.what & eDataspaceChanged) {
+        what |= eDataspaceChanged;
+        dataspace = other.dataspace;
+    }
+    if (other.what & eHdrMetadataChanged) {
+        what |= eHdrMetadataChanged;
+        hdrMetadata = other.hdrMetadata;
+    }
+    if (other.what & eSurfaceDamageRegionChanged) {
+        what |= eSurfaceDamageRegionChanged;
+        surfaceDamageRegion = other.surfaceDamageRegion;
+    }
+    if (other.what & eApiChanged) {
+        what |= eApiChanged;
+        api = other.api;
+    }
+    if (other.what & eSidebandStreamChanged) {
+        what |= eSidebandStreamChanged;
+        sidebandStream = other.sidebandStream;
+    }
+    if (other.what & eColorTransformChanged) {
+        what |= eColorTransformChanged;
+        colorTransform = other.colorTransform;
+    }
+    if (other.what & eListenerCallbacksChanged) {
+        what |= eListenerCallbacksChanged;
+        listenerCallbacks = other.listenerCallbacks;
+    }
+
+#ifndef NO_INPUT
+    if (other.what & eInputInfoChanged) {
+        what |= eInputInfoChanged;
+        inputInfo = other.inputInfo;
+    }
+#endif
+
+    if (other.what & eCachedBufferChanged) {
+        what |= eCachedBufferChanged;
+        cachedBuffer = other.cachedBuffer;
+    }
+    if (other.what & eColorAlphaChanged) {
+        what |= eColorAlphaChanged;
+        colorAlpha = other.colorAlpha;
+    }
+    if (other.what & eColorDataspaceChanged) {
+        what |= eColorDataspaceChanged;
+        colorDataspace = other.colorDataspace;
+    }
+    if (other.what & eMetadataChanged) {
+        what |= eMetadataChanged;
+        metadata.merge(other.metadata);
+    }
+    if ((other.what & what) != other.what) {
+        ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
+              "other.what=0x%" PRIu64 " what=0x%" PRIu64,
+              other.what, what);
+    }
+}
+
+// ------------------------------- InputWindowCommands ----------------------------------------
+
+void InputWindowCommands::merge(const InputWindowCommands& other) {
+    transferTouchFocusCommands
+            .insert(transferTouchFocusCommands.end(),
+                    std::make_move_iterator(other.transferTouchFocusCommands.begin()),
+                    std::make_move_iterator(other.transferTouchFocusCommands.end()));
+}
+
+void InputWindowCommands::clear() {
+    transferTouchFocusCommands.clear();
+}
+
+void InputWindowCommands::write(Parcel& output) const {
+    output.writeUint32(static_cast<uint32_t>(transferTouchFocusCommands.size()));
+    for (const auto& transferTouchFocusCommand : transferTouchFocusCommands) {
+        output.writeStrongBinder(transferTouchFocusCommand.fromToken);
+        output.writeStrongBinder(transferTouchFocusCommand.toToken);
+    }
+}
+
+void InputWindowCommands::read(const Parcel& input) {
+    size_t count = input.readUint32();
+    transferTouchFocusCommands.clear();
+    for (size_t i = 0; i < count; i++) {
+        TransferTouchFocusCommand transferTouchFocusCommand;
+        transferTouchFocusCommand.fromToken = input.readStrongBinder();
+        transferTouchFocusCommand.toToken = input.readStrongBinder();
+        transferTouchFocusCommands.emplace_back(transferTouchFocusCommand);
+    }
 }
 
 }; // namespace android
diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp
index 52c9067..2f8e104 100644
--- a/libs/gui/StreamSplitter.cpp
+++ b/libs/gui/StreamSplitter.cpp
@@ -38,11 +38,11 @@
 status_t StreamSplitter::createSplitter(
         const sp<IGraphicBufferConsumer>& inputQueue,
         sp<StreamSplitter>* outSplitter) {
-    if (inputQueue == NULL) {
+    if (inputQueue == nullptr) {
         ALOGE("createSplitter: inputQueue must not be NULL");
         return BAD_VALUE;
     }
-    if (outSplitter == NULL) {
+    if (outSplitter == nullptr) {
         ALOGE("createSplitter: outSplitter must not be NULL");
         return BAD_VALUE;
     }
@@ -74,7 +74,7 @@
 
 status_t StreamSplitter::addOutput(
         const sp<IGraphicBufferProducer>& outputQueue) {
-    if (outputQueue == NULL) {
+    if (outputQueue == nullptr) {
         ALOGE("addOutput: outputQueue must not be NULL");
         return BAD_VALUE;
     }
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 2de14c8..1f726b2 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -39,9 +39,6 @@
 #include <gui/ISurfaceComposer.h>
 #include <private/gui/ComposerService.h>
 
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
-
 namespace android {
 
 using ui::ColorMode;
@@ -156,7 +153,7 @@
     ATRACE_CALL();
 
     DisplayStatInfo stats;
-    status_t result = composerService()->getDisplayStats(NULL, &stats);
+    status_t result = composerService()->getDisplayStats(nullptr, &stats);
     if (result != NO_ERROR) {
         return result;
     }
@@ -321,41 +318,14 @@
     return NO_ERROR;
 }
 
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-
 status_t Surface::getWideColorSupport(bool* supported) {
     ATRACE_CALL();
 
     sp<IBinder> display(
         composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
-    Vector<ColorMode> colorModes;
-    status_t err =
-        composerService()->getDisplayColorModes(display, &colorModes);
-
-    if (err)
-        return err;
-
-    bool wideColorBoardConfig =
-        getBool<ISurfaceFlingerConfigs,
-                &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
-
     *supported = false;
-    for (ColorMode colorMode : colorModes) {
-        switch (colorMode) {
-            case ColorMode::DISPLAY_P3:
-            case ColorMode::ADOBE_RGB:
-            case ColorMode::DCI_P3:
-                if (wideColorBoardConfig) {
-                    *supported = true;
-                }
-                break;
-            default:
-                break;
-        }
-    }
-
-    return NO_ERROR;
+    status_t error = composerService()->isWideColorDisplay(display, supported);
+    return error;
 }
 
 status_t Surface::getHdrSupport(bool* supported) {
@@ -501,7 +471,7 @@
         if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
                 BufferItem::INVALID_BUFFER_SLOT) {
             sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
-            if (gbuf != NULL) {
+            if (gbuf != nullptr) {
                 *buffer = gbuf.get();
                 *fenceFd = -1;
                 return OK;
@@ -541,7 +511,7 @@
     sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
 
     // this should never happen
-    ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf);
+    ALOGE_IF(fence == nullptr, "Surface::dequeueBuffer: received null Fence! buf=%d", buf);
 
     if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
         freeAllBuffers();
@@ -619,7 +589,7 @@
 int Surface::getSlotFromBufferLocked(
         android_native_buffer_t* buffer) const {
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
-        if (mSlots[i].buffer != NULL &&
+        if (mSlots[i].buffer != nullptr &&
                 mSlots[i].buffer->handle == buffer->handle) {
             return i;
         }
@@ -965,6 +935,9 @@
     case NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA:
         res = dispatchSetBuffersCta8613Metadata(args);
         break;
+    case NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA:
+        res = dispatchSetBuffersHdr10PlusMetadata(args);
+        break;
     case NATIVE_WINDOW_SET_SURFACE_DAMAGE:
         res = dispatchSetSurfaceDamage(args);
         break;
@@ -1120,6 +1093,12 @@
     return setBuffersCta8613Metadata(metadata);
 }
 
+int Surface::dispatchSetBuffersHdr10PlusMetadata(va_list args) {
+    const size_t size = va_arg(args, size_t);
+    const uint8_t* metadata = va_arg(args, const uint8_t*);
+    return setBuffersHdr10PlusMetadata(size, metadata);
+}
+
 int Surface::dispatchSetSurfaceDamage(va_list args) {
     android_native_rect_t* rects = va_arg(args, android_native_rect_t*);
     size_t numRects = va_arg(args, size_t);
@@ -1268,7 +1247,7 @@
     ATRACE_CALL();
     ALOGV("Surface::detachNextBuffer");
 
-    if (outBuffer == NULL || outFence == NULL) {
+    if (outBuffer == nullptr || outFence == nullptr) {
         return BAD_VALUE;
     }
 
@@ -1277,8 +1256,8 @@
         mRemovedBuffers.clear();
     }
 
-    sp<GraphicBuffer> buffer(NULL);
-    sp<Fence> fence(NULL);
+    sp<GraphicBuffer> buffer(nullptr);
+    sp<Fence> fence(nullptr);
     status_t result = mGraphicBufferProducer->detachNextBuffer(
             &buffer, &fence);
     if (result != NO_ERROR) {
@@ -1286,19 +1265,19 @@
     }
 
     *outBuffer = buffer;
-    if (fence != NULL && fence->isValid()) {
+    if (fence != nullptr && fence->isValid()) {
         *outFence = fence;
     } else {
         *outFence = Fence::NO_FENCE;
     }
 
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
-        if (mSlots[i].buffer != NULL &&
+        if (mSlots[i].buffer != nullptr &&
                 mSlots[i].buffer->getId() == buffer->getId()) {
             if (mReportRemovedBuffers) {
                 mRemovedBuffers.push_back(mSlots[i].buffer);
             }
-            mSlots[i].buffer = NULL;
+            mSlots[i].buffer = nullptr;
         }
     }
 
@@ -1349,7 +1328,7 @@
     ATRACE_CALL();
 
     Rect realRect(Rect::EMPTY_RECT);
-    if (rect == NULL || rect->isEmpty()) {
+    if (rect == nullptr || rect->isEmpty()) {
         realRect.clear();
     } else {
         realRect = *rect;
@@ -1568,6 +1547,19 @@
     return NO_ERROR;
 }
 
+int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) {
+    ALOGV("Surface::setBuffersBlobMetadata");
+    Mutex::Autolock lock(mMutex);
+    if (size > 0) {
+        mHdrMetadata.hdr10plus.assign(metadata, metadata + size);
+        mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS;
+    } else {
+        mHdrMetadata.validTypes &= ~HdrMetadata::HDR10PLUS;
+        mHdrMetadata.hdr10plus.clear();
+    }
+    return NO_ERROR;
+}
+
 Dataspace Surface::getBuffersDataSpace() {
     ALOGV("Surface::getBuffersDataSpace");
     Mutex::Autolock lock(mMutex);
@@ -1576,7 +1568,7 @@
 
 void Surface::freeAllBuffers() {
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
-        mSlots[i].buffer = 0;
+        mSlots[i].buffer = nullptr;
     }
 }
 
@@ -1616,12 +1608,12 @@
     // src and dst with, height and format must be identical. no verification
     // is done here.
     status_t err;
-    uint8_t* src_bits = NULL;
+    uint8_t* src_bits = nullptr;
     err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(),
             reinterpret_cast<void**>(&src_bits));
     ALOGE_IF(err, "error locking src buffer %s", strerror(-err));
 
-    uint8_t* dst_bits = NULL;
+    uint8_t* dst_bits = nullptr;
     err = dst->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(),
             reinterpret_cast<void**>(&dst_bits), *dstFenceFd);
     ALOGE_IF(err, "error locking dst buffer %s", strerror(-err));
@@ -1669,7 +1661,7 @@
 status_t Surface::lock(
         ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
 {
-    if (mLockedBuffer != 0) {
+    if (mLockedBuffer != nullptr) {
         ALOGE("Surface::lock failed, already locked");
         return INVALID_OPERATION;
     }
@@ -1701,7 +1693,7 @@
 
         // figure out if we can copy the frontbuffer back
         const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
-        const bool canCopyBack = (frontBuffer != 0 &&
+        const bool canCopyBack = (frontBuffer != nullptr &&
                 backBuffer->width  == frontBuffer->width &&
                 backBuffer->height == frontBuffer->height &&
                 backBuffer->format == frontBuffer->format);
@@ -1763,7 +1755,7 @@
 
 status_t Surface::unlockAndPost()
 {
-    if (mLockedBuffer == 0) {
+    if (mLockedBuffer == nullptr) {
         ALOGE("Surface::unlockAndPost failed, no locked buffer");
         return INVALID_OPERATION;
     }
@@ -1777,7 +1769,7 @@
             mLockedBuffer->handle, strerror(-err));
 
     mPostedBuffer = mLockedBuffer;
-    mLockedBuffer = 0;
+    mLockedBuffer = nullptr;
     return err;
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index f3c6fd2..c712bde 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -25,7 +25,9 @@
 #include <utils/String8.h>
 #include <utils/threads.h>
 
+#include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
 
 #include <system/graphics.h>
 
@@ -40,6 +42,10 @@
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 
+#ifndef NO_INPUT
+#include <input/InputWindow.h>
+#endif
+
 #include <private/gui/ComposerService.h>
 
 namespace android {
@@ -60,7 +66,7 @@
     while (getService(name, &mComposerService) != NO_ERROR) {
         usleep(250000);
     }
-    assert(mComposerService != NULL);
+    assert(mComposerService != nullptr);
 
     // Create the death listener.
     class DeathObserver : public IBinder::DeathRecipient {
@@ -81,9 +87,9 @@
 /*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() {
     ComposerService& instance = ComposerService::getInstance();
     Mutex::Autolock _l(instance.mLock);
-    if (instance.mComposerService == NULL) {
+    if (instance.mComposerService == nullptr) {
         ComposerService::getInstance().connectLocked();
-        assert(instance.mComposerService != NULL);
+        assert(instance.mComposerService != nullptr);
         ALOGD("ComposerService reconnected");
     }
     return instance.mComposerService;
@@ -92,19 +98,147 @@
 void ComposerService::composerServiceDied()
 {
     Mutex::Autolock _l(mLock);
-    mComposerService = NULL;
-    mDeathObserver = NULL;
+    mComposerService = nullptr;
+    mDeathObserver = nullptr;
+}
+
+class DefaultComposerClient: public Singleton<DefaultComposerClient> {
+    Mutex mLock;
+    sp<SurfaceComposerClient> mClient;
+    friend class Singleton<ComposerService>;
+public:
+    static sp<SurfaceComposerClient> getComposerClient() {
+        DefaultComposerClient& dc = DefaultComposerClient::getInstance();
+        Mutex::Autolock _l(dc.mLock);
+        if (dc.mClient == nullptr) {
+            dc.mClient = new SurfaceComposerClient;
+        }
+        return dc.mClient;
+    }
+};
+ANDROID_SINGLETON_STATIC_INSTANCE(DefaultComposerClient);
+
+
+sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() {
+    return DefaultComposerClient::getComposerClient();
 }
 
 // ---------------------------------------------------------------------------
 
-SurfaceComposerClient::Transaction::Transaction(const Transaction& other) :
-    mForceSynchronous(other.mForceSynchronous),
-    mTransactionNestCount(other.mTransactionNestCount),
-    mAnimation(other.mAnimation),
-    mEarlyWakeup(other.mEarlyWakeup) {
+// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
+// to be able to return a sp<> to its instance to pass to SurfaceFlinger.
+// ANDROID_SINGLETON_STATIC_INSTANCE only allows a reference to an instance.
+
+// 0 is an invalid callback id
+TransactionCompletedListener::TransactionCompletedListener() : mCallbackIdCounter(1) {}
+
+CallbackId TransactionCompletedListener::getNextIdLocked() {
+    return mCallbackIdCounter++;
+}
+
+sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() {
+    static sp<TransactionCompletedListener> sInstance = new TransactionCompletedListener;
+    return sInstance;
+}
+
+sp<ITransactionCompletedListener> TransactionCompletedListener::getIInstance() {
+    return static_cast<sp<ITransactionCompletedListener>>(getInstance());
+}
+
+void TransactionCompletedListener::startListeningLocked() {
+    if (mListening) {
+        return;
+    }
+    ProcessState::self()->startThreadPool();
+    mListening = true;
+}
+
+CallbackId TransactionCompletedListener::addCallbackFunction(
+        const TransactionCompletedCallback& callbackFunction,
+        const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
+                surfaceControls) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    startListeningLocked();
+
+    CallbackId callbackId = getNextIdLocked();
+    mCallbacks[callbackId].callbackFunction = callbackFunction;
+
+    auto& callbackSurfaceControls = mCallbacks[callbackId].surfaceControls;
+
+    for (const auto& surfaceControl : surfaceControls) {
+        callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl;
+    }
+
+    return callbackId;
+}
+
+void TransactionCompletedListener::addSurfaceControlToCallbacks(
+        const sp<SurfaceControl>& surfaceControl,
+        const std::unordered_set<CallbackId>& callbackIds) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    for (auto callbackId : callbackIds) {
+        mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct,
+                                                       std::forward_as_tuple(
+                                                               surfaceControl->getHandle()),
+                                                       std::forward_as_tuple(surfaceControl));
+    }
+}
+
+void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
+    std::lock_guard lock(mMutex);
+
+    /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered
+     * callbackIds, except for when Transactions are merged together. This probably cannot be
+     * solved before this point because the Transactions could be merged together and applied in a
+     * different process.
+     *
+     * Fortunately, we get all the callbacks for this listener for the same frame together at the
+     * same time. This means if any Transactions were merged together, we will get their callbacks
+     * at the same time. We can combine all the sp<IBinder> to sp<SurfaceControl> maps for all the
+     * callbackIds to generate one super map that contains all the sp<IBinder> to sp<SurfaceControl>
+     * that could possibly exist for the callbacks.
+     */
+    std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
+    for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+        for (auto callbackId : callbackIds) {
+            auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+            surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
+        }
+    }
+
+    for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+        for (auto callbackId : callbackIds) {
+            auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+            if (!callbackFunction) {
+                ALOGE("cannot call null callback function, skipping");
+                continue;
+            }
+            std::vector<SurfaceControlStats> surfaceControlStats;
+            for (const auto& surfaceStats : transactionStats.surfaceStats) {
+                surfaceControlStats.emplace_back(surfaceControls[surfaceStats.surfaceControl],
+                                                 surfaceStats.acquireTime,
+                                                 surfaceStats.previousReleaseFence);
+            }
+
+            callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
+                             surfaceControlStats);
+            mCallbacks.erase(callbackId);
+        }
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
+      : mForceSynchronous(other.mForceSynchronous),
+        mTransactionNestCount(other.mTransactionNestCount),
+        mAnimation(other.mAnimation),
+        mEarlyWakeup(other.mEarlyWakeup),
+        mDesiredPresentTime(other.mDesiredPresentTime) {
     mDisplayStates = other.mDisplayStates;
     mComposerStates = other.mComposerStates;
+    mInputWindowCommands = other.mInputWindowCommands;
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
@@ -127,9 +261,38 @@
     }
     other.mDisplayStates.clear();
 
+    for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) {
+        auto& [callbackIds, surfaceControls] = callbackInfo;
+        mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
+                                                                callbackIds.begin()),
+                                                        std::make_move_iterator(callbackIds.end()));
+        mListenerCallbacks[listener]
+                .surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()),
+                                        std::make_move_iterator(surfaceControls.end()));
+    }
+    other.mListenerCallbacks.clear();
+
+    mInputWindowCommands.merge(other.mInputWindowCommands);
+
     return *this;
 }
 
+void SurfaceComposerClient::doDropReferenceTransaction(const sp<IBinder>& handle,
+        const sp<ISurfaceComposerClient>& client) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    Vector<ComposerState> composerStates;
+    Vector<DisplayState> displayStates;
+
+    ComposerState s;
+    s.client = client;
+    s.state.surface = handle;
+    s.state.what |= layer_state_t::eReparent;
+    s.state.parentHandleForChild = nullptr;
+
+    composerStates.add(s);
+    sf->setTransactionState(composerStates, displayStates, 0, nullptr, {}, -1);
+}
+
 status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
     if (mStatus != NO_ERROR) {
         return mStatus;
@@ -137,6 +300,32 @@
 
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
 
+    // For every listener with registered callbacks
+    for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
+        auto& [callbackIds, surfaceControls] = callbackInfo;
+        if (callbackIds.empty()) {
+            continue;
+        }
+
+        // If the listener does not have any SurfaceControls set on this Transaction, send the
+        // callback now
+        if (surfaceControls.empty()) {
+            listener->onTransactionCompleted(ListenerStats::createEmpty(listener, callbackIds));
+        }
+
+        // If the listener has any SurfaceControls set on this Transaction update the surface state
+        for (const auto& surfaceControl : surfaceControls) {
+            layer_state_t* s = getLayerState(surfaceControl);
+            if (!s) {
+                ALOGE("failed to get layer state");
+                continue;
+            }
+            s->what |= layer_state_t::eListenerCallbacksChanged;
+            s->listenerCallbacks.emplace_back(listener, std::move(callbackIds));
+        }
+    }
+    mListenerCallbacks.clear();
+
     Vector<ComposerState> composerStates;
     Vector<DisplayState> displayStates;
     uint32_t flags = 0;
@@ -166,7 +355,10 @@
     mAnimation = false;
     mEarlyWakeup = false;
 
-    sf->setTransactionState(composerStates, displayStates, flags);
+    sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
+                            mDesiredPresentTime);
+    mInputWindowCommands.clear();
     mStatus = NO_ERROR;
     return NO_ERROR;
 }
@@ -206,6 +398,15 @@
     return &(mComposerStates[sc].state);
 }
 
+void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
+        const sp<SurfaceControl>& sc) {
+    auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()];
+    callbackInfo.surfaceControls.insert(sc);
+
+    TransactionCompletedListener::getInstance()
+            ->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds);
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
         const sp<SurfaceControl>& sc, float x, float y) {
     layer_state_t* s = getLayerState(sc);
@@ -216,6 +417,8 @@
     s->what |= layer_state_t::ePositionChanged;
     s->x = x;
     s->y = y;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -240,9 +443,7 @@
     s->w = w;
     s->h = h;
 
-    // Resizing a surface makes the transaction synchronous.
-    mForceSynchronous = true;
-
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -255,6 +456,8 @@
     }
     s->what |= layer_state_t::eLayerChanged;
     s->z = z;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -267,6 +470,8 @@
     s->what |= layer_state_t::eRelativeLayerChanged;
     s->relativeLayerHandle = relativeTo;
     s->z = z;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -286,6 +491,8 @@
     s->flags &= ~mask;
     s->flags |= (flags & mask);
     s->mask |= mask;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -299,6 +506,8 @@
     }
     s->what |= layer_state_t::eTransparentRegionChanged;
     s->transparentRegion = transparentRegion;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -311,6 +520,8 @@
     }
     s->what |= layer_state_t::eAlphaChanged;
     s->alpha = alpha;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -323,6 +534,22 @@
     }
     s->what |= layer_state_t::eLayerStackChanged;
     s->layerStack = layerStack;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMetadata(
+        const sp<SurfaceControl>& sc, uint32_t key, std::vector<uint8_t> data) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eMetadataChanged;
+    s->metadata.mMap[key] = std::move(data);
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -341,57 +568,68 @@
     matrix.dsdy = dsdy;
     matrix.dtdy = dtdy;
     s->matrix = matrix;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop_legacy(
         const sp<SurfaceControl>& sc, const Rect& crop) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eCropChanged;
-    s->crop = crop;
+    s->what |= layer_state_t::eCropChanged_legacy;
+    s->crop_legacy = crop;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFinalCrop(const sp<SurfaceControl>& sc, const Rect& crop) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCornerRadius(
+        const sp<SurfaceControl>& sc, float cornerRadius) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eFinalCropChanged;
-    s->finalCrop = crop;
+    s->what |= layer_state_t::eCornerRadiusChanged;
+    s->cornerRadius = cornerRadius;
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil(
-        const sp<SurfaceControl>& sc,
-        const sp<IBinder>& handle, uint64_t frameNumber) {
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
+                                                                 const sp<IBinder>& handle,
+                                                                 uint64_t frameNumber) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eDeferTransaction;
-    s->barrierHandle = handle;
-    s->frameNumber = frameNumber;
+    s->what |= layer_state_t::eDeferTransaction_legacy;
+    s->barrierHandle_legacy = handle;
+    s->frameNumber_legacy = frameNumber;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil(
-        const sp<SurfaceControl>& sc,
-        const sp<Surface>& barrierSurface, uint64_t frameNumber) {
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
+                                                                 const sp<Surface>& barrierSurface,
+                                                                 uint64_t frameNumber) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eDeferTransaction;
-    s->barrierGbp = barrierSurface->getIGraphicBufferProducer();
-    s->frameNumber = frameNumber;
+    s->what |= layer_state_t::eDeferTransaction_legacy;
+    s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer();
+    s->frameNumber_legacy = frameNumber;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -405,6 +643,8 @@
     }
     s->what |= layer_state_t::eReparentChildren;
     s->reparentHandle = newParentHandle;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -418,6 +658,8 @@
     }
     s->what |= layer_state_t::eReparent;
     s->parentHandleForChild = newParentHandle;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -431,6 +673,231 @@
     }
     s->what |= layer_state_t::eColorChanged;
     s->color = color;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorAlpha(
+        const sp<SurfaceControl>& sc, float alpha) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eColorAlphaChanged;
+    s->colorAlpha = alpha;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorDataspace(
+        const sp<SurfaceControl>& sc, ui::Dataspace dataspace) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eColorDataspaceChanged;
+    s->colorDataspace = dataspace;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTransform(
+        const sp<SurfaceControl>& sc, uint32_t transform) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eTransformChanged;
+    s->transform = transform;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
+                                                                 bool transformToDisplayInverse) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eTransformToDisplayInverseChanged;
+    s->transformToDisplayInverse = transformToDisplayInverse;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
+        const sp<SurfaceControl>& sc, const Rect& crop) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eCropChanged;
+    s->crop = crop;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame(
+        const sp<SurfaceControl>& sc, const Rect& frame) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eFrameChanged;
+    s->frame = frame;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
+        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eBufferChanged;
+    s->buffer = buffer;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachedBuffer(
+        const sp<SurfaceControl>& sc, int32_t bufferId) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eCachedBufferChanged;
+    s->cachedBuffer.token = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    s->cachedBuffer.bufferId = bufferId;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
+        const sp<SurfaceControl>& sc, const sp<Fence>& fence) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eAcquireFenceChanged;
+    s->acquireFence = fence;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace(
+        const sp<SurfaceControl>& sc, ui::Dataspace dataspace) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eDataspaceChanged;
+    s->dataspace = dataspace;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setHdrMetadata(
+        const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eHdrMetadataChanged;
+    s->hdrMetadata = hdrMetadata;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSurfaceDamageRegion(
+        const sp<SurfaceControl>& sc, const Region& surfaceDamageRegion) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eSurfaceDamageRegionChanged;
+    s->surfaceDamageRegion = surfaceDamageRegion;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApi(
+        const sp<SurfaceControl>& sc, int32_t api) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eApiChanged;
+    s->api = api;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSidebandStream(
+        const sp<SurfaceControl>& sc, const sp<NativeHandle>& sidebandStream) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eSidebandStreamChanged;
+    s->sidebandStream = sidebandStream;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
+        nsecs_t desiredPresentTime) {
+    mDesiredPresentTime = desiredPresentTime;
+    return *this;
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
+        TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+    auto listener = TransactionCompletedListener::getInstance();
+
+    auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1,
+                                         std::placeholders::_2, std::placeholders::_3);
+    const auto& surfaceControls =
+            mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls;
+
+    CallbackId callbackId = listener->addCallbackFunction(callbackWithContext, surfaceControls);
+
+    mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace(
+            callbackId);
     return *this;
 }
 
@@ -441,6 +908,8 @@
         mStatus = BAD_INDEX;
     }
     s->what |= layer_state_t::eDetachChildren;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -468,6 +937,8 @@
 
     s->what |= layer_state_t::eOverrideScalingModeChanged;
     s->overrideScalingMode = overrideScalingMode;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -479,17 +950,95 @@
         return *this;
     }
     s->what |= layer_state_t::eGeometryAppliesWithResize;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::destroySurface(
-        const sp<SurfaceControl>& sc) {
+#ifndef NO_INPUT
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
+        const sp<SurfaceControl>& sc,
+        const InputWindowInfo& info) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eDestroySurface;
+    s->inputInfo = info;
+    s->what |= layer_state_t::eInputInfoChanged;
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::transferTouchFocus(
+        const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+    InputWindowCommands::TransferTouchFocusCommand transferTouchFocusCommand;
+    transferTouchFocusCommand.fromToken = fromToken;
+    transferTouchFocusCommand.toToken = toToken;
+    mInputWindowCommands.transferTouchFocusCommands.emplace_back(transferTouchFocusCommand);
+    return *this;
+}
+
+#endif
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorTransform(
+    const sp<SurfaceControl>& sc, const mat3& matrix, const vec3& translation) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eColorTransformChanged;
+    s->colorTransform = mat4(matrix, translation);
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometry(
+        const sp<SurfaceControl>& sc, const Rect& source, const Rect& dst, int transform) {
+    setCrop_legacy(sc, source);
+
+    int x = dst.left;
+    int y = dst.top;
+    float xScale = dst.getWidth() / static_cast<float>(source.getWidth());
+    float yScale = dst.getHeight() / static_cast<float>(source.getHeight());
+    float matrix[4] = {1, 0, 0, 1};
+
+    switch (transform) {
+        case NATIVE_WINDOW_TRANSFORM_FLIP_H:
+            matrix[0] = -xScale; matrix[1] = 0;
+            matrix[2] = 0; matrix[3] = yScale;
+            x += source.getWidth();
+            break;
+        case NATIVE_WINDOW_TRANSFORM_FLIP_V:
+            matrix[0] = xScale; matrix[1] = 0;
+            matrix[2] = 0; matrix[3] = -yScale;
+            y += source.getHeight();
+            break;
+        case NATIVE_WINDOW_TRANSFORM_ROT_90:
+            matrix[0] = 0; matrix[1] = -yScale;
+            matrix[2] = xScale; matrix[3] = 0;
+            x += source.getHeight();
+            break;
+        case NATIVE_WINDOW_TRANSFORM_ROT_180:
+            matrix[0] = -xScale; matrix[1] = 0;
+            matrix[2] = 0; matrix[3] = -yScale;
+            x += source.getWidth();
+            y += source.getHeight();
+            break;
+        case NATIVE_WINDOW_TRANSFORM_ROT_270:
+            matrix[0] = 0; matrix[1] = yScale;
+            matrix[2] = -xScale; matrix[3] = 0;
+            y += source.getWidth();
+            break;
+        default:
+            matrix[0] = xScale; matrix[1] = 0;
+            matrix[2] = 0; matrix[3] = yScale;
+            break;
+    }
+    setMatrix(sc, matrix[0], matrix[1], matrix[2], matrix[3]);
+    setPosition(sc, x, y);
+
     return *this;
 }
 
@@ -559,11 +1108,6 @@
 {
 }
 
-SurfaceComposerClient::SurfaceComposerClient(const sp<IGraphicBufferProducer>& root)
-    : mStatus(NO_INIT), mParent(root)
-{
-}
-
 SurfaceComposerClient::SurfaceComposerClient(const sp<ISurfaceComposerClient>& client)
     : mStatus(NO_ERROR), mClient(client)
 {
@@ -571,12 +1115,10 @@
 
 void SurfaceComposerClient::onFirstRef() {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    if (sf != 0 && mStatus == NO_INIT) {
-        auto rootProducer = mParent.promote();
+    if (sf != nullptr && mStatus == NO_INIT) {
         sp<ISurfaceComposerClient> conn;
-        conn = (rootProducer != nullptr) ? sf->createScopedConnection(rootProducer) :
-                sf->createConnection();
-        if (conn != 0) {
+        conn = sf->createConnection();
+        if (conn != nullptr) {
             mClient = conn;
             mStatus = NO_ERROR;
         }
@@ -606,39 +1148,49 @@
     // this can be called more than once.
     sp<ISurfaceComposerClient> client;
     Mutex::Autolock _lm(mLock);
-    if (mClient != 0) {
+    if (mClient != nullptr) {
         client = mClient; // hold ref while lock is held
         mClient.clear();
     }
     mStatus = NO_INIT;
 }
 
-sp<SurfaceControl> SurfaceComposerClient::createSurface(
-        const String8& name,
-        uint32_t w,
-        uint32_t h,
-        PixelFormat format,
-        uint32_t flags,
-        SurfaceControl* parent,
-        int32_t windowType,
-        int32_t ownerUid)
-{
+sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
+                                                        PixelFormat format, uint32_t flags,
+                                                        SurfaceControl* parent,
+                                                        LayerMetadata metadata) {
     sp<SurfaceControl> s;
-    createSurfaceChecked(name, w, h, format, &s, flags, parent, windowType, ownerUid);
+    createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata));
     return s;
 }
 
-status_t SurfaceComposerClient::createSurfaceChecked(
-        const String8& name,
-        uint32_t w,
-        uint32_t h,
-        PixelFormat format,
-        sp<SurfaceControl>* outSurface,
-        uint32_t flags,
-        SurfaceControl* parent,
-        int32_t windowType,
-        int32_t ownerUid)
-{
+sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& name, uint32_t w,
+                                                                  uint32_t h, PixelFormat format,
+                                                                  uint32_t flags, Surface* parent,
+                                                                  LayerMetadata metadata) {
+    sp<SurfaceControl> sur;
+    status_t err = mStatus;
+
+    if (mStatus == NO_ERROR) {
+        sp<IBinder> handle;
+        sp<IGraphicBufferProducer> parentGbp = parent->getIGraphicBufferProducer();
+        sp<IGraphicBufferProducer> gbp;
+
+        err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp,
+                                               std::move(metadata), &handle, &gbp);
+        ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err));
+        if (err == NO_ERROR) {
+            return new SurfaceControl(this, handle, gbp, true /* owned */);
+        }
+    }
+    return nullptr;
+}
+
+status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
+                                                     PixelFormat format,
+                                                     sp<SurfaceControl>* outSurface, uint32_t flags,
+                                                     SurfaceControl* parent,
+                                                     LayerMetadata metadata) {
     sp<SurfaceControl> sur;
     status_t err = mStatus;
 
@@ -650,8 +1202,9 @@
         if (parent != nullptr) {
             parentHandle = parent->getHandle();
         }
-        err = mClient->createSurface(name, w, h, format, flags, parentHandle,
-                windowType, ownerUid, &handle, &gbp);
+
+        err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
+                                     &handle, &gbp);
         ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
         if (err == NO_ERROR) {
             *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
@@ -660,13 +1213,6 @@
     return err;
 }
 
-status_t SurfaceComposerClient::destroySurface(const sp<IBinder>& sid) {
-    if (mStatus != NO_ERROR)
-        return mStatus;
-    status_t err = mClient->destroySurface(sid);
-    return err;
-}
-
 status_t SurfaceComposerClient::clearLayerFrameStats(const sp<IBinder>& token) const {
     if (mStatus != NO_ERROR) {
         return mStatus;
@@ -684,6 +1230,26 @@
 
 // ----------------------------------------------------------------------------
 
+status_t SurfaceComposerClient::cacheBuffer(const sp<GraphicBuffer>& buffer, int32_t* outBufferId) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    if (buffer == nullptr || outBufferId == nullptr) {
+        return BAD_VALUE;
+    }
+    return sf->cacheBuffer(IInterface::asBinder(TransactionCompletedListener::getIInstance()),
+                           buffer, outBufferId);
+}
+
+status_t SurfaceComposerClient::uncacheBuffer(int32_t bufferId) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    if (bufferId < 0) {
+        return BAD_VALUE;
+    }
+    return sf->uncacheBuffer(IInterface::asBinder(TransactionCompletedListener::getIInstance()),
+                             bufferId);
+}
+
+// ----------------------------------------------------------------------------
+
 status_t SurfaceComposerClient::enableVSyncInjections(bool enable) {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
     return sf->enableVSyncInjections(enable);
@@ -718,10 +1284,6 @@
     return NO_ERROR;
 }
 
-status_t SurfaceComposerClient::getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) {
-    return ComposerService::getComposerService()->getDisplayViewport(display, outViewport);
-}
-
 int SurfaceComposerClient::getActiveConfig(const sp<IBinder>& display) {
     return ComposerService::getComposerService()->getActiveConfig(display);
 }
@@ -735,6 +1297,11 @@
     return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes);
 }
 
+status_t SurfaceComposerClient::getDisplayNativePrimaries(const sp<IBinder>& display,
+        ui::DisplayPrimaries& outPrimaries) {
+    return ComposerService::getComposerService()->getDisplayNativePrimaries(display, outPrimaries);
+}
+
 ColorMode SurfaceComposerClient::getActiveColorMode(const sp<IBinder>& display) {
     return ComposerService::getComposerService()->getActiveColorMode(display);
 }
@@ -749,6 +1316,20 @@
     ComposerService::getComposerService()->setPowerMode(token, mode);
 }
 
+status_t SurfaceComposerClient::getCompositionPreference(
+        ui::Dataspace* defaultDataspace, ui::PixelFormat* defaultPixelFormat,
+        ui::Dataspace* wideColorGamutDataspace, ui::PixelFormat* wideColorGamutPixelFormat) {
+    return ComposerService::getComposerService()
+            ->getCompositionPreference(defaultDataspace, defaultPixelFormat,
+                                       wideColorGamutDataspace, wideColorGamutPixelFormat);
+}
+
+bool SurfaceComposerClient::getProtectedContentSupport() {
+    bool supported = false;
+    ComposerService::getComposerService()->getProtectedContentSupport(&supported);
+    return supported;
+}
+
 status_t SurfaceComposerClient::clearAnimationFrameStats() {
     return ComposerService::getComposerService()->clearAnimationFrameStats();
 }
@@ -763,16 +1344,46 @@
             outCapabilities);
 }
 
+status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+                                                                      ui::PixelFormat* outFormat,
+                                                                      ui::Dataspace* outDataspace,
+                                                                      uint8_t* outComponentMask) {
+    return ComposerService::getComposerService()
+            ->getDisplayedContentSamplingAttributes(display, outFormat, outDataspace,
+                                                    outComponentMask);
+}
+
+status_t SurfaceComposerClient::setDisplayContentSamplingEnabled(const sp<IBinder>& display,
+                                                                 bool enable, uint8_t componentMask,
+                                                                 uint64_t maxFrames) {
+    return ComposerService::getComposerService()->setDisplayContentSamplingEnabled(display, enable,
+                                                                                   componentMask,
+                                                                                   maxFrames);
+}
+
+status_t SurfaceComposerClient::getDisplayedContentSample(const sp<IBinder>& display,
+                                                          uint64_t maxFrames, uint64_t timestamp,
+                                                          DisplayedFrameStats* outStats) {
+    return ComposerService::getComposerService()->getDisplayedContentSample(display, maxFrames,
+                                                                            timestamp, outStats);
+}
+
+status_t SurfaceComposerClient::isWideColorDisplay(const sp<IBinder>& display,
+                                                   bool* outIsWideColorDisplay) {
+    return ComposerService::getComposerService()->isWideColorDisplay(display,
+                                                                     outIsWideColorDisplay);
+}
+
 // ----------------------------------------------------------------------------
 
-status_t ScreenshotClient::capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth,
-                                   uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ,
-                                   bool useIdentityTransform, uint32_t rotation,
-                                   sp<GraphicBuffer>* outBuffer) {
+status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
+                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+                                   uint32_t rotation, sp<GraphicBuffer>* outBuffer) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
-    if (s == NULL) return NO_INIT;
-    status_t ret = s->captureScreen(display, outBuffer, sourceCrop, reqWidth, reqHeight, minLayerZ,
-                                    maxLayerZ, useIdentityTransform,
+    if (s == nullptr) return NO_INIT;
+    status_t ret = s->captureScreen(display, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop,
+                                    reqWidth, reqHeight, useIdentityTransform,
                                     static_cast<ISurfaceComposer::Rotation>(rotation));
     if (ret != NO_ERROR) {
         return ret;
@@ -780,21 +1391,25 @@
     return ret;
 }
 
-status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop,
+status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle,
+                                         const ui::Dataspace reqDataSpace,
+                                         const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
                                          float frameScale, sp<GraphicBuffer>* outBuffer) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
-    if (s == NULL) return NO_INIT;
-    status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale,
-                                    false /* childrenOnly */);
+    if (s == nullptr) return NO_INIT;
+    status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat,
+                                    sourceCrop, frameScale, false /* childrenOnly */);
     return ret;
 }
 
-status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop,
+status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle,
+                                              const ui::Dataspace reqDataSpace,
+                                              const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
                                               float frameScale, sp<GraphicBuffer>* outBuffer) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
-    if (s == NULL) return NO_INIT;
-    status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale,
-                                    true /* childrenOnly */);
+    if (s == nullptr) return NO_INIT;
+    status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat,
+                                    sourceCrop, frameScale, true /* childrenOnly */);
     return ret;
 }
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 5eafbb3..008f520 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -54,9 +54,22 @@
 {
 }
 
+SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) {
+    mClient = other->mClient;
+    mHandle = other->mHandle;
+    mGraphicBufferProducer = other->mGraphicBufferProducer;
+    mOwned = false;
+}
+
 SurfaceControl::~SurfaceControl()
 {
-    destroy();
+    if (mClient != nullptr && mHandle != nullptr && mOwned) {
+        SurfaceComposerClient::doDropReferenceTransaction(mHandle, mClient->getClient());
+    }
+    mClient.clear();
+    mHandle.clear();
+    mGraphicBufferProducer.clear();
+    IPCThreadState::self()->flushCommands();
 }
 
 void SurfaceControl::destroy()
@@ -64,7 +77,7 @@
     // Avoid destroying the server-side surface if we are not the owner of it, meaning that we
     // retrieved it from another process.
     if (isValid() && mOwned) {
-        mClient->destroySurface(mHandle);
+        SurfaceComposerClient::Transaction().reparent(this, nullptr).apply();
     }
     // clear all references and trigger an IPC now, to make sure things
     // happen without delay, since these resources are quite heavy.
@@ -86,7 +99,7 @@
 }
 
 void SurfaceControl::disconnect() {
-    if (mGraphicBufferProducer != NULL) {
+    if (mGraphicBufferProducer != nullptr) {
         mGraphicBufferProducer->disconnect(
                 BufferQueueCore::CURRENTLY_CONNECTED_API);
     }
@@ -95,28 +108,28 @@
 bool SurfaceControl::isSameSurface(
         const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs)
 {
-    if (lhs == 0 || rhs == 0)
+    if (lhs == nullptr || rhs == nullptr)
         return false;
     return lhs->mHandle == rhs->mHandle;
 }
 
 status_t SurfaceControl::clearLayerFrameStats() const {
     status_t err = validate();
-    if (err < 0) return err;
+    if (err != NO_ERROR) return err;
     const sp<SurfaceComposerClient>& client(mClient);
     return client->clearLayerFrameStats(mHandle);
 }
 
 status_t SurfaceControl::getLayerFrameStats(FrameStats* outStats) const {
     status_t err = validate();
-    if (err < 0) return err;
+    if (err != NO_ERROR) return err;
     const sp<SurfaceComposerClient>& client(mClient);
     return client->getLayerFrameStats(mHandle, outStats);
 }
 
 status_t SurfaceControl::validate() const
 {
-    if (mHandle==0 || mClient==0) {
+    if (mHandle==nullptr || mClient==nullptr) {
         ALOGE("invalid handle (%p) or client (%p)",
                 mHandle.get(), mClient.get());
         return NO_INIT;
@@ -128,7 +141,7 @@
         const sp<SurfaceControl>& control, Parcel* parcel)
 {
     sp<IGraphicBufferProducer> bp;
-    if (control != NULL) {
+    if (control != nullptr) {
         bp = control->mGraphicBufferProducer;
     }
     return parcel->writeStrongBinder(IInterface::asBinder(bp));
@@ -146,7 +159,7 @@
 sp<Surface> SurfaceControl::getSurface() const
 {
     Mutex::Autolock _l(mLock);
-    if (mSurfaceData == 0) {
+    if (mSurfaceData == nullptr) {
         return generateSurfaceLocked();
     }
     return mSurfaceData;
@@ -164,6 +177,12 @@
     return mHandle;
 }
 
+sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer() const
+{
+    Mutex::Autolock _l(mLock);
+    return mGraphicBufferProducer;
+}
+
 sp<SurfaceComposerClient> SurfaceControl::getClient() const
 {
     return mClient;
diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp
index afa15c5..fcae05c 100644
--- a/libs/gui/SyncFeatures.cpp
+++ b/libs/gui/SyncFeatures.cpp
@@ -41,7 +41,7 @@
     // This can only be called after EGL has been initialized; otherwise the
     // check below will abort.
     const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
-    LOG_ALWAYS_FATAL_IF(exts == NULL, "eglQueryStringImplementationANDROID failed");
+    LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed");
     if (strstr(exts, "EGL_ANDROID_native_fence_sync")) {
         // This makes GLConsumer use the EGL_ANDROID_native_fence_sync
         // extension to create Android native fences to signal when all
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index b1e44bb..e64ba9b 100644
--- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -100,7 +100,12 @@
  */
 // 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;
+    if (t.isOk()) {
+        return static_cast<status_t>(static_cast<Status>(t));
+    } else if (t.isDeadObject()) {
+        return DEAD_OBJECT;
+    }
+    return UNKNOWN_ERROR;
 }
 
 /**
@@ -111,7 +116,7 @@
  */
 // convert: Return<void> -> status_t
 inline status_t toStatusT(Return<void> const& t) {
-    return t.isOk() ? OK : UNKNOWN_ERROR;
+    return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR);
 }
 
 /**
diff --git a/libs/gui/include/gui/BufferHubProducer.h b/libs/gui/include/gui/BufferHubProducer.h
index 23c9909..0e925ce 100644
--- a/libs/gui/include/gui/BufferHubProducer.h
+++ b/libs/gui/include/gui/BufferHubProducer.h
@@ -165,6 +165,10 @@
     // buffers are acquired by the consumer, we can't .
     status_t FreeAllBuffers();
 
+    // Helper function that implements the detachBuffer() call, but assuming |mutex_| has been
+    // locked already.
+    status_t DetachBufferLocked(size_t slot);
+
     // Concreate implementation backed by BufferHubBuffer.
     std::shared_ptr<dvr::ProducerQueue> queue_;
 
@@ -199,10 +203,10 @@
     // requested buffer usage or geometry differs from that of the buffer
     // allocated to a slot.
     struct BufferHubSlot : public BufferSlot {
-        BufferHubSlot() : mBufferProducer(nullptr), mIsReallocating(false) {}
+        BufferHubSlot() : mProducerBuffer(nullptr), mIsReallocating(false) {}
         // BufferSlot comes from android framework, using m prefix to comply with
         // the name convention with the reset of data fields from BufferSlot.
-        std::shared_ptr<dvr::BufferProducer> mBufferProducer;
+        std::shared_ptr<dvr::ProducerBuffer> mProducerBuffer;
         bool mIsReallocating;
     };
     BufferHubSlot buffers_[dvr::BufferHubQueue::kMaxQueueCapacity];
diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h
index d375611..806fbe8 100644
--- a/libs/gui/include/gui/CpuConsumer.h
+++ b/libs/gui/include/gui/CpuConsumer.h
@@ -70,7 +70,7 @@
         uint32_t    chromaStep;
 
         LockedBuffer() :
-            data(NULL),
+            data(nullptr),
             width(0),
             height(0),
             format(PIXEL_FORMAT_NONE),
@@ -82,8 +82,8 @@
             dataSpace(HAL_DATASPACE_UNKNOWN),
             frameNumber(0),
             flexFormat(PIXEL_FORMAT_NONE),
-            dataCb(NULL),
-            dataCr(NULL),
+            dataCb(nullptr),
+            dataCr(nullptr),
             chromaStride(0),
             chromaStep(0)
         {}
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index e06e40f..df02494 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -30,7 +30,6 @@
 
 struct FrameEvents;
 class FrameEventHistoryDelta;
-class String8;
 
 
 // Identifiers for all the events that may be recorded or reported.
@@ -72,7 +71,7 @@
     bool hasDequeueReadyInfo() const;
 
     void checkFencesForCompletion();
-    void dump(String8& outString) const;
+    void dump(std::string& outString) const;
 
     bool valid{false};
     int connectId{0};
@@ -112,7 +111,7 @@
     FrameEvents* getFrame(uint64_t frameNumber);
     FrameEvents* getFrame(uint64_t frameNumber, size_t* iHint);
     void checkFencesForCompletion();
-    void dump(String8& outString) const;
+    void dump(std::string& outString) const;
 
     static constexpr size_t MAX_FRAME_HISTORY = 8;
 
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index 8bc7cbf..ddd868d 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -140,7 +140,8 @@
 
     // Scale the crop down horizontally or vertically such that it has the
     // same aspect ratio as the buffer does.
-    static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight);
+    static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth,
+            uint32_t bufferHeight);
 
     // getTimestamp retrieves the timestamp associated with the texture image
     // set by the most recent call to updateTexImage.
@@ -305,7 +306,6 @@
         // createIfNeeded creates an EGLImage if required (we haven't created
         // one yet, or the EGLDisplay or crop-rect has changed).
         status_t createIfNeeded(EGLDisplay display,
-                                const Rect& cropRect,
                                 bool forceCreate = false);
 
         // This calls glEGLImageTargetTexture2DOES to bind the image to the
@@ -314,7 +314,7 @@
 
         const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
         const native_handle* graphicBufferHandle() {
-            return mGraphicBuffer == NULL ? NULL : mGraphicBuffer->handle;
+            return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
         }
 
     private:
@@ -324,7 +324,7 @@
 
         // createImage creates a new EGLImage from a GraphicBuffer.
         EGLImageKHR createImage(EGLDisplay dpy,
-                const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
+                const sp<GraphicBuffer>& graphicBuffer);
 
         // Disallow copying
         EglImage(const EglImage& rhs);
diff --git a/libs/gui/include/gui/GuiConfig.h b/libs/gui/include/gui/GuiConfig.h
index b020ed9..7aa5432 100644
--- a/libs/gui/include/gui/GuiConfig.h
+++ b/libs/gui/include/gui/GuiConfig.h
@@ -17,12 +17,12 @@
 #ifndef ANDROID_GUI_CONFIG_H
 #define ANDROID_GUI_CONFIG_H
 
-#include <utils/String8.h>
+#include <string>
 
 namespace android {
 
 // Append the libgui configuration details to configStr.
-void appendGuiConfigString(String8& configStr);
+void appendGuiConfigString(std::string& configStr);
 
 }; // namespace android
 
diff --git a/libs/gui/include/gui/HdrMetadata.h b/libs/gui/include/gui/HdrMetadata.h
index 9800602..1e9c3e7 100644
--- a/libs/gui/include/gui/HdrMetadata.h
+++ b/libs/gui/include/gui/HdrMetadata.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <stdint.h>
+#include <vector>
 
 #include <system/graphics.h>
 #include <utils/Flattenable.h>
@@ -26,12 +27,15 @@
 struct HdrMetadata : public LightFlattenable<HdrMetadata> {
     enum Type : uint32_t {
         SMPTE2086 = 1 << 0,
-        CTA861_3  = 1 << 1,
+        CTA861_3 = 1 << 1,
+        HDR10PLUS = 1 << 2,
     };
+
     uint32_t validTypes{0};
 
     android_smpte2086_metadata smpte2086{};
     android_cta861_3_metadata cta8613{};
+    std::vector<uint8_t> hdr10plus{};
 
     // LightFlattenable
     bool isFixedSize() const { return false; }
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 887654e..8ff8d81 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -345,7 +345,7 @@
             *outScalingMode = scalingMode;
             *outTransform = transform;
             *outFence = fence;
-            if (outStickyTransform != NULL) {
+            if (outStickyTransform != nullptr) {
                 *outStickyTransform = stickyTransform;
             }
             if (outGetFrameTimestamps) {
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 99a3a75..3899f6a 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -27,10 +27,12 @@
 
 #include <binder/IInterface.h>
 
+#include <ui/ConfigStoreTypes.h>
+#include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
-#include <ui/PixelFormat.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
+#include <ui/PixelFormat.h>
 
 #include <vector>
 
@@ -41,6 +43,7 @@
 struct DisplayState;
 struct DisplayInfo;
 struct DisplayStatInfo;
+struct InputWindowCommands;
 class LayerDebugInfo;
 class HdrCapabilities;
 class IDisplayEventConnection;
@@ -85,22 +88,11 @@
         eVsyncSourceSurfaceFlinger = 1
     };
 
-    /* create connection with surface flinger, requires
-     * ACCESS_SURFACE_FLINGER permission
+    /* 
+     * Create a connection with SurfaceFlinger.
      */
     virtual sp<ISurfaceComposerClient> createConnection() = 0;
 
-    /** create a scoped connection with surface flinger.
-     * Surfaces produced with this connection will act
-     * as children of the passed in GBP. That is to say
-     * SurfaceFlinger will draw them relative and confined to
-     * drawing of buffers from the layer associated with parent.
-     * As this is graphically equivalent in reach to just drawing
-     * pixels into the parent buffers, it requires no special permission.
-     */
-    virtual sp<ISurfaceComposerClient> createScopedConnection(
-            const sp<IGraphicBufferProducer>& parent) = 0;
-
     /* return an IDisplayEventConnection */
     virtual sp<IDisplayEventConnection> createDisplayEventConnection(
             VsyncSource vsyncSource = eVsyncSourceApp) = 0;
@@ -123,7 +115,10 @@
 
     /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
     virtual void setTransactionState(const Vector<ComposerState>& state,
-            const Vector<DisplayState>& displays, uint32_t flags) = 0;
+                                     const Vector<DisplayState>& displays, uint32_t flags,
+                                     const sp<IBinder>& applyToken,
+                                     const InputWindowCommands& inputWindowCommands,
+                                     int64_t desiredPresentTime) = 0;
 
     /* signal that we're done booting.
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -157,9 +152,6 @@
     virtual status_t getDisplayStats(const sp<IBinder>& display,
             DisplayStatInfo* stats) = 0;
 
-    /* returns display viewport information of the given display */
-    virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) = 0;
-
     /* indicates which of the configurations returned by getDisplayInfo is
      * currently active */
     virtual int getActiveConfig(const sp<IBinder>& display) = 0;
@@ -170,25 +162,92 @@
 
     virtual status_t getDisplayColorModes(const sp<IBinder>& display,
             Vector<ui::ColorMode>* outColorModes) = 0;
+    virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+            ui::DisplayPrimaries& primaries) = 0;
     virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& display) = 0;
     virtual status_t setActiveColorMode(const sp<IBinder>& display,
             ui::ColorMode colorMode) = 0;
 
-    /* Capture the specified screen. requires READ_FRAME_BUFFER permission
-     * This function will fail if there is a secure window on screen.
+    /**
+     * Capture the specified screen. This requires READ_FRAME_BUFFER
+     * permission.  This function will fail if there is a secure window on
+     * screen.
+     *
+     * This function can capture a subregion (the source crop) of the screen.
+     * The subregion can be optionally rotated.  It will also be scaled to
+     * match the size of the output buffer.
+     *
+     * reqDataspace and reqPixelFormat specify the data space and pixel format
+     * of the buffer. The caller should pick the data space and pixel format
+     * that it can consume.
+     *
+     * At the moment, sourceCrop is ignored and is always set to the visible
+     * region (projected display viewport) of the screen.
+     *
+     * reqWidth and reqHeight specifies the size of the buffer.  When either
+     * of them is 0, they are set to the size of the logical display viewport.
+     *
+     * When useIdentityTransform is true, layer transformations are disabled.
+     *
+     * rotation specifies the rotation of the source crop (and the pixels in
+     * it) around its center.
+     */
+    virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
+                                   const ui::Dataspace reqDataspace,
+                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+                                   Rotation rotation = eRotateNone) = 0;
+    /**
+     * Capture the specified screen. This requires READ_FRAME_BUFFER
+     * permission.  This function will fail if there is a secure window on
+     * screen.
+     *
+     * This function can capture a subregion (the source crop) of the screen
+     * into an sRGB buffer with RGBA_8888 pixel format.
+     * The subregion can be optionally rotated.  It will also be scaled to
+     * match the size of the output buffer.
+     *
+     * At the moment, sourceCrop is ignored and is always set to the visible
+     * region (projected display viewport) of the screen.
+     *
+     * reqWidth and reqHeight specifies the size of the buffer.  When either
+     * of them is 0, they are set to the size of the logical display viewport.
+     *
+     * When useIdentityTransform is true, layer transformations are disabled.
+     *
+     * rotation specifies the rotation of the source crop (and the pixels in
+     * it) around its center.
      */
     virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
                                    Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-                                   int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform,
-                                   Rotation rotation = eRotateNone) = 0;
+                                   bool useIdentityTransform, Rotation rotation = eRotateNone) {
+        return captureScreen(display, outBuffer, ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+                             sourceCrop, reqWidth, reqHeight, useIdentityTransform, rotation);
+    }
 
     /**
      * Capture a subtree of the layer hierarchy, potentially ignoring the root node.
+     *
+     * reqDataspace and reqPixelFormat specify the data space and pixel format
+     * of the buffer. The caller should pick the data space and pixel format
+     * that it can consume.
      */
     virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder,
-                                   sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
+                                   sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace,
+                                   const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                    float frameScale = 1.0, bool childrenOnly = false) = 0;
 
+    /**
+     * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format,
+     * potentially ignoring the root node.
+     */
+    status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
+                           const Rect& sourceCrop, float frameScale = 1.0,
+                           bool childrenOnly = false) {
+        return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB,
+                             ui::PixelFormat::RGBA_8888, sourceCrop, frameScale, childrenOnly);
+    }
+
     /* Clears the frame statistics for animations.
      *
      * Requires the ACCESS_SURFACE_FLINGER permission.
@@ -217,18 +276,73 @@
      * Requires the ACCESS_SURFACE_FLINGER permission.
      */
     virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0;
+
+    virtual status_t getColorManagement(bool* outGetColorManagement) const = 0;
+
+    /* Gets the composition preference of the default data space and default pixel format,
+     * as well as the wide color gamut data space and wide color gamut pixel format.
+     * If the wide color gamut data space is V0_SRGB, then it implies that the platform
+     * has no wide color gamut support.
+     *
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace,
+                                              ui::PixelFormat* defaultPixelFormat,
+                                              ui::Dataspace* wideColorGamutDataspace,
+                                              ui::PixelFormat* wideColorGamutPixelFormat) const = 0;
+    /*
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+                                                           ui::PixelFormat* outFormat,
+                                                           ui::Dataspace* outDataspace,
+                                                           uint8_t* outComponentMask) const = 0;
+
+    /* Turns on the color sampling engine on the display.
+     *
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
+                                                      uint8_t componentMask,
+                                                      uint64_t maxFrames) const = 0;
+
+    /* Returns statistics on the color profile of the last frame displayed for a given display
+     *
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+                                               uint64_t timestamp,
+                                               DisplayedFrameStats* outStats) const = 0;
+
+    /*
+     * Gets whether SurfaceFlinger can support protected content in GPU composition.
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t getProtectedContentSupport(bool* outSupported) const = 0;
+
+    virtual status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+                                 int32_t* outBufferId) = 0;
+
+    virtual status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) = 0;
+
+    /*
+     * Queries whether the given display is a wide color display.
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t isWideColorDisplay(const sp<IBinder>& token,
+                                        bool* outIsWideColorDisplay) const = 0;
 };
 
 // ----------------------------------------------------------------------------
 
 class BnSurfaceComposer: public BnInterface<ISurfaceComposer> {
 public:
-    enum {
+    enum ISurfaceComposerTag {
         // Note: BOOT_FINISHED must remain this value, it is called from
         // Java by ActivityManagerService.
         BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
         CREATE_CONNECTION,
-        UNUSED, // formerly CREATE_GRAPHIC_BUFFER_ALLOC
+        CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED, // unused, fails permissions check
         CREATE_DISPLAY_EVENT_CONNECTION,
         CREATE_DISPLAY,
         DESTROY_DISPLAY,
@@ -239,7 +353,7 @@
         GET_DISPLAY_CONFIGS,
         GET_ACTIVE_CONFIG,
         SET_ACTIVE_CONFIG,
-        CONNECT_DISPLAY,
+        CONNECT_DISPLAY_UNUSED, // unused, fails permissions check
         CAPTURE_SCREEN,
         CAPTURE_LAYERS,
         CLEAR_ANIMATION_FRAME_STATS,
@@ -253,8 +367,17 @@
         ENABLE_VSYNC_INJECTIONS,
         INJECT_VSYNC,
         GET_LAYER_DEBUG_INFO,
-        CREATE_SCOPED_CONNECTION,
-        GET_DISPLAY_VIEWPORT
+        GET_COMPOSITION_PREFERENCE,
+        GET_COLOR_MANAGEMENT,
+        GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
+        SET_DISPLAY_CONTENT_SAMPLING_ENABLED,
+        GET_DISPLAYED_CONTENT_SAMPLE,
+        GET_PROTECTED_CONTENT_SUPPORT,
+        CACHE_BUFFER,
+        UNCACHE_BUFFER,
+        IS_WIDE_COLOR_DISPLAY,
+        GET_DISPLAY_NATIVE_PRIMARIES,
+        // Always append new enum to the end.
     };
 
     virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index b4fd956..32ac9e8 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -18,8 +18,11 @@
 
 #include <binder/IInterface.h>
 #include <binder/SafeInterface.h>
+#include <gui/LayerMetadata.h>
 #include <ui/PixelFormat.h>
 
+#include <unordered_map>
+
 namespace android {
 
 class FrameStats;
@@ -40,8 +43,9 @@
         eProtectedByDRM = 0x00001000,
         eCursorWindow = 0x00002000,
 
-        eFXSurfaceNormal = 0x00000000,
+        eFXSurfaceBufferQueue = 0x00000000,
         eFXSurfaceColor = 0x00020000,
+        eFXSurfaceBufferState = 0x00040000,
         eFXSurfaceContainer = 0x00080000,
         eFXSurfaceMask = 0x000F0000,
     };
@@ -50,14 +54,18 @@
      * Requires ACCESS_SURFACE_FLINGER permission
      */
     virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
-                                   uint32_t flags, const sp<IBinder>& parent, int32_t windowType,
-                                   int32_t ownerUid, sp<IBinder>* handle,
+                                   uint32_t flags, const sp<IBinder>& parent,
+                                   LayerMetadata metadata, sp<IBinder>* handle,
                                    sp<IGraphicBufferProducer>* gbp) = 0;
 
     /*
      * Requires ACCESS_SURFACE_FLINGER permission
      */
-    virtual status_t destroySurface(const sp<IBinder>& handle) = 0;
+    virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
+                                             PixelFormat format, uint32_t flags,
+                                             const sp<IGraphicBufferProducer>& parent,
+                                             LayerMetadata metadata, sp<IBinder>* handle,
+                                             sp<IGraphicBufferProducer>* gbp) = 0;
 
     /*
      * Requires ACCESS_SURFACE_FLINGER permission
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
new file mode 100644
index 0000000..29ab026
--- /dev/null
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/SafeInterface.h>
+
+#include <ui/Fence.h>
+#include <utils/Timers.h>
+
+#include <cstdint>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace android {
+
+class ITransactionCompletedListener;
+
+using CallbackId = int64_t;
+
+struct CallbackIdsHash {
+    // CallbackId vectors have several properties that let us get away with this simple hash.
+    // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
+    // empty we can still hash 0.
+    // 2) CallbackId vectors for the same listener either are identical or contain none of the
+    // same members. It is sufficient to just check the first CallbackId in the vectors. If
+    // they match, they are the same. If they do not match, they are not the same.
+    std::size_t operator()(const std::vector<CallbackId> callbackIds) const {
+        return std::hash<CallbackId>{}((callbackIds.size() == 0) ? 0 : callbackIds.front());
+    }
+};
+
+class SurfaceStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    SurfaceStats() = default;
+    SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence)
+          : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {}
+
+    sp<IBinder> surfaceControl;
+    nsecs_t acquireTime = -1;
+    sp<Fence> previousReleaseFence;
+};
+
+class TransactionStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    nsecs_t latchTime = -1;
+    sp<Fence> presentFence = nullptr;
+    std::vector<SurfaceStats> surfaceStats;
+};
+
+class ListenerStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    static ListenerStats createEmpty(const sp<ITransactionCompletedListener>& listener,
+                                     const std::unordered_set<CallbackId>& callbackIds);
+
+    sp<ITransactionCompletedListener> listener;
+    std::unordered_map<std::vector<CallbackId>, TransactionStats, CallbackIdsHash> transactionStats;
+};
+
+class ITransactionCompletedListener : public IInterface {
+public:
+    DECLARE_META_INTERFACE(TransactionCompletedListener)
+
+    virtual void onTransactionCompleted(ListenerStats stats) = 0;
+};
+
+class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
+public:
+    BnTransactionCompletedListener()
+          : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {}
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t flags = 0) override;
+};
+
+class ListenerCallbacks {
+public:
+    ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
+                      const std::unordered_set<CallbackId>& callbacks)
+          : transactionCompletedListener(listener),
+            callbackIds(callbacks.begin(), callbacks.end()) {}
+
+    ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
+                      const std::vector<CallbackId>& ids)
+          : transactionCompletedListener(listener), callbackIds(ids) {}
+
+    sp<ITransactionCompletedListener> transactionCompletedListener;
+    std::vector<CallbackId> callbackIds;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h
index 92bd8c5..66a7b4d 100644
--- a/libs/gui/include/gui/LayerDebugInfo.h
+++ b/libs/gui/include/gui/LayerDebugInfo.h
@@ -52,7 +52,6 @@
     int32_t mWidth = -1;
     int32_t mHeight = -1;
     Rect mCrop = Rect::INVALID_RECT;
-    Rect mFinalCrop = Rect::INVALID_RECT;
     half4 mColor = half4(1.0_hf, 1.0_hf, 1.0_hf, 0.0_hf);
     uint32_t mFlags = 0;
     PixelFormat mPixelFormat = PIXEL_FORMAT_NONE;
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
new file mode 100644
index 0000000..3ae10e4
--- /dev/null
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 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 <binder/Parcelable.h>
+
+#include <unordered_map>
+
+namespace android {
+
+enum { METADATA_OWNER_UID = 1, METADATA_WINDOW_TYPE = 2, METADATA_TASK_ID = 3 };
+
+struct LayerMetadata : public Parcelable {
+    std::unordered_map<uint32_t, std::vector<uint8_t>> mMap;
+
+    LayerMetadata();
+    LayerMetadata(const LayerMetadata& other);
+    LayerMetadata(LayerMetadata&& other);
+    explicit LayerMetadata(std::unordered_map<uint32_t, std::vector<uint8_t>> map);
+    LayerMetadata& operator=(const LayerMetadata& other);
+    LayerMetadata& operator=(LayerMetadata&& other);
+
+    void merge(const LayerMetadata& other);
+
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
+
+    bool has(uint32_t key) const;
+    int32_t getInt32(uint32_t key, int32_t fallback) const;
+    void setInt32(uint32_t key, int32_t value);
+
+    std::string itemToString(uint32_t key, const char* separator) const;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 788962e..afd843f 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -22,10 +22,19 @@
 
 #include <utils/Errors.h>
 
-#include <ui/Region.h>
-#include <ui/Rect.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <math/mat4.h>
+
+#ifndef NO_INPUT
+#include <input/InputWindow.h>
+#endif
+
+#include <gui/LayerMetadata.h>
 #include <math/vec3.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
 
 namespace android {
 
@@ -36,113 +45,176 @@
  * Used to communicate layer information between SurfaceFlinger and its clients.
  */
 struct layer_state_t {
-
-
     enum {
-        eLayerHidden        = 0x01,     // SURFACE_HIDDEN in SurfaceControl.java
-        eLayerOpaque        = 0x02,     // SURFACE_OPAQUE
-        eLayerSecure        = 0x80,     // SECURE
+        eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java
+        eLayerOpaque = 0x02, // SURFACE_OPAQUE
+        eLayerSecure = 0x80, // SECURE
     };
 
     enum {
-        ePositionChanged            = 0x00000001,
-        eLayerChanged               = 0x00000002,
-        eSizeChanged                = 0x00000004,
-        eAlphaChanged               = 0x00000008,
-        eMatrixChanged              = 0x00000010,
-        eTransparentRegionChanged   = 0x00000020,
-        eFlagsChanged               = 0x00000040,
-        eLayerStackChanged          = 0x00000080,
-        eCropChanged                = 0x00000100,
-        eDeferTransaction           = 0x00000200,
-        eFinalCropChanged           = 0x00000400,
-        eOverrideScalingModeChanged = 0x00000800,
-        eGeometryAppliesWithResize  = 0x00001000,
-        eReparentChildren           = 0x00002000,
-        eDetachChildren             = 0x00004000,
-        eRelativeLayerChanged       = 0x00008000,
-        eReparent                   = 0x00010000,
-        eColorChanged               = 0x00020000,
-        eDestroySurface             = 0x00040000
+        ePositionChanged = 0x00000001,
+        eLayerChanged = 0x00000002,
+        eSizeChanged = 0x00000004,
+        eAlphaChanged = 0x00000008,
+        eMatrixChanged = 0x00000010,
+        eTransparentRegionChanged = 0x00000020,
+        eFlagsChanged = 0x00000040,
+        eLayerStackChanged = 0x00000080,
+        eCropChanged_legacy = 0x00000100,
+        eDeferTransaction_legacy = 0x00000200,
+        eOverrideScalingModeChanged = 0x00000400,
+        eGeometryAppliesWithResize = 0x00000800,
+        eReparentChildren = 0x00001000,
+        eDetachChildren = 0x00002000,
+        eRelativeLayerChanged = 0x00004000,
+        eReparent = 0x00008000,
+        eColorChanged = 0x00010000,
+        eDestroySurface = 0x00020000,
+        eTransformChanged = 0x00040000,
+        eTransformToDisplayInverseChanged = 0x00080000,
+        eCropChanged = 0x00100000,
+        eBufferChanged = 0x00200000,
+        eAcquireFenceChanged = 0x00400000,
+        eDataspaceChanged = 0x00800000,
+        eHdrMetadataChanged = 0x01000000,
+        eSurfaceDamageRegionChanged = 0x02000000,
+        eApiChanged = 0x04000000,
+        eSidebandStreamChanged = 0x08000000,
+        eColorTransformChanged = 0x10000000,
+        eListenerCallbacksChanged = 0x20000000,
+        eInputInfoChanged = 0x40000000,
+        eCornerRadiusChanged = 0x80000000,
+        eFrameChanged = 0x1'00000000,
+        eCachedBufferChanged = 0x2'00000000,
+        eColorAlphaChanged = 0x4'00000000,
+        eColorDataspaceChanged = 0x8'00000000,
+        eMetadataChanged = 0x10'00000000,
     };
 
     layer_state_t()
-        :   what(0),
-            x(0), y(0), z(0), w(0), h(0), layerStack(0),
-            alpha(0), flags(0), mask(0),
-            reserved(0), crop(Rect::INVALID_RECT),
-            finalCrop(Rect::INVALID_RECT), frameNumber(0),
-            overrideScalingMode(-1)
-    {
+          : what(0),
+            x(0),
+            y(0),
+            z(0),
+            w(0),
+            h(0),
+            layerStack(0),
+            alpha(0),
+            flags(0),
+            mask(0),
+            reserved(0),
+            crop_legacy(Rect::INVALID_RECT),
+            cornerRadius(0.0f),
+            frameNumber_legacy(0),
+            overrideScalingMode(-1),
+            transform(0),
+            transformToDisplayInverse(false),
+            crop(Rect::INVALID_RECT),
+            frame(Rect::INVALID_RECT),
+            dataspace(ui::Dataspace::UNKNOWN),
+            surfaceDamageRegion(),
+            api(-1),
+            colorTransform(mat4()),
+            colorAlpha(0),
+            colorDataspace(ui::Dataspace::UNKNOWN) {
         matrix.dsdx = matrix.dtdy = 1.0f;
         matrix.dsdy = matrix.dtdx = 0.0f;
+        hdrMetadata.validTypes = 0;
     }
 
     void merge(const layer_state_t& other);
-    status_t    write(Parcel& output) const;
-    status_t    read(const Parcel& input);
+    status_t write(Parcel& output) const;
+    status_t read(const Parcel& input);
 
-            struct matrix22_t {
-                float   dsdx{0};
-                float   dtdx{0};
-                float   dtdy{0};
-                float   dsdy{0};
-            };
-            sp<IBinder>     surface;
-            uint32_t        what;
-            float           x;
-            float           y;
-            int32_t         z;
-            uint32_t        w;
-            uint32_t        h;
-            uint32_t        layerStack;
-            float           alpha;
-            uint8_t         flags;
-            uint8_t         mask;
-            uint8_t         reserved;
-            matrix22_t      matrix;
-            Rect            crop;
-            Rect            finalCrop;
-            sp<IBinder>     barrierHandle;
-            sp<IBinder>     reparentHandle;
-            uint64_t        frameNumber;
-            int32_t         overrideScalingMode;
+    struct matrix22_t {
+        float dsdx{0};
+        float dtdx{0};
+        float dtdy{0};
+        float dsdy{0};
+    };
+    struct cached_buffer_t {
+        sp<IBinder> token = nullptr;
+        int32_t bufferId = -1;
+    };
+    sp<IBinder> surface;
+    uint64_t what;
+    float x;
+    float y;
+    int32_t z;
+    uint32_t w;
+    uint32_t h;
+    uint32_t layerStack;
+    float alpha;
+    uint8_t flags;
+    uint8_t mask;
+    uint8_t reserved;
+    matrix22_t matrix;
+    Rect crop_legacy;
+    float cornerRadius;
+    sp<IBinder> barrierHandle_legacy;
+    sp<IBinder> reparentHandle;
+    uint64_t frameNumber_legacy;
+    int32_t overrideScalingMode;
 
-            sp<IGraphicBufferProducer> barrierGbp;
+    sp<IGraphicBufferProducer> barrierGbp_legacy;
 
-            sp<IBinder>     relativeLayerHandle;
+    sp<IBinder> relativeLayerHandle;
 
-            sp<IBinder>     parentHandleForChild;
+    sp<IBinder> parentHandleForChild;
 
-            half3           color;
+    half3 color;
 
-            // non POD must be last. see write/read
-            Region          transparentRegion;
+    // non POD must be last. see write/read
+    Region transparentRegion;
+
+    uint32_t transform;
+    bool transformToDisplayInverse;
+    Rect crop;
+    Rect frame;
+    sp<GraphicBuffer> buffer;
+    sp<Fence> acquireFence;
+    ui::Dataspace dataspace;
+    HdrMetadata hdrMetadata;
+    Region surfaceDamageRegion;
+    int32_t api;
+    sp<NativeHandle> sidebandStream;
+    mat4 colorTransform;
+
+    std::vector<ListenerCallbacks> listenerCallbacks;
+#ifndef NO_INPUT
+    InputWindowInfo inputInfo;
+#endif
+
+    cached_buffer_t cachedBuffer;
+
+    float colorAlpha;
+    ui::Dataspace colorDataspace;
+
+    LayerMetadata metadata;
 };
 
 struct ComposerState {
     sp<ISurfaceComposerClient> client;
     layer_state_t state;
-    status_t    write(Parcel& output) const;
-    status_t    read(const Parcel& input);
+    status_t write(Parcel& output) const;
+    status_t read(const Parcel& input);
 };
 
 struct DisplayState {
-
     enum {
-        eOrientationDefault     = 0,
-        eOrientation90          = 1,
-        eOrientation180         = 2,
-        eOrientation270         = 3,
-        eOrientationUnchanged   = 4,
-        eOrientationSwapMask    = 0x01
+        eOrientationDefault = 0,
+        eOrientation90 = 1,
+        eOrientation180 = 2,
+        eOrientation270 = 3,
+        eOrientationUnchanged = 4,
+        eOrientationSwapMask = 0x01
     };
 
     enum {
-        eSurfaceChanged             = 0x01,
-        eLayerStackChanged          = 0x02,
-        eDisplayProjectionChanged   = 0x04,
-        eDisplaySizeChanged         = 0x08
+        eSurfaceChanged = 0x01,
+        eLayerStackChanged = 0x02,
+        eDisplayProjectionChanged = 0x04,
+        eDisplaySizeChanged = 0x08
     };
 
     DisplayState();
@@ -152,29 +224,54 @@
     sp<IBinder> token;
     sp<IGraphicBufferProducer> surface;
     uint32_t layerStack;
+
+    // These states define how layers are projected onto the physical display.
+    //
+    // Layers are first clipped to `viewport'.  They are then translated and
+    // scaled from `viewport' to `frame'.  Finally, they are rotated according
+    // to `orientation', `width', and `height'.
+    //
+    // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20,
+    // 10, 420, 210), and the size of the display is WxH.  When orientation is
+    // 0, layers will be scaled by a factor of 2 and translated by (20, 10).
+    // When orientation is 1, layers will be additionally rotated by 90
+    // degrees around the origin clockwise and translated by (W, 0).
     uint32_t orientation;
     Rect viewport;
     Rect frame;
+
     uint32_t width, height;
+
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
 };
 
-static inline
-int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
+struct InputWindowCommands {
+    struct TransferTouchFocusCommand {
+        sp<IBinder> fromToken;
+        sp<IBinder> toToken;
+    };
+
+    std::vector<TransferTouchFocusCommand> transferTouchFocusCommands;
+
+    void merge(const InputWindowCommands& other);
+    void clear();
+    void write(Parcel& output) const;
+    void read(const Parcel& input);
+};
+
+static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
     if (lhs.client < rhs.client) return -1;
     if (lhs.client > rhs.client) return 1;
-    if (lhs.state.surface < rhs.state.surface)  return -1;
-    if (lhs.state.surface > rhs.state.surface)  return 1;
+    if (lhs.state.surface < rhs.state.surface) return -1;
+    if (lhs.state.surface > rhs.state.surface) return 1;
     return 0;
 }
 
-static inline
-int compare_type(const DisplayState& lhs, const DisplayState& rhs) {
+static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) {
     return compare_type(lhs.token, rhs.token);
 }
 
 }; // namespace android
 
 #endif // ANDROID_SF_LAYER_STATE_H
-
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 9aeafae..248e105 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -80,7 +80,7 @@
     /* convenience function to check that the given surface is non NULL as
      * well as its IGraphicBufferProducer */
     static bool isValid(const sp<Surface>& surface) {
-        return surface != NULL && surface->getIGraphicBufferProducer() != NULL;
+        return surface != nullptr && surface->getIGraphicBufferProducer() != nullptr;
     }
 
     /* Attaches a sideband buffer stream to the Surface's IGraphicBufferProducer.
@@ -218,6 +218,7 @@
     int dispatchSetBuffersDataSpace(va_list args);
     int dispatchSetBuffersSmpte2086Metadata(va_list args);
     int dispatchSetBuffersCta8613Metadata(va_list args);
+    int dispatchSetBuffersHdr10PlusMetadata(va_list args);
     int dispatchSetSurfaceDamage(va_list args);
     int dispatchSetSharedBufferMode(va_list args);
     int dispatchSetAutoRefresh(va_list args);
@@ -249,6 +250,7 @@
     virtual int setBuffersDataSpace(ui::Dataspace dataSpace);
     virtual int setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata);
     virtual int setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata);
+    virtual int setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata);
     virtual int setCrop(Rect const* rect);
     virtual int setUsage(uint64_t reqUsage);
     virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects);
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index ad8a8b0..8e2bb2b 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -19,7 +19,9 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <set>
 #include <unordered_map>
+#include <unordered_set>
 
 #include <binder/IBinder.h>
 
@@ -28,14 +30,17 @@
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
+#include <ui/ConfigStoreTypes.h>
+#include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
 
 #include <gui/CpuConsumer.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <gui/LayerState.h>
 #include <gui/SurfaceControl.h>
 #include <math/vec3.h>
-#include <gui/LayerState.h>
 
 namespace android {
 
@@ -49,13 +54,32 @@
 
 // ---------------------------------------------------------------------------
 
+struct SurfaceControlStats {
+    SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t time,
+                        const sp<Fence>& prevReleaseFence)
+          : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {}
+
+    sp<SurfaceControl> surfaceControl;
+    nsecs_t acquireTime = -1;
+    sp<Fence> previousReleaseFence;
+};
+
+using TransactionCompletedCallbackTakesContext =
+        std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
+                           const sp<Fence>& /*presentFence*/,
+                           const std::vector<SurfaceControlStats>& /*stats*/)>;
+using TransactionCompletedCallback =
+        std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
+                           const std::vector<SurfaceControlStats>& /*stats*/)>;
+
+// ---------------------------------------------------------------------------
+
 class SurfaceComposerClient : public RefBase
 {
     friend class Composer;
 public:
                 SurfaceComposerClient();
                 SurfaceComposerClient(const sp<ISurfaceComposerClient>& client);
-                SurfaceComposerClient(const sp<IGraphicBufferProducer>& parent);
     virtual     ~SurfaceComposerClient();
 
     // Always make sure we could initialize
@@ -69,7 +93,7 @@
 
     // callback when the composer is dies
     status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient,
-            void* cookie = NULL, uint32_t flags = 0);
+            void* cookie = nullptr, uint32_t flags = 0);
 
     // Get a list of supported configurations for a given display
     static status_t getDisplayConfigs(const sp<IBinder>& display,
@@ -79,9 +103,6 @@
     static status_t getDisplayInfo(const sp<IBinder>& display,
             DisplayInfo* info);
 
-    // Get the display viewport for the given display
-    static status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport);
-
     // Get the index of the current active configuration (relative to the list
     // returned by getDisplayInfo)
     static int getActiveConfig(const sp<IBinder>& display);
@@ -94,6 +115,10 @@
     static status_t getDisplayColorModes(const sp<IBinder>& display,
             Vector<ui::ColorMode>* outColorModes);
 
+    // Get the coordinates of the display's native color primaries
+    static status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+            ui::DisplayPrimaries& outPrimaries);
+
     // Gets the active color mode for the given display
     static ui::ColorMode getActiveColorMode(const sp<IBinder>& display);
 
@@ -104,31 +129,71 @@
     /* Triggers screen on/off or low power mode and waits for it to complete */
     static void setDisplayPowerMode(const sp<IBinder>& display, int mode);
 
+    /* Returns the composition preference of the default data space and default pixel format,
+     * as well as the wide color gamut data space and wide color gamut pixel format.
+     * If the wide color gamut data space is V0_SRGB, then it implies that the platform
+     * has no wide color gamut support.
+     */
+    static status_t getCompositionPreference(ui::Dataspace* defaultDataspace,
+                                             ui::PixelFormat* defaultPixelFormat,
+                                             ui::Dataspace* wideColorGamutDataspace,
+                                             ui::PixelFormat* wideColorGamutPixelFormat);
+
+    /*
+     * Gets whether SurfaceFlinger can support protected content in GPU composition.
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    static bool getProtectedContentSupport();
+
+    /**
+     * Called from SurfaceControl d'tor to 'destroy' the surface (or rather, reparent it
+     * to null), but without needing an sp<SurfaceControl> to avoid infinite ressurection.
+     */
+    static void doDropReferenceTransaction(const sp<IBinder>& handle,
+            const sp<ISurfaceComposerClient>& client);
+
+    // Caches a buffer with the ISurfaceComposer so the buffer does not need to be resent across
+    // processes
+    static status_t cacheBuffer(const sp<GraphicBuffer>& buffer, int32_t* outBufferId);
+    // Uncaches a buffer set by cacheBuffer
+    static status_t uncacheBuffer(int32_t bufferId);
+
+    // Queries whether a given display is wide color display.
+    static status_t isWideColorDisplay(const sp<IBinder>& display, bool* outIsWideColorDisplay);
+
     // ------------------------------------------------------------------------
     // surface creation / destruction
 
+    static sp<SurfaceComposerClient> getDefault();
+
     //! Create a surface
-    sp<SurfaceControl> createSurface(
-            const String8& name,// name of the surface
-            uint32_t w,         // width in pixel
-            uint32_t h,         // height in pixel
-            PixelFormat format, // pixel-format desired
-            uint32_t flags = 0, // usage flags
-            SurfaceControl* parent = nullptr, // parent
-            int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
-            int32_t ownerUid = -1 // UID of the task
+    sp<SurfaceControl> createSurface(const String8& name,              // name of the surface
+                                     uint32_t w,                       // width in pixel
+                                     uint32_t h,                       // height in pixel
+                                     PixelFormat format,               // pixel-format desired
+                                     uint32_t flags = 0,               // usage flags
+                                     SurfaceControl* parent = nullptr, // parent
+                                     LayerMetadata metadata = LayerMetadata() // metadata
     );
 
-    status_t createSurfaceChecked(
-            const String8& name,// name of the surface
-            uint32_t w,         // width in pixel
-            uint32_t h,         // height in pixel
-            PixelFormat format, // pixel-format desired
-            sp<SurfaceControl>* outSurface,
-            uint32_t flags = 0, // usage flags
-            SurfaceControl* parent = nullptr, // parent
-            int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
-            int32_t ownerUid = -1 // UID of the task
+    status_t createSurfaceChecked(const String8& name, // name of the surface
+                                  uint32_t w,          // width in pixel
+                                  uint32_t h,          // height in pixel
+                                  PixelFormat format,  // pixel-format desired
+                                  sp<SurfaceControl>* outSurface,
+                                  uint32_t flags = 0,                      // usage flags
+                                  SurfaceControl* parent = nullptr,        // parent
+                                  LayerMetadata metadata = LayerMetadata() // metadata
+    );
+
+    //! Create a surface
+    sp<SurfaceControl> createWithSurfaceParent(const String8& name,       // name of the surface
+                                               uint32_t w,                // width in pixel
+                                               uint32_t h,                // height in pixel
+                                               PixelFormat format,        // pixel-format desired
+                                               uint32_t flags = 0,        // usage flags
+                                               Surface* parent = nullptr, // parent
+                                               LayerMetadata metadata = LayerMetadata() // metadata
     );
 
     //! Create a virtual display
@@ -151,19 +216,52 @@
         }
     };
 
+    struct TCLHash {
+        std::size_t operator()(const sp<ITransactionCompletedListener>& tcl) const {
+            return std::hash<IBinder*>{}((tcl) ? IInterface::asBinder(tcl).get() : nullptr);
+        }
+    };
+
+    struct CallbackInfo {
+        // All the callbacks that have been requested for a TransactionCompletedListener in the
+        // Transaction
+        std::unordered_set<CallbackId> callbackIds;
+        // All the SurfaceControls that have been modified in this TransactionCompletedListener's
+        // process that require a callback if there is one or more callbackIds set.
+        std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
+    };
+
     class Transaction {
         std::unordered_map<sp<SurfaceControl>, ComposerState, SCHash> mComposerStates;
         SortedVector<DisplayState > mDisplayStates;
+        std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+                mListenerCallbacks;
+
         uint32_t                    mForceSynchronous = 0;
         uint32_t                    mTransactionNestCount = 0;
         bool                        mAnimation = false;
         bool                        mEarlyWakeup = false;
 
+        // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
+        // to be presented. When it is not possible to present at exactly that time, it will be
+        // presented after the time has passed.
+        //
+        // Desired present times that are more than 1 second in the future may be ignored.
+        // When a desired present time has already passed, the transaction will be presented as soon
+        // as possible.
+        //
+        // Transactions from the same process are presented in the same order that they are applied.
+        // The desired present time does not affect this ordering.
+        int64_t mDesiredPresentTime = -1;
+
+        InputWindowCommands mInputWindowCommands;
         int mStatus = NO_ERROR;
 
         layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
         DisplayState& getDisplayState(const sp<IBinder>& token);
 
+        void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
+
     public:
         Transaction() = default;
         virtual ~Transaction() = default;
@@ -203,22 +301,23 @@
                 float alpha);
         Transaction& setMatrix(const sp<SurfaceControl>& sc,
                 float dsdx, float dtdx, float dtdy, float dsdy);
-        Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
-        Transaction& setFinalCrop(const sp<SurfaceControl>& sc, const Rect& crop);
+        Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
+        Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
         Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
+        Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key,
+                                 std::vector<uint8_t> data);
         // Defers applying any changes made in this transaction until the Layer
         // identified by handle reaches the given frameNumber. If the Layer identified
         // by handle is removed, then we will apply this transaction regardless of
         // what frame number has been reached.
-        Transaction& deferTransactionUntil(const sp<SurfaceControl>& sc,
-                const sp<IBinder>& handle,
-                uint64_t frameNumber);
-        // A variant of deferTransactionUntil which identifies the Layer we wait for by
+        Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
+                                                  const sp<IBinder>& handle, uint64_t frameNumber);
+        // A variant of deferTransactionUntil_legacy which identifies the Layer we wait for by
         // Surface instead of Handle. Useful for clients which may not have the
         // SurfaceControl for some of their Surfaces. Otherwise behaves identically.
-        Transaction& deferTransactionUntil(const sp<SurfaceControl>& sc,
-                const sp<Surface>& barrierSurface,
-                uint64_t frameNumber);
+        Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
+                                                  const sp<Surface>& barrierSurface,
+                                                  uint64_t frameNumber);
         // Reparents all children of this layer to the new parent handle.
         Transaction& reparentChildren(const sp<SurfaceControl>& sc,
                 const sp<IBinder>& newParentHandle);
@@ -231,6 +330,32 @@
 
         Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color);
 
+        // Sets the alpha of the background color layer if it exists.
+        Transaction& setColorAlpha(const sp<SurfaceControl>& sc, float alpha);
+
+        // Sets the dataspace of the background color layer if it exists.
+        Transaction& setColorDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
+
+        Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform);
+        Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
+                                                  bool transformToDisplayInverse);
+        Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
+        Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame);
+        Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer);
+        Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
+        Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
+        Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
+        Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
+        Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
+                                            const Region& surfaceDamageRegion);
+        Transaction& setApi(const sp<SurfaceControl>& sc, int32_t api);
+        Transaction& setSidebandStream(const sp<SurfaceControl>& sc,
+                                       const sp<NativeHandle>& sidebandStream);
+        Transaction& setDesiredPresentTime(nsecs_t desiredPresentTime);
+
+        Transaction& addTransactionCompletedCallback(
+                TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+
         // Detaches all child surfaces (and their children recursively)
         // from their SurfaceControl.
         // The child SurfaceControls will not throw exceptions or return errors,
@@ -254,7 +379,17 @@
         // freezing the total geometry of a surface until a resize is completed.
         Transaction& setGeometryAppliesWithResize(const sp<SurfaceControl>& sc);
 
-        Transaction& destroySurface(const sp<SurfaceControl>& sc);
+#ifndef NO_INPUT
+        Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
+        Transaction& transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
+#endif
+
+        // Set a color transform matrix on the given layer on the built-in display.
+        Transaction& setColorTransform(const sp<SurfaceControl>& sc, const mat3& matrix,
+                                       const vec3& translation);
+
+        Transaction& setGeometry(const sp<SurfaceControl>& sc,
+                const Rect& source, const Rect& dst, int transform);
 
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
@@ -280,8 +415,6 @@
         void setEarlyWakeup();
     };
 
-    status_t    destroySurface(const sp<IBinder>& id);
-
     status_t clearLayerFrameStats(const sp<IBinder>& token) const;
     status_t getLayerFrameStats(const sp<IBinder>& token, FrameStats* outStats) const;
     static status_t clearAnimationFrameStats();
@@ -297,13 +430,22 @@
 
     inline sp<ISurfaceComposerClient> getClient() { return mClient; }
 
+    static status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+                                                          ui::PixelFormat* outFormat,
+                                                          ui::Dataspace* outDataspace,
+                                                          uint8_t* outComponentMask);
+    static status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
+                                                     uint8_t componentMask, uint64_t maxFrames);
+
+    static status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+                                              uint64_t timestamp, DisplayedFrameStats* outStats);
+
 private:
     virtual void onFirstRef();
 
     mutable     Mutex                       mLock;
                 status_t                    mStatus;
                 sp<ISurfaceComposerClient>  mClient;
-                wp<IGraphicBufferProducer>  mParent;
 };
 
 // ---------------------------------------------------------------------------
@@ -312,17 +454,65 @@
 public:
     // if cropping isn't required, callers may pass in a default Rect, e.g.:
     //   capture(display, producer, Rect(), reqWidth, ...);
-    static status_t capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth,
-                            uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ,
-                            bool useIdentityTransform, uint32_t rotation,
-                            sp<GraphicBuffer>* outBuffer);
-    static status_t captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, float frameScale,
-                                  sp<GraphicBuffer>* outBuffer);
-    static status_t captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop,
+    static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
+                            const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                            uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+                            uint32_t rotation, sp<GraphicBuffer>* outBuffer);
+    static status_t captureLayers(const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace,
+                                  const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                  float frameScale, sp<GraphicBuffer>* outBuffer);
+    static status_t captureChildLayers(const sp<IBinder>& layerHandle,
+                                       const ui::Dataspace reqDataSpace,
+                                       const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
                                        float frameScale, sp<GraphicBuffer>* outBuffer);
 };
 
 // ---------------------------------------------------------------------------
+
+class TransactionCompletedListener : public BnTransactionCompletedListener {
+    TransactionCompletedListener();
+
+    CallbackId getNextIdLocked() REQUIRES(mMutex);
+
+    std::mutex mMutex;
+
+    bool mListening GUARDED_BY(mMutex) = false;
+
+    CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
+
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& iBinder) const {
+            return std::hash<IBinder*>{}(iBinder.get());
+        }
+    };
+
+    struct CallbackTranslation {
+        TransactionCompletedCallback callbackFunction;
+        std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
+    };
+
+    std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
+
+public:
+    static sp<TransactionCompletedListener> getInstance();
+    static sp<ITransactionCompletedListener> getIInstance();
+
+    void startListeningLocked() REQUIRES(mMutex);
+
+    CallbackId addCallbackFunction(
+            const TransactionCompletedCallback& callbackFunction,
+            const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
+                    surfaceControls);
+
+    void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl,
+                                      const std::unordered_set<CallbackId>& callbackIds);
+
+    // Overrides BnTransactionCompletedListener's onTransactionCompleted
+    void onTransactionCompleted(ListenerStats stats) override;
+};
+
+// ---------------------------------------------------------------------------
+
 }; // namespace android
 
 #endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index bd987dd..b584f36 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -48,11 +48,11 @@
     void writeToParcel(Parcel* parcel);
 
     static bool isValid(const sp<SurfaceControl>& surface) {
-        return (surface != 0) && surface->isValid();
+        return (surface != nullptr) && surface->isValid();
     }
 
     bool isValid() {
-        return mHandle!=0 && mClient!=0;
+        return mHandle!=nullptr && mClient!=nullptr;
     }
 
     static bool isSameSurface(
@@ -71,10 +71,14 @@
     sp<Surface> createSurface() const;
     sp<IBinder> getHandle() const;
 
+    sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
+
     status_t clearLayerFrameStats() const;
     status_t getLayerFrameStats(FrameStats* outStats) const;
 
     sp<SurfaceComposerClient> getClient() const;
+    
+    explicit SurfaceControl(const sp<SurfaceControl>& other);
 
 private:
     // can't be copied
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 01e90e0..f020a40 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -16,6 +16,8 @@
         "BufferItemConsumer_test.cpp",
         "BufferQueue_test.cpp",
         "CpuConsumer_test.cpp",
+        "EndToEndNativeInputTest.cpp",
+        "DisplayedContentSampling_test.cpp",
         "FillBuffer.cpp",
         "GLTest.cpp",
         "IGraphicBufferProducer_test.cpp",
@@ -35,6 +37,7 @@
     shared_libs: [
         "android.hardware.configstore@1.0",
         "android.hardware.configstore-utils",
+        "libbase",
         "liblog",
         "libEGL",
         "libGLESv1_CM",
@@ -44,15 +47,19 @@
         "libgui",
         "libhidlbase",
         "libhidltransport",
+        "libinput",
         "libui",
         "libutils",
         "libnativewindow"
     ],
 }
 
-// Build a separate binary for each source file to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+// This test has a main method, and requires a separate binary to be built.
+// To add move tests like this, just add additional cc_test statements,
+// as opposed to adding more source files to this one.
 cc_test {
-    name: "libgui_separate_binary_test",
+    name: "SurfaceParcelable_test",
     test_suites: ["device-tests"],
 
     clang: true,
@@ -61,7 +68,6 @@
         "-Werror",
     ],
 
-    test_per_src: true,
     srcs: [
         "SurfaceParcelable_test.cpp",
     ],
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 9a20859..119e888 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -61,7 +61,7 @@
     }
 
     void GetMinUndequeuedBufferCount(int* bufferCount) {
-        ASSERT_TRUE(bufferCount != NULL);
+        ASSERT_TRUE(bufferCount != nullptr);
         ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
                     bufferCount));
         ASSERT_GE(*bufferCount, 0);
@@ -82,7 +82,7 @@
         sp<Fence> fence;
 
         input.deflate(&timestamp, &isAutoTimestamp, &dataSpace, &crop,
-                &scalingMode, &transform, &fence, NULL);
+                &scalingMode, &transform, &fence, nullptr);
         ASSERT_EQ(timestamp, item.mTimestamp);
         ASSERT_EQ(isAutoTimestamp, item.mIsAutoTimestamp);
         ASSERT_EQ(dataSpace, item.mDataSpace);
@@ -128,17 +128,17 @@
     sp<IBinder> binderProducer =
         serviceManager->getService(PRODUCER_NAME);
     mProducer = interface_cast<IGraphicBufferProducer>(binderProducer);
-    EXPECT_TRUE(mProducer != NULL);
+    EXPECT_TRUE(mProducer != nullptr);
     sp<IBinder> binderConsumer =
         serviceManager->getService(CONSUMER_NAME);
     mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer);
-    EXPECT_TRUE(mConsumer != NULL);
+    EXPECT_TRUE(mConsumer != nullptr);
 
     sp<DummyConsumer> dc(new DummyConsumer);
     ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
     IGraphicBufferProducer::QueueBufferOutput output;
     ASSERT_EQ(OK,
-            mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));
+            mProducer->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output));
 
     int slot;
     sp<Fence> fence;
@@ -353,8 +353,8 @@
     ASSERT_EQ(OK, buffer->unlock());
 
     int newSlot;
-    ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(NULL, safeToClobberBuffer));
-    ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, NULL));
+    ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(nullptr, safeToClobberBuffer));
+    ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, nullptr));
 
     ASSERT_EQ(OK, mProducer->attachBuffer(&newSlot, buffer));
     IGraphicBufferProducer::QueueBufferInput input(0, false,
@@ -412,8 +412,8 @@
 
     int newSlot;
     sp<GraphicBuffer> safeToClobberBuffer;
-    ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(NULL, safeToClobberBuffer));
-    ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, NULL));
+    ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(nullptr, safeToClobberBuffer));
+    ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, nullptr));
     ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer));
 
     ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, EGL_NO_DISPLAY,
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 36be7d9..00e32d9 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -484,12 +484,12 @@
     err = native_window_dequeue_buffer_and_wait(anw.get(), &anb);
     ASSERT_NO_ERROR(err, "dequeueBuffer error: ");
 
-    ASSERT_TRUE(anb != NULL);
+    ASSERT_TRUE(anb != nullptr);
 
     sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
     *stride = buf->getStride();
-    uint8_t* img = NULL;
+    uint8_t* img = nullptr;
 
     ALOGVV("Lock buffer from %p for write", anw.get());
     err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
@@ -554,7 +554,7 @@
     err = mCC->lockNextBuffer(&b);
     ASSERT_NO_ERROR(err, "getNextBuffer error: ");
 
-    ASSERT_TRUE(b.data != NULL);
+    ASSERT_TRUE(b.data != nullptr);
     EXPECT_EQ(params.width,  b.width);
     EXPECT_EQ(params.height, b.height);
     EXPECT_EQ(params.format, b.format);
@@ -595,7 +595,7 @@
         err = mCC->lockNextBuffer(&b);
         ASSERT_NO_ERROR(err, "getNextBuffer error: ");
 
-        ASSERT_TRUE(b.data != NULL);
+        ASSERT_TRUE(b.data != nullptr);
         EXPECT_EQ(params.width,  b.width);
         EXPECT_EQ(params.height, b.height);
         EXPECT_EQ(params.format, b.format);
@@ -637,7 +637,7 @@
         err = mCC->lockNextBuffer(&b[i]);
         ASSERT_NO_ERROR(err, "getNextBuffer error: ");
 
-        ASSERT_TRUE(b[i].data != NULL);
+        ASSERT_TRUE(b[i].data != nullptr);
         EXPECT_EQ(params.width,  b[i].width);
         EXPECT_EQ(params.height, b[i].height);
         EXPECT_EQ(params.format, b[i].format);
@@ -660,7 +660,7 @@
     err = mCC->lockNextBuffer(&bTooMuch);
     ASSERT_NO_ERROR(err, "Did not allow new lock after unlock");
 
-    ASSERT_TRUE(bTooMuch.data != NULL);
+    ASSERT_TRUE(bTooMuch.data != nullptr);
     EXPECT_EQ(params.width,  bTooMuch.width);
     EXPECT_EQ(params.height, bTooMuch.height);
     EXPECT_EQ(params.format, bTooMuch.format);
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
new file mode 100644
index 0000000..5443812
--- /dev/null
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <binder/ProcessState.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <inttypes.h>
+
+namespace android {
+
+using Transaction = SurfaceComposerClient::Transaction;
+
+static constexpr uint32_t INVALID_MASK = 0x10;
+class DisplayedContentSamplingTest : public ::testing::Test {
+protected:
+    void SetUp() {
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(OK, mComposerClient->initCheck());
+        mDisplayToken = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+        ASSERT_TRUE(mDisplayToken);
+    }
+
+    bool shouldSkipTest() {
+        ui::PixelFormat format;
+        ui::Dataspace dataspace;
+        status_t status =
+                mComposerClient->getDisplayedContentSamplingAttributes(mDisplayToken, &format,
+                                                                       &dataspace, &componentMask);
+        if (status == PERMISSION_DENIED) {
+            SUCCEED() << "permissions denial, skipping test";
+            return true;
+        }
+        if (status == INVALID_OPERATION) {
+            SUCCEED() << "optional function not supported, skipping test";
+            return true;
+        }
+        return false;
+    }
+
+    sp<SurfaceComposerClient> mComposerClient;
+    sp<IBinder> mDisplayToken;
+    uint8_t componentMask = 0;
+};
+
+TEST_F(DisplayedContentSamplingTest, GetDisplayedContentSamplingAttributesAreSane) {
+    // tradefed infrastructure does not support use of GTEST_SKIP
+    if (shouldSkipTest()) return;
+
+    ui::PixelFormat format;
+    ui::Dataspace dataspace;
+    status_t status =
+            mComposerClient->getDisplayedContentSamplingAttributes(mDisplayToken, &format,
+                                                                   &dataspace, &componentMask);
+    EXPECT_EQ(OK, status);
+    EXPECT_LE(componentMask, INVALID_MASK);
+}
+
+TEST_F(DisplayedContentSamplingTest, EnableWithInvalidMaskReturnsBadValue) {
+    if (shouldSkipTest()) return;
+
+    status_t status =
+            mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true, INVALID_MASK, 0);
+    EXPECT_EQ(BAD_VALUE, status);
+}
+
+TEST_F(DisplayedContentSamplingTest, EnableAndDisableSucceed) {
+    if (shouldSkipTest()) return;
+
+    status_t status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true,
+                                                                        componentMask, 10);
+    EXPECT_EQ(OK, status);
+
+    status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, false, componentMask,
+                                                               0);
+    EXPECT_EQ(OK, status);
+}
+
+TEST_F(DisplayedContentSamplingTest, SelectivelyDisableComponentOk) {
+    if (shouldSkipTest()) return;
+
+    status_t status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true,
+                                                                        componentMask, 0);
+    EXPECT_EQ(OK, status);
+
+    // Clear the lowest bit.
+    componentMask &= (componentMask - 1);
+    status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, false, componentMask,
+                                                               0);
+    EXPECT_EQ(OK, status);
+}
+
+TEST_F(DisplayedContentSamplingTest, SampleCollectionCoherentWithSupportMask) {
+    if (shouldSkipTest()) return;
+
+    DisplayedFrameStats stats;
+    status_t status = mComposerClient->getDisplayedContentSample(mDisplayToken, 0, 0, &stats);
+    EXPECT_EQ(OK, status);
+    if (stats.numFrames <= 0) return;
+
+    if (componentMask & (0x1 << 0)) EXPECT_NE(0, stats.component_0_sample.size());
+    if (componentMask & (0x1 << 1)) EXPECT_NE(0, stats.component_1_sample.size());
+    if (componentMask & (0x1 << 2)) EXPECT_NE(0, stats.component_2_sample.size());
+    if (componentMask & (0x1 << 3)) EXPECT_NE(0, stats.component_3_sample.size());
+}
+
+} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
new file mode 100644
index 0000000..ac97733
--- /dev/null
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -0,0 +1,501 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <poll.h>
+
+#include <memory>
+
+#include <android/native_window.h>
+
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+#include <input/InputWindow.h>
+#include <input/IInputFlinger.h>
+#include <input/InputTransport.h>
+#include <input/Input.h>
+
+#include <ui/DisplayInfo.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+
+namespace android {
+namespace test {
+
+using Transaction = SurfaceComposerClient::Transaction;
+
+sp<IInputFlinger> getInputFlinger() {
+   sp<IBinder> input(defaultServiceManager()->getService(
+            String16("inputflinger")));
+    if (input == nullptr) {
+        ALOGE("Failed to link to input service");
+    } else { ALOGE("Linked to input"); }
+    return interface_cast<IInputFlinger>(input);
+}
+
+// We use the top 10 layers as a way to haphazardly place ourselves above anything else.
+static const int LAYER_BASE = INT32_MAX - 10;
+
+class InputSurface {
+public:
+    InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
+        mSurfaceControl = sc;
+
+        InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
+        mServerChannel->setToken(new BBinder());
+
+        mInputFlinger = getInputFlinger();
+        mInputFlinger->registerInputChannel(mServerChannel);
+
+        populateInputInfo(width, height);
+
+        mInputConsumer = new InputConsumer(mClientChannel);
+    }
+
+    static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient> &scc,
+                                                               int width, int height) {
+        sp<SurfaceControl> surfaceControl =
+                scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */,
+                                   PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+        return std::make_unique<InputSurface>(surfaceControl, width, height);
+    }
+
+    static std::unique_ptr<InputSurface> makeBufferInputSurface(
+            const sp<SurfaceComposerClient> &scc, int width, int height) {
+        sp<SurfaceControl> surfaceControl =
+                scc->createSurface(String8("Test Buffer Surface"), width, height,
+                                   PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
+        return std::make_unique<InputSurface>(surfaceControl, width, height);
+    }
+
+    static std::unique_ptr<InputSurface> makeContainerInputSurface(
+            const sp<SurfaceComposerClient> &scc, int width, int height) {
+        sp<SurfaceControl> surfaceControl =
+                scc->createSurface(String8("Test Container Surface"), 0 /* bufHeight */,
+                                   0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceContainer);
+        return std::make_unique<InputSurface>(surfaceControl, width, height);
+    }
+
+    InputEvent* consumeEvent() {
+        waitForEventAvailable();
+
+        InputEvent *ev;
+        uint32_t seqId;
+        status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev);
+        if (consumed != OK) {
+            return nullptr;
+        }
+        mInputConsumer->sendFinishedSignal(seqId, true);
+        return ev;
+    }
+
+    void expectTap(int x, int y) {
+        InputEvent* ev = consumeEvent();
+        EXPECT_TRUE(ev != nullptr);
+        EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+        MotionEvent* mev = static_cast<MotionEvent*>(ev);
+        EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
+        EXPECT_EQ(x, mev->getX(0));
+        EXPECT_EQ(y, mev->getY(0));
+
+        ev = consumeEvent();
+        EXPECT_TRUE(ev != nullptr);
+        EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+        mev = static_cast<MotionEvent*>(ev);
+        EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+    }
+
+    void expectMotionEvent(int motionEventType, int x, int y) {
+        InputEvent *ev = consumeEvent();
+        ASSERT_NE(ev, nullptr);
+        ASSERT_EQ(ev->getType(), AINPUT_EVENT_TYPE_MOTION);
+        MotionEvent *mev = static_cast<MotionEvent *>(ev);
+        EXPECT_EQ(motionEventType, mev->getAction());
+        EXPECT_EQ(x, mev->getX(0));
+        EXPECT_EQ(y, mev->getY(0));
+    }
+
+    void expectNoMotionEvent(int motionEventType) {
+        InputEvent *ev = consumeEvent();
+        if (ev == nullptr || ev->getType() != AINPUT_EVENT_TYPE_MOTION) {
+            // Didn't find an event or a motion event so assume action didn't occur.
+            return;
+        }
+
+        MotionEvent *mev = static_cast<MotionEvent *>(ev);
+        EXPECT_NE(motionEventType, mev->getAction());
+    }
+
+    ~InputSurface() {
+        mInputFlinger->unregisterInputChannel(mServerChannel);
+    }
+
+    void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
+                    const sp<SurfaceControl>&)> transactionBody) {
+        SurfaceComposerClient::Transaction t;
+        transactionBody(t, mSurfaceControl);
+        t.apply(true);
+    }
+
+    void showAt(int x, int y) {
+        SurfaceComposerClient::Transaction t;
+        t.show(mSurfaceControl);
+        t.setInputWindowInfo(mSurfaceControl, mInputInfo);
+        t.setLayer(mSurfaceControl, LAYER_BASE);
+        t.setPosition(mSurfaceControl, x, y);
+        t.setCrop_legacy(mSurfaceControl, Rect(0, 0, 100, 100));
+        t.setAlpha(mSurfaceControl, 1);
+        t.apply(true);
+    }
+
+private:
+    void waitForEventAvailable() {
+        struct pollfd fd;
+
+        fd.fd = mClientChannel->getFd();
+        fd.events = POLLIN;
+        poll(&fd, 1, 3000);
+    }
+
+    void populateInputInfo(int width, int height) {
+        mInputInfo.token = mServerChannel->getToken();
+        mInputInfo.name = "Test info";
+        mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
+        mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
+        mInputInfo.dispatchingTimeout = 100000;
+        mInputInfo.globalScaleFactor = 1.0;
+        mInputInfo.canReceiveKeys = true;
+        mInputInfo.hasFocus = true;
+        mInputInfo.hasWallpaper = false;
+        mInputInfo.paused = false;
+
+        mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
+
+        // TODO: Fill in from SF?
+        mInputInfo.ownerPid = 11111;
+        mInputInfo.ownerUid = 11111;
+        mInputInfo.inputFeatures = 0;
+        mInputInfo.displayId = 0;
+
+        InputApplicationInfo aInfo;
+        aInfo.token = new BBinder();
+        aInfo.name = "Test app info";
+        aInfo.dispatchingTimeout = 100000;
+
+        mInputInfo.applicationInfo = aInfo;
+    }
+public:
+    sp<SurfaceControl> mSurfaceControl;
+    sp<InputChannel> mServerChannel, mClientChannel;
+    sp<IInputFlinger> mInputFlinger;
+
+    InputWindowInfo mInputInfo;
+
+    PreallocatedInputEventFactory mInputEventFactory;
+    InputConsumer* mInputConsumer;
+};
+
+class InputSurfacesTest : public ::testing::Test {
+public:
+    InputSurfacesTest() {
+        ProcessState::self()->startThreadPool();
+    }
+
+    void SetUp() {
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+        DisplayInfo info;
+        auto display = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+        SurfaceComposerClient::getDisplayInfo(display, &info);
+
+        // After a new buffer is queued, SurfaceFlinger is notified and will
+        // latch the new buffer on next vsync.  Let's heuristically wait for 3
+        // vsyncs.
+        mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
+    }
+
+    void TearDown() {
+        mComposerClient->dispose();
+    }
+
+    std::unique_ptr<InputSurface> makeSurface(int width, int height) {
+        return InputSurface::makeColorInputSurface(mComposerClient, width, height);
+    }
+
+    void postBuffer(const sp<SurfaceControl> &layer) {
+        // wait for previous transactions (such as setSize) to complete
+        Transaction().apply(true);
+        ANativeWindow_Buffer buffer = {};
+        EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
+        ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
+        // Request an empty transaction to get applied synchronously to ensure the buffer is
+        // latched.
+        Transaction().apply(true);
+        usleep(mBufferPostDelay);
+    }
+
+    sp<SurfaceComposerClient> mComposerClient;
+    int32_t mBufferPostDelay;
+};
+
+void injectTap(int x, int y) {
+    char *buf1, *buf2;
+    asprintf(&buf1, "%d", x);
+    asprintf(&buf2, "%d", y);
+    if (fork() == 0) {
+        execlp("input", "input", "tap", buf1, buf2, NULL);
+    }
+}
+
+void injectMotionEvent(std::string event, int x, int y) {
+    char *buf1, *buf2;
+    asprintf(&buf1, "%d", x);
+    asprintf(&buf2, "%d", y);
+    if (fork() == 0) {
+        execlp("input", "input", "motionevent", event.c_str(), buf1, buf2, NULL);
+    }
+}
+
+TEST_F(InputSurfacesTest, can_receive_input) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(100, 100);
+
+    injectTap(101, 101);
+
+    EXPECT_TRUE(surface->consumeEvent() != nullptr);
+}
+
+TEST_F(InputSurfacesTest, input_respects_positioning) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(100, 100);
+
+    std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+    surface2->showAt(200, 200);
+
+    injectTap(201, 201);
+    surface2->expectTap(1, 1);
+
+    injectTap(101, 101);
+    surface->expectTap(1, 1);
+
+    surface2->doTransaction([](auto &t, auto &sc) {
+         t.setPosition(sc, 100, 100);
+    });
+    surface->doTransaction([](auto &t, auto &sc) {
+         t.setPosition(sc, 200, 200);
+    });
+
+    injectTap(101, 101);
+    surface2->expectTap(1, 1);
+
+    injectTap(201, 201);
+    surface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_layering) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+
+    surface->showAt(10, 10);
+    surface2->showAt(10, 10);
+
+    surface->doTransaction([](auto &t, auto &sc) {
+         t.setLayer(sc, LAYER_BASE + 1);
+    });
+
+    injectTap(11, 11);
+    surface->expectTap(1, 1);
+
+    surface2->doTransaction([](auto &t, auto &sc) {
+         t.setLayer(sc, LAYER_BASE + 1);
+    });
+
+    injectTap(11, 11);
+    surface2->expectTap(1, 1);
+
+    surface2->doTransaction([](auto &t, auto &sc) {
+         t.hide(sc);
+    });
+
+    injectTap(11, 11);
+    surface->expectTap(1, 1);
+}
+
+// Surface Insets are set to offset the client content and draw a border around the client surface
+// (such as shadows in dialogs). Inputs sent to the client are offset such that 0,0 is the start
+// of the client content.
+TEST_F(InputSurfacesTest, input_respects_surface_insets) {
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+    bgSurface->showAt(100, 100);
+
+    fgSurface->mInputInfo.surfaceInset = 5;
+    fgSurface->showAt(100, 100);
+
+    injectTap(106, 106);
+    fgSurface->expectTap(1, 1);
+
+    injectTap(101, 101);
+    bgSurface->expectTap(1, 1);
+}
+
+// Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463
+TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) {
+    std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
+    parentSurface->showAt(100, 100);
+
+    childSurface->mInputInfo.surfaceInset = 10;
+    childSurface->showAt(100, 100);
+
+    childSurface->doTransaction([&](auto &t, auto &sc) {
+        t.setPosition(sc, -5, -5);
+        t.reparent(sc, parentSurface->mSurfaceControl->getHandle());
+    });
+
+    injectTap(106, 106);
+    childSurface->expectTap(1, 1);
+
+    injectTap(101, 101);
+    parentSurface->expectTap(1, 1);
+}
+
+// Ensure we ignore transparent region when getting screen bounds when positioning input frame.
+TEST_F(InputSurfacesTest, input_ignores_transparent_region) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->doTransaction([](auto &t, auto &sc) {
+        Region transparentRegion(Rect(0, 0, 10, 10));
+        t.setTransparentRegionHint(sc, transparentRegion);
+    });
+    surface->showAt(100, 100);
+    injectTap(101, 101);
+    surface->expectTap(1, 1);
+}
+
+// Ensure we send the input to the right surface when the surface visibility changes due to the
+// first buffer being submitted. ref: b/120839715
+TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) {
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> bufferSurface =
+            InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+
+    bgSurface->showAt(10, 10);
+    bufferSurface->showAt(10, 10);
+
+    injectTap(11, 11);
+    bgSurface->expectTap(1, 1);
+
+    postBuffer(bufferSurface->mSurfaceControl);
+    injectTap(11, 11);
+    bufferSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) {
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> bufferSurface =
+            InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+    postBuffer(bufferSurface->mSurfaceControl);
+
+    bgSurface->showAt(10, 10);
+    bufferSurface->showAt(10, 10);
+
+    injectTap(11, 11);
+    bufferSurface->expectTap(1, 1);
+
+    bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
+
+    injectTap(11, 11);
+    bgSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) {
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+
+    bgSurface->showAt(10, 10);
+    fgSurface->showAt(10, 10);
+
+    injectTap(11, 11);
+    fgSurface->expectTap(1, 1);
+
+    fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
+
+    injectTap(11, 11);
+    bgSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) {
+    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+    std::unique_ptr<InputSurface> containerSurface =
+            InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+
+    bgSurface->showAt(10, 10);
+    containerSurface->showAt(10, 10);
+
+    injectTap(11, 11);
+    containerSurface->expectTap(1, 1);
+
+    containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });
+
+    injectTap(11, 11);
+    bgSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, transfer_touch_focus) {
+    std::unique_ptr<InputSurface> fromSurface = makeSurface(100, 100);
+
+    fromSurface->showAt(10, 10);
+    injectMotionEvent("DOWN", 11, 11);
+    fromSurface->expectMotionEvent(AMOTION_EVENT_ACTION_DOWN, 1, 1);
+
+    std::unique_ptr<InputSurface> toSurface = makeSurface(100, 100);
+    toSurface->showAt(10, 10);
+
+    sp<IBinder> fromToken = fromSurface->mServerChannel->getToken();
+    sp<IBinder> toToken = toSurface->mServerChannel->getToken();
+    SurfaceComposerClient::Transaction t;
+    t.transferTouchFocus(fromToken, toToken).apply(true);
+
+    injectMotionEvent("UP", 11, 11);
+    toSurface->expectMotionEvent(AMOTION_EVENT_ACTION_UP, 1, 1);
+    fromSurface->expectNoMotionEvent(AMOTION_EVENT_ACTION_UP);
+}
+
+TEST_F(InputSurfacesTest, input_respects_outscreen) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(-1, -1);
+
+    injectTap(0, 0);
+    surface->expectTap(1, 1);
+}
+}
+}
diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp
index ccd674f..b60995a 100644
--- a/libs/gui/tests/FillBuffer.cpp
+++ b/libs/gui/tests/FillBuffer.cpp
@@ -93,11 +93,11 @@
     android_native_buffer_t* anb;
     ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
             &anb));
-    ASSERT_TRUE(anb != NULL);
+    ASSERT_TRUE(anb != nullptr);
 
     sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
-    uint8_t* img = NULL;
+    uint8_t* img = nullptr;
     ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN,
             (void**)(&img)));
     fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride());
diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp
index a91552f..a1405fc 100644
--- a/libs/gui/tests/GLTest.cpp
+++ b/libs/gui/tests/GLTest.cpp
@@ -50,7 +50,7 @@
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
 
     char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS");
-    if (displaySecsEnv != NULL) {
+    if (displaySecsEnv != nullptr) {
         mDisplaySecs = atoi(displaySecsEnv);
         if (mDisplaySecs < 0) {
             mDisplaySecs = 0;
@@ -67,7 +67,7 @@
                 String8("Test Surface"), getSurfaceWidth(), getSurfaceHeight(),
                 PIXEL_FORMAT_RGB_888, 0);
 
-        ASSERT_TRUE(mSurfaceControl != NULL);
+        ASSERT_TRUE(mSurfaceControl != nullptr);
         ASSERT_TRUE(mSurfaceControl->isValid());
 
         Transaction t;
@@ -117,7 +117,7 @@
         sleep(mDisplaySecs);
     }
 
-    if (mComposerClient != NULL) {
+    if (mComposerClient != nullptr) {
         mComposerClient->dispose();
     }
     if (mEglContext != EGL_NO_CONTEXT) {
@@ -171,7 +171,7 @@
 
 EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config,
                                        sp<ANativeWindow>& window) const {
-    return eglCreateWindowSurface(display, config, window.get(), NULL);
+    return eglCreateWindowSurface(display, config, window.get(), nullptr);
 }
 
 ::testing::AssertionResult GLTest::checkPixel(int x, int y,
@@ -256,7 +256,7 @@
     GLuint shader = glCreateShader(shaderType);
     ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
     if (shader) {
-        glShaderSource(shader, 1, &pSource, NULL);
+        glShaderSource(shader, 1, &pSource, nullptr);
         ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
         glCompileShader(shader);
         ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
@@ -270,7 +270,7 @@
             if (infoLen) {
                 char* buf = (char*) malloc(infoLen);
                 if (buf) {
-                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
+                    glGetShaderInfoLog(shader, infoLen, nullptr, buf);
                     printf("Shader compile log:\n%s\n", buf);
                     free(buf);
                     FAIL();
@@ -278,7 +278,7 @@
             } else {
                 char* buf = (char*) malloc(0x1000);
                 if (buf) {
-                    glGetShaderInfoLog(shader, 0x1000, NULL, buf);
+                    glGetShaderInfoLog(shader, 0x1000, nullptr, buf);
                     printf("Shader compile log:\n%s\n", buf);
                     free(buf);
                     FAIL();
@@ -322,7 +322,7 @@
             if (bufLength) {
                 char* buf = (char*) malloc(bufLength);
                 if (buf) {
-                    glGetProgramInfoLog(program, bufLength, NULL, buf);
+                    glGetProgramInfoLog(program, bufLength, nullptr, buf);
                     printf("Program link log:\n%s\n", buf);
                     free(buf);
                     FAIL();
diff --git a/libs/gui/tests/GLTest.h b/libs/gui/tests/GLTest.h
index f0d27a8..f290b3c 100644
--- a/libs/gui/tests/GLTest.h
+++ b/libs/gui/tests/GLTest.h
@@ -39,7 +39,7 @@
             mEglDisplay(EGL_NO_DISPLAY),
             mEglSurface(EGL_NO_SURFACE),
             mEglContext(EGL_NO_CONTEXT),
-            mGlConfig(NULL) {
+            mGlConfig(nullptr) {
     }
 
     virtual void SetUp();
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index a35cf11..aef7aed 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -228,9 +228,9 @@
     void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
             sp<GraphicBuffer> *buffer)
     {
-        ASSERT_TRUE(slot != NULL);
-        ASSERT_TRUE(fence != NULL);
-        ASSERT_TRUE(buffer != NULL);
+        ASSERT_TRUE(slot != nullptr);
+        ASSERT_TRUE(fence != nullptr);
+        ASSERT_TRUE(buffer != nullptr);
 
         ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
@@ -263,7 +263,7 @@
     EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN,
                                             TEST_API,
                                             TEST_CONTROLLED_BY_APP,
-                                            /*output*/NULL));
+                                            /*output*/nullptr));
 
     // Invalid API returns bad value
     EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN,
@@ -359,7 +359,7 @@
     // TODO: Consider documented the above enums as unsupported or make a new enum for IGBP
 
     // Value was NULL
-    EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/NULL));
+    EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/nullptr));
 
     ASSERT_OK(mConsumer->consumerDisconnect());
 
@@ -465,7 +465,7 @@
 
     // Fence was NULL
     {
-        sp<Fence> nullFence = NULL;
+        sp<Fence> nullFence = nullptr;
 
         IGraphicBufferProducer::QueueBufferInput input =
                 QueueBufferInputBuilder().setFence(nullFence).build();
@@ -695,10 +695,7 @@
     sp<Fence> fence;
     sp<GraphicBuffer> buffer;
 
-    if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) {
-        // TODO(b/38137191): Implement BufferHubProducer::detachBuffer
-        ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence));
-    }
+    ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence));
 }
 
 TEST_P(IGraphicBufferProducerTest,
@@ -735,10 +732,7 @@
 
     ASSERT_OK(mProducer->disconnect(TEST_API));
 
-    if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) {
-        // TODO(b/38137191): Implement BufferHubProducer::detachBuffer
-        ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot));
-    }
+    ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot));
 }
 
 TEST_P(IGraphicBufferProducerTest,
@@ -778,18 +772,29 @@
     sp<GraphicBuffer> buffer;
 
     setupDequeueRequestBuffer(&slot, &fence, &buffer);
+    ASSERT_TRUE(buffer != nullptr);
 
-    if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) {
-        // TODO(b/38137191): Implement BufferHubProducer::detachBuffer
-        ASSERT_OK(mProducer->detachBuffer(slot));
-    }
+    ASSERT_OK(mProducer->detachBuffer(slot));
+    EXPECT_OK(buffer->initCheck());
 
     ASSERT_OK(mProducer->disconnect(TEST_API));
 
-    if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) {
-        // TODO(b/69981968): Implement BufferHubProducer::attachBuffer
-        ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer));
-    }
+    ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer));
+}
+
+TEST_P(IGraphicBufferProducerTest, DetachThenAttach_Succeeds) {
+    int slot = -1;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+
+    setupDequeueRequestBuffer(&slot, &fence, &buffer);
+    ASSERT_TRUE(buffer != nullptr);
+
+    ASSERT_OK(mProducer->detachBuffer(slot));
+    EXPECT_OK(buffer->initCheck());
+
+    EXPECT_OK(mProducer->attachBuffer(&slot, buffer));
+    EXPECT_OK(buffer->initCheck());
 }
 
 #if USE_BUFFER_HUB_AS_BUFFER_QUEUE
diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp
index 3a25ac5..7d3d4aa 100644
--- a/libs/gui/tests/MultiTextureConsumer_test.cpp
+++ b/libs/gui/tests/MultiTextureConsumer_test.cpp
@@ -47,7 +47,7 @@
         GLTest::TearDown();
     }
     virtual EGLint const* getContextAttribs() {
-        return NULL;
+        return nullptr;
     }
     virtual EGLint const* getConfigAttribs() {
         static EGLint sDefaultConfigAttribs[] = {
@@ -105,7 +105,7 @@
 
     glClear(GL_COLOR_BUFFER_BIT);
     for (int i=0 ; i<8 ; i++) {
-        mSurface->lock(&buffer, NULL);
+        mSurface->lock(&buffer, nullptr);
         memset(buffer.bits, (i&7) * 0x20, buffer.stride * buffer.height * 4);
         mSurface->unlockAndPost();
 
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index d5b2f00..65e09f2 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -29,7 +29,6 @@
 #include <utils/Thread.h>
 
 extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-#define CROP_EXT_STR "EGL_ANDROID_image_crop"
 
 namespace android {
 
@@ -39,7 +38,7 @@
             mEglDisplay(EGL_NO_DISPLAY),
             mEglSurface(EGL_NO_SURFACE),
             mEglContext(EGL_NO_CONTEXT),
-            mEglConfig(NULL) {
+            mEglConfig(nullptr) {
     }
 
     virtual void SetUp() {
@@ -82,7 +81,7 @@
         ASSERT_EQ(EGL_SUCCESS, eglGetError());
         ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
 
-        mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0);
+        mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, nullptr);
         ASSERT_EQ(EGL_SUCCESS, eglGetError());
         ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
 
@@ -127,7 +126,7 @@
 
 TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) {
     sp<IGraphicBufferProducer> ist(mSTC->getIGraphicBufferProducer());
-    ASSERT_TRUE(ist != NULL);
+    ASSERT_TRUE(ist != nullptr);
 }
 
 TEST_F(SurfaceTextureClientTest, QueuesToWindowCompositorIsFalse) {
@@ -155,7 +154,7 @@
     EXPECT_TRUE(eglInitialize(dpy, &majorVersion, &minorVersion));
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
 
-    EGLConfig myConfig = {0};
+    EGLConfig myConfig = {nullptr};
     EGLint numConfigs = 0;
     EGLint configAttribs[] = {
         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
@@ -172,7 +171,7 @@
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
 
     EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, mANW.get(),
-            NULL);
+            nullptr);
     EXPECT_NE(EGL_NO_SURFACE, eglSurface);
     EXPECT_EQ(EGL_SUCCESS, eglGetError());
 
@@ -185,7 +184,7 @@
 
 TEST_F(SurfaceTextureClientTest, EglSwapBuffersAbandonErrorIsEglBadSurface) {
 
-    EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), NULL);
+    EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), nullptr);
     EXPECT_NE(EGL_NO_SURFACE, eglSurface);
     EXPECT_EQ(EGL_SUCCESS, eglGetError());
 
@@ -638,18 +637,6 @@
 }
 
 TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) {
-    // Query to see if the image crop extension exists
-    EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
-    size_t cropExtLen = strlen(CROP_EXT_STR);
-    size_t extsLen = strlen(exts);
-    bool equal = !strcmp(CROP_EXT_STR, exts);
-    bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1);
-    bool atEnd = (cropExtLen+1) < extsLen &&
-            !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1));
-    bool inMiddle = strstr(exts, " " CROP_EXT_STR " ");
-    bool hasEglAndroidImageCrop = equal || atStart || atEnd || inMiddle;
-
     android_native_buffer_t* buf[3];
     float mtx[16] = {};
     android_native_rect_t crop;
@@ -669,17 +656,15 @@
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers
     mST->getTransformMatrix(mtx);
 
-    // If the egl image crop extension is not present, this accounts for the
-    // .5 texel shrink for each edge that's included in the transform matrix
-    // to avoid texturing outside the crop region. Otherwise the crop is not
-    // included in the transform matrix.
-    EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5, mtx[0]);
+    // This accounts for the .5 texel shrink for each edge that's included in
+    // the transform matrix to avoid texturing outside the crop region.
+    EXPECT_EQ(0.5f, mtx[0]);
     EXPECT_EQ(0.f, mtx[1]);
     EXPECT_EQ(0.f, mtx[2]);
     EXPECT_EQ(0.f, mtx[3]);
 
     EXPECT_EQ(0.f, mtx[4]);
-    EXPECT_EQ(hasEglAndroidImageCrop ? -1 : -0.5, mtx[5]);
+    EXPECT_EQ(-0.5f, mtx[5]);
     EXPECT_EQ(0.f, mtx[6]);
     EXPECT_EQ(0.f, mtx[7]);
 
@@ -688,8 +673,8 @@
     EXPECT_EQ(1.f, mtx[10]);
     EXPECT_EQ(0.f, mtx[11]);
 
-    EXPECT_EQ(hasEglAndroidImageCrop ? 0 : 0.0625f, mtx[12]);
-    EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5625f, mtx[13]);
+    EXPECT_EQ(0.0625f, mtx[12]);
+    EXPECT_EQ(0.5625f, mtx[13]);
     EXPECT_EQ(0.f, mtx[14]);
     EXPECT_EQ(1.f, mtx[15]);
 }
@@ -753,7 +738,7 @@
         ASSERT_EQ(EGL_SUCCESS, eglGetError());
 
         mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT,
-                0);
+                nullptr);
         ASSERT_EQ(EGL_SUCCESS, eglGetError());
         ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
 
@@ -765,7 +750,7 @@
                     GLConsumer::TEXTURE_EXTERNAL, true, false));
             sp<Surface> stc(new Surface(producer));
             mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig,
-                    static_cast<ANativeWindow*>(stc.get()), NULL);
+                    static_cast<ANativeWindow*>(stc.get()), nullptr);
             ASSERT_EQ(EGL_SUCCESS, eglGetError());
             ASSERT_NE(EGL_NO_SURFACE, mEglSurfaces[i]);
         }
diff --git a/libs/gui/tests/SurfaceTextureFBO.h b/libs/gui/tests/SurfaceTextureFBO.h
index 7f1ae84..70f988d 100644
--- a/libs/gui/tests/SurfaceTextureFBO.h
+++ b/libs/gui/tests/SurfaceTextureFBO.h
@@ -34,7 +34,7 @@
         glGenTextures(1, &mFboTex);
         glBindTexture(GL_TEXTURE_2D, mFboTex);
         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(),
-                getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+                getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
         glBindTexture(GL_TEXTURE_2D, 0);
         ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
 
diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp
index 0134273..f34561f 100644
--- a/libs/gui/tests/SurfaceTextureFBO_test.cpp
+++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp
@@ -39,12 +39,12 @@
     android_native_buffer_t* anb;
     ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
             &anb));
-    ASSERT_TRUE(anb != NULL);
+    ASSERT_TRUE(anb != nullptr);
 
     sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
     // Fill the buffer with green
-    uint8_t* img = NULL;
+    uint8_t* img = nullptr;
     buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
     fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255,
             0, 255);
@@ -63,7 +63,7 @@
 
         ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
                 &anb));
-        ASSERT_TRUE(anb != NULL);
+        ASSERT_TRUE(anb != nullptr);
 
         buf = GraphicBuffer::from(anb);
 
diff --git a/libs/gui/tests/SurfaceTextureGLThreadToGL.h b/libs/gui/tests/SurfaceTextureGLThreadToGL.h
index 2ce20eb..03975b1 100644
--- a/libs/gui/tests/SurfaceTextureGLThreadToGL.h
+++ b/libs/gui/tests/SurfaceTextureGLThreadToGL.h
@@ -158,7 +158,7 @@
     }
 
     virtual void TearDown() {
-        if (mProducerThread != NULL) {
+        if (mProducerThread != nullptr) {
             mProducerThread->requestExitAndWait();
         }
         mProducerThread.clear();
@@ -167,7 +167,7 @@
     }
 
     void runProducerThread(const sp<ProducerThread> producerThread) {
-        ASSERT_TRUE(mProducerThread == NULL);
+        ASSERT_TRUE(mProducerThread == nullptr);
         mProducerThread = producerThread;
         producerThread->setEglObjects(mEglDisplay, mProducerEglSurface,
                 mProducerEglContext);
diff --git a/libs/gui/tests/SurfaceTextureGLToGL.h b/libs/gui/tests/SurfaceTextureGLToGL.h
index 5d43a48..3a87c12 100644
--- a/libs/gui/tests/SurfaceTextureGLToGL.h
+++ b/libs/gui/tests/SurfaceTextureGLToGL.h
@@ -38,7 +38,7 @@
 
     void SetUpWindowAndContext() {
         mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
-                mANW.get(), NULL);
+                mANW.get(), nullptr);
         ASSERT_EQ(EGL_SUCCESS, eglGetError());
         ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);
 
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 5639286..e2b4f3d 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -40,12 +40,12 @@
     ANativeWindowBuffer* anb;
     ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
             &anb));
-    ASSERT_TRUE(anb != NULL);
+    ASSERT_TRUE(anb != nullptr);
 
     sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
     // Fill the buffer with the a checkerboard pattern
-    uint8_t* img = NULL;
+    uint8_t* img = nullptr;
     buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
     fillYV12Buffer(img, texWidth, texHeight, buf->getStride());
     buf->unlock();
@@ -90,12 +90,12 @@
     ANativeWindowBuffer* anb;
     ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
             &anb));
-    ASSERT_TRUE(anb != NULL);
+    ASSERT_TRUE(anb != nullptr);
 
     sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
     // Fill the buffer with the a checkerboard pattern
-    uint8_t* img = NULL;
+    uint8_t* img = nullptr;
     buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
     fillYV12Buffer(img, texWidth, texHeight, buf->getStride());
     buf->unlock();
@@ -155,11 +155,11 @@
         ANativeWindowBuffer* anb;
         ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
                 &anb));
-        ASSERT_TRUE(anb != NULL);
+        ASSERT_TRUE(anb != nullptr);
 
         sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
 
-        uint8_t* img = NULL;
+        uint8_t* img = nullptr;
         buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
         fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop);
         buf->unlock();
@@ -234,7 +234,7 @@
                         &anb) != NO_ERROR) {
                     return false;
                 }
-                if (anb == NULL) {
+                if (anb == nullptr) {
                     return false;
                 }
 
@@ -248,7 +248,7 @@
                 int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * texHeight/2;
                 int yuvTexStrideU = yuvTexStrideV;
 
-                uint8_t* img = NULL;
+                uint8_t* img = nullptr;
                 buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
 
                 // Gray out all the test pixels first, so we're more likely to
@@ -457,7 +457,7 @@
                         &anb) != NO_ERROR) {
                     return false;
                 }
-                if (anb == NULL) {
+                if (anb == nullptr) {
                     return false;
                 }
                 if (mANW->queueBuffer(mANW.get(), anb, -1)
@@ -641,7 +641,7 @@
                     &anb) != NO_ERROR) {
                 return false;
             }
-            if (anb == NULL) {
+            if (anb == nullptr) {
                 return false;
             }
             if (mANW->queueBuffer(mANW.get(), anb, -1)
@@ -654,7 +654,7 @@
                     &anb) != NO_ERROR) {
                 return false;
             }
-            if (anb == NULL) {
+            if (anb == nullptr) {
                 return false;
             }
             if (mANW->queueBuffer(mANW.get(), anb, -1)
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 959aafc..1705fd7 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -72,7 +72,7 @@
         mSurfaceControl = mComposerClient->createSurface(
                 String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0);
 
-        ASSERT_TRUE(mSurfaceControl != NULL);
+        ASSERT_TRUE(mSurfaceControl != nullptr);
         ASSERT_TRUE(mSurfaceControl->isValid());
 
         Transaction t;
@@ -81,7 +81,7 @@
                 .apply());
 
         mSurface = mSurfaceControl->getSurface();
-        ASSERT_TRUE(mSurface != NULL);
+        ASSERT_TRUE(mSurface != nullptr);
     }
 
     virtual void TearDown() {
@@ -134,8 +134,9 @@
     sp<IBinder> display(sf->getBuiltInDisplay(
             ISurfaceComposer::eDisplayIdMain));
     sp<GraphicBuffer> outBuffer;
-    ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(),
-            64, 64, 0, 0x7fffffff, false));
+    ASSERT_EQ(NO_ERROR,
+              sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB,
+                                ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
 
     ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
             NATIVE_WINDOW_API_CPU));
@@ -145,7 +146,7 @@
     ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(),
             GRALLOC_USAGE_PROTECTED));
     ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3));
-    ANativeWindowBuffer* buf = 0;
+    ANativeWindowBuffer* buf = nullptr;
 
     status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf);
     if (err) {
@@ -165,8 +166,9 @@
                 &buf));
         ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
     }
-    ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(),
-            64, 64, 0, 0x7fffffff, false));
+    ASSERT_EQ(NO_ERROR,
+              sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB,
+                                ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
 }
 
 TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
@@ -205,7 +207,7 @@
 }
 
 TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) {
-    const android_dataspace TEST_DATASPACE = HAL_DATASPACE_SRGB;
+    const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB;
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
@@ -364,10 +366,17 @@
         78.0,
         62.0,
     };
+
+    std::vector<uint8_t> hdr10plus;
+    hdr10plus.push_back(0xff);
+
     int error = native_window_set_buffers_smpte2086_metadata(window.get(), &smpte2086);
     ASSERT_EQ(error, NO_ERROR);
     error = native_window_set_buffers_cta861_3_metadata(window.get(), &cta861_3);
     ASSERT_EQ(error, NO_ERROR);
+    error = native_window_set_buffers_hdr10_plus_metadata(window.get(), hdr10plus.size(),
+                                                          hdr10plus.data());
+    ASSERT_EQ(error, NO_ERROR);
 }
 
 TEST_F(SurfaceTest, DynamicSetBufferCount) {
@@ -536,10 +545,6 @@
     }
 
     sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
-    sp<ISurfaceComposerClient> createScopedConnection(
-            const sp<IGraphicBufferProducer>& /* parent */) override {
-        return nullptr;
-    }
     sp<IDisplayEventConnection> createDisplayEventConnection(ISurfaceComposer::VsyncSource)
             override {
         return nullptr;
@@ -549,8 +554,11 @@
     void destroyDisplay(const sp<IBinder>& /*display */) override {}
     sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; }
     void setTransactionState(const Vector<ComposerState>& /*state*/,
-            const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/)
-            override {}
+                             const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+                             const sp<IBinder>& /*applyToken*/,
+                             const InputWindowCommands& /*inputWindowCommands*/,
+                             int64_t /*desiredPresentTime*/) override {}
+
     void bootFinished() override {}
     bool authenticateSurfaceTexture(
             const sp<IGraphicBufferProducer>& /*surface*/) const override {
@@ -581,9 +589,6 @@
             Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; }
     status_t getDisplayStats(const sp<IBinder>& /*display*/,
             DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
-    status_t getDisplayViewport(const sp<IBinder>& /*display*/, Rect* /*outViewport*/) override {
-        return NO_ERROR;
-    }
     int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
     status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/)
             override {
@@ -593,21 +598,29 @@
             Vector<ColorMode>* /*outColorModes*/) override {
         return NO_ERROR;
     }
+    status_t getDisplayNativePrimaries(const sp<IBinder>& /*display*/,
+            ui::DisplayPrimaries& /*primaries*/) override {
+        return NO_ERROR;
+    }
     ColorMode getActiveColorMode(const sp<IBinder>& /*display*/)
             override {
         return ColorMode::NATIVE;
     }
     status_t setActiveColorMode(const sp<IBinder>& /*display*/,
         ColorMode /*colorMode*/) override { return NO_ERROR; }
-    status_t captureScreen(const sp<IBinder>& /*display*/,
-            sp<GraphicBuffer>* /*outBuffer*/,
-            Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
-            int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/,
-            bool /*useIdentityTransform*/,
-            Rotation /*rotation*/) override { return NO_ERROR; }
+    status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/,
+                           const ui::Dataspace /*reqDataspace*/,
+                           const ui::PixelFormat /*reqPixelFormat*/, Rect /*sourceCrop*/,
+                           uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
+                           bool /*useIdentityTransform*/, Rotation /*rotation*/) override {
+        return NO_ERROR;
+    }
     virtual status_t captureLayers(const sp<IBinder>& /*parentHandle*/,
-                                   sp<GraphicBuffer>* /*outBuffer*/, const Rect& /*sourceCrop*/,
-                                   float /*frameScale*/, bool /*childrenOnly*/) override {
+                                   sp<GraphicBuffer>* /*outBuffer*/,
+                                   const ui::Dataspace /*reqDataspace*/,
+                                   const ui::PixelFormat /*reqPixelFormat*/,
+                                   const Rect& /*sourceCrop*/, float /*frameScale*/,
+                                   bool /*childrenOnly*/) override {
         return NO_ERROR;
     }
     status_t clearAnimationFrameStats() override { return NO_ERROR; }
@@ -625,6 +638,38 @@
     status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override {
         return NO_ERROR;
     }
+    status_t getCompositionPreference(
+            ui::Dataspace* /*outDefaultDataspace*/, ui::PixelFormat* /*outDefaultPixelFormat*/,
+            ui::Dataspace* /*outWideColorGamutDataspace*/,
+            ui::PixelFormat* /*outWideColorGamutPixelFormat*/) const override {
+        return NO_ERROR;
+    }
+    status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& /*display*/,
+                                                   ui::PixelFormat* /*outFormat*/,
+                                                   ui::Dataspace* /*outDataspace*/,
+                                                   uint8_t* /*outComponentMask*/) const override {
+        return NO_ERROR;
+    }
+    status_t setDisplayContentSamplingEnabled(const sp<IBinder>& /*display*/, bool /*enable*/,
+                                              uint8_t /*componentMask*/,
+                                              uint64_t /*maxFrames*/) const override {
+        return NO_ERROR;
+    }
+    status_t getDisplayedContentSample(const sp<IBinder>& /*display*/, uint64_t /*maxFrames*/,
+                                       uint64_t /*timestamp*/,
+                                       DisplayedFrameStats* /*outStats*/) const override {
+        return NO_ERROR;
+    }
+
+    status_t getColorManagement(bool* /*outGetColorManagement*/) const override { return NO_ERROR; }
+    status_t getProtectedContentSupport(bool* /*outSupported*/) const override { return NO_ERROR; }
+
+    status_t cacheBuffer(const sp<IBinder>& /*token*/, const sp<GraphicBuffer>& /*buffer*/,
+                         int32_t* /*outBufferId*/) {
+        return NO_ERROR;
+    }
+    status_t uncacheBuffer(const sp<IBinder>& /*token*/, int32_t /*bufferId*/) { return NO_ERROR; }
+    status_t isWideColorDisplay(const sp<IBinder>&, bool*) const override { return NO_ERROR; }
 
 protected:
     IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 2f39976..fc676f1 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -42,15 +42,18 @@
     target: {
         android: {
             srcs: [
-                "IInputFlinger.cpp",
                 "InputTransport.cpp",
                 "VelocityControl.cpp",
                 "VelocityTracker.cpp",
+                "InputApplication.cpp",
+                "InputWindow.cpp",
+                "IInputFlinger.cpp"
             ],
 
             shared_libs: [
                 "libutils",
                 "libbinder",
+                "libui"
             ],
 
             sanitize: {
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
index 003e73d..acf40bc 100644
--- a/libs/input/IInputFlinger.cpp
+++ b/libs/input/IInputFlinger.cpp
@@ -23,7 +23,6 @@
 
 #include <input/IInputFlinger.h>
 
-
 namespace android {
 
 class BpInputFlinger : public BpInterface<IInputFlinger> {
@@ -31,23 +30,81 @@
     explicit BpInputFlinger(const sp<IBinder>& impl) :
             BpInterface<IInputFlinger>(impl) { }
 
-    virtual status_t doSomething() {
+    virtual void setInputWindows(const Vector<InputWindowInfo>& inputInfo) {
         Parcel data, reply;
         data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
-        remote()->transact(BnInputFlinger::DO_SOMETHING_TRANSACTION, data, &reply);
-        return reply.readInt32();
+
+        data.writeUint32(static_cast<uint32_t>(inputInfo.size()));
+        for (const auto& info : inputInfo) {
+            info.write(data);
+        }
+        remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply,
+                IBinder::FLAG_ONEWAY);
+    }
+
+    virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
+
+        data.writeStrongBinder(fromToken);
+        data.writeStrongBinder(toToken);
+        remote()->transact(BnInputFlinger::TRANSFER_TOUCH_FOCUS, data, &reply,
+                IBinder::FLAG_ONEWAY);
+    }
+
+    virtual void registerInputChannel(const sp<InputChannel>& channel) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
+        channel->write(data);
+        remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
+    }
+
+    virtual void unregisterInputChannel(const sp<InputChannel>& channel) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
+        channel->write(data);
+        remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
     }
 };
 
 IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger");
 
-
 status_t BnInputFlinger::onTransact(
         uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
     switch(code) {
-    case DO_SOMETHING_TRANSACTION: {
+    case SET_INPUT_WINDOWS_TRANSACTION: {
         CHECK_INTERFACE(IInputFlinger, data, reply);
-        reply->writeInt32(0);
+        size_t count = data.readUint32();
+        if (count > data.dataSize()) {
+            return BAD_VALUE;
+        }
+        Vector<InputWindowInfo> handles;
+        handles.setCapacity(count);
+        for (size_t i = 0; i < count; i++) {
+            handles.add(InputWindowInfo(data));
+        }
+        setInputWindows(handles);
+        break;
+    }
+    case REGISTER_INPUT_CHANNEL_TRANSACTION: {
+        CHECK_INTERFACE(IInputFlinger, data, reply);
+        sp<InputChannel> channel = new InputChannel();
+        channel->read(data);
+        registerInputChannel(channel);
+        break;
+    }
+    case UNREGISTER_INPUT_CHANNEL_TRANSACTION: {
+        CHECK_INTERFACE(IInputFlinger, data, reply);
+        sp<InputChannel> channel = new InputChannel();
+        channel->read(data);
+        unregisterInputChannel(channel);
+        break;
+    }
+    case TRANSFER_TOUCH_FOCUS: {
+        CHECK_INTERFACE(IInputFlinger, data, reply);
+        sp<IBinder> fromToken = data.readStrongBinder();
+        sp<IBinder> toToken = data.readStrongBinder();
+        transferTouchFocus(fromToken, toToken);
         break;
     }
     default:
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index a624663..9fd25f9 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -29,16 +29,29 @@
 
 namespace android {
 
+const char* motionClassificationToString(MotionClassification classification) {
+    switch (classification) {
+        case MotionClassification::NONE:
+            return "NONE";
+        case MotionClassification::AMBIGUOUS_GESTURE:
+            return "AMBIGUOUS_GESTURE";
+        case MotionClassification::DEEP_PRESS:
+            return "DEEP_PRESS";
+    }
+}
+
 // --- InputEvent ---
 
-void InputEvent::initialize(int32_t deviceId, int32_t source) {
+void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
     mDeviceId = deviceId;
     mSource = source;
+    mDisplayId = displayId;
 }
 
 void InputEvent::initialize(const InputEvent& from) {
     mDeviceId = from.mDeviceId;
     mSource = from.mSource;
+    mDisplayId = from.mDisplayId;
 }
 
 // --- KeyEvent ---
@@ -54,6 +67,7 @@
 void KeyEvent::initialize(
         int32_t deviceId,
         int32_t source,
+        int32_t displayId,
         int32_t action,
         int32_t flags,
         int32_t keyCode,
@@ -62,7 +76,7 @@
         int32_t repeatCount,
         nsecs_t downTime,
         nsecs_t eventTime) {
-    InputEvent::initialize(deviceId, source);
+    InputEvent::initialize(deviceId, source, displayId);
     mAction = action;
     mFlags = flags;
     mKeyCode = keyCode;
@@ -128,15 +142,24 @@
     }
 }
 
-void PointerCoords::scale(float scaleFactor) {
+void PointerCoords::scale(float globalScaleFactor, float windowXScale, float windowYScale) {
     // No need to scale pressure or size since they are normalized.
     // No need to scale orientation since it is meaningless to do so.
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor);
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor);
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor);
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor);
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor);
-    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor);
+
+    // If there is a global scale factor, it is included in the windowX/YScale
+    // so we don't need to apply it twice to the X/Y axes.
+    // However we don't want to apply any windowXYScale not included in the global scale
+    // to the TOUCH_MAJOR/MINOR coordinates.
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, windowXScale);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, windowYScale);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, globalScaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, globalScaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, globalScaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, globalScaleFactor);
+}
+
+void PointerCoords::scale(float globalScaleFactor) {
+    scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
 }
 
 void PointerCoords::applyOffset(float xOffset, float yOffset) {
@@ -215,12 +238,14 @@
 void MotionEvent::initialize(
         int32_t deviceId,
         int32_t source,
+        int32_t displayId,
         int32_t action,
         int32_t actionButton,
         int32_t flags,
         int32_t edgeFlags,
         int32_t metaState,
         int32_t buttonState,
+        MotionClassification classification,
         float xOffset,
         float yOffset,
         float xPrecision,
@@ -230,13 +255,14 @@
         size_t pointerCount,
         const PointerProperties* pointerProperties,
         const PointerCoords* pointerCoords) {
-    InputEvent::initialize(deviceId, source);
+    InputEvent::initialize(deviceId, source, displayId);
     mAction = action;
     mActionButton = actionButton;
     mFlags = flags;
     mEdgeFlags = edgeFlags;
     mMetaState = metaState;
     mButtonState = buttonState;
+    mClassification = classification;
     mXOffset = xOffset;
     mYOffset = yOffset;
     mXPrecision = xPrecision;
@@ -250,13 +276,14 @@
 }
 
 void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
-    InputEvent::initialize(other->mDeviceId, other->mSource);
+    InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId);
     mAction = other->mAction;
     mActionButton = other->mActionButton;
     mFlags = other->mFlags;
     mEdgeFlags = other->mEdgeFlags;
     mMetaState = other->mMetaState;
     mButtonState = other->mButtonState;
+    mClassification = other->mClassification;
     mXOffset = other->mXOffset;
     mYOffset = other->mYOffset;
     mXPrecision = other->mXPrecision;
@@ -341,15 +368,15 @@
     mYOffset += yOffset;
 }
 
-void MotionEvent::scale(float scaleFactor) {
-    mXOffset *= scaleFactor;
-    mYOffset *= scaleFactor;
-    mXPrecision *= scaleFactor;
-    mYPrecision *= scaleFactor;
+void MotionEvent::scale(float globalScaleFactor) {
+    mXOffset *= globalScaleFactor;
+    mYOffset *= globalScaleFactor;
+    mXPrecision *= globalScaleFactor;
+    mYPrecision *= globalScaleFactor;
 
     size_t numSamples = mSamplePointerCoords.size();
     for (size_t i = 0; i < numSamples; i++) {
-        mSamplePointerCoords.editItemAt(i).scale(scaleFactor);
+        mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor);
     }
 }
 
@@ -431,12 +458,14 @@
 
     mDeviceId = parcel->readInt32();
     mSource = parcel->readInt32();
+    mDisplayId = parcel->readInt32();
     mAction = parcel->readInt32();
     mActionButton = parcel->readInt32();
     mFlags = parcel->readInt32();
     mEdgeFlags = parcel->readInt32();
     mMetaState = parcel->readInt32();
     mButtonState = parcel->readInt32();
+    mClassification = static_cast<MotionClassification>(parcel->readByte());
     mXOffset = parcel->readFloat();
     mYOffset = parcel->readFloat();
     mXPrecision = parcel->readFloat();
@@ -480,12 +509,14 @@
 
     parcel->writeInt32(mDeviceId);
     parcel->writeInt32(mSource);
+    parcel->writeInt32(mDisplayId);
     parcel->writeInt32(mAction);
     parcel->writeInt32(mActionButton);
     parcel->writeInt32(mFlags);
     parcel->writeInt32(mEdgeFlags);
     parcel->writeInt32(mMetaState);
     parcel->writeInt32(mButtonState);
+    parcel->writeByte(static_cast<int8_t>(mClassification));
     parcel->writeFloat(mXOffset);
     parcel->writeFloat(mYOffset);
     parcel->writeFloat(mXPrecision);
diff --git a/services/inputflinger/InputApplication.cpp b/libs/input/InputApplication.cpp
similarity index 64%
rename from services/inputflinger/InputApplication.cpp
rename to libs/input/InputApplication.cpp
index 9e90631..7936f50 100644
--- a/services/inputflinger/InputApplication.cpp
+++ b/libs/input/InputApplication.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "InputApplication"
 
-#include "InputApplication.h"
+#include <input/InputApplication.h>
 
 #include <android/log.h>
 
@@ -25,7 +25,7 @@
 // --- InputApplicationHandle ---
 
 InputApplicationHandle::InputApplicationHandle() :
-    mInfo(NULL) {
+    mInfo(nullptr) {
 }
 
 InputApplicationHandle::~InputApplicationHandle() {
@@ -35,8 +35,25 @@
 void InputApplicationHandle::releaseInfo() {
     if (mInfo) {
         delete mInfo;
-        mInfo = NULL;
+        mInfo = nullptr;
     }
 }
 
+InputApplicationInfo InputApplicationInfo::read(const Parcel& from) {
+    InputApplicationInfo ret;
+    ret.token = from.readStrongBinder();
+    ret.name = from.readString8().c_str();
+    ret.dispatchingTimeout = from.readInt64();
+
+    return ret;
+}
+
+status_t InputApplicationInfo::write(Parcel& output) const {
+    output.writeStrongBinder(token);
+    output.writeString8(String8(name.c_str()));
+    output.writeInt64(dispatchingTimeout);
+    
+    return OK;
+}
+
 } // namespace android
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 4287abe..778c453 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -20,9 +20,12 @@
 #include <unistd.h>
 #include <ctype.h>
 
+#include <android-base/stringprintf.h>
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
 
+using android::base::StringPrintf;
+
 namespace android {
 
 static const char* CONFIGURATION_FILE_DIR[] = {
@@ -41,8 +44,8 @@
     return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_');
 }
 
-static void appendInputDeviceConfigurationFileRelativePath(String8& path,
-        const String8& name, InputDeviceConfigurationFileType type) {
+static void appendInputDeviceConfigurationFileRelativePath(std::string& path,
+        const std::string& name, InputDeviceConfigurationFileType type) {
     path.append(CONFIGURATION_FILE_DIR[type]);
     for (size_t i = 0; i < name.length(); i++) {
         char ch = name[i];
@@ -54,28 +57,28 @@
     path.append(CONFIGURATION_FILE_EXTENSION[type]);
 }
 
-String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
+std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
         const InputDeviceIdentifier& deviceIdentifier,
         InputDeviceConfigurationFileType type) {
     if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
         if (deviceIdentifier.version != 0) {
             // Try vendor product version.
-            String8 versionPath(getInputDeviceConfigurationFilePathByName(
-                    String8::format("Vendor_%04x_Product_%04x_Version_%04x",
+            std::string versionPath = getInputDeviceConfigurationFilePathByName(
+                    StringPrintf("Vendor_%04x_Product_%04x_Version_%04x",
                             deviceIdentifier.vendor, deviceIdentifier.product,
                             deviceIdentifier.version),
-                    type));
-            if (!versionPath.isEmpty()) {
+                    type);
+            if (!versionPath.empty()) {
                 return versionPath;
             }
         }
 
         // Try vendor product.
-        String8 productPath(getInputDeviceConfigurationFilePathByName(
-                String8::format("Vendor_%04x_Product_%04x",
+        std::string productPath = getInputDeviceConfigurationFilePathByName(
+                StringPrintf("Vendor_%04x_Product_%04x",
                         deviceIdentifier.vendor, deviceIdentifier.product),
-                type));
-        if (!productPath.isEmpty()) {
+                type);
+        if (!productPath.empty()) {
             return productPath;
         }
     }
@@ -84,22 +87,25 @@
     return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
 }
 
-String8 getInputDeviceConfigurationFilePathByName(
-        const String8& name, InputDeviceConfigurationFileType type) {
+std::string getInputDeviceConfigurationFilePathByName(
+        const std::string& name, InputDeviceConfigurationFileType type) {
     // Search system repository.
-    String8 path;
+    std::string path;
 
     // Treblized input device config files will be located /odm/usr or /vendor/usr.
     const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
     for (size_t i = 0; i < size(rootsForPartition); i++) {
-        path.setTo(rootsForPartition[i]);
-        path.append("/usr/");
+        if (rootsForPartition[i] == nullptr) {
+            continue;
+        }
+        path = rootsForPartition[i];
+        path += "/usr/";
         appendInputDeviceConfigurationFileRelativePath(path, name, type);
 #if DEBUG_PROBE
         ALOGD("Probing for system provided input device configuration file: path='%s'",
-              path.string());
+              path.c_str());
 #endif
-        if (!access(path.string(), R_OK)) {
+        if (!access(path.c_str(), R_OK)) {
 #if DEBUG_PROBE
             ALOGD("Found");
 #endif
@@ -109,13 +115,17 @@
 
     // Search user repository.
     // TODO Should only look here if not in safe mode.
-    path.setTo(getenv("ANDROID_DATA"));
-    path.append("/system/devices/");
+    path = "";
+    char *androidData = getenv("ANDROID_DATA");
+    if (androidData != nullptr) {
+        path += androidData;
+    }
+    path += "/system/devices/";
     appendInputDeviceConfigurationFileRelativePath(path, name, type);
 #if DEBUG_PROBE
-    ALOGD("Probing for system user input device configuration file: path='%s'", path.string());
+    ALOGD("Probing for system user input device configuration file: path='%s'", path.c_str());
 #endif
-    if (!access(path.string(), R_OK)) {
+    if (!access(path.c_str(), R_OK)) {
 #if DEBUG_PROBE
         ALOGD("Found");
 #endif
@@ -125,16 +135,16 @@
     // Not found.
 #if DEBUG_PROBE
     ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
-            name.string(), type);
+            name.c_str(), type);
 #endif
-    return String8();
+    return "";
 }
 
 
 // --- InputDeviceInfo ---
 
 InputDeviceInfo::InputDeviceInfo() {
-    initialize(-1, 0, -1, InputDeviceIdentifier(), String8(), false, false);
+    initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false);
 }
 
 InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
@@ -150,7 +160,7 @@
 }
 
 void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber,
-        const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal,
+        const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal,
         bool hasMic) {
     mId = id;
     mGeneration = generation;
@@ -175,7 +185,7 @@
             return &range;
         }
     }
-    return NULL;
+    return nullptr;
 }
 
 void InputDeviceInfo::addSource(uint32_t source) {
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 03f593f..0f7a1f0 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -30,6 +30,7 @@
 #include <cutils/properties.h>
 #include <log/log.h>
 
+#include <binder/Parcel.h>
 #include <input/InputTransport.h>
 
 namespace android {
@@ -156,6 +157,8 @@
             msg->body.motion.metaState = body.motion.metaState;
             // int32_t buttonState
             msg->body.motion.buttonState = body.motion.buttonState;
+            // MotionClassification classification
+            msg->body.motion.classification = body.motion.classification;
             // int32_t edgeFlags
             msg->body.motion.edgeFlags = body.motion.edgeFlags;
             // nsecs_t downTime
@@ -200,15 +203,13 @@
 // --- InputChannel ---
 
 InputChannel::InputChannel(const std::string& name, int fd) :
-        mName(name), mFd(fd) {
+        mName(name) {
 #if DEBUG_CHANNEL_LIFECYCLE
     ALOGD("Input channel constructed: name='%s', fd=%d",
             mName.c_str(), fd);
 #endif
 
-    int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
-    LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "
-            "non-blocking.  errno=%d", mName.c_str(), errno);
+    setFd(fd);
 }
 
 InputChannel::~InputChannel() {
@@ -220,6 +221,18 @@
     ::close(mFd);
 }
 
+void InputChannel::setFd(int fd) {
+    if (mFd > 0) {
+        ::close(mFd);
+    }
+    mFd = fd;
+    if (mFd > 0) {
+        int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
+        LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "
+            "non-blocking.  errno=%d", mName.c_str(), errno);
+    }
+}
+
 status_t InputChannel::openInputChannelPair(const std::string& name,
         sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
     int sockets[2];
@@ -328,10 +341,51 @@
 
 sp<InputChannel> InputChannel::dup() const {
     int fd = ::dup(getFd());
-    return fd >= 0 ? new InputChannel(getName(), fd) : NULL;
+    return fd >= 0 ? new InputChannel(getName(), fd) : nullptr;
 }
 
 
+status_t InputChannel::write(Parcel& out) const {
+    status_t s = out.writeString8(String8(getName().c_str()));
+
+    if (s != OK) {
+        return s;
+    }
+    s = out.writeStrongBinder(mToken);
+    if (s != OK) {
+        return s;
+    }
+
+    s = out.writeDupFileDescriptor(getFd());
+
+    return s;
+}
+
+status_t InputChannel::read(const Parcel& from) {
+    mName = from.readString8();
+    mToken = from.readStrongBinder();
+
+    int rawFd = from.readFileDescriptor();
+    setFd(::dup(rawFd));
+
+    if (mFd < 0) {
+        return BAD_VALUE;
+    }
+
+    return OK;
+}
+
+sp<IBinder> InputChannel::getToken() const {
+    return mToken;
+}
+
+void InputChannel::setToken(const sp<IBinder>& token) {
+    if (mToken != nullptr) {
+        ALOGE("Assigning InputChannel (%s) a second handle?", mName.c_str());
+    }
+    mToken = token;
+}
+
 // --- InputPublisher ---
 
 InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
@@ -345,6 +399,7 @@
         uint32_t seq,
         int32_t deviceId,
         int32_t source,
+        int32_t displayId,
         int32_t action,
         int32_t flags,
         int32_t keyCode,
@@ -372,6 +427,7 @@
     msg.body.key.seq = seq;
     msg.body.key.deviceId = deviceId;
     msg.body.key.source = source;
+    msg.body.key.displayId = displayId;
     msg.body.key.action = action;
     msg.body.key.flags = flags;
     msg.body.key.keyCode = keyCode;
@@ -394,6 +450,7 @@
         int32_t edgeFlags,
         int32_t metaState,
         int32_t buttonState,
+        MotionClassification classification,
         float xOffset,
         float yOffset,
         float xPrecision,
@@ -405,12 +462,14 @@
         const PointerCoords* pointerCoords) {
 #if DEBUG_TRANSPORT_ACTIONS
     ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
+            "displayId=%" PRId32 ", "
             "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
-            "metaState=0x%x, buttonState=0x%x, xOffset=%f, yOffset=%f, "
+            "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
             "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
             "pointerCount=%" PRIu32,
             mChannel->getName().c_str(), seq,
-            deviceId, source, action, actionButton, flags, edgeFlags, metaState, buttonState,
+            deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState,
+            buttonState, motionClassificationToString(classification),
             xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
 #endif
 
@@ -437,6 +496,7 @@
     msg.body.motion.edgeFlags = edgeFlags;
     msg.body.motion.metaState = metaState;
     msg.body.motion.buttonState = buttonState;
+    msg.body.motion.classification = classification;
     msg.body.motion.xOffset = xOffset;
     msg.body.motion.yOffset = yOffset;
     msg.body.motion.xPrecision = xPrecision;
@@ -486,7 +546,7 @@
 
 bool InputConsumer::isTouchResamplingEnabled() {
     char value[PROPERTY_VALUE_MAX];
-    int length = property_get("ro.input.noresample", value, NULL);
+    int length = property_get("ro.input.noresample", value, nullptr);
     if (length > 0) {
         if (!strcmp("1", value)) {
             return false;
@@ -500,16 +560,14 @@
 }
 
 status_t InputConsumer::consume(InputEventFactoryInterface* factory,
-        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent,
-        int32_t* displayId) {
+        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
 #if DEBUG_TRANSPORT_ACTIONS
     ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
             mChannel->getName().c_str(), consumeBatches ? "true" : "false", frameTime);
 #endif
 
     *outSeq = 0;
-    *outEvent = NULL;
-    *displayId = -1;  // Invalid display.
+    *outEvent = nullptr;
 
     // Fetch the next input message.
     // Loop until an event can be returned or no additional events are received.
@@ -524,7 +582,7 @@
             if (result) {
                 // Consume the next batched event unless batches are being held for later.
                 if (consumeBatches || result != WOULD_BLOCK) {
-                    result = consumeBatch(factory, frameTime, outSeq, outEvent, displayId);
+                    result = consumeBatch(factory, frameTime, outSeq, outEvent);
                     if (*outEvent) {
 #if DEBUG_TRANSPORT_ACTIONS
                         ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
@@ -568,7 +626,7 @@
                     // the previous batch right now and defer the new message until later.
                     mMsgDeferred = true;
                     status_t result = consumeSamples(factory,
-                            batch, batch.samples.size(), outSeq, outEvent, displayId);
+                            batch, batch.samples.size(), outSeq, outEvent);
                     mBatches.removeAt(batchIndex);
                     if (result) {
                         return result;
@@ -602,7 +660,7 @@
             initializeMotionEvent(motionEvent, &mMsg);
             *outSeq = mMsg.body.motion.seq;
             *outEvent = motionEvent;
-            *displayId = mMsg.body.motion.displayId;
+
 #if DEBUG_TRANSPORT_ACTIONS
             ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
                     mChannel->getName().c_str(), *outSeq);
@@ -620,14 +678,13 @@
 }
 
 status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory,
-        nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) {
+        nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
     status_t result;
     for (size_t i = mBatches.size(); i > 0; ) {
         i--;
         Batch& batch = mBatches.editItemAt(i);
         if (frameTime < 0) {
-            result = consumeSamples(factory, batch, batch.samples.size(),
-                    outSeq, outEvent, displayId);
+            result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent);
             mBatches.removeAt(i);
             return result;
         }
@@ -641,11 +698,11 @@
             continue;
         }
 
-        result = consumeSamples(factory, batch, split + 1, outSeq, outEvent, displayId);
+        result = consumeSamples(factory, batch, split + 1, outSeq, outEvent);
         const InputMessage* next;
         if (batch.samples.isEmpty()) {
             mBatches.removeAt(i);
-            next = NULL;
+            next = nullptr;
         } else {
             next = &batch.samples.itemAt(0);
         }
@@ -659,7 +716,7 @@
 }
 
 status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory,
-        Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) {
+        Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) {
     MotionEvent* motionEvent = factory->createMotionEvent();
     if (! motionEvent) return NO_MEMORY;
 
@@ -674,7 +731,6 @@
             mSeqChains.push(seqChain);
             addSample(motionEvent, &msg);
         } else {
-            *displayId = msg.body.motion.displayId;
             initializeMotionEvent(motionEvent, &msg);
         }
         chain = msg.body.motion.seq;
@@ -1030,6 +1086,7 @@
     event->initialize(
             msg->body.key.deviceId,
             msg->body.key.source,
+            msg->body.key.displayId,
             msg->body.key.action,
             msg->body.key.flags,
             msg->body.key.keyCode,
@@ -1052,12 +1109,14 @@
     event->initialize(
             msg->body.motion.deviceId,
             msg->body.motion.source,
+            msg->body.motion.displayId,
             msg->body.motion.action,
             msg->body.motion.actionButton,
             msg->body.motion.flags,
             msg->body.motion.edgeFlags,
             msg->body.motion.metaState,
             msg->body.motion.buttonState,
+            msg->body.motion.classification,
             msg->body.motion.xOffset,
             msg->body.motion.yOffset,
             msg->body.motion.xPrecision,
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
new file mode 100644
index 0000000..5c5613d
--- /dev/null
+++ b/libs/input/InputWindow.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 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_TAG "InputWindow"
+#define LOG_NDEBUG 0
+
+#include <binder/Parcel.h>
+#include <input/InputWindow.h>
+#include <input/InputTransport.h>
+
+#include <log/log.h>
+
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+namespace android {
+
+// --- InputWindowInfo ---
+void InputWindowInfo::addTouchableRegion(const Rect& region) {
+    touchableRegion.orSelf(region);
+}
+
+bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
+    return touchableRegion.contains(x,y);
+}
+
+bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
+    return x >= frameLeft && x < frameRight
+            && y >= frameTop && y < frameBottom;
+}
+
+bool InputWindowInfo::isTrustedOverlay() const {
+    return layoutParamsType == TYPE_INPUT_METHOD
+            || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
+            || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY
+            || layoutParamsType == TYPE_STATUS_BAR
+            || layoutParamsType == TYPE_NAVIGATION_BAR
+            || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL
+            || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY
+            || layoutParamsType == TYPE_DOCK_DIVIDER
+            || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY
+            || layoutParamsType == TYPE_INPUT_CONSUMER;
+}
+
+bool InputWindowInfo::supportsSplitTouch() const {
+    return layoutParamsFlags & FLAG_SPLIT_TOUCH;
+}
+
+bool InputWindowInfo::overlaps(const InputWindowInfo* other) const {
+    return frameLeft < other->frameRight && frameRight > other->frameLeft
+            && frameTop < other->frameBottom && frameBottom > other->frameTop;
+}
+
+status_t InputWindowInfo::write(Parcel& output) const {
+    if (token == nullptr) {
+        output.writeInt32(0);
+        return OK;
+    }
+    output.writeInt32(1);
+    status_t s = output.writeStrongBinder(token);
+    if (s != OK) return s;
+
+    output.writeString8(String8(name.c_str()));
+    output.writeInt32(layoutParamsFlags);
+    output.writeInt32(layoutParamsType);
+    output.writeInt64(dispatchingTimeout);
+    output.writeInt32(frameLeft);
+    output.writeInt32(frameTop);
+    output.writeInt32(frameRight);
+    output.writeInt32(frameBottom);
+    output.writeInt32(surfaceInset);
+    output.writeFloat(globalScaleFactor);
+    output.writeFloat(windowXScale);
+    output.writeFloat(windowYScale);
+    output.writeBool(visible);
+    output.writeBool(canReceiveKeys);
+    output.writeBool(hasFocus);
+    output.writeBool(hasWallpaper);
+    output.writeBool(paused);
+    output.writeInt32(layer);
+    output.writeInt32(ownerPid);
+    output.writeInt32(ownerUid);
+    output.writeInt32(inputFeatures);
+    output.writeInt32(displayId);
+    output.writeInt32(portalToDisplayId);
+    applicationInfo.write(output);
+    output.write(touchableRegion);
+
+    return OK;
+}
+
+InputWindowInfo InputWindowInfo::read(const Parcel& from) {
+    InputWindowInfo ret;
+
+    if (from.readInt32() == 0) {
+        return ret;
+    }
+
+    sp<IBinder> token = from.readStrongBinder();
+    if (token == nullptr) {
+        return ret;
+    }
+
+    ret.token = token;
+    ret.name = from.readString8().c_str();
+    ret.layoutParamsFlags = from.readInt32();
+    ret.layoutParamsType = from.readInt32();
+    ret.dispatchingTimeout = from.readInt64();
+    ret.frameLeft = from.readInt32();
+    ret.frameTop = from.readInt32();
+    ret.frameRight = from.readInt32();
+    ret.frameBottom = from.readInt32();
+    ret.surfaceInset = from.readInt32();
+    ret.globalScaleFactor = from.readFloat();
+    ret.windowXScale = from.readFloat();
+    ret.windowYScale = from.readFloat();
+    ret.visible = from.readBool();
+    ret.canReceiveKeys = from.readBool();
+    ret.hasFocus = from.readBool();
+    ret.hasWallpaper = from.readBool();
+    ret.paused = from.readBool();
+    ret.layer = from.readInt32();
+    ret.ownerPid = from.readInt32();
+    ret.ownerUid = from.readInt32();
+    ret.inputFeatures = from.readInt32();
+    ret.displayId = from.readInt32();
+    ret.portalToDisplayId = from.readInt32();
+    ret.applicationInfo = InputApplicationInfo::read(from);
+    from.read(ret.touchableRegion);
+
+    return ret;
+}
+
+InputWindowInfo::InputWindowInfo(const Parcel& from) {
+    *this = read(from);
+}
+
+// --- InputWindowHandle ---
+
+InputWindowHandle::InputWindowHandle() {
+}
+
+InputWindowHandle::~InputWindowHandle() {
+}
+
+void InputWindowHandle::releaseChannel() {
+    mInfo.token.clear();
+}
+
+sp<IBinder> InputWindowHandle::getToken() const {
+    return mInfo.token;
+}
+
+void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) {
+    mInfo = handle->mInfo;
+}
+
+} // namespace android
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index cba1111..e189d20 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -106,14 +106,14 @@
     }
 }
 
-status_t KeyCharacterMap::load(const String8& filename,
+status_t KeyCharacterMap::load(const std::string& filename,
         Format format, sp<KeyCharacterMap>* outMap) {
     outMap->clear();
 
     Tokenizer* tokenizer;
-    status_t status = Tokenizer::open(filename, &tokenizer);
+    status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
     if (status) {
-        ALOGE("Error %d opening key character map file %s.", status, filename.string());
+        ALOGE("Error %d opening key character map file %s.", status, filename.c_str());
     } else {
         status = load(tokenizer, format, outMap);
         delete tokenizer;
@@ -121,12 +121,12 @@
     return status;
 }
 
-status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents,
+status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents,
         Format format, sp<KeyCharacterMap>* outMap) {
     outMap->clear();
 
     Tokenizer* tokenizer;
-    status_t status = Tokenizer::fromContents(filename, contents, &tokenizer);
+    status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
     if (status) {
         ALOGE("Error %d opening key character map.", status);
     } else {
@@ -164,10 +164,10 @@
 
 sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
         const sp<KeyCharacterMap>& overlay) {
-    if (overlay == NULL) {
+    if (overlay == nullptr) {
         return base;
     }
-    if (base == NULL) {
+    if (base == nullptr) {
         return overlay;
     }
 
@@ -468,7 +468,7 @@
 
         // Try to find the most general behavior that maps to this character.
         // For example, the base key behavior will usually be last in the list.
-        const Behavior* found = NULL;
+        const Behavior* found = nullptr;
         for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
             if (behavior->character == ch) {
                 found = behavior;
@@ -487,7 +487,7 @@
         int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
     outEvents.push();
     KeyEvent& event = outEvents.editTop();
-    event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD,
+    event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
             down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
             0, keyCode, 0, metaState, 0, time, time);
 }
@@ -605,11 +605,11 @@
     map->mType = parcel->readInt32();
     size_t numKeys = parcel->readInt32();
     if (parcel->errorCheck()) {
-        return NULL;
+        return nullptr;
     }
     if (numKeys > MAX_KEYS) {
         ALOGE("Too many keys in KeyCharacterMap (%zu > %d)", numKeys, MAX_KEYS);
-        return NULL;
+        return nullptr;
     }
 
     for (size_t i = 0; i < numKeys; i++) {
@@ -617,7 +617,7 @@
         char16_t label = parcel->readInt32();
         char16_t number = parcel->readInt32();
         if (parcel->errorCheck()) {
-            return NULL;
+            return nullptr;
         }
 
         Key* key = new Key();
@@ -625,14 +625,14 @@
         key->number = number;
         map->mKeys.add(keyCode, key);
 
-        Behavior* lastBehavior = NULL;
+        Behavior* lastBehavior = nullptr;
         while (parcel->readInt32()) {
             int32_t metaState = parcel->readInt32();
             char16_t character = parcel->readInt32();
             int32_t fallbackKeyCode = parcel->readInt32();
             int32_t replacementKeyCode = parcel->readInt32();
             if (parcel->errorCheck()) {
-                return NULL;
+                return nullptr;
             }
 
             Behavior* behavior = new Behavior();
@@ -649,7 +649,7 @@
         }
 
         if (parcel->errorCheck()) {
-            return NULL;
+            return nullptr;
         }
     }
     return map;
@@ -666,7 +666,7 @@
         parcel->writeInt32(keyCode);
         parcel->writeInt32(key->label);
         parcel->writeInt32(key->number);
-        for (const Behavior* behavior = key->firstBehavior; behavior != NULL;
+        for (const Behavior* behavior = key->firstBehavior; behavior != nullptr;
                 behavior = behavior->next) {
             parcel->writeInt32(1);
             parcel->writeInt32(behavior->metaState);
@@ -683,12 +683,12 @@
 // --- KeyCharacterMap::Key ---
 
 KeyCharacterMap::Key::Key() :
-        label(0), number(0), firstBehavior(NULL) {
+        label(0), number(0), firstBehavior(nullptr) {
 }
 
 KeyCharacterMap::Key::Key(const Key& other) :
         label(other.label), number(other.number),
-        firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) {
+        firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : nullptr) {
 }
 
 KeyCharacterMap::Key::~Key() {
@@ -704,11 +704,11 @@
 // --- KeyCharacterMap::Behavior ---
 
 KeyCharacterMap::Behavior::Behavior() :
-        next(NULL), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) {
+        next(nullptr), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) {
 }
 
 KeyCharacterMap::Behavior::Behavior(const Behavior& other) :
-        next(other.next ? new Behavior(*other.next) : NULL),
+        next(other.next ? new Behavior(*other.next) : nullptr),
         metaState(other.metaState), character(other.character),
         fallbackKeyCode(other.fallbackKeyCode),
         replacementKeyCode(other.replacementKeyCode) {
@@ -944,7 +944,7 @@
             properties.add(Property(PROPERTY_NUMBER));
         } else {
             int32_t metaState;
-            status_t status = parseModifier(token, &metaState);
+            status_t status = parseModifier(token.string(), &metaState);
             if (status) {
                 ALOGE("%s: Expected a property name or modifier, got '%s'.",
                         mTokenizer->getLocation().string(), token.string());
@@ -1137,7 +1137,7 @@
     return NO_ERROR;
 }
 
-status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) {
+status_t KeyCharacterMap::Parser::parseModifier(const std::string& token, int32_t* outMetaState) {
     if (token == "base") {
         *outMetaState = 0;
         return NO_ERROR;
@@ -1145,7 +1145,7 @@
 
     int32_t combinedMeta = 0;
 
-    const char* str = token.string();
+    const char* str = token.c_str();
     const char* start = str;
     for (const char* cur = str; ; cur++) {
         char ch = *cur;
@@ -1164,7 +1164,7 @@
             }
             if (combinedMeta & metaState) {
                 ALOGE("%s: Duplicate modifier combination '%s'.",
-                        mTokenizer->getLocation().string(), token.string());
+                        mTokenizer->getLocation().string(), token.c_str());
                 return BAD_VALUE;
             }
 
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 2b2f13e..88cb0db 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -49,13 +49,13 @@
 KeyLayoutMap::~KeyLayoutMap() {
 }
 
-status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) {
+status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) {
     outMap->clear();
 
     Tokenizer* tokenizer;
-    status_t status = Tokenizer::open(filename, &tokenizer);
+    status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
     if (status) {
-        ALOGE("Error %d opening key layout map file %s.", status, filename.string());
+        ALOGE("Error %d opening key layout map file %s.", status, filename.c_str());
     } else {
         sp<KeyLayoutMap> map = new KeyLayoutMap();
         if (!map.get()) {
@@ -117,7 +117,7 @@
             return &mKeysByScanCode.valueAt(index);
         }
     }
-    return NULL;
+    return nullptr;
 }
 
 status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 11842ee..0c22bfe 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -45,22 +45,22 @@
         String8 keyLayoutName;
         if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
                 keyLayoutName)) {
-            status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
+            status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName.c_str());
             if (status == NAME_NOT_FOUND) {
                 ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
                         "it was not found.",
-                        deviceIdenfifier.name.string(), keyLayoutName.string());
+                        deviceIdenfifier.name.c_str(), keyLayoutName.string());
             }
         }
 
         String8 keyCharacterMapName;
         if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
                 keyCharacterMapName)) {
-            status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
+            status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName.c_str());
             if (status == NAME_NOT_FOUND) {
                 ALOGE("Configuration for keyboard device '%s' requested keyboard character "
                         "map '%s' but it was not found.",
-                        deviceIdenfifier.name.string(), keyLayoutName.string());
+                        deviceIdenfifier.name.c_str(), keyLayoutName.string());
             }
         }
 
@@ -70,30 +70,30 @@
     }
 
     // Try searching by device identifier.
-    if (probeKeyMap(deviceIdenfifier, String8::empty())) {
+    if (probeKeyMap(deviceIdenfifier, "")) {
         return OK;
     }
 
     // Fall back on the Generic key map.
     // TODO Apply some additional heuristics here to figure out what kind of
     //      generic key map to use (US English, etc.) for typical external keyboards.
-    if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
+    if (probeKeyMap(deviceIdenfifier, "Generic")) {
         return OK;
     }
 
     // Try the Virtual key map as a last resort.
-    if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {
+    if (probeKeyMap(deviceIdenfifier, "Virtual")) {
         return OK;
     }
 
     // Give up!
     ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
-            deviceIdenfifier.name.string());
+            deviceIdenfifier.name.c_str());
     return NAME_NOT_FOUND;
 }
 
 bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
-        const String8& keyMapName) {
+        const std::string& keyMapName) {
     if (!haveKeyLayout()) {
         loadKeyLayout(deviceIdentifier, keyMapName);
     }
@@ -104,10 +104,10 @@
 }
 
 status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
-        const String8& name) {
-    String8 path(getPath(deviceIdentifier, name,
+        const std::string& name) {
+    std::string path(getPath(deviceIdentifier, name,
             INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
-    if (path.isEmpty()) {
+    if (path.empty()) {
         return NAME_NOT_FOUND;
     }
 
@@ -116,15 +116,15 @@
         return status;
     }
 
-    keyLayoutFile.setTo(path);
+    keyLayoutFile = path;
     return OK;
 }
 
 status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
-        const String8& name) {
-    String8 path(getPath(deviceIdentifier, name,
-            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
-    if (path.isEmpty()) {
+        const std::string& name) {
+    std::string path = getPath(deviceIdentifier, name,
+            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP);
+    if (path.empty()) {
         return NAME_NOT_FOUND;
     }
 
@@ -134,13 +134,13 @@
         return status;
     }
 
-    keyCharacterMapFile.setTo(path);
+    keyCharacterMapFile = path;
     return OK;
 }
 
-String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
-        const String8& name, InputDeviceConfigurationFileType type) {
-    return name.isEmpty()
+std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
+        const std::string& name, InputDeviceConfigurationFileType type) {
+    return name.empty()
             ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
             : getInputDeviceConfigurationFilePathByName(name, type);
 }
@@ -174,7 +174,7 @@
         }
     }
 
-    return strstr(deviceIdentifier.name.string(), "-keypad");
+    return strstr(deviceIdentifier.name.c_str(), "-keypad");
 }
 
 static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index f834c55..42d774e 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -117,7 +117,7 @@
 
     // Allow the default strategy to be overridden using a system property for debugging.
     if (!strategy) {
-        int length = property_get("debug.velocitytracker.strategy", value, NULL);
+        int length = property_get("persist.input.velocitytracker.strategy", value, nullptr);
         if (length > 0) {
             strategy = value;
         } else {
@@ -141,7 +141,7 @@
 
 bool VelocityTracker::configureStrategy(const char* strategy) {
     mStrategy = createStrategy(strategy);
-    return mStrategy != NULL;
+    return mStrategy != nullptr;
 }
 
 VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
@@ -206,7 +206,7 @@
         // time to adjust to changes in direction.
         return new LegacyVelocityTrackerStrategy();
     }
-    return NULL;
+    return nullptr;
 }
 
 void VelocityTracker::clear() {
diff --git a/libs/input/VirtualKeyMap.cpp b/libs/input/VirtualKeyMap.cpp
index 28ea717..3ec53bf 100644
--- a/libs/input/VirtualKeyMap.cpp
+++ b/libs/input/VirtualKeyMap.cpp
@@ -46,13 +46,13 @@
 VirtualKeyMap::~VirtualKeyMap() {
 }
 
-status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) {
-    *outMap = NULL;
+status_t VirtualKeyMap::load(const std::string& filename, VirtualKeyMap** outMap) {
+    *outMap = nullptr;
 
     Tokenizer* tokenizer;
-    status_t status = Tokenizer::open(filename, &tokenizer);
+    status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
     if (status) {
-        ALOGE("Error %d opening virtual key map file %s.", status, filename.string());
+        ALOGE("Error %d opening virtual key map file %s.", status, filename.c_str());
     } else {
         VirtualKeyMap* map = new VirtualKeyMap();
         if (!map) {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index f06119f..fdd945e 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -6,6 +6,7 @@
         "InputEvent_test.cpp",
         "InputPublisherAndConsumer_test.cpp",
         "VelocityTracker_test.cpp",
+        "InputWindow_test.cpp"
     ],
     cflags: [
         "-Wall",
@@ -34,4 +35,12 @@
         "-Wall",
         "-Werror",
     ],
+    shared_libs: [
+        "libinput",
+        "libcutils",
+        "libutils",
+        "libbinder",
+        "libui",
+        "libbase",
+    ]
 }
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index 96c165c..f1675c0 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <array>
+
 #include "TestHelpers.h"
 
 #include <unistd.h>
@@ -155,5 +157,37 @@
             << "sendMessage should have returned DEAD_OBJECT";
 }
 
+TEST_F(InputChannelTest, SendAndReceive_MotionClassification) {
+    sp<InputChannel> serverChannel, clientChannel;
+    status_t result = InputChannel::openInputChannelPair("channel name",
+            serverChannel, clientChannel);
+    ASSERT_EQ(OK, result)
+            << "should have successfully opened a channel pair";
+
+    std::array<MotionClassification, 3> classifications = {
+        MotionClassification::NONE,
+        MotionClassification::AMBIGUOUS_GESTURE,
+        MotionClassification::DEEP_PRESS,
+    };
+
+    InputMessage serverMsg = {}, clientMsg;
+    serverMsg.header.type = InputMessage::TYPE_MOTION;
+    serverMsg.body.motion.seq = 1;
+    serverMsg.body.motion.pointerCount = 1;
+
+    for (MotionClassification classification : classifications) {
+        // Send and receive a message with classification
+        serverMsg.body.motion.classification = classification;
+        EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg))
+                << "server channel should be able to send message to client channel";
+
+        EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg))
+                << "client channel should be able to receive message from server channel";
+        EXPECT_EQ(serverMsg.header.type, clientMsg.header.type);
+        EXPECT_EQ(classification, clientMsg.body.motion.classification) <<
+                "Expected to receive " << motionClassificationToString(classification);
+    }
+}
+
 
 } // namespace android
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index fd3b7c8..2b75c82 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <array>
 #include <math.h>
 
 #include <binder/Parcel.h>
@@ -22,11 +23,10 @@
 
 namespace android {
 
-class BaseTest : public testing::Test {
-protected:
-    virtual void SetUp() { }
-    virtual void TearDown() { }
-};
+// Default display id.
+static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+
+class BaseTest : public testing::Test { };
 
 // --- PointerCoordsTest ---
 
@@ -178,13 +178,14 @@
     // Initialize and get properties.
     const nsecs_t ARBITRARY_DOWN_TIME = 1;
     const nsecs_t ARBITRARY_EVENT_TIME = 2;
-    event.initialize(2, AINPUT_SOURCE_GAMEPAD, AKEY_EVENT_ACTION_DOWN,
+    event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN,
             AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121,
             AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME);
 
     ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType());
     ASSERT_EQ(2, event.getDeviceId());
     ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_GAMEPAD), event.getSource());
+    ASSERT_EQ(DISPLAY_ID, event.getDisplayId());
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction());
     ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags());
     ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode());
@@ -197,6 +198,11 @@
     // Set source.
     event.setSource(AINPUT_SOURCE_JOYSTICK);
     ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+
+    // Set display id.
+    constexpr int32_t newDisplayId = 2;
+    event.setDisplayId(newDisplayId);
+    ASSERT_EQ(newDisplayId, event.getDisplayId());
 }
 
 
@@ -248,10 +254,10 @@
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
-    event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, 0,
+    event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0,
             AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
             AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
-            X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
+            MotionClassification::NONE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
             ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME,
             2, pointerProperties, pointerCoords);
 
@@ -301,11 +307,13 @@
     ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
     ASSERT_EQ(2, event->getDeviceId());
     ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_TOUCHSCREEN), event->getSource());
+    ASSERT_EQ(DISPLAY_ID, event->getDisplayId());
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
     ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
     ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
     ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
     ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
+    ASSERT_EQ(MotionClassification::NONE, event->getClassification());
     ASSERT_EQ(X_OFFSET, event->getXOffset());
     ASSERT_EQ(Y_OFFSET, event->getYOffset());
     ASSERT_EQ(2.0f, event->getXPrecision());
@@ -434,6 +442,11 @@
     event.setSource(AINPUT_SOURCE_JOYSTICK);
     ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
 
+    // Set displayId.
+    constexpr int32_t newDisplayId = 2;
+    event.setDisplayId(newDisplayId);
+    ASSERT_EQ(newDisplayId, event.getDisplayId());
+
     // Set action.
     event.setAction(AMOTION_EVENT_ACTION_CANCEL);
     ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction());
@@ -557,8 +570,11 @@
         pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
     }
     MotionEvent event;
-    event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0,
-            0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
+    event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE,
+            0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE,
+            AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
+            0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
+            0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
     float originalRawX = 0 + 3;
     float originalRawY = -RADIUS + 2;
 
@@ -591,4 +607,30 @@
     ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
 }
 
+TEST_F(MotionEventTest, Initialize_SetsClassification) {
+    std::array<MotionClassification, 3> classifications = {
+            MotionClassification::NONE,
+            MotionClassification::AMBIGUOUS_GESTURE,
+            MotionClassification::DEEP_PRESS,
+    };
+
+    MotionEvent event;
+    constexpr size_t pointerCount = 1;
+    PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+    for (size_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].clear();
+        pointerProperties[i].id = i;
+        pointerCoords[i].clear();
+    }
+
+    for (MotionClassification classification : classifications) {
+        event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
+                AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0,
+                classification, 0, 0, 0, 0, 0 /*downTime*/, 0 /*eventTime*/,
+                pointerCount, pointerProperties, pointerCoords);
+        ASSERT_EQ(classification, event.getClassification());
+    }
+}
+
 } // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index c532241..f2cd1be 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -46,12 +46,12 @@
     virtual void TearDown() {
         if (mPublisher) {
             delete mPublisher;
-            mPublisher = NULL;
+            mPublisher = nullptr;
         }
 
         if (mConsumer) {
             delete mConsumer;
-            mConsumer = NULL;
+            mConsumer = nullptr;
         }
 
         serverChannel.clear();
@@ -70,32 +70,31 @@
 void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
     status_t status;
 
-    const uint32_t seq = 15;
-    const int32_t deviceId = 1;
-    const int32_t source = AINPUT_SOURCE_KEYBOARD;
-    const int32_t action = AKEY_EVENT_ACTION_DOWN;
-    const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
-    const int32_t keyCode = AKEYCODE_ENTER;
-    const int32_t scanCode = 13;
-    const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
-    const int32_t repeatCount = 1;
-    const nsecs_t downTime = 3;
-    const nsecs_t eventTime = 4;
+    constexpr uint32_t seq = 15;
+    constexpr int32_t deviceId = 1;
+    constexpr int32_t source = AINPUT_SOURCE_KEYBOARD;
+    constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+    constexpr int32_t action = AKEY_EVENT_ACTION_DOWN;
+    constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
+    constexpr int32_t keyCode = AKEYCODE_ENTER;
+    constexpr int32_t scanCode = 13;
+    constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
+    constexpr int32_t repeatCount = 1;
+    constexpr nsecs_t downTime = 3;
+    constexpr nsecs_t eventTime = 4;
 
-    status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags,
+    status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags,
             keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
     ASSERT_EQ(OK, status)
             << "publisher publishKeyEvent should return OK";
 
     uint32_t consumeSeq;
     InputEvent* event;
-    int32_t displayId;
-    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event,
-            &displayId);
+    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
     ASSERT_EQ(OK, status)
             << "consumer consume should return OK";
 
-    ASSERT_TRUE(event != NULL)
+    ASSERT_TRUE(event != nullptr)
             << "consumer should have returned non-NULL event";
     ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType())
             << "consumer should have returned a key event";
@@ -104,6 +103,7 @@
     EXPECT_EQ(seq, consumeSeq);
     EXPECT_EQ(deviceId, keyEvent->getDeviceId());
     EXPECT_EQ(source, keyEvent->getSource());
+    EXPECT_EQ(displayId, keyEvent->getDisplayId());
     EXPECT_EQ(action, keyEvent->getAction());
     EXPECT_EQ(flags, keyEvent->getFlags());
     EXPECT_EQ(keyCode, keyEvent->getKeyCode());
@@ -131,23 +131,24 @@
 void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
     status_t status;
 
-    const uint32_t seq = 15;
-    const int32_t deviceId = 1;
-    const int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
-    int32_t displayId = 0;
-    const int32_t action = AMOTION_EVENT_ACTION_MOVE;
-    const int32_t actionButton = 0;
-    const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
-    const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
-    const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
-    const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
-    const float xOffset = -10;
-    const float yOffset = -20;
-    const float xPrecision = 0.25;
-    const float yPrecision = 0.5;
-    const nsecs_t downTime = 3;
-    const size_t pointerCount = 3;
-    const nsecs_t eventTime = 4;
+    constexpr uint32_t seq = 15;
+    constexpr int32_t deviceId = 1;
+    constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+    constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+    constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE;
+    constexpr int32_t actionButton = 0;
+    constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+    constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
+    constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
+    constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
+    constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE;
+    constexpr float xOffset = -10;
+    constexpr float yOffset = -20;
+    constexpr float xPrecision = 0.25;
+    constexpr float yPrecision = 0.5;
+    constexpr nsecs_t downTime = 3;
+    constexpr size_t pointerCount = 3;
+    constexpr nsecs_t eventTime = 4;
     PointerProperties pointerProperties[pointerCount];
     PointerCoords pointerCoords[pointerCount];
     for (size_t i = 0; i < pointerCount; i++) {
@@ -168,20 +169,19 @@
     }
 
     status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton,
-            flags, edgeFlags, metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision,
-            downTime, eventTime, pointerCount,
+            flags, edgeFlags, metaState, buttonState, classification,
+            xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount,
             pointerProperties, pointerCoords);
     ASSERT_EQ(OK, status)
             << "publisher publishMotionEvent should return OK";
 
     uint32_t consumeSeq;
     InputEvent* event;
-    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event,
-            &displayId);
+    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
     ASSERT_EQ(OK, status)
             << "consumer consume should return OK";
 
-    ASSERT_TRUE(event != NULL)
+    ASSERT_TRUE(event != nullptr)
             << "consumer should have returned non-NULL event";
     ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
             << "consumer should have returned a motion event";
@@ -190,11 +190,13 @@
     EXPECT_EQ(seq, consumeSeq);
     EXPECT_EQ(deviceId, motionEvent->getDeviceId());
     EXPECT_EQ(source, motionEvent->getSource());
+    EXPECT_EQ(displayId, motionEvent->getDisplayId());
     EXPECT_EQ(action, motionEvent->getAction());
     EXPECT_EQ(flags, motionEvent->getFlags());
     EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
     EXPECT_EQ(metaState, motionEvent->getMetaState());
     EXPECT_EQ(buttonState, motionEvent->getButtonState());
+    EXPECT_EQ(classification, motionEvent->getClassification());
     EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
     EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
     EXPECT_EQ(downTime, motionEvent->getDownTime());
@@ -264,7 +266,8 @@
         pointerCoords[i].clear();
     }
 
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
@@ -276,7 +279,8 @@
     PointerProperties pointerProperties[pointerCount];
     PointerCoords pointerCoords[pointerCount];
 
-    status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
@@ -293,7 +297,8 @@
         pointerCoords[i].clear();
     }
 
-    status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
new file mode 100644
index 0000000..09dd72b
--- /dev/null
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
+
+#include <input/InputWindow.h>
+#include <input/InputTransport.h>
+
+namespace android {
+namespace test {
+
+TEST(InputWindowInfo, ParcellingWithoutToken) {
+    InputWindowInfo i;
+    i.token = nullptr;
+
+    Parcel p;
+    ASSERT_EQ(OK, i.write(p));
+    p.setDataPosition(0);
+    InputWindowInfo i2 = InputWindowInfo::read(p);
+    ASSERT_TRUE(i2.token == nullptr);
+}
+
+TEST(InputWindowInfo, Parcelling) {
+    InputWindowInfo i;
+    i.token = new BBinder();
+    i.name = "Foobar";
+    i.layoutParamsFlags = 7;
+    i.layoutParamsType = 39;
+    i.dispatchingTimeout = 12;
+    i.frameLeft = 93;
+    i.frameTop = 34;
+    i.frameRight = 16;
+    i.frameBottom = 19;
+    i.surfaceInset = 17;
+    i.globalScaleFactor = 0.3;
+    i.windowXScale = 0.4;
+    i.windowYScale = 0.5;
+    i.visible = false;
+    i.canReceiveKeys = false;
+    i.hasFocus = false;
+    i.hasWallpaper = false;
+    i.paused = false;
+    i.layer = 7;
+    i.ownerPid = 19;
+    i.ownerUid = 24;
+    i.inputFeatures = 29;
+    i.displayId = 34;
+    i.portalToDisplayId = 2;
+
+    Parcel p;
+    i.write(p);
+
+    p.setDataPosition(0);
+    InputWindowInfo i2 = InputWindowInfo::read(p);
+    ASSERT_EQ(i.token, i2.token);
+    ASSERT_EQ(i.name, i2.name);
+    ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
+    ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
+    ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout);
+    ASSERT_EQ(i.frameLeft, i2.frameLeft);
+    ASSERT_EQ(i.frameTop, i2.frameTop);
+    ASSERT_EQ(i.frameRight, i2.frameRight);
+    ASSERT_EQ(i.frameBottom, i2.frameBottom);
+    ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
+    ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
+    ASSERT_EQ(i.windowXScale, i2.windowXScale);
+    ASSERT_EQ(i.windowYScale, i2.windowYScale);
+    ASSERT_EQ(i.visible, i2.visible);
+    ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys);
+    ASSERT_EQ(i.hasFocus, i2.hasFocus);
+    ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
+    ASSERT_EQ(i.paused, i2.paused);
+    ASSERT_EQ(i.layer, i2.layer);
+    ASSERT_EQ(i.ownerPid, i2.ownerPid);
+    ASSERT_EQ(i.ownerUid, i2.ownerUid);
+    ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
+    ASSERT_EQ(i.displayId, i2.displayId);
+    ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
+}
+
+} // namespace test
+} // namespace android
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 12a6782..62023fb 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -57,7 +57,8 @@
   CHECK_OFFSET(InputMessage::Body::Motion, flags, 36);
   CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40);
   CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44);
-  CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 48);
+  CHECK_OFFSET(InputMessage::Body::Motion, classification, 48);
+  CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52);
   CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56);
   CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64);
   CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 9da2e2a..3c67542 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -27,6 +27,8 @@
 
 namespace android {
 
+constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; // default display id
+
 constexpr int32_t DEFAULT_POINTER_ID = 0; // pointer ID used for manually defined tests
 
 // velocity must be in the range (1-tol)*EV <= velocity <= (1+tol)*EV
@@ -88,7 +90,8 @@
 
     MotionEvent* event = new MotionEvent();
     PointerCoords coords;
-    PointerProperties properties[1];
+    constexpr size_t pointerCount = 1;
+    PointerProperties properties[pointerCount];
 
     properties[0].id = DEFAULT_POINTER_ID;
     properties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
@@ -96,8 +99,11 @@
     // First sample added separately with initialize
     coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x);
     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y);
-    event->initialize(0, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE,
-            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, positions[0].time, 1, properties, &coords);
+    event->initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
+            AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
+            AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
+            0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
+            0 /*downTime*/, positions[0].time, pointerCount, properties, &coords);
 
     for (size_t i = 1; i < numSamples; i++) {
         coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[i].x);
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 7e26b0b..6ea1270 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -41,33 +41,11 @@
 // ----------------------------------------------------------------------------
 
 int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) {
-    if (!outBuffer || !desc)
-        return BAD_VALUE;
-
-    if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
-        ALOGE("Invalid AHardwareBuffer pixel format %u (%#x))", desc->format, desc->format);
-        return BAD_VALUE;
-    }
+    if (!outBuffer || !desc) return BAD_VALUE;
+    if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) return BAD_VALUE;
 
     int format = AHardwareBuffer_convertToPixelFormat(desc->format);
-    if (desc->rfu0 != 0 || desc->rfu1 != 0) {
-        ALOGE("AHardwareBuffer_Desc::rfu fields must be 0");
-        return BAD_VALUE;
-    }
-
-    if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) {
-        ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB format");
-        return BAD_VALUE;
-    }
-
-    if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) &&
-        (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) {
-        ALOGE("AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER "
-              "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER");
-        return BAD_VALUE;
-    }
-
-    uint64_t usage =  AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
+    uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
     sp<GraphicBuffer> gbuffer(new GraphicBuffer(
             desc->width, desc->height, format, desc->layers, usage,
             std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]"));
@@ -122,19 +100,26 @@
     if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
                   AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
         ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
-                " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+                "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
         return BAD_VALUE;
     }
 
     usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
-    GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+    if (gbuffer->getLayerCount() > 1) {
+        ALOGE("Buffer with multiple layers passed to AHardwareBuffer_lock; "
+                "only buffers with one layer are allowed");
+        return INVALID_OPERATION;
+    }
+
     Rect bounds;
     if (!rect) {
-        bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight()));
+        bounds.set(Rect(gbuffer->getWidth(), gbuffer->getHeight()));
     } else {
         bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
     }
-    return gBuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence);
+    return gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence);
 }
 
 int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
@@ -274,6 +259,29 @@
     return NO_ERROR;
 }
 
+int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) {
+    if (!desc) return 0;
+    if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0;
+
+    // Make a trial allocation.
+    // TODO(b/115660272): add implementation that uses a HAL query.
+    AHardwareBuffer_Desc trialDesc = *desc;
+    trialDesc.width = 4;
+    trialDesc.height = desc->format == AHARDWAREBUFFER_FORMAT_BLOB ? 1 : 4;
+    if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) {
+        trialDesc.layers = desc->layers == 6 ? 6 : 12;
+    } else {
+        trialDesc.layers = desc->layers == 1 ? 1 : 2;
+    }
+    AHardwareBuffer* trialBuffer = nullptr;
+    int result = AHardwareBuffer_allocate(&trialDesc, &trialBuffer);
+    if (result == NO_ERROR) {
+        AHardwareBuffer_release(trialBuffer);
+        return 1;
+    }
+    return 0;
+}
+
 
 // ----------------------------------------------------------------------------
 // VNDK functions
@@ -322,14 +330,71 @@
 
 namespace android {
 
-// A 1:1 mapping of AHardwaqreBuffer bitmasks to gralloc1 bitmasks.
-struct UsageMaskMapping {
-    uint64_t hardwareBufferMask;
-    uint64_t grallocMask;
-};
+bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log) {
+    if (desc->width == 0 || desc->height == 0 || desc->layers == 0) {
+        ALOGE_IF(log, "Width, height and layers must all be nonzero");
+        return false;
+    }
 
-static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) {
-    return (mask & bitsToCheck) == bitsToCheck && bitsToCheck;
+    if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
+        ALOGE_IF(log, "Invalid AHardwareBuffer pixel format %u (%#x))",
+                desc->format, desc->format);
+        return false;
+    }
+
+    if (desc->rfu0 != 0 || desc->rfu1 != 0) {
+        ALOGE_IF(log, "AHardwareBuffer_Desc::rfu fields must be 0");
+        return false;
+    }
+
+    if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB) {
+        if (desc->height != 1 || desc->layers != 1) {
+            ALOGE_IF(log, "Height and layers must be 1 for AHARDWAREBUFFER_FORMAT_BLOB");
+            return false;
+        }
+        const uint64_t blobInvalidGpuMask =
+            AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+            AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER |
+            AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE |
+            AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;
+        if (desc->usage & blobInvalidGpuMask) {
+            ALOGE_IF(log, "Invalid GPU usage flag for AHARDWAREBUFFER_FORMAT_BLOB; "
+                    "only AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER is allowed");
+            return false;
+        }
+        if (desc->usage & AHARDWAREBUFFER_USAGE_VIDEO_ENCODE) {
+            ALOGE_IF(log, "AHARDWAREBUFFER_FORMAT_BLOB cannot be encoded as video");
+            return false;
+        }
+    } else {
+        if (desc->usage & AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA) {
+            ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA requires AHARDWAREBUFFER_FORMAT_BLOB");
+            return false;
+        }
+        if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) {
+            ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER requires AHARDWAREBUFFER_FORMAT_BLOB");
+            return false;
+        }
+    }
+
+    if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) &&
+        (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) {
+        ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER "
+              "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER");
+        return false;
+    }
+
+    if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) {
+        if (desc->width != desc->height) {
+            ALOGE_IF(log, "Cube maps must be square");
+            return false;
+        }
+        if (desc->layers % 6 != 0) {
+            ALOGE_IF(log, "Cube map layers must be a multiple of 6");
+            return false;
+        }
+    }
+    return true;
 }
 
 bool AHardwareBuffer_isValidPixelFormat(uint32_t format) {
@@ -445,7 +510,7 @@
             "gralloc and AHardwareBuffer flags don't match");
     static_assert(AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE == (uint64_t)BufferUsage::GPU_TEXTURE,
             "gralloc and AHardwareBuffer flags don't match");
-    static_assert(AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT == (uint64_t)BufferUsage::GPU_RENDER_TARGET,
+    static_assert(AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER == (uint64_t)BufferUsage::GPU_RENDER_TARGET,
             "gralloc and AHardwareBuffer flags don't match");
     static_assert(AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT == (uint64_t)BufferUsage::PROTECTED,
             "gralloc and AHardwareBuffer flags don't match");
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index d74bdb3..d847884 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -13,13 +13,20 @@
 // limitations under the License.
 
 ndk_headers {
-    name: "libnativewindow_headers",
+    name: "libnativewindow_ndk_headers",
     from: "include/android",
     to: "android",
     srcs: ["include/android/*.h"],
     license: "NOTICE",
 }
 
+// TODO(b/118715870): cleanup header files
+cc_library_headers {
+    name: "libnativewindow_headers",
+    export_include_dirs: ["include"],
+    vendor_available: false,
+}
+
 ndk_library {
     name: "libnativewindow",
     symbol_file: "libnativewindow.map.txt",
@@ -40,6 +47,7 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wno-enum-compare",
         "-Wno-unused-function",
     ],
 
@@ -66,6 +74,7 @@
 
     header_libs: [
         "libnativebase_headers",
+        "libnativewindow_headers",
     ],
 
     // headers we include in our public headers
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index 71f5634..bf688f8 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -28,10 +28,15 @@
 #include <stdint.h>
 
 struct AHardwareBuffer;
+struct AHardwareBuffer_Desc;
 struct ANativeWindowBuffer;
 
 namespace android {
 
+// Validates whether the passed description does not have conflicting
+// parameters. Note: this does not verify any platform-specific contraints.
+bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log);
+
 // whether this AHardwareBuffer format is valid
 bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format);
 
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 52b4582..2796c75 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -194,6 +194,8 @@
 
     /// The buffer will be read from by the GPU as a texture.
     AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE      = 1UL << 8,
+    /// The buffer will be written to by the GPU as a framebuffer attachment.
+    AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER        = 1UL << 9,
     /**
      * The buffer will be written to by the GPU as a framebuffer
      * attachment.
@@ -201,9 +203,21 @@
      * Note that the name of this flag is somewhat misleading: it does
      * not imply that the buffer contains a color format. A buffer with
      * depth or stencil format that will be used as a framebuffer
-     * attachment should also have this flag.
+     * attachment should also have this flag. Use the equivalent flag
+     * AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER to avoid this confusion.
      */
-    AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT       = 1UL << 9,
+    AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT       = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER,
+    /**
+     * The buffer will be used as a composer HAL overlay layer.
+     *
+     * This flag is currently only needed when using ASurfaceTransaction_setBuffer
+     * to set a buffer. In all other cases, the framework adds this flag
+     * internally to buffers that could be presented in a composer overlay.
+     * ASurfaceTransaction_setBuffer is special because it uses buffers allocated
+     * directly through AHardwareBuffer_allocate instead of buffers allocated
+     * by the framework.
+     */
+    AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY       = 1ULL << 11,
     /**
      * The buffer is protected from direct CPU access or being read by
      * non-secure hardware, such as video encoders.
@@ -309,7 +323,7 @@
         AHardwareBuffer** outBuffer) __INTRODUCED_IN(26);
 /**
  * Acquire a reference on the given AHardwareBuffer object.
- *
+ * 
  * This prevents the object from being deleted until the last reference
  * is removed.
  */
@@ -417,6 +431,29 @@
 
 #endif // __ANDROID_API__ >= 26
 
+#if __ANDROID_API__ >= 29
+
+/**
+ * Test whether the given format and usage flag combination is
+ * allocatable.
+ *
+ * If this function returns true, it means that a buffer with the given
+ * description can be allocated on this implementation, unless resource
+ * exhaustion occurs. If this function returns false, it means that the
+ * allocation of the given description will never succeed.
+ *
+ * The return value of this function may depend on all fields in the
+ * description, except stride, which is always ignored. For example,
+ * some implementations have implementation-defined limits on texture
+ * size and layer count.
+ *
+ * \return 1 if the format and usage flag combination is allocatable,
+ *     0 otherwise.
+ */
+int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
 __END_DECLS
 
 #endif // ANDROID_HARDWARE_BUFFER_H
diff --git a/libs/nativewindow/include/android/hdr_metadata.h b/libs/nativewindow/include/android/hdr_metadata.h
new file mode 100644
index 0000000..7e1313b
--- /dev/null
+++ b/libs/nativewindow/include/android/hdr_metadata.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2019 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.
+ */
+
+/**
+ * @file hdr_metadata.h
+ */
+
+#ifndef ANDROID_HDR_METADATA_H
+#define ANDROID_HDR_METADATA_H
+
+#include <inttypes.h>
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * These structures are used to define the display's capabilities for HDR content.
+ * They can be used to better tone map content to user's display.
+ */
+
+/**
+ * Color is defined in CIE XYZ coordinates.
+ */
+struct AColor_xy {
+    float x;
+    float y;
+};
+
+/**
+ * SMPTE ST 2086 "Mastering Display Color Volume" static metadata
+ */
+struct AHdrMetadata_smpte2086 {
+    struct AColor_xy displayPrimaryRed;
+    struct AColor_xy displayPrimaryGreen;
+    struct AColor_xy displayPrimaryBlue;
+    struct AColor_xy whitePoint;
+    float maxLuminance;
+    float minLuminance;
+};
+
+/**
+ * CTA 861.3 "HDR Static Metadata Extension" static metadata
+ */
+struct AHdrMetadata_cta861_3 {
+    float maxContentLightLevel;
+    float maxFrameAverageLightLevel;
+};
+
+__END_DECLS
+
+#endif // ANDROID_HDR_METADATA_H
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 197f73f..61590e0 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -202,7 +202,7 @@
  * ANativeWindow.
  */
 enum {
-// clang-format off
+    // clang-format off
     NATIVE_WINDOW_SET_USAGE                     =  0,   /* deprecated */
     NATIVE_WINDOW_CONNECT                       =  1,   /* deprecated */
     NATIVE_WINDOW_DISCONNECT                    =  2,   /* deprecated */
@@ -237,7 +237,8 @@
     NATIVE_WINDOW_GET_CONSUMER_USAGE64          = 31,
     NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32,
     NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33,
-// clang-format on
+    NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34,
+    // clang-format on
 };
 
 /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -748,6 +749,27 @@
 }
 
 /*
+ * native_window_set_buffers_hdr10_plus_metadata(..., metadata)
+ * All buffers queued after this call will be associated with the
+ * HDR10+ dynamic metadata specified.
+ *
+ * metadata specifies additional dynamic information about the
+ * contents of the buffer that may affect how it is displayed.  When
+ * it is nullptr, it means no such information is available.  No
+ * HDR10+ dynamic emtadata is associated with the buffers by default.
+ *
+ * Parameter "size" refers to the length of the metadata blob pointed to
+ * by parameter "data".  The metadata blob will adhere to the HDR10+ SEI
+ * message standard.
+ */
+static inline int native_window_set_buffers_hdr10_plus_metadata(struct ANativeWindow* window,
+                                                           const size_t size,
+                                                           const uint8_t* metadata) {
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA, size,
+                           metadata);
+}
+
+/*
  * native_window_set_buffers_transform(..., int transform)
  * All buffers queued after this call will be displayed transformed according
  * to the transform parameter specified.
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 753954d..a796e97 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -5,6 +5,7 @@
     AHardwareBuffer_createFromHandle; # vndk
     AHardwareBuffer_describe;
     AHardwareBuffer_getNativeHandle; # vndk
+    AHardwareBuffer_isSupported; # introduced=29
     AHardwareBuffer_lock;
     AHardwareBuffer_recvHandleFromUnixSocket;
     AHardwareBuffer_release;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
new file mode 100644
index 0000000..36211ca
--- /dev/null
+++ b/libs/renderengine/Android.bp
@@ -0,0 +1,98 @@
+cc_defaults {
+    name: "renderengine_defaults",
+    cflags: [
+        "-DLOG_TAG=\"RenderEngine\"",
+        "-Wall",
+        "-Werror",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
+
+cc_defaults {
+    name: "librenderengine_defaults",
+    defaults: ["renderengine_defaults"],
+    cflags: [
+        "-DGL_GLEXT_PROTOTYPES",
+        "-DEGL_EGLEXT_PROTOTYPES",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libEGL",
+        "libGLESv1_CM",
+        "libGLESv2",
+        "libgui",
+        "liblog",
+        "libnativewindow",
+        "libsync",
+        "libui",
+        "libutils",
+    ],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+}
+
+filegroup {
+    name: "librenderengine_sources",
+    srcs: [
+        "Description.cpp",
+        "Mesh.cpp",
+        "RenderEngine.cpp",
+        "Texture.cpp",
+    ],
+}
+
+filegroup {
+    name: "librenderengine_gl_sources",
+    srcs: [
+        "gl/GLESRenderEngine.cpp",
+        "gl/GLExtensions.cpp",
+        "gl/GLFramebuffer.cpp",
+        "gl/GLImage.cpp",
+        "gl/Program.cpp",
+        "gl/ProgramCache.cpp",
+    ],
+}
+
+cc_library_static {
+    name: "librenderengine",
+    defaults: ["librenderengine_defaults"],
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+    double_loadable: true,
+    clang: true,
+    cflags: [
+        "-fvisibility=hidden",
+        "-Werror=format",
+    ],
+    cppflags: [
+        "-fwhole-program-vtables", // requires ThinLTO
+    ],
+    srcs: [
+        ":librenderengine_sources",
+        ":librenderengine_gl_sources",
+    ],
+    lto: {
+        thin: true,
+    },
+}
+
+cc_library_static {
+    name: "librenderengine_mocks",
+    defaults: ["librenderengine_defaults"],
+    srcs: [
+        "mock/Framebuffer.cpp",
+        "mock/Image.cpp",
+        "mock/RenderEngine.cpp",
+    ],
+    static_libs: [
+        "libgtest",
+        "libgmock",
+    ],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+}
diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp
new file mode 100644
index 0000000..b9cea10
--- /dev/null
+++ b/libs/renderengine/Description.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 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 <renderengine/private/Description.h>
+
+#include <stdint.h>
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+namespace renderengine {
+
+Description::TransferFunction Description::dataSpaceToTransferFunction(ui::Dataspace dataSpace) {
+    ui::Dataspace transfer = static_cast<ui::Dataspace>(dataSpace & ui::Dataspace::TRANSFER_MASK);
+    switch (transfer) {
+        case ui::Dataspace::TRANSFER_ST2084:
+            return Description::TransferFunction::ST2084;
+        case ui::Dataspace::TRANSFER_HLG:
+            return Description::TransferFunction::HLG;
+        case ui::Dataspace::TRANSFER_LINEAR:
+            return Description::TransferFunction::LINEAR;
+        default:
+            return Description::TransferFunction::SRGB;
+    }
+}
+
+bool Description::hasInputTransformMatrix() const {
+    const mat4 identity;
+    return inputTransformMatrix != identity;
+}
+
+bool Description::hasOutputTransformMatrix() const {
+    const mat4 identity;
+    return outputTransformMatrix != identity;
+}
+
+bool Description::hasColorMatrix() const {
+    const mat4 identity;
+    return colorMatrix != identity;
+}
+
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Mesh.cpp b/libs/renderengine/Mesh.cpp
similarity index 72%
rename from services/surfaceflinger/RenderEngine/Mesh.cpp
rename to libs/renderengine/Mesh.cpp
index 6a62b1d..f5387f2 100644
--- a/services/surfaceflinger/RenderEngine/Mesh.cpp
+++ b/libs/renderengine/Mesh.cpp
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include "Mesh.h"
+#include <renderengine/Mesh.h>
 
 #include <utils/Log.h>
 
 namespace android {
+namespace renderengine {
 
 Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize)
       : mVertexCount(vertexCount),
@@ -26,20 +27,22 @@
         mTexCoordsSize(texCoordSize),
         mPrimitive(primitive) {
     if (vertexCount == 0) {
-        mVertices = new float[1];
+        mVertices.resize(1);
         mVertices[0] = 0.0f;
         mStride = 0;
         return;
     }
 
-    size_t stride = vertexSize + texCoordSize;
+    const size_t CROP_COORD_SIZE = 2;
+    size_t stride = vertexSize + texCoordSize + CROP_COORD_SIZE;
     size_t remainder = (stride * vertexCount) / vertexCount;
     // Since all of the input parameters are unsigned, if stride is less than
     // either vertexSize or texCoordSize, it must have overflowed. remainder
     // will be equal to stride as long as stride * vertexCount doesn't overflow.
     if ((stride < vertexSize) || (remainder != stride)) {
-        ALOGE("Overflow in Mesh(..., %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize);
-        mVertices = new float[1];
+        ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize,
+              CROP_COORD_SIZE);
+        mVertices.resize(1);
         mVertices[0] = 0.0f;
         mVertexCount = 0;
         mVertexSize = 0;
@@ -48,30 +51,33 @@
         return;
     }
 
-    mVertices = new float[stride * vertexCount];
+    mVertices.resize(stride * vertexCount);
     mStride = stride;
 }
 
-Mesh::~Mesh() {
-    delete[] mVertices;
-}
-
 Mesh::Primitive Mesh::getPrimitive() const {
     return mPrimitive;
 }
 
 float const* Mesh::getPositions() const {
-    return mVertices;
+    return mVertices.data();
 }
 float* Mesh::getPositions() {
-    return mVertices;
+    return mVertices.data();
 }
 
 float const* Mesh::getTexCoords() const {
-    return mVertices + mVertexSize;
+    return mVertices.data() + mVertexSize;
 }
 float* Mesh::getTexCoords() {
-    return mVertices + mVertexSize;
+    return mVertices.data() + mVertexSize;
+}
+
+float const* Mesh::getCropCoords() const {
+    return mVertices.data() + mVertexSize + mTexCoordsSize;
+}
+float* Mesh::getCropCoords() {
+    return mVertices.data() + mVertexSize + mTexCoordsSize;
 }
 
 size_t Mesh::getVertexCount() const {
@@ -94,4 +100,5 @@
     return mStride;
 }
 
-} /* namespace android */
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
new file mode 100644
index 0000000..6dd7283
--- /dev/null
+++ b/libs/renderengine/RenderEngine.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 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 <renderengine/RenderEngine.h>
+
+#include <cutils/properties.h>
+#include <log/log.h>
+#include <private/gui/SyncFeatures.h>
+#include "gl/GLESRenderEngine.h"
+
+namespace android {
+namespace renderengine {
+
+std::unique_ptr<impl::RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags) {
+    char prop[PROPERTY_VALUE_MAX];
+    property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles");
+    if (strcmp(prop, "gles") == 0) {
+        ALOGD("RenderEngine GLES Backend");
+        return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags);
+    }
+    ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop);
+    return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags);
+}
+
+RenderEngine::~RenderEngine() = default;
+
+namespace impl {
+
+RenderEngine::RenderEngine(uint32_t featureFlags) : mFeatureFlags(featureFlags) {}
+
+RenderEngine::~RenderEngine() = default;
+
+bool RenderEngine::useNativeFenceSync() const {
+    return SyncFeatures::getInstance().useNativeFenceSync();
+}
+
+bool RenderEngine::useWaitSync() const {
+    return SyncFeatures::getInstance().useWaitSync();
+}
+
+} // namespace impl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/TEST_MAPPING b/libs/renderengine/TEST_MAPPING
new file mode 100644
index 0000000..995dba1
--- /dev/null
+++ b/libs/renderengine/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "librenderengine_test"
+    }
+  ]
+}
diff --git a/services/surfaceflinger/RenderEngine/Texture.cpp b/libs/renderengine/Texture.cpp
similarity index 93%
rename from services/surfaceflinger/RenderEngine/Texture.cpp
rename to libs/renderengine/Texture.cpp
index 351430f..154cde8 100644
--- a/services/surfaceflinger/RenderEngine/Texture.cpp
+++ b/libs/renderengine/Texture.cpp
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-#include <string.h>
-
-#include "Texture.h"
+#include <renderengine/Texture.h>
 
 namespace android {
+namespace renderengine {
 
 Texture::Texture()
       : mTextureName(0), mTextureTarget(TEXTURE_2D), mWidth(0), mHeight(0), mFiltering(false) {}
@@ -74,4 +73,5 @@
     return mHeight;
 }
 
-} /* namespace android */
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
new file mode 100644
index 0000000..c137394
--- /dev/null
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -0,0 +1,1372 @@
+/*
+ * Copyright 2013 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
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLESRenderEngine.h"
+
+#include <math.h>
+#include <fstream>
+#include <sstream>
+#include <unordered_set>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <android-base/stringprintf.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/Texture.h>
+#include <renderengine/private/Description.h>
+#include <sync/sync.h>
+#include <ui/ColorSpace.h>
+#include <ui/DebugUtils.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <utils/KeyedVector.h>
+#include <utils/Trace.h>
+#include "GLExtensions.h"
+#include "GLFramebuffer.h"
+#include "GLImage.h"
+#include "Program.h"
+#include "ProgramCache.h"
+
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+
+bool checkGlError(const char* op, int lineNumber) {
+    bool errorFound = false;
+    GLint error = glGetError();
+    while (error != GL_NO_ERROR) {
+        errorFound = true;
+        error = glGetError();
+        ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
+    }
+    return errorFound;
+}
+
+static constexpr bool outputDebugPPMs = false;
+
+void writePPM(const char* basename, GLuint width, GLuint height) {
+    ALOGV("writePPM #%s: %d x %d", basename, width, height);
+
+    std::vector<GLubyte> pixels(width * height * 4);
+    std::vector<GLubyte> outBuffer(width * height * 3);
+
+    // TODO(courtneygo): We can now have float formats, need
+    // to remove this code or update to support.
+    // Make returned pixels fit in uint32_t, one byte per component
+    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
+    if (checkGlError(__FUNCTION__, __LINE__)) {
+        return;
+    }
+
+    std::string filename(basename);
+    filename.append(".ppm");
+    std::ofstream file(filename.c_str(), std::ios::binary);
+    if (!file.is_open()) {
+        ALOGE("Unable to open file: %s", filename.c_str());
+        ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
+              "surfaceflinger to write debug images");
+        return;
+    }
+
+    file << "P6\n";
+    file << width << "\n";
+    file << height << "\n";
+    file << 255 << "\n";
+
+    auto ptr = reinterpret_cast<char*>(pixels.data());
+    auto outPtr = reinterpret_cast<char*>(outBuffer.data());
+    for (int y = height - 1; y >= 0; y--) {
+        char* data = ptr + y * width * sizeof(uint32_t);
+
+        for (GLuint x = 0; x < width; x++) {
+            // Only copy R, G and B components
+            outPtr[0] = data[0];
+            outPtr[1] = data[1];
+            outPtr[2] = data[2];
+            data += sizeof(uint32_t);
+            outPtr += 3;
+        }
+    }
+    file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+}
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+using base::StringAppendF;
+using ui::Dataspace;
+
+static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
+                                         EGLint wanted, EGLConfig* outConfig) {
+    EGLint numConfigs = -1, n = 0;
+    eglGetConfigs(dpy, nullptr, 0, &numConfigs);
+    std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
+    eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n);
+    configs.resize(n);
+
+    if (!configs.empty()) {
+        if (attribute != EGL_NONE) {
+            for (EGLConfig config : configs) {
+                EGLint value = 0;
+                eglGetConfigAttrib(dpy, config, attribute, &value);
+                if (wanted == value) {
+                    *outConfig = config;
+                    return NO_ERROR;
+                }
+            }
+        } else {
+            // just pick the first one
+            *outConfig = configs[0];
+            return NO_ERROR;
+        }
+    }
+
+    return NAME_NOT_FOUND;
+}
+
+class EGLAttributeVector {
+    struct Attribute;
+    class Adder;
+    friend class Adder;
+    KeyedVector<Attribute, EGLint> mList;
+    struct Attribute {
+        Attribute() : v(0){};
+        explicit Attribute(EGLint v) : v(v) {}
+        EGLint v;
+        bool operator<(const Attribute& other) const {
+            // this places EGL_NONE at the end
+            EGLint lhs(v);
+            EGLint rhs(other.v);
+            if (lhs == EGL_NONE) lhs = 0x7FFFFFFF;
+            if (rhs == EGL_NONE) rhs = 0x7FFFFFFF;
+            return lhs < rhs;
+        }
+    };
+    class Adder {
+        friend class EGLAttributeVector;
+        EGLAttributeVector& v;
+        EGLint attribute;
+        Adder(EGLAttributeVector& v, EGLint attribute) : v(v), attribute(attribute) {}
+
+    public:
+        void operator=(EGLint value) {
+            if (attribute != EGL_NONE) {
+                v.mList.add(Attribute(attribute), value);
+            }
+        }
+        operator EGLint() const { return v.mList[attribute]; }
+    };
+
+public:
+    EGLAttributeVector() { mList.add(Attribute(EGL_NONE), EGL_NONE); }
+    void remove(EGLint attribute) {
+        if (attribute != EGL_NONE) {
+            mList.removeItem(Attribute(attribute));
+        }
+    }
+    Adder operator[](EGLint attribute) { return Adder(*this, attribute); }
+    EGLint operator[](EGLint attribute) const { return mList[attribute]; }
+    // cast-operator to (EGLint const*)
+    operator EGLint const*() const { return &mList.keyAt(0).v; }
+};
+
+static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
+                                EGLConfig* config) {
+    // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
+    // it is to be used with WIFI displays
+    status_t err;
+    EGLint wantedAttribute;
+    EGLint wantedAttributeValue;
+
+    EGLAttributeVector attribs;
+    if (renderableType) {
+        attribs[EGL_RENDERABLE_TYPE] = renderableType;
+        attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
+        attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
+        attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
+        attribs[EGL_RED_SIZE] = 8;
+        attribs[EGL_GREEN_SIZE] = 8;
+        attribs[EGL_BLUE_SIZE] = 8;
+        attribs[EGL_ALPHA_SIZE] = 8;
+        wantedAttribute = EGL_NONE;
+        wantedAttributeValue = EGL_NONE;
+    } else {
+        // if no renderable type specified, fallback to a simplified query
+        wantedAttribute = EGL_NATIVE_VISUAL_ID;
+        wantedAttributeValue = format;
+    }
+
+    err = selectConfigForAttribute(display, attribs, wantedAttribute, wantedAttributeValue, config);
+    if (err == NO_ERROR) {
+        EGLint caveat;
+        if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
+            ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
+    }
+
+    return err;
+}
+
+std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(int hwcFormat, uint32_t featureFlags) {
+    // initialize EGL for the default display
+    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (!eglInitialize(display, nullptr, nullptr)) {
+        LOG_ALWAYS_FATAL("failed to initialize EGL");
+    }
+
+    GLExtensions& extensions = GLExtensions::getInstance();
+    extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION),
+                                  eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS));
+
+    // The code assumes that ES2 or later is available if this extension is
+    // supported.
+    EGLConfig config = EGL_NO_CONFIG;
+    if (!extensions.hasNoConfigContext()) {
+        config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+    }
+
+    bool useContextPriority = extensions.hasContextPriority() &&
+            (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT);
+    EGLContext protectedContext = EGL_NO_CONTEXT;
+    if (extensions.hasProtectedContent()) {
+        protectedContext = createEglContext(display, config, nullptr, useContextPriority,
+                                            Protection::PROTECTED);
+        ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
+    }
+
+    EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
+                                       Protection::UNPROTECTED);
+
+    // if can't create a GL context, we can only abort.
+    LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
+
+    EGLSurface dummy = EGL_NO_SURFACE;
+    if (!extensions.hasSurfacelessContext()) {
+        dummy = createDummyEglPbufferSurface(display, config, hwcFormat, Protection::UNPROTECTED);
+        LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
+    }
+    EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
+    LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
+    extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
+                                 glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
+
+    // In order to have protected contents in GPU composition, the OpenGL ES extension
+    // GL_EXT_protected_textures must be supported. If it's not supported, reset
+    // protected context to EGL_NO_CONTEXT to indicate that protected contents is not supported.
+    if (!extensions.hasProtectedTexture()) {
+        protectedContext = EGL_NO_CONTEXT;
+    }
+
+    EGLSurface protectedDummy = EGL_NO_SURFACE;
+    if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
+        protectedDummy =
+                createDummyEglPbufferSurface(display, config, hwcFormat, Protection::PROTECTED);
+        ALOGE_IF(protectedDummy == EGL_NO_SURFACE, "can't create protected dummy pbuffer");
+    }
+
+    // now figure out what version of GL did we actually get
+    GlesVersion version = parseGlesVersion(extensions.getVersion());
+
+    // initialize the renderer while GL is current
+    std::unique_ptr<GLESRenderEngine> engine;
+    switch (version) {
+        case GLES_VERSION_1_0:
+        case GLES_VERSION_1_1:
+            LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
+            break;
+        case GLES_VERSION_2_0:
+        case GLES_VERSION_3_0:
+            engine = std::make_unique<GLESRenderEngine>(featureFlags, display, config, ctxt, dummy,
+                                                        protectedContext, protectedDummy);
+            break;
+    }
+
+    ALOGI("OpenGL ES informations:");
+    ALOGI("vendor    : %s", extensions.getVendor());
+    ALOGI("renderer  : %s", extensions.getRenderer());
+    ALOGI("version   : %s", extensions.getVersion());
+    ALOGI("extensions: %s", extensions.getExtensions());
+    ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
+    ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
+
+    return engine;
+}
+
+EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
+    status_t err;
+    EGLConfig config;
+
+    // First try to get an ES3 config
+    err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config);
+    if (err != NO_ERROR) {
+        // If ES3 fails, try to get an ES2 config
+        err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
+        if (err != NO_ERROR) {
+            // If ES2 still doesn't work, probably because we're on the emulator.
+            // try a simplified query
+            ALOGW("no suitable EGLConfig found, trying a simpler query");
+            err = selectEGLConfig(display, format, 0, &config);
+            if (err != NO_ERROR) {
+                // this EGL is too lame for android
+                LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+            }
+        }
+    }
+
+    if (logConfig) {
+        // print some debugging info
+        EGLint r, g, b, a;
+        eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+        eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+        eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+        eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
+        ALOGI("EGL information:");
+        ALOGI("vendor    : %s", eglQueryString(display, EGL_VENDOR));
+        ALOGI("version   : %s", eglQueryString(display, EGL_VERSION));
+        ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
+        ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
+        ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
+    }
+
+    return config;
+}
+
+GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EGLConfig config,
+                                   EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext,
+                                   EGLSurface protectedDummy)
+      : renderengine::impl::RenderEngine(featureFlags),
+        mEGLDisplay(display),
+        mEGLConfig(config),
+        mEGLContext(ctxt),
+        mDummySurface(dummy),
+        mProtectedEGLContext(protectedContext),
+        mProtectedDummySurface(protectedDummy),
+        mVpWidth(0),
+        mVpHeight(0),
+        mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) {
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
+
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+    glPixelStorei(GL_PACK_ALIGNMENT, 4);
+
+    // Initialize protected EGL Context.
+    if (mProtectedEGLContext != EGL_NO_CONTEXT) {
+        EGLBoolean success = eglMakeCurrent(display, mProtectedDummySurface, mProtectedDummySurface,
+                                            mProtectedEGLContext);
+        ALOGE_IF(!success, "can't make protected context current");
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+        glPixelStorei(GL_PACK_ALIGNMENT, 4);
+        success = eglMakeCurrent(display, mDummySurface, mDummySurface, mEGLContext);
+        LOG_ALWAYS_FATAL_IF(!success, "can't make default context current");
+    }
+
+    const uint16_t protTexData[] = {0};
+    glGenTextures(1, &mProtectedTexName);
+    glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
+
+    // mColorBlindnessCorrection = M;
+
+    if (mUseColorManagement) {
+        const ColorSpace srgb(ColorSpace::sRGB());
+        const ColorSpace displayP3(ColorSpace::DisplayP3());
+        const ColorSpace bt2020(ColorSpace::BT2020());
+
+        // no chromatic adaptation needed since all color spaces use D65 for their white points.
+        mSrgbToXyz = mat4(srgb.getRGBtoXYZ());
+        mDisplayP3ToXyz = mat4(displayP3.getRGBtoXYZ());
+        mBt2020ToXyz = mat4(bt2020.getRGBtoXYZ());
+        mXyzToSrgb = mat4(srgb.getXYZtoRGB());
+        mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB());
+        mXyzToBt2020 = mat4(bt2020.getXYZtoRGB());
+
+        // Compute sRGB to Display P3 and BT2020 transform matrix.
+        // NOTE: For now, we are limiting output wide color space support to
+        // Display-P3 and BT2020 only.
+        mSrgbToDisplayP3 = mXyzToDisplayP3 * mSrgbToXyz;
+        mSrgbToBt2020 = mXyzToBt2020 * mSrgbToXyz;
+
+        // Compute Display P3 to sRGB and BT2020 transform matrix.
+        mDisplayP3ToSrgb = mXyzToSrgb * mDisplayP3ToXyz;
+        mDisplayP3ToBt2020 = mXyzToBt2020 * mDisplayP3ToXyz;
+
+        // Compute BT2020 to sRGB and Display P3 transform matrix
+        mBt2020ToSrgb = mXyzToSrgb * mBt2020ToXyz;
+        mBt2020ToDisplayP3 = mXyzToDisplayP3 * mBt2020ToXyz;
+    }
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.egl.traceGpuCompletion", value, "0");
+    if (atoi(value)) {
+        mTraceGpuCompletion = true;
+        mFlushTracer = std::make_unique<FlushTracer>(this);
+    }
+}
+
+GLESRenderEngine::~GLESRenderEngine() {
+    eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    eglTerminate(mEGLDisplay);
+}
+
+std::unique_ptr<Framebuffer> GLESRenderEngine::createFramebuffer() {
+    return std::make_unique<GLFramebuffer>(*this);
+}
+
+std::unique_ptr<Image> GLESRenderEngine::createImage() {
+    return std::make_unique<GLImage>(*this);
+}
+
+void GLESRenderEngine::primeCache() const {
+    ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
+                                           mFeatureFlags & USE_COLOR_MANAGEMENT);
+}
+
+bool GLESRenderEngine::isCurrent() const {
+    return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext();
+}
+
+base::unique_fd GLESRenderEngine::flush() {
+    ATRACE_CALL();
+    if (!GLExtensions::getInstance().hasNativeFenceSync()) {
+        return base::unique_fd();
+    }
+
+    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+    if (sync == EGL_NO_SYNC_KHR) {
+        ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
+        return base::unique_fd();
+    }
+
+    // native fence fd will not be populated until flush() is done.
+    glFlush();
+
+    // get the fence fd
+    base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
+    eglDestroySyncKHR(mEGLDisplay, sync);
+    if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+        ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
+    }
+
+    // Only trace if we have a valid fence, as current usage falls back to
+    // calling finish() if the fence fd is invalid.
+    if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer) && fenceFd.get() >= 0) {
+        mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr));
+    }
+
+    return fenceFd;
+}
+
+bool GLESRenderEngine::finish() {
+    ATRACE_CALL();
+    if (!GLExtensions::getInstance().hasFenceSync()) {
+        ALOGW("no synchronization support");
+        return false;
+    }
+
+    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr);
+    if (sync == EGL_NO_SYNC_KHR) {
+        ALOGW("failed to create EGL fence sync: %#x", eglGetError());
+        return false;
+    }
+
+    if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer)) {
+        mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr));
+    }
+
+    return waitSync(sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR);
+}
+
+bool GLESRenderEngine::waitSync(EGLSyncKHR sync, EGLint flags) {
+    EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, flags, 2000000000 /*2 sec*/);
+    EGLint error = eglGetError();
+    eglDestroySyncKHR(mEGLDisplay, sync);
+    if (result != EGL_CONDITION_SATISFIED_KHR) {
+        if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+            ALOGW("fence wait timed out");
+        } else {
+            ALOGW("error waiting on EGL fence: %#x", error);
+        }
+        return false;
+    }
+
+    return true;
+}
+
+bool GLESRenderEngine::waitFence(base::unique_fd fenceFd) {
+    if (!GLExtensions::getInstance().hasNativeFenceSync() ||
+        !GLExtensions::getInstance().hasWaitSync()) {
+        return false;
+    }
+
+    EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+    if (sync == EGL_NO_SYNC_KHR) {
+        ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
+        return false;
+    }
+
+    // fenceFd is now owned by EGLSync
+    (void)fenceFd.release();
+
+    // XXX: The spec draft is inconsistent as to whether this should return an
+    // EGLint or void.  Ignore the return value for now, as it's not strictly
+    // needed.
+    eglWaitSyncKHR(mEGLDisplay, sync, 0);
+    EGLint error = eglGetError();
+    eglDestroySyncKHR(mEGLDisplay, sync);
+    if (error != EGL_SUCCESS) {
+        ALOGE("failed to wait for EGL native fence sync: %#x", error);
+        return false;
+    }
+
+    return true;
+}
+
+void GLESRenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
+    glClearColor(red, green, blue, alpha);
+    glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void GLESRenderEngine::fillRegionWithColor(const Region& region, float red, float green, float blue,
+                                           float alpha) {
+    size_t c;
+    Rect const* r = region.getArray(&c);
+    Mesh mesh(Mesh::TRIANGLES, c * 6, 2);
+    Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+    for (size_t i = 0; i < c; i++, r++) {
+        position[i * 6 + 0].x = r->left;
+        position[i * 6 + 0].y = r->top;
+        position[i * 6 + 1].x = r->left;
+        position[i * 6 + 1].y = r->bottom;
+        position[i * 6 + 2].x = r->right;
+        position[i * 6 + 2].y = r->bottom;
+        position[i * 6 + 3].x = r->left;
+        position[i * 6 + 3].y = r->top;
+        position[i * 6 + 4].x = r->right;
+        position[i * 6 + 4].y = r->bottom;
+        position[i * 6 + 5].x = r->right;
+        position[i * 6 + 5].y = r->top;
+    }
+    setupFillWithColor(red, green, blue, alpha);
+    drawMesh(mesh);
+}
+
+void GLESRenderEngine::setScissor(const Rect& region) {
+    // Invert y-coordinate to map to GL-space.
+    int32_t canvasHeight = mFboHeight;
+    int32_t glBottom = canvasHeight - region.bottom;
+
+    glScissor(region.left, glBottom, region.getWidth(), region.getHeight());
+    glEnable(GL_SCISSOR_TEST);
+}
+
+void GLESRenderEngine::disableScissor() {
+    glDisable(GL_SCISSOR_TEST);
+}
+
+void GLESRenderEngine::genTextures(size_t count, uint32_t* names) {
+    glGenTextures(count, names);
+}
+
+void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) {
+    glDeleteTextures(count, names);
+}
+
+void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) {
+    ATRACE_CALL();
+    const GLImage& glImage = static_cast<const GLImage&>(image);
+    const GLenum target = GL_TEXTURE_EXTERNAL_OES;
+
+    glBindTexture(target, texName);
+    if (supportsProtectedContent()) {
+        glTexParameteri(target, GL_TEXTURE_PROTECTED_EXT,
+                        glImage.isProtected() ? GL_TRUE : GL_FALSE);
+    }
+    if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) {
+        glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage()));
+    }
+}
+
+status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
+                                                     sp<Fence> bufferFence, bool readCache) {
+    return bindExternalTextureBuffer(texName, buffer, bufferFence, readCache,
+                                     /*persistCache=*/false);
+}
+
+status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
+                                                     sp<Fence> bufferFence, bool readCache,
+                                                     bool persistCache) {
+    ATRACE_CALL();
+    if (readCache) {
+        auto cachedImage = mImageCache.find(buffer->getId());
+
+        if (cachedImage != mImageCache.end()) {
+            bindExternalTextureImage(texName, *cachedImage->second);
+            return NO_ERROR;
+        }
+    }
+
+    std::unique_ptr<Image> newImage = createImage();
+
+    bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
+                                                   buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+    if (!created) {
+        ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+              buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
+              buffer->getPixelFormat());
+        bindExternalTextureImage(texName, *createImage());
+        return NO_INIT;
+    }
+
+    bindExternalTextureImage(texName, *newImage);
+
+    // Wait for the new buffer to be ready.
+    if (bufferFence != nullptr && bufferFence->isValid()) {
+        if (GLExtensions::getInstance().hasWaitSync()) {
+            base::unique_fd fenceFd(bufferFence->dup());
+            if (fenceFd == -1) {
+                ALOGE("error dup'ing fence fd: %d", errno);
+                return -errno;
+            }
+            if (!waitFence(std::move(fenceFd))) {
+                ALOGE("failed to wait on fence fd");
+                return UNKNOWN_ERROR;
+            }
+        } else {
+            status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer");
+            if (err != NO_ERROR) {
+                ALOGE("error waiting for fence: %d", err);
+                return err;
+            }
+        }
+    }
+
+    // We don't always want to persist to the cache, e.g. on older devices we
+    // might bind for synchronization purposes, but that might leak if we never
+    // call drawLayers again, so it's just better to recreate the image again
+    // if needed when we draw.
+    if (persistCache) {
+        mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
+    }
+
+    return NO_ERROR;
+}
+
+void GLESRenderEngine::evictImages(const std::vector<LayerSettings>& layers) {
+    // destroy old image references that we're not going to draw with.
+    std::unordered_set<uint64_t> bufIds;
+    for (auto layer : layers) {
+        if (layer.source.buffer.buffer != nullptr) {
+            bufIds.emplace(layer.source.buffer.buffer->getId());
+        }
+    }
+
+    for (auto it = mImageCache.begin(); it != mImageCache.end();) {
+        if (bufIds.count(it->first) == 0) {
+            it = mImageCache.erase(it);
+        } else {
+            it++;
+        }
+    }
+}
+
+FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
+    // Translate win by the rounded corners rect coordinates, to have all values in
+    // layer coordinate space.
+    FloatRect cropWin = layer.geometry.boundaries;
+    const FloatRect& roundedCornersCrop = layer.geometry.roundedCornersCrop;
+    cropWin.left -= roundedCornersCrop.left;
+    cropWin.right -= roundedCornersCrop.left;
+    cropWin.top -= roundedCornersCrop.top;
+    cropWin.bottom -= roundedCornersCrop.top;
+    Mesh::VertexArray<vec2> cropCoords(mesh.getCropCoordArray<vec2>());
+    cropCoords[0] = vec2(cropWin.left, cropWin.top);
+    cropCoords[1] = vec2(cropWin.left, cropWin.top + cropWin.getHeight());
+    cropCoords[2] = vec2(cropWin.right, cropWin.top + cropWin.getHeight());
+    cropCoords[3] = vec2(cropWin.right, cropWin.top);
+
+    setupCornerRadiusCropSize(roundedCornersCrop.getWidth(), roundedCornersCrop.getHeight());
+    return cropWin;
+}
+
+status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
+    ATRACE_CALL();
+    GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
+    EGLImageKHR eglImage = glFramebuffer->getEGLImage();
+    uint32_t textureName = glFramebuffer->getTextureName();
+    uint32_t framebufferName = glFramebuffer->getFramebufferName();
+
+    // Bind the texture and turn our EGLImage into a texture
+    glBindTexture(GL_TEXTURE_2D, textureName);
+    if (supportsProtectedContent()) {
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_PROTECTED_EXT,
+                        mInProtectedContext ? GL_TRUE : GL_FALSE);
+    }
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage);
+
+    // Bind the Framebuffer to render into
+    glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
+
+    mFboHeight = glFramebuffer->getBufferHeight();
+
+    uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+
+    ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
+             glStatus);
+
+    return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
+}
+
+void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
+    mFboHeight = 0;
+
+    // back to main framebuffer
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+void GLESRenderEngine::checkErrors() const {
+    do {
+        // there could be more than one error flag
+        GLenum error = glGetError();
+        if (error == GL_NO_ERROR) break;
+        ALOGE("GL error 0x%04x", int(error));
+    } while (true);
+}
+
+bool GLESRenderEngine::supportsProtectedContent() const {
+    return mProtectedEGLContext != EGL_NO_CONTEXT;
+}
+
+bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) {
+    if (useProtectedContext == mInProtectedContext) {
+        return true;
+    }
+    if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) {
+        return false;
+    }
+    const EGLSurface surface = useProtectedContext ? mProtectedDummySurface : mDummySurface;
+    const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
+    const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
+    if (success) {
+        mInProtectedContext = useProtectedContext;
+    }
+    return success;
+}
+
+status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
+                                      const std::vector<LayerSettings>& layers,
+                                      ANativeWindowBuffer* const buffer,
+                                      base::unique_fd* drawFence) {
+    ATRACE_CALL();
+    if (layers.empty()) {
+        ALOGV("Drawing empty layer stack");
+        return NO_ERROR;
+    }
+
+    BindNativeBufferAsFramebuffer fbo(*this, buffer);
+
+    if (fbo.getStatus() != NO_ERROR) {
+        ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+              buffer->handle);
+        checkErrors();
+        return fbo.getStatus();
+    }
+
+    evictImages(layers);
+
+    // clear the entire buffer, sometimes when we reuse buffers we'd persist
+    // ghost images otherwise.
+    // we also require a full transparent framebuffer for overlays. This is
+    // probably not quite efficient on all GPUs, since we could filter out
+    // opaque layers.
+    clearWithColor(0.0, 0.0, 0.0, 0.0);
+
+    setViewportAndProjection(display.physicalDisplay, display.clip);
+
+    setOutputDataSpace(display.outputDataspace);
+    setDisplayMaxLuminance(display.maxLuminance);
+
+    mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
+    mState.projectionMatrix = projectionMatrix;
+    if (!display.clearRegion.isEmpty()) {
+        glDisable(GL_BLEND);
+        fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
+    }
+
+    Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
+    for (auto layer : layers) {
+        mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
+
+        const FloatRect bounds = layer.geometry.boundaries;
+        Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+        position[0] = vec2(bounds.left, bounds.top);
+        position[1] = vec2(bounds.left, bounds.bottom);
+        position[2] = vec2(bounds.right, bounds.bottom);
+        position[3] = vec2(bounds.right, bounds.top);
+
+        setupLayerCropping(layer, mesh);
+        setColorTransform(display.colorTransform * layer.colorTransform);
+
+        bool usePremultipliedAlpha = true;
+        bool disableTexture = true;
+        bool isOpaque = false;
+
+        if (layer.source.buffer.buffer != nullptr) {
+            disableTexture = false;
+            isOpaque = layer.source.buffer.isOpaque;
+
+            sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
+
+            bool readCache = layer.source.buffer.cacheHint == Buffer::CachingHint::USE_CACHE;
+            bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
+                                      layer.source.buffer.fence, readCache, /*persistCache=*/true);
+
+            usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
+            Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
+            mat4 texMatrix = layer.source.buffer.textureTransform;
+
+            texture.setMatrix(texMatrix.asArray());
+            texture.setFiltering(layer.source.buffer.useTextureFiltering);
+
+            texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
+            setSourceY410BT2020(layer.source.buffer.isY410BT2020);
+
+            renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
+            texCoords[0] = vec2(0.0, 0.0);
+            texCoords[1] = vec2(0.0, 1.0);
+            texCoords[2] = vec2(1.0, 1.0);
+            texCoords[3] = vec2(1.0, 0.0);
+            setupLayerTexturing(texture);
+        }
+
+        const half3 solidColor = layer.source.solidColor;
+        const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+        // Buffer sources will have a black solid color ignored in the shader,
+        // so in that scenario the solid color passed here is arbitrary.
+        setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
+                           layer.geometry.roundedCornersRadius);
+        if (layer.disableBlending) {
+            glDisable(GL_BLEND);
+        }
+        setSourceDataSpace(layer.sourceDataspace);
+
+        drawMesh(mesh);
+
+        // Cleanup if there's a buffer source
+        if (layer.source.buffer.buffer != nullptr) {
+            disableBlending();
+            setSourceY410BT2020(false);
+            disableTexturing();
+        }
+    }
+
+    *drawFence = flush();
+    // If flush failed or we don't support native fences, we need to force the
+    // gl command stream to be executed.
+    if (drawFence->get() < 0) {
+        bool success = finish();
+        if (!success) {
+            ALOGE("Failed to flush RenderEngine commands");
+            checkErrors();
+            // Chances are, something illegal happened (either the caller passed
+            // us bad parameters, or we messed up our shader generation).
+            return INVALID_OPERATION;
+        }
+    }
+
+    checkErrors();
+    return NO_ERROR;
+}
+
+void GLESRenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
+                                                ui::Transform::orientation_flags rotation) {
+    setViewportAndProjection(Rect(vpw, vph), sourceCrop);
+
+    if (rotation == ui::Transform::ROT_0) {
+        return;
+    }
+
+    // Apply custom rotation to the projection.
+    float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
+    mat4 m = mState.projectionMatrix;
+    switch (rotation) {
+        case ui::Transform::ROT_90:
+            m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m;
+            break;
+        case ui::Transform::ROT_180:
+            m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m;
+            break;
+        case ui::Transform::ROT_270:
+            m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m;
+            break;
+        default:
+            break;
+    }
+    mState.projectionMatrix = m;
+}
+
+void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
+    ATRACE_CALL();
+    mVpWidth = viewport.getWidth();
+    mVpHeight = viewport.getHeight();
+
+    // We pass the the top left corner instead of the bottom left corner,
+    // because since we're rendering off-screen first.
+    glViewport(viewport.left, viewport.top, mVpWidth, mVpHeight);
+
+    mState.projectionMatrix = mat4::ortho(clip.left, clip.right, clip.top, clip.bottom, 0, 1);
+}
+
+void GLESRenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+                                          const half4& color, float cornerRadius) {
+    mState.isPremultipliedAlpha = premultipliedAlpha;
+    mState.isOpaque = opaque;
+    mState.color = color;
+    mState.cornerRadius = cornerRadius;
+
+    if (disableTexture) {
+        mState.textureEnabled = false;
+    }
+
+    if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) {
+        glEnable(GL_BLEND);
+        glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    } else {
+        glDisable(GL_BLEND);
+    }
+}
+
+void GLESRenderEngine::setSourceY410BT2020(bool enable) {
+    mState.isY410BT2020 = enable;
+}
+
+void GLESRenderEngine::setSourceDataSpace(Dataspace source) {
+    mDataSpace = source;
+}
+
+void GLESRenderEngine::setOutputDataSpace(Dataspace dataspace) {
+    mOutputDataSpace = dataspace;
+}
+
+void GLESRenderEngine::setDisplayMaxLuminance(const float maxLuminance) {
+    mState.displayMaxLuminance = maxLuminance;
+}
+
+void GLESRenderEngine::setupLayerTexturing(const Texture& texture) {
+    GLuint target = texture.getTextureTarget();
+    glBindTexture(target, texture.getTextureName());
+    GLenum filter = GL_NEAREST;
+    if (texture.getFiltering()) {
+        filter = GL_LINEAR;
+    }
+    glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
+    glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
+
+    mState.texture = texture;
+    mState.textureEnabled = true;
+}
+
+void GLESRenderEngine::setupLayerBlackedOut() {
+    glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
+    Texture texture(Texture::TEXTURE_2D, mProtectedTexName);
+    texture.setDimensions(1, 1); // FIXME: we should get that from somewhere
+    mState.texture = texture;
+    mState.textureEnabled = true;
+}
+
+void GLESRenderEngine::setColorTransform(const mat4& colorTransform) {
+    mState.colorMatrix = colorTransform;
+}
+
+void GLESRenderEngine::disableTexturing() {
+    mState.textureEnabled = false;
+}
+
+void GLESRenderEngine::disableBlending() {
+    glDisable(GL_BLEND);
+}
+
+void GLESRenderEngine::setupFillWithColor(float r, float g, float b, float a) {
+    mState.isPremultipliedAlpha = true;
+    mState.isOpaque = false;
+    mState.color = half4(r, g, b, a);
+    mState.textureEnabled = false;
+    glDisable(GL_BLEND);
+}
+
+void GLESRenderEngine::setupCornerRadiusCropSize(float width, float height) {
+    mState.cropSize = half2(width, height);
+}
+
+void GLESRenderEngine::drawMesh(const Mesh& mesh) {
+    ATRACE_CALL();
+    if (mesh.getTexCoordsSize()) {
+        glEnableVertexAttribArray(Program::texCoords);
+        glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE,
+                              mesh.getByteStride(), mesh.getTexCoords());
+    }
+
+    glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
+                          mesh.getByteStride(), mesh.getPositions());
+
+    if (mState.cornerRadius > 0.0f) {
+        glEnableVertexAttribArray(Program::cropCoords);
+        glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
+                              mesh.getByteStride(), mesh.getCropCoords());
+    }
+
+    // By default, DISPLAY_P3 is the only supported wide color output. However,
+    // when HDR content is present, hardware composer may be able to handle
+    // BT2020 data space, in that case, the output data space is set to be
+    // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
+    // to respect this and convert non-HDR content to HDR format.
+    if (mUseColorManagement) {
+        Description managedState = mState;
+        Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
+        Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
+        Dataspace outputStandard =
+                static_cast<Dataspace>(mOutputDataSpace & Dataspace::STANDARD_MASK);
+        Dataspace outputTransfer =
+                static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
+        bool needsXYZConversion = needsXYZTransformMatrix();
+
+        // NOTE: if the input standard of the input dataspace is not STANDARD_DCI_P3 or
+        // STANDARD_BT2020, it will be  treated as STANDARD_BT709
+        if (inputStandard != Dataspace::STANDARD_DCI_P3 &&
+            inputStandard != Dataspace::STANDARD_BT2020) {
+            inputStandard = Dataspace::STANDARD_BT709;
+        }
+
+        if (needsXYZConversion) {
+            // The supported input color spaces are standard RGB, Display P3 and BT2020.
+            switch (inputStandard) {
+                case Dataspace::STANDARD_DCI_P3:
+                    managedState.inputTransformMatrix = mDisplayP3ToXyz;
+                    break;
+                case Dataspace::STANDARD_BT2020:
+                    managedState.inputTransformMatrix = mBt2020ToXyz;
+                    break;
+                default:
+                    managedState.inputTransformMatrix = mSrgbToXyz;
+                    break;
+            }
+
+            // The supported output color spaces are BT2020, Display P3 and standard RGB.
+            switch (outputStandard) {
+                case Dataspace::STANDARD_BT2020:
+                    managedState.outputTransformMatrix = mXyzToBt2020;
+                    break;
+                case Dataspace::STANDARD_DCI_P3:
+                    managedState.outputTransformMatrix = mXyzToDisplayP3;
+                    break;
+                default:
+                    managedState.outputTransformMatrix = mXyzToSrgb;
+                    break;
+            }
+        } else if (inputStandard != outputStandard) {
+            // At this point, the input data space and output data space could be both
+            // HDR data spaces, but they match each other, we do nothing in this case.
+            // In addition to the case above, the input data space could be
+            // - scRGB linear
+            // - scRGB non-linear
+            // - sRGB
+            // - Display P3
+            // - BT2020
+            // The output data spaces could be
+            // - sRGB
+            // - Display P3
+            // - BT2020
+            switch (outputStandard) {
+                case Dataspace::STANDARD_BT2020:
+                    if (inputStandard == Dataspace::STANDARD_BT709) {
+                        managedState.outputTransformMatrix = mSrgbToBt2020;
+                    } else if (inputStandard == Dataspace::STANDARD_DCI_P3) {
+                        managedState.outputTransformMatrix = mDisplayP3ToBt2020;
+                    }
+                    break;
+                case Dataspace::STANDARD_DCI_P3:
+                    if (inputStandard == Dataspace::STANDARD_BT709) {
+                        managedState.outputTransformMatrix = mSrgbToDisplayP3;
+                    } else if (inputStandard == Dataspace::STANDARD_BT2020) {
+                        managedState.outputTransformMatrix = mBt2020ToDisplayP3;
+                    }
+                    break;
+                default:
+                    if (inputStandard == Dataspace::STANDARD_DCI_P3) {
+                        managedState.outputTransformMatrix = mDisplayP3ToSrgb;
+                    } else if (inputStandard == Dataspace::STANDARD_BT2020) {
+                        managedState.outputTransformMatrix = mBt2020ToSrgb;
+                    }
+                    break;
+            }
+        }
+
+        // we need to convert the RGB value to linear space and convert it back when:
+        // - there is a color matrix that is not an identity matrix, or
+        // - there is an output transform matrix that is not an identity matrix, or
+        // - the input transfer function doesn't match the output transfer function.
+        if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() ||
+            inputTransfer != outputTransfer) {
+            managedState.inputTransferFunction =
+                    Description::dataSpaceToTransferFunction(inputTransfer);
+            managedState.outputTransferFunction =
+                    Description::dataSpaceToTransferFunction(outputTransfer);
+        }
+
+        ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
+                                                                   : mEGLContext,
+                                               managedState);
+
+        glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
+
+        if (outputDebugPPMs) {
+            static uint64_t managedColorFrameCount = 0;
+            std::ostringstream out;
+            out << "/data/texture_out" << managedColorFrameCount++;
+            writePPM(out.str().c_str(), mVpWidth, mVpHeight);
+        }
+    } else {
+        ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
+                                                                   : mEGLContext,
+                                               mState);
+
+        glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
+    }
+
+    if (mesh.getTexCoordsSize()) {
+        glDisableVertexAttribArray(Program::texCoords);
+    }
+
+    if (mState.cornerRadius > 0.0f) {
+        glDisableVertexAttribArray(Program::cropCoords);
+    }
+}
+
+size_t GLESRenderEngine::getMaxTextureSize() const {
+    return mMaxTextureSize;
+}
+
+size_t GLESRenderEngine::getMaxViewportDims() const {
+    return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1];
+}
+
+void GLESRenderEngine::dump(std::string& result) {
+    const GLExtensions& extensions = GLExtensions::getInstance();
+    ProgramCache& cache = ProgramCache::getInstance();
+
+    StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
+    StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
+    StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
+                  extensions.getVersion());
+    StringAppendF(&result, "%s\n", extensions.getExtensions());
+    StringAppendF(&result, "RenderEngine is in protected context : %d\n", mInProtectedContext);
+    StringAppendF(&result, "RenderEngine program cache size for unprotected context: %zu\n",
+                  cache.getSize(mEGLContext));
+    StringAppendF(&result, "RenderEngine program cache size for protected context: %zu\n",
+                  cache.getSize(mProtectedEGLContext));
+    StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n",
+                  dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
+                  dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
+}
+
+GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) {
+    int major, minor;
+    if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
+        if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) {
+            ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
+            return GLES_VERSION_1_0;
+        }
+    }
+
+    if (major == 1 && minor == 0) return GLES_VERSION_1_0;
+    if (major == 1 && minor >= 1) return GLES_VERSION_1_1;
+    if (major == 2 && minor >= 0) return GLES_VERSION_2_0;
+    if (major == 3 && minor >= 0) return GLES_VERSION_3_0;
+
+    ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor);
+    return GLES_VERSION_1_0;
+}
+
+EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
+                                              EGLContext shareContext, bool useContextPriority,
+                                              Protection protection) {
+    EGLint renderableType = 0;
+    if (config == EGL_NO_CONFIG) {
+        renderableType = EGL_OPENGL_ES3_BIT;
+    } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
+        LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
+    }
+    EGLint contextClientVersion = 0;
+    if (renderableType & EGL_OPENGL_ES3_BIT) {
+        contextClientVersion = 3;
+    } else if (renderableType & EGL_OPENGL_ES2_BIT) {
+        contextClientVersion = 2;
+    } else if (renderableType & EGL_OPENGL_ES_BIT) {
+        contextClientVersion = 1;
+    } else {
+        LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
+    }
+
+    std::vector<EGLint> contextAttributes;
+    contextAttributes.reserve(7);
+    contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+    contextAttributes.push_back(contextClientVersion);
+    if (useContextPriority) {
+        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+    }
+    if (protection == Protection::PROTECTED) {
+        contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+        contextAttributes.push_back(EGL_TRUE);
+    }
+    contextAttributes.push_back(EGL_NONE);
+
+    EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+
+    if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) {
+        // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus
+        // EGL_NO_CONTEXT so that we can abort.
+        if (config != EGL_NO_CONFIG) {
+            return context;
+        }
+        // If |config| is EGL_NO_CONFIG, we speculatively try to create GLES 3 context, so we should
+        // try to fall back to GLES 2.
+        contextAttributes[1] = 2;
+        context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+    }
+
+    return context;
+}
+
+EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
+                                                          int hwcFormat, Protection protection) {
+    EGLConfig dummyConfig = config;
+    if (dummyConfig == EGL_NO_CONFIG) {
+        dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+    }
+    std::vector<EGLint> attributes;
+    attributes.reserve(7);
+    attributes.push_back(EGL_WIDTH);
+    attributes.push_back(1);
+    attributes.push_back(EGL_HEIGHT);
+    attributes.push_back(1);
+    if (protection == Protection::PROTECTED) {
+        attributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+        attributes.push_back(EGL_TRUE);
+    }
+    attributes.push_back(EGL_NONE);
+
+    return eglCreatePbufferSurface(display, dummyConfig, attributes.data());
+}
+
+bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
+    const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK);
+    const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK);
+    return standard == Dataspace::STANDARD_BT2020 &&
+            (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG);
+}
+
+// For convenience, we want to convert the input color space to XYZ color space first,
+// and then convert from XYZ color space to output color space when
+// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or
+//   HDR content will be tone-mapped to SDR; Or,
+// - there are HDR PQ and HLG contents presented at the same time, where we want to convert
+//   HLG content to PQ content.
+// In either case above, we need to operate the Y value in XYZ color space. Thus, when either
+// input data space or output data space is HDR data space, and the input transfer function
+// doesn't match the output transfer function, we would enable an intermediate transfrom to
+// XYZ color space.
+bool GLESRenderEngine::needsXYZTransformMatrix() const {
+    const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace);
+    const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace);
+    const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
+    const Dataspace outputTransfer =
+            static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
+
+    return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
+}
+
+// FlushTracer implementation
+GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) {
+    mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this);
+}
+
+GLESRenderEngine::FlushTracer::~FlushTracer() {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mRunning = false;
+    }
+    mCondition.notify_all();
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+void GLESRenderEngine::FlushTracer::queueSync(EGLSyncKHR sync) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    char name[64];
+    const uint64_t frameNum = mFramesQueued++;
+    snprintf(name, sizeof(name), "Queueing sync for frame: %lu",
+             static_cast<unsigned long>(frameNum));
+    ATRACE_NAME(name);
+    mQueue.push({sync, frameNum});
+    ATRACE_INT("GPU Frames Outstanding", mQueue.size());
+    mCondition.notify_one();
+}
+
+void GLESRenderEngine::FlushTracer::loop() {
+    while (mRunning) {
+        QueueEntry entry;
+        {
+            std::lock_guard<std::mutex> lock(mMutex);
+
+            mCondition.wait(mMutex,
+                            [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; });
+
+            if (!mRunning) {
+                // if mRunning is false, then FlushTracer is being destroyed, so
+                // bail out now.
+                break;
+            }
+            entry = mQueue.front();
+            mQueue.pop();
+        }
+        {
+            char name[64];
+            snprintf(name, sizeof(name), "waiting for frame %lu",
+                     static_cast<unsigned long>(entry.mFrameNum));
+            ATRACE_NAME(name);
+            mEngine->waitSync(entry.mSync, 0);
+        }
+    }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
new file mode 100644
index 0000000..34187f1
--- /dev/null
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2013 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 SF_GLESRENDERENGINE_H_
+#define SF_GLESRENDERENGINE_H_
+
+#include <android-base/thread_annotations.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <renderengine/RenderEngine.h>
+#include <renderengine/private/Description.h>
+#include <unordered_map>
+
+#define EGL_NO_CONFIG ((EGLConfig)0)
+
+namespace android {
+
+namespace renderengine {
+
+class Mesh;
+class Texture;
+
+namespace gl {
+
+class GLImage;
+
+class GLESRenderEngine : public impl::RenderEngine {
+public:
+    static std::unique_ptr<GLESRenderEngine> create(int hwcFormat, uint32_t featureFlags);
+    static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
+
+    GLESRenderEngine(uint32_t featureFlags, // See RenderEngine::FeatureFlag
+                     EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy,
+                     EGLContext protectedContext, EGLSurface protectedDummy);
+    ~GLESRenderEngine() override;
+
+    std::unique_ptr<Framebuffer> createFramebuffer() override;
+    std::unique_ptr<Image> createImage() override;
+
+    void primeCache() const override;
+    bool isCurrent() const override;
+    base::unique_fd flush() override;
+    bool finish() override;
+    bool waitFence(base::unique_fd fenceFd) override;
+    void clearWithColor(float red, float green, float blue, float alpha) override;
+    void fillRegionWithColor(const Region& region, float red, float green, float blue,
+                             float alpha) override;
+    void setScissor(const Rect& region) override;
+    void disableScissor() override;
+    void genTextures(size_t count, uint32_t* names) override;
+    void deleteTextures(size_t count, uint32_t const* names) override;
+    void bindExternalTextureImage(uint32_t texName, const Image& image) override;
+    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence,
+                                       bool readCache);
+    status_t bindFrameBuffer(Framebuffer* framebuffer) override;
+    void unbindFrameBuffer(Framebuffer* framebuffer) override;
+    void checkErrors() const override;
+
+    bool isProtected() const override { return mInProtectedContext; }
+    bool supportsProtectedContent() const override;
+    bool useProtectedContext(bool useProtectedContext) override;
+    status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+                        ANativeWindowBuffer* buffer, base::unique_fd* drawFence) override;
+
+    // internal to RenderEngine
+    EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
+    EGLConfig getEGLConfig() const { return mEGLConfig; }
+
+protected:
+    void dump(std::string& result) override;
+    void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
+                                  ui::Transform::orientation_flags rotation) override;
+    void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+                            const half4& color, float cornerRadius) override;
+    void setupLayerTexturing(const Texture& texture) override;
+    void setupLayerBlackedOut() override;
+    void setupFillWithColor(float r, float g, float b, float a) override;
+    void setColorTransform(const mat4& colorTransform) override;
+    void disableTexturing() override;
+    void disableBlending() override;
+    void setupCornerRadiusCropSize(float width, float height) override;
+
+    // HDR and color management related functions and state
+    void setSourceY410BT2020(bool enable) override;
+    void setSourceDataSpace(ui::Dataspace source) override;
+    void setOutputDataSpace(ui::Dataspace dataspace) override;
+    void setDisplayMaxLuminance(const float maxLuminance) override;
+
+    // drawing
+    void drawMesh(const Mesh& mesh) override;
+
+    size_t getMaxTextureSize() const override;
+    size_t getMaxViewportDims() const override;
+
+private:
+    enum GlesVersion {
+        GLES_VERSION_1_0 = 0x10000,
+        GLES_VERSION_1_1 = 0x10001,
+        GLES_VERSION_2_0 = 0x20000,
+        GLES_VERSION_3_0 = 0x30000,
+    };
+
+    static GlesVersion parseGlesVersion(const char* str);
+    static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
+                                       EGLContext shareContext, bool useContextPriority,
+                                       Protection protection);
+    static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
+                                                   int hwcFormat, Protection protection);
+    bool waitSync(EGLSyncKHR sync, EGLint flags);
+
+    // A data space is considered HDR data space if it has BT2020 color space
+    // with PQ or HLG transfer function.
+    bool isHdrDataSpace(const ui::Dataspace dataSpace) const;
+    bool needsXYZTransformMatrix() const;
+    // Defines the viewport, and sets the projection matrix to the projection
+    // defined by the clip.
+    void setViewportAndProjection(Rect viewport, Rect clip);
+    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence,
+                                       bool readCache, bool persistCache);
+    // Evicts stale images from the buffer cache.
+    void evictImages(const std::vector<LayerSettings>& layers);
+    // Computes the cropping window for the layer and sets up cropping
+    // coordinates for the mesh.
+    FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh);
+
+    EGLDisplay mEGLDisplay;
+    EGLConfig mEGLConfig;
+    EGLContext mEGLContext;
+    EGLSurface mDummySurface;
+    EGLContext mProtectedEGLContext;
+    EGLSurface mProtectedDummySurface;
+    GLuint mProtectedTexName;
+    GLint mMaxViewportDims[2];
+    GLint mMaxTextureSize;
+    GLuint mVpWidth;
+    GLuint mVpHeight;
+    Description mState;
+
+    mat4 mSrgbToXyz;
+    mat4 mDisplayP3ToXyz;
+    mat4 mBt2020ToXyz;
+    mat4 mXyzToSrgb;
+    mat4 mXyzToDisplayP3;
+    mat4 mXyzToBt2020;
+    mat4 mSrgbToDisplayP3;
+    mat4 mSrgbToBt2020;
+    mat4 mDisplayP3ToSrgb;
+    mat4 mDisplayP3ToBt2020;
+    mat4 mBt2020ToSrgb;
+    mat4 mBt2020ToDisplayP3;
+
+    bool mInProtectedContext = false;
+    // If set to true, then enables tracing flush() and finish() to systrace.
+    bool mTraceGpuCompletion = false;
+    int32_t mFboHeight = 0;
+
+    // Current dataspace of layer being rendered
+    ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN;
+
+    // Current output dataspace of the render engine
+    ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN;
+
+    // Whether device supports color management, currently color management
+    // supports sRGB, DisplayP3 color spaces.
+    const bool mUseColorManagement = false;
+
+    // Cache of GL images that we'll store per GraphicBuffer ID
+    // TODO: Layer should call back on destruction instead to clean this up,
+    // as it has better system utilization at the potential expense of a
+    // more complicated interface.
+    std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache;
+
+    class FlushTracer {
+    public:
+        FlushTracer(GLESRenderEngine* engine);
+        ~FlushTracer();
+        void queueSync(EGLSyncKHR sync) EXCLUDES(mMutex);
+
+        struct QueueEntry {
+            EGLSyncKHR mSync = nullptr;
+            uint64_t mFrameNum = 0;
+        };
+
+    private:
+        void loop();
+        GLESRenderEngine* const mEngine;
+        std::thread mThread;
+        std::condition_variable_any mCondition;
+        std::mutex mMutex;
+        std::queue<QueueEntry> mQueue GUARDED_BY(mMutex);
+        uint64_t mFramesQueued GUARDED_BY(mMutex) = 0;
+        bool mRunning = true;
+    };
+    friend class FlushTracer;
+    std::unique_ptr<FlushTracer> mFlushTracer;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp
similarity index 60%
rename from services/surfaceflinger/RenderEngine/GLExtensions.cpp
rename to libs/renderengine/gl/GLExtensions.cpp
index dc09a37..2924b0e 100644
--- a/services/surfaceflinger/RenderEngine/GLExtensions.cpp
+++ b/libs/renderengine/gl/GLExtensions.cpp
@@ -14,33 +14,45 @@
  * limitations under the License.
  */
 
+#include "GLExtensions.h"
+
+#include <string>
+#include <unordered_set>
+
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "GLExtensions.h"
+ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions)
 
 namespace android {
-// ---------------------------------------------------------------------------
+namespace renderengine {
+namespace gl {
 
-ANDROID_SINGLETON_STATIC_INSTANCE(GLExtensions)
+namespace {
 
-SortedVector<String8> GLExtensions::parseExtensionString(char const* extensions) {
-    SortedVector<String8> list;
+class ExtensionSet {
+public:
+    ExtensionSet(const char* extensions) {
+        char const* curr = extensions;
+        char const* head = curr;
+        do {
+            head = strchr(curr, ' ');
+            size_t len = head ? head - curr : strlen(curr);
+            if (len > 0) {
+                mExtensions.emplace(curr, len);
+            }
+            curr = head + 1;
+        } while (head);
+    }
 
-    char const* curr = extensions;
-    char const* head = curr;
-    do {
-        head = strchr(curr, ' ');
-        String8 s(curr, head ? head - curr : strlen(curr));
-        if (s.length()) {
-            list.add(s);
-        }
-        curr = head + 1;
-    } while (head);
+    bool hasExtension(const char* extension) const { return mExtensions.count(extension) > 0; }
 
-    return list;
-}
+private:
+    std::unordered_set<std::string> mExtensions;
+};
+
+} // anonymous namespace
 
 void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer,
                                      GLubyte const* version, GLubyte const* extensions) {
@@ -48,12 +60,11 @@
     mRenderer = (char const*)renderer;
     mVersion = (char const*)version;
     mExtensions = (char const*)extensions;
-    mExtensionList = parseExtensionString(mExtensions);
-}
 
-bool GLExtensions::hasExtension(char const* extension) const {
-    const String8 s(extension);
-    return mExtensionList.indexOf(s) >= 0;
+    ExtensionSet extensionSet(mExtensions.c_str());
+    if (extensionSet.hasExtension("GL_EXT_protected_textures")) {
+        mHasProtectedTexture = true;
+    }
 }
 
 char const* GLExtensions::getVendor() const {
@@ -75,7 +86,8 @@
 void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) {
     mEGLVersion = eglVersion;
     mEGLExtensions = eglExtensions;
-    mEGLExtensionList = parseExtensionString(mEGLExtensions);
+
+    ExtensionSet extensionSet(eglExtensions);
 
     // EGL_ANDROIDX_no_config_context is an experimental extension with no
     // written specification. It will be replaced by something more formal.
@@ -85,30 +97,29 @@
     //
     // EGL_KHR_no_config_context is official extension to allow creating a
     // context that works with any surface of a display.
-    if (hasEGLExtension("EGL_ANDROIDX_no_config_context") ||
-        hasEGLExtension("EGL_KHR_no_config_context")) {
+    if (extensionSet.hasExtension("EGL_ANDROIDX_no_config_context") ||
+        extensionSet.hasExtension("EGL_KHR_no_config_context")) {
         mHasNoConfigContext = true;
     }
 
-    if (hasEGLExtension("EGL_ANDROID_native_fence_sync")) {
+    if (extensionSet.hasExtension("EGL_ANDROID_native_fence_sync")) {
         mHasNativeFenceSync = true;
     }
-    if (hasEGLExtension("EGL_KHR_fence_sync")) {
+    if (extensionSet.hasExtension("EGL_KHR_fence_sync")) {
         mHasFenceSync = true;
     }
-    if (hasEGLExtension("EGL_KHR_wait_sync")) {
+    if (extensionSet.hasExtension("EGL_KHR_wait_sync")) {
         mHasWaitSync = true;
     }
-
-    if (hasEGLExtension("EGL_ANDROID_image_crop")) {
-        mHasImageCrop = true;
-    }
-    if (hasEGLExtension("EGL_EXT_protected_content")) {
+    if (extensionSet.hasExtension("EGL_EXT_protected_content")) {
         mHasProtectedContent = true;
     }
-    if (hasEGLExtension("EGL_IMG_context_priority")) {
+    if (extensionSet.hasExtension("EGL_IMG_context_priority")) {
         mHasContextPriority = true;
     }
+    if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) {
+        mHasSurfacelessContext = true;
+    }
 }
 
 char const* GLExtensions::getEGLVersion() const {
@@ -119,10 +130,6 @@
     return mEGLExtensions.string();
 }
 
-bool GLExtensions::hasEGLExtension(char const* extension) const {
-    const String8 s(extension);
-    return mEGLExtensionList.indexOf(s) >= 0;
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h
similarity index 80%
rename from services/surfaceflinger/RenderEngine/GLExtensions.h
rename to libs/renderengine/gl/GLExtensions.h
index 0d8c10b..ef00009 100644
--- a/services/surfaceflinger/RenderEngine/GLExtensions.h
+++ b/libs/renderengine/gl/GLExtensions.h
@@ -20,55 +20,27 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Singleton.h>
-#include <utils/SortedVector.h>
-#include <utils/String8.h>
-
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <GLES/gl.h>
 #include <GLES/glext.h>
+#include <utils/Singleton.h>
+#include <utils/String8.h>
 
 namespace android {
-// ---------------------------------------------------------------------------
+namespace renderengine {
+namespace gl {
 
 class GLExtensions : public Singleton<GLExtensions> {
-    friend class Singleton<GLExtensions>;
-
-    bool mHasNoConfigContext = false;
-    bool mHasNativeFenceSync = false;
-    bool mHasFenceSync = false;
-    bool mHasWaitSync = false;
-    bool mHasImageCrop = false;
-    bool mHasProtectedContent = false;
-    bool mHasContextPriority = false;
-
-    String8 mVendor;
-    String8 mRenderer;
-    String8 mVersion;
-    String8 mExtensions;
-    SortedVector<String8> mExtensionList;
-
-    String8 mEGLVersion;
-    String8 mEGLExtensions;
-    SortedVector<String8> mEGLExtensionList;
-
-    static SortedVector<String8> parseExtensionString(char const* extensions);
-
-    GLExtensions(const GLExtensions&);
-    GLExtensions& operator=(const GLExtensions&);
-
-protected:
-    GLExtensions() = default;
-
 public:
     bool hasNoConfigContext() const { return mHasNoConfigContext; }
     bool hasNativeFenceSync() const { return mHasNativeFenceSync; }
     bool hasFenceSync() const { return mHasFenceSync; }
     bool hasWaitSync() const { return mHasWaitSync; }
-    bool hasImageCrop() const { return mHasImageCrop; }
     bool hasProtectedContent() const { return mHasProtectedContent; }
     bool hasContextPriority() const { return mHasContextPriority; }
+    bool hasSurfacelessContext() const { return mHasSurfacelessContext; }
+    bool hasProtectedTexture() const { return mHasProtectedTexture; }
 
     void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
                            GLubyte const* extensions);
@@ -76,15 +48,39 @@
     char const* getRenderer() const;
     char const* getVersion() const;
     char const* getExtensions() const;
-    bool hasExtension(char const* extension) const;
 
     void initWithEGLStrings(char const* eglVersion, char const* eglExtensions);
     char const* getEGLVersion() const;
     char const* getEGLExtensions() const;
-    bool hasEGLExtension(char const* extension) const;
+
+protected:
+    GLExtensions() = default;
+
+private:
+    friend class Singleton<GLExtensions>;
+
+    bool mHasNoConfigContext = false;
+    bool mHasNativeFenceSync = false;
+    bool mHasFenceSync = false;
+    bool mHasWaitSync = false;
+    bool mHasProtectedContent = false;
+    bool mHasContextPriority = false;
+    bool mHasSurfacelessContext = false;
+    bool mHasProtectedTexture = false;
+
+    String8 mVendor;
+    String8 mRenderer;
+    String8 mVersion;
+    String8 mExtensions;
+    String8 mEGLVersion;
+    String8 mEGLExtensions;
+
+    GLExtensions(const GLExtensions&);
+    GLExtensions& operator=(const GLExtensions&);
 };
 
-// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace gl
+} // namespace renderengine
+} // namespace android
 
 #endif // ANDROID_SF_GLEXTENSION_H
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
new file mode 100644
index 0000000..0e3b405
--- /dev/null
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLFramebuffer.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <nativebase/nativebase.h>
+#include <utils/Trace.h>
+#include "GLESRenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLFramebuffer::GLFramebuffer(const GLESRenderEngine& engine)
+      : mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
+    glGenTextures(1, &mTextureName);
+    glGenFramebuffers(1, &mFramebufferName);
+}
+
+GLFramebuffer::~GLFramebuffer() {
+    glDeleteFramebuffers(1, &mFramebufferName);
+    glDeleteTextures(1, &mTextureName);
+    eglDestroyImageKHR(mEGLDisplay, mEGLImage);
+}
+
+bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) {
+    ATRACE_CALL();
+    if (mEGLImage != EGL_NO_IMAGE_KHR) {
+        eglDestroyImageKHR(mEGLDisplay, mEGLImage);
+        mEGLImage = EGL_NO_IMAGE_KHR;
+        mBufferWidth = 0;
+        mBufferHeight = 0;
+    }
+
+    if (nativeBuffer) {
+        EGLint attributes[] = {
+                isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
+                isProtected ? EGL_TRUE : EGL_NONE,
+                EGL_NONE,
+        };
+        mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+                                      nativeBuffer, attributes);
+        if (mEGLImage == EGL_NO_IMAGE_KHR) {
+            return false;
+        }
+        mBufferWidth = nativeBuffer->width;
+        mBufferHeight = nativeBuffer->height;
+    }
+    return true;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
new file mode 100644
index 0000000..5043c59
--- /dev/null
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <renderengine/Framebuffer.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLFramebuffer : public renderengine::Framebuffer {
+public:
+    explicit GLFramebuffer(const GLESRenderEngine& engine);
+    ~GLFramebuffer() override;
+
+    bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) override;
+    EGLImageKHR getEGLImage() const { return mEGLImage; }
+    uint32_t getTextureName() const { return mTextureName; }
+    uint32_t getFramebufferName() const { return mFramebufferName; }
+    int32_t getBufferHeight() const { return mBufferHeight; }
+    int32_t getBufferWidth() const { return mBufferWidth; }
+
+private:
+    EGLDisplay mEGLDisplay;
+    EGLImageKHR mEGLImage;
+    uint32_t mTextureName, mFramebufferName;
+
+    int32_t mBufferHeight = 0;
+    int32_t mBufferWidth = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Image.cpp b/libs/renderengine/gl/GLImage.cpp
similarity index 61%
rename from services/surfaceflinger/RenderEngine/Image.cpp
rename to libs/renderengine/gl/GLImage.cpp
index 0d06422..77e648e 100644
--- a/services/surfaceflinger/RenderEngine/Image.cpp
+++ b/libs/renderengine/gl/GLImage.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,30 +14,22 @@
  * limitations under the License.
  */
 
-#include "Image.h"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLImage.h"
 
 #include <vector>
 
 #include <log/log.h>
-
+#include <utils/Trace.h>
+#include "GLESRenderEngine.h"
 #include "GLExtensions.h"
-#include "RenderEngine.h"
 
 namespace android {
-namespace RE {
+namespace renderengine {
+namespace gl {
 
-Image::~Image() = default;
-
-namespace impl {
-
-Image::Image(const RenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {}
-
-Image::~Image() {
-    setNativeWindowBuffer(nullptr, false, 0, 0);
-}
-
-static std::vector<EGLint> buildAttributeList(bool isProtected, int32_t cropWidth,
-                                              int32_t cropHeight) {
+static std::vector<EGLint> buildAttributeList(bool isProtected) {
     std::vector<EGLint> attrs;
     attrs.reserve(16);
 
@@ -49,24 +41,19 @@
         attrs.push_back(EGL_TRUE);
     }
 
-    if (cropWidth > 0 && cropHeight > 0) {
-        attrs.push_back(EGL_IMAGE_CROP_LEFT_ANDROID);
-        attrs.push_back(0);
-        attrs.push_back(EGL_IMAGE_CROP_TOP_ANDROID);
-        attrs.push_back(0);
-        attrs.push_back(EGL_IMAGE_CROP_RIGHT_ANDROID);
-        attrs.push_back(cropWidth);
-        attrs.push_back(EGL_IMAGE_CROP_BOTTOM_ANDROID);
-        attrs.push_back(cropHeight);
-    }
-
     attrs.push_back(EGL_NONE);
 
     return attrs;
 }
 
-bool Image::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth,
-                                  int32_t cropHeight) {
+GLImage::GLImage(const GLESRenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {}
+
+GLImage::~GLImage() {
+    setNativeWindowBuffer(nullptr, false);
+}
+
+bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) {
+    ATRACE_CALL();
     if (mEGLImage != EGL_NO_IMAGE_KHR) {
         if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
             ALOGE("failed to destroy image: %#x", eglGetError());
@@ -75,18 +62,19 @@
     }
 
     if (buffer) {
-        std::vector<EGLint> attrs = buildAttributeList(isProtected, cropWidth, cropHeight);
+        std::vector<EGLint> attrs = buildAttributeList(isProtected);
         mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
                                       static_cast<EGLClientBuffer>(buffer), attrs.data());
         if (mEGLImage == EGL_NO_IMAGE_KHR) {
             ALOGE("failed to create EGLImage: %#x", eglGetError());
             return false;
         }
+        mProtected = isProtected;
     }
 
     return true;
 }
 
-} // namespace impl
-} // namespace RE
+} // namespace gl
+} // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/gl/GLImage.h b/libs/renderengine/gl/GLImage.h
new file mode 100644
index 0000000..59d6ce3
--- /dev/null
+++ b/libs/renderengine/gl/GLImage.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <android-base/macros.h>
+#include <renderengine/Image.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLImage : public renderengine::Image {
+public:
+    explicit GLImage(const GLESRenderEngine& engine);
+    ~GLImage() override;
+
+    bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override;
+
+    EGLImageKHR getEGLImage() const { return mEGLImage; }
+    bool isProtected() const { return mProtected; }
+
+private:
+    EGLDisplay mEGLDisplay;
+    EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR;
+    bool mProtected = false;
+
+    DISALLOW_COPY_AND_ASSIGN(GLImage);
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Program.cpp b/libs/renderengine/gl/Program.cpp
similarity index 82%
rename from services/surfaceflinger/RenderEngine/Program.cpp
rename to libs/renderengine/gl/Program.cpp
index fe536f0..fe9d909 100644
--- a/services/surfaceflinger/RenderEngine/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
+#include "Program.h"
+
 #include <stdint.h>
 
 #include <log/log.h>
-#include <utils/String8.h>
-
 #include <math/mat4.h>
-#include "Description.h"
-#include "Program.h"
+#include <utils/String8.h>
 #include "ProgramCache.h"
 
 namespace android {
+namespace renderengine {
+namespace gl {
 
 Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment)
       : mInitialized(false) {
@@ -35,6 +36,7 @@
     glAttachShader(programId, fragmentId);
     glBindAttribLocation(programId, position, "position");
     glBindAttribLocation(programId, texCoords, "texCoords");
+    glBindAttribLocation(programId, cropCoords, "cropCoords");
     glLinkProgram(programId);
 
     GLint status;
@@ -65,6 +67,8 @@
         mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
         mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
         mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
+        mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
+        mCropCenterLoc = glGetUniformLocation(programId, "cropCenter");
 
         // set-up the default values for our uniforms
         glUseProgram(programId);
@@ -73,8 +77,6 @@
     }
 }
 
-Program::~Program() {}
-
 bool Program::isValid() const {
     return mInitialized;
 }
@@ -111,45 +113,41 @@
     return shader;
 }
 
-String8& Program::dumpShader(String8& result, GLenum /*type*/) {
-    GLuint shader = GL_FRAGMENT_SHADER ? mFragmentShader : mVertexShader;
-    GLint l;
-    glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &l);
-    char* src = new char[l];
-    glGetShaderSource(shader, l, nullptr, src);
-    result.append(src);
-    delete[] src;
-    return result;
-}
-
 void Program::setUniforms(const Description& desc) {
     // TODO: we should have a mechanism here to not always reset uniforms that
     // didn't change for this program.
 
     if (mSamplerLoc >= 0) {
         glUniform1i(mSamplerLoc, 0);
-        glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.mTexture.getMatrix().asArray());
+        glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.texture.getMatrix().asArray());
     }
     if (mColorLoc >= 0) {
-        const float color[4] = {desc.mColor.r, desc.mColor.g, desc.mColor.b, desc.mColor.a};
+        const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a};
         glUniform4fv(mColorLoc, 1, color);
     }
     if (mInputTransformMatrixLoc >= 0) {
-        mat4 inputTransformMatrix = mat4(desc.mInputTransformMatrix);
+        mat4 inputTransformMatrix = desc.inputTransformMatrix;
         glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
     }
     if (mOutputTransformMatrixLoc >= 0) {
         // The output transform matrix and color matrix can be combined as one matrix
         // that is applied right before applying OETF.
-        mat4 outputTransformMatrix = desc.mColorMatrix * desc.mOutputTransformMatrix;
-        glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE,
-                           outputTransformMatrix.asArray());
+        mat4 outputTransformMatrix = desc.colorMatrix * desc.outputTransformMatrix;
+        glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, outputTransformMatrix.asArray());
     }
     if (mDisplayMaxLuminanceLoc >= 0) {
-        glUniform1f(mDisplayMaxLuminanceLoc, desc.mDisplayMaxLuminance);
+        glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
+    }
+    if (mCornerRadiusLoc >= 0) {
+        glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
+    }
+    if (mCropCenterLoc >= 0) {
+        glUniform2f(mCropCenterLoc, desc.cropSize.x / 2.0f, desc.cropSize.y / 2.0f);
     }
     // these uniforms are always present
-    glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.mProjectionMatrix.asArray());
+    glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray());
 }
 
-} /* namespace android */
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Program.h b/libs/renderengine/gl/Program.h
similarity index 78%
rename from services/surfaceflinger/RenderEngine/Program.h
rename to libs/renderengine/gl/Program.h
index ae796c5..bc9cf08 100644
--- a/services/surfaceflinger/RenderEngine/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -20,24 +20,35 @@
 #include <stdint.h>
 
 #include <GLES2/gl2.h>
-
-#include "Description.h"
+#include <renderengine/private/Description.h>
 #include "ProgramCache.h"
 
 namespace android {
 
 class String8;
 
+namespace renderengine {
+namespace gl {
+
 /*
  * Abstracts a GLSL program comprising a vertex and fragment shader
  */
 class Program {
 public:
     // known locations for position and texture coordinates
-    enum { position = 0, texCoords = 1 };
+    enum {
+        /* position of each vertex for vertex shader */
+        position = 0,
+
+        /* UV coordinates for texture mapping */
+        texCoords = 1,
+
+        /* Crop coordinates, in pixels */
+        cropCoords = 2
+    };
 
     Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
-    ~Program();
+    ~Program() = default;
 
     /* whether this object is usable */
     bool isValid() const;
@@ -56,7 +67,6 @@
 
 private:
     GLuint buildShader(const char* source, GLenum type);
-    String8& dumpShader(String8& result, GLenum type);
 
     // whether the initialization succeeded
     bool mInitialized;
@@ -84,8 +94,16 @@
     /* location of transform matrix */
     GLint mInputTransformMatrixLoc;
     GLint mOutputTransformMatrixLoc;
+
+    /* location of corner radius uniform */
+    GLint mCornerRadiusLoc;
+
+    /* location of surface crop origin uniform, for rounded corner clipping */
+    GLint mCropCenterLoc;
 };
 
-} /* namespace android */
+} // namespace gl
+} // namespace renderengine
+} // namespace android
 
 #endif /* SF_RENDER_ENGINE_PROGRAM_H */
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
similarity index 85%
rename from services/surfaceflinger/RenderEngine/ProgramCache.cpp
rename to libs/renderengine/gl/ProgramCache.cpp
index 2073b05..49bdd2a 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -16,18 +16,21 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-#include "Description.h"
-#include "Program.h"
 #include "ProgramCache.h"
 
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <log/log.h>
+#include <renderengine/private/Description.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+#include "Program.h"
+
+ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::ProgramCache)
+
 namespace android {
-// -----------------------------------------------------------------------------------------------
+namespace renderengine {
+namespace gl {
 
 /*
  * A simple formatter class to automatically add the endl and
@@ -74,17 +77,11 @@
     return f;
 }
 
-// -----------------------------------------------------------------------------------------------
-
-ANDROID_SINGLETON_STATIC_INSTANCE(ProgramCache)
-
-ProgramCache::ProgramCache() {}
-
-ProgramCache::~ProgramCache() {}
-
-void ProgramCache::primeCache(bool hasWideColor) {
+void ProgramCache::primeCache(EGLContext context, bool useColorManagement) {
+    auto& cache = mCaches[context];
     uint32_t shaderCount = 0;
-    uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK;
+    uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK
+        | Key::ROUNDED_CORNERS_MASK;
     // Prime the cache for all combinations of the above masks,
     // leaving off the experimental color matrix mask options.
 
@@ -96,16 +93,14 @@
         if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) {
             continue;
         }
-        Program* program = mCache.valueFor(shaderKey);
-        if (program == nullptr) {
-            program = generateProgram(shaderKey);
-            mCache.add(shaderKey, program);
+        if (cache.count(shaderKey) == 0) {
+            cache.emplace(shaderKey, generateProgram(shaderKey));
             shaderCount++;
         }
     }
 
     // Prime for sRGB->P3 conversion
-    if (hasWideColor) {
+    if (useColorManagement) {
         Key shaderKey;
         shaderKey.set(Key::BLEND_MASK | Key::TEXTURE_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK |
                               Key::INPUT_TF_MASK | Key::OUTPUT_TF_MASK,
@@ -115,10 +110,8 @@
             shaderKey.set(Key::OPACITY_MASK,
                           (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT);
             shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE);
-            Program* program = mCache.valueFor(shaderKey);
-            if (program == nullptr) {
-                program = generateProgram(shaderKey);
-                mCache.add(shaderKey, program);
+            if (cache.count(shaderKey) == 0) {
+                cache.emplace(shaderKey, generateProgram(shaderKey));
                 shaderCount++;
             }
         }
@@ -132,31 +125,35 @@
 ProgramCache::Key ProgramCache::computeKey(const Description& description) {
     Key needs;
     needs.set(Key::TEXTURE_MASK,
-              !description.mTextureEnabled
+              !description.textureEnabled
                       ? Key::TEXTURE_OFF
-                      : description.mTexture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES
+                      : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES
                               ? Key::TEXTURE_EXT
-                              : description.mTexture.getTextureTarget() == GL_TEXTURE_2D
+                              : description.texture.getTextureTarget() == GL_TEXTURE_2D
                                       ? Key::TEXTURE_2D
                                       : Key::TEXTURE_OFF)
-            .set(Key::ALPHA_MASK,
-                 (description.mColor.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE)
+            .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE)
             .set(Key::BLEND_MASK,
-                 description.mPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
+                 description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
             .set(Key::OPACITY_MASK,
-                 description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
+                 description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
             .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
-                 description.hasInputTransformMatrix() ?
-                     Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF)
+                 description.hasInputTransformMatrix()
+                         ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF)
             .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
-                 description.hasOutputTransformMatrix() || description.hasColorMatrix() ?
-                     Key::OUTPUT_TRANSFORM_MATRIX_ON : Key::OUTPUT_TRANSFORM_MATRIX_OFF);
+                 description.hasOutputTransformMatrix() || description.hasColorMatrix()
+                         ? Key::OUTPUT_TRANSFORM_MATRIX_ON
+                         : Key::OUTPUT_TRANSFORM_MATRIX_OFF)
+            .set(Key::ROUNDED_CORNERS_MASK,
+                 description.cornerRadius > 0
+                         ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
 
     needs.set(Key::Y410_BT2020_MASK,
-              description.mY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
+              description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
 
-    if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
-        switch (description.mInputTransferFunction) {
+    if (needs.hasTransformMatrix() ||
+        (description.inputTransferFunction != description.outputTransferFunction)) {
+        switch (description.inputTransferFunction) {
             case Description::TransferFunction::LINEAR:
             default:
                 needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR);
@@ -172,7 +169,7 @@
                 break;
         }
 
-        switch (description.mOutputTransferFunction) {
+        switch (description.outputTransferFunction) {
             case Description::TransferFunction::LINEAR:
             default:
                 needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR);
@@ -522,6 +519,10 @@
         vs << "attribute vec4 texCoords;"
            << "varying vec2 outTexCoords;";
     }
+    if (needs.hasRoundedCorners()) {
+        vs << "attribute lowp vec4 cropCoords;";
+        vs << "varying lowp vec2 outCropCoords;";
+    }
     vs << "attribute vec4 position;"
        << "uniform mat4 projection;"
        << "uniform mat4 texture;"
@@ -529,6 +530,9 @@
     if (needs.isTexturing()) {
         vs << "outTexCoords = (texture * texCoords).st;";
     }
+    if (needs.hasRoundedCorners()) {
+        vs << "outCropCoords = cropCoords.st;";
+    }
     vs << dedent << "}";
     return vs.getString();
 }
@@ -550,6 +554,27 @@
            << "varying vec2 outTexCoords;";
     }
 
+    if (needs.hasRoundedCorners()) {
+        // Rounded corners implementation using a signed distance function.
+        fs << R"__SHADER__(
+            uniform float cornerRadius;
+            uniform vec2 cropCenter;
+            varying vec2 outCropCoords;
+
+            /**
+             * This function takes the current crop coordinates and calculates an alpha value based
+             * on the corner radius and distance from the crop center.
+             */
+            float applyCornerRadius(vec2 cropCoords)
+            {
+                vec2 position = cropCoords - cropCenter;
+                vec2 dist = abs(position) + vec2(cornerRadius) - cropCenter;
+                float plane = length(max(dist, vec2(0.0)));
+                return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0);
+            }
+            )__SHADER__";
+    }
+
     if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
         fs << "uniform vec4 color;";
     }
@@ -640,18 +665,27 @@
             // avoid divide by 0 by adding 0.5/256 to the alpha channel
             fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
         }
-        fs << "gl_FragColor.rgb = OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));";
+        fs << "gl_FragColor.rgb = "
+              "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));";
         if (!needs.isOpaque() && needs.isPremultiplied()) {
             // and re-premultiply if needed after gamma correction
             fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
         }
     }
 
+    if (needs.hasRoundedCorners()) {
+        if (needs.isPremultiplied()) {
+            fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));";
+        } else {
+            fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);";
+        }
+    }
+
     fs << dedent << "}";
     return fs.getString();
 }
 
-Program* ProgramCache::generateProgram(const Key& needs) {
+std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) {
     ATRACE_CALL();
 
     // vertex shader
@@ -660,32 +694,34 @@
     // fragment shader
     String8 fs = generateFragmentShader(needs);
 
-    Program* program = new Program(needs, vs.string(), fs.string());
-    return program;
+    return std::make_unique<Program>(needs, vs.string(), fs.string());
 }
 
-void ProgramCache::useProgram(const Description& description) {
+void ProgramCache::useProgram(EGLContext context, const Description& description) {
     // generate the key for the shader based on the description
     Key needs(computeKey(description));
 
     // look-up the program in the cache
-    Program* program = mCache.valueFor(needs);
-    if (program == nullptr) {
+    auto& cache = mCaches[context];
+    auto it = cache.find(needs);
+    if (it == cache.end()) {
         // we didn't find our program, so generate one...
-        nsecs_t time = -systemTime();
-        program = generateProgram(needs);
-        mCache.add(needs, program);
-        time += systemTime();
+        nsecs_t time = systemTime();
+        it = cache.emplace(needs, generateProgram(needs)).first;
+        time = systemTime() - time;
 
-        ALOGV(">>> generated new program: needs=%08X, time=%u ms (%zu programs)", needs.mKey,
-              uint32_t(ns2ms(time)), mCache.size());
+        ALOGV(">>> generated new program for context %p: needs=%08X, time=%u ms (%zu programs)",
+              context, needs.mKey, uint32_t(ns2ms(time)), cache.size());
     }
 
     // here we have a suitable program for this description
+    std::unique_ptr<Program>& program = it->second;
     if (program->isValid()) {
         program->use();
         program->setUniforms(description);
     }
 }
 
-} /* namespace android */
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
similarity index 79%
rename from services/surfaceflinger/RenderEngine/ProgramCache.h
rename to libs/renderengine/gl/ProgramCache.h
index 983e7ba..400ad74 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -17,20 +17,27 @@
 #ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H
 #define SF_RENDER_ENGINE_PROGRAMCACHE_H
 
-#include <GLES2/gl2.h>
+#include <memory>
+#include <unordered_map>
 
-#include <utils/KeyedVector.h>
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <renderengine/private/Description.h>
 #include <utils/Singleton.h>
 #include <utils/TypeHelpers.h>
 
-#include "Description.h"
-
 namespace android {
 
-class Description;
+class String8;
+
+namespace renderengine {
+
+struct Description;
+
+namespace gl {
+
 class Formatter;
 class Program;
-class String8;
 
 /*
  * This class generates GLSL programs suitable to handle a given
@@ -72,31 +79,36 @@
             TEXTURE_EXT = 1 << TEXTURE_SHIFT,
             TEXTURE_2D = 2 << TEXTURE_SHIFT,
 
-            INPUT_TRANSFORM_MATRIX_SHIFT = 5,
+            ROUNDED_CORNERS_SHIFT = 5,
+            ROUNDED_CORNERS_MASK = 1 << ROUNDED_CORNERS_SHIFT,
+            ROUNDED_CORNERS_OFF = 0 << ROUNDED_CORNERS_SHIFT,
+            ROUNDED_CORNERS_ON = 1 << ROUNDED_CORNERS_SHIFT,
+
+            INPUT_TRANSFORM_MATRIX_SHIFT = 6,
             INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
             INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT,
             INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
 
-            OUTPUT_TRANSFORM_MATRIX_SHIFT = 6,
+            OUTPUT_TRANSFORM_MATRIX_SHIFT = 7,
             OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
             OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
             OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
 
-            INPUT_TF_SHIFT = 7,
+            INPUT_TF_SHIFT = 8,
             INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
             INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
             INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
             INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT,
             INPUT_TF_HLG = 3 << INPUT_TF_SHIFT,
 
-            OUTPUT_TF_SHIFT = 9,
+            OUTPUT_TF_SHIFT = 10,
             OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
             OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
             OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
             OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT,
             OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT,
 
-            Y410_BT2020_SHIFT = 11,
+            Y410_BT2020_SHIFT = 12,
             Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
             Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
             Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
@@ -115,6 +127,9 @@
         inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
         inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
         inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
+        inline bool hasRoundedCorners() const {
+            return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
+        }
         inline bool hasInputTransformMatrix() const {
             return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
         }
@@ -151,21 +166,26 @@
         }
         inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; }
 
-        // this is the definition of a friend function -- not a method of class Needs
-        friend inline int strictly_order_type(const Key& lhs, const Key& rhs) {
-            return (lhs.mKey < rhs.mKey) ? 1 : 0;
-        }
+        // for use by std::unordered_map
+
+        bool operator==(const Key& other) const { return mKey == other.mKey; }
+
+        struct Hash {
+            size_t operator()(const Key& key) const { return static_cast<size_t>(key.mKey); }
+        };
     };
 
-    ProgramCache();
-    ~ProgramCache();
+    ProgramCache() = default;
+    ~ProgramCache() = default;
 
     // Generate shaders to populate the cache
-    void primeCache(bool hasWideColor);
+    void primeCache(const EGLContext context, bool useColorManagement);
+
+    size_t getSize(const EGLContext context) { return mCaches[context].size(); }
 
     // useProgram lookup a suitable program in the cache or generates one
     // if none can be found.
-    void useProgram(const Description& description);
+    void useProgram(const EGLContext context, const Description& description);
 
 private:
     // compute a cache Key from a Description
@@ -179,19 +199,23 @@
     // Generate OETF based from Key.
     static void generateOETF(Formatter& fs, const Key& needs);
     // generates a program from the Key
-    static Program* generateProgram(const Key& needs);
+    static std::unique_ptr<Program> generateProgram(const Key& needs);
     // generates the vertex shader from the Key
     static String8 generateVertexShader(const Key& needs);
     // generates the fragment shader from the Key
     static String8 generateFragmentShader(const Key& needs);
 
     // Key/Value map used for caching Programs. Currently the cache
-    // is never shrunk.
-    DefaultKeyedVector<Key, Program*> mCache;
+    // is never shrunk (and the GL program objects are never deleted).
+    std::unordered_map<EGLContext, std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash>>
+            mCaches;
 };
 
-ANDROID_BASIC_TYPES_TRAITS(ProgramCache::Key)
+} // namespace gl
+} // namespace renderengine
 
-} /* namespace android */
+ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key)
+
+} // namespace android
 
 #endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
new file mode 100644
index 0000000..af8de23
--- /dev/null
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/mat4.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+namespace android {
+namespace renderengine {
+
+// DisplaySettings contains the settings that are applicable when drawing all
+// layers for a given display.
+struct DisplaySettings {
+    // Rectangle describing the physical display. We will project from the
+    // logical clip onto this rectangle.
+    Rect physicalDisplay = Rect::INVALID_RECT;
+
+    // Rectangle bounded by the x,y- clipping planes in the logical display, so
+    // that the orthographic projection matrix can be computed. When
+    // constructing this matrix, z-coordinate bound are assumed to be at z=0 and
+    // z=1.
+    Rect clip = Rect::INVALID_RECT;
+
+    // Global transform to apply to all layers.
+    mat4 globalTransform = mat4();
+
+    // Maximum luminance pulled from the display's HDR capabilities.
+    float maxLuminance = 1.0f;
+
+    // Output dataspace that will be populated if wide color gamut is used, or
+    // DataSpace::UNKNOWN otherwise.
+    ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN;
+
+    // Additional color transform to apply in linear space after transforming
+    // to the output dataspace.
+    mat4 colorTransform = mat4();
+
+    // Region that will be cleared to (0, 0, 0, 1) prior to rendering.
+    // RenderEngine will transform the clearRegion passed in here, by
+    // globalTransform, so that it will be in the same coordinate space as the
+    // rendered layers.
+    Region clearRegion = Region::INVALID_REGION;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/renderengine/include/renderengine/Framebuffer.h
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/renderengine/include/renderengine/Framebuffer.h
index e6ac6bf..66eb9ef 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/renderengine/include/renderengine/Framebuffer.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,21 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+
+#include <cstdint>
+
+struct ANativeWindowBuffer;
 
 namespace android {
-namespace mock {
+namespace renderengine {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+class Framebuffer {
+public:
+    virtual ~Framebuffer() = default;
 
-} // namespace mock
+    virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) = 0;
+};
+
+} // namespace renderengine
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/renderengine/include/renderengine/Image.h
similarity index 66%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/renderengine/include/renderengine/Image.h
index e6ac6bf..3bb4731 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/renderengine/include/renderengine/Image.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,14 +14,18 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+
+struct ANativeWindowBuffer;
 
 namespace android {
-namespace mock {
+namespace renderengine {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+class Image {
+public:
+    virtual ~Image() = default;
+    virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0;
+};
 
-} // namespace mock
+} // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
new file mode 100644
index 0000000..aa45ed8
--- /dev/null
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/mat4.h>
+#include <math/vec3.h>
+#include <renderengine/Texture.h>
+#include <ui/Fence.h>
+#include <ui/FloatRect.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android {
+namespace renderengine {
+
+// Metadata describing the input buffer to render from.
+struct Buffer {
+    // Hint for whether to use the Image cache or not.
+    // If NO_CACHE is specified, then upload the contents of the GraphicBuffer
+    // to the GPU, without checking against any implementation defined cache.
+    // If USE_CACHE is specified, then check against an implementation defined
+    // cache first. If there is an Image cached for the given GraphicBuffer id,
+    // then use that instead of the provided buffer contents. If there is no
+    // cached image or the RenderEngine implementation does not support caching,
+    // then use the GraphicBuffer contents.
+    enum class CachingHint { NO_CACHE, USE_CACHE };
+
+    // Buffer containing the image that we will render.
+    // If buffer == nullptr, then the rest of the fields in this struct will be
+    // ignored.
+    sp<GraphicBuffer> buffer = nullptr;
+
+    // Fence that will fire when the buffer is ready to be bound.
+    sp<Fence> fence = nullptr;
+
+    // Caching hint to use when uploading buffer contents.
+    CachingHint cacheHint = CachingHint::NO_CACHE;
+
+    // Texture identifier to bind the external texture to.
+    // TODO(alecmouri): This is GL-specific...make the type backend-agnostic.
+    uint32_t textureName = 0;
+
+    // Whether to use filtering when rendering the texture.
+    bool useTextureFiltering = false;
+
+    // Transform matrix to apply to texture coordinates.
+    mat4 textureTransform = mat4();
+
+    // Wheteher to use pre-multiplied alpha
+    bool usePremultipliedAlpha = true;
+
+    // Override flag that alpha for each pixel in the buffer *must* be 1.0.
+    // LayerSettings::alpha is still used if isOpaque==true - this flag only
+    // overrides the alpha channel of the buffer.
+    bool isOpaque = false;
+
+    // HDR color-space setting for Y410.
+    bool isY410BT2020 = false;
+};
+
+// Metadata describing the layer geometry.
+struct Geometry {
+    // Boundaries of the layer.
+    FloatRect boundaries = FloatRect();
+
+    // Transform matrix to apply to mesh coordinates.
+    mat4 positionTransform = mat4();
+
+    // Radius of rounded corners, if greater than 0. Otherwise, this layer's
+    // corners are not rounded.
+    // Having corner radius will force GPU composition on the layer and its children, drawing it
+    // with a special shader. The shader will receive the radius and the crop rectangle as input,
+    // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1.
+    // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop
+    // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
+    // in local layer coordinate space, so we have to take the layer transform into account when
+    // walking up the tree.
+    float roundedCornersRadius = 0.0;
+
+    // Rectangle within which corners will be rounded.
+    FloatRect roundedCornersCrop = FloatRect();
+};
+
+// Descriptor of the source pixels for this layer.
+struct PixelSource {
+    // Source buffer
+    Buffer buffer = Buffer();
+
+    // The solid color with which to fill the layer.
+    // This should only be populated if we don't render from an application
+    // buffer.
+    half3 solidColor = half3(0.0f, 0.0f, 0.0f);
+};
+
+// The settings that RenderEngine requires for correctly rendering a Layer.
+struct LayerSettings {
+    // Geometry information
+    Geometry geometry = Geometry();
+
+    // Source pixels for this layer.
+    PixelSource source = PixelSource();
+
+    // Alpha option to blend with the source pixels
+    half alpha = half(0.0);
+
+    // Color space describing how the source pixels should be interpreted.
+    ui::Dataspace sourceDataspace = ui::Dataspace::UNKNOWN;
+
+    // Additional layer-specific color transform to be applied before the global
+    // transform.
+    mat4 colorTransform = mat4();
+
+    // True if blending will be forced to be disabled.
+    bool disableBlending = false;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
similarity index 84%
rename from services/surfaceflinger/RenderEngine/Mesh.h
rename to libs/renderengine/include/renderengine/Mesh.h
index d0a9ac0..7618424 100644
--- a/services/surfaceflinger/RenderEngine/Mesh.h
+++ b/libs/renderengine/include/renderengine/Mesh.h
@@ -17,9 +17,12 @@
 #ifndef SF_RENDER_ENGINE_MESH_H
 #define SF_RENDER_ENGINE_MESH_H
 
+#include <vector>
+
 #include <stdint.h>
 
 namespace android {
+namespace renderengine {
 
 class Mesh {
 public:
@@ -30,7 +33,7 @@
     };
 
     Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0);
-    ~Mesh();
+    ~Mesh() = default;
 
     /*
      * VertexArray handles the stride automatically.
@@ -59,14 +62,22 @@
         return VertexArray<TYPE>(getTexCoords(), mStride);
     }
 
+    template <typename TYPE>
+    VertexArray<TYPE> getCropCoordArray() {
+        return VertexArray<TYPE>(getCropCoords(), mStride);
+    }
+
     Primitive getPrimitive() const;
 
     // returns a pointer to the vertices positions
     float const* getPositions() const;
 
-    // returns a pointer to the vertices  texture coordinates
+    // returns a pointer to the vertices texture coordinates
     float const* getTexCoords() const;
 
+    // returns a pointer to the vertices crop coordinates
+    float const* getCropCoords() const;
+
     // number of vertices in this mesh
     size_t getVertexCount() const;
 
@@ -89,7 +100,9 @@
 
     float* getPositions();
     float* getTexCoords();
-    float* mVertices;
+    float* getCropCoords();
+
+    std::vector<float> mVertices;
     size_t mVertexCount;
     size_t mVertexSize;
     size_t mTexCoordsSize;
@@ -97,5 +110,6 @@
     Primitive mPrimitive;
 };
 
-} /* namespace android */
+} // namespace renderengine
+} // namespace android
 #endif /* SF_RENDER_ENGINE_MESH_H */
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
new file mode 100644
index 0000000..bc1a4da
--- /dev/null
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2013 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 SF_RENDERENGINE_H_
+#define SF_RENDERENGINE_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <math/mat4.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/Framebuffer.h>
+#include <renderengine/Image.h>
+#include <renderengine/LayerSettings.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+/**
+ * Allows to set RenderEngine backend to GLES (default) or Vulkan (NOT yet supported).
+ */
+#define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend"
+
+struct ANativeWindowBuffer;
+
+namespace android {
+
+class Rect;
+class Region;
+
+namespace renderengine {
+
+class BindNativeBufferAsFramebuffer;
+class Image;
+class Mesh;
+class Texture;
+
+namespace impl {
+class RenderEngine;
+}
+
+enum class Protection {
+    UNPROTECTED = 1,
+    PROTECTED = 2,
+};
+
+class RenderEngine {
+public:
+    enum FeatureFlag {
+        USE_COLOR_MANAGEMENT = 1 << 0,      // Device manages color
+        USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context
+    };
+
+    static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags);
+
+    virtual ~RenderEngine() = 0;
+
+    // ----- BEGIN DEPRECATED INTERFACE -----
+    // This interface, while still in use until a suitable replacement is built,
+    // should be considered deprecated, minus some methods which still may be
+    // used to support legacy behavior.
+
+    virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0;
+    virtual std::unique_ptr<Image> createImage() = 0;
+
+    virtual void primeCache() const = 0;
+
+    // dump the extension strings. always call the base class.
+    virtual void dump(std::string& result) = 0;
+
+    virtual bool useNativeFenceSync() const = 0;
+    virtual bool useWaitSync() const = 0;
+
+    virtual bool isCurrent() const = 0;
+
+    // helpers
+    // flush submits RenderEngine command stream for execution and returns a
+    // native fence fd that is signaled when the execution has completed.  It
+    // returns -1 on errors.
+    virtual base::unique_fd flush() = 0;
+    // finish waits until RenderEngine command stream has been executed.  It
+    // returns false on errors.
+    virtual bool finish() = 0;
+    // waitFence inserts a wait on an external fence fd to RenderEngine
+    // command stream.  It returns false on errors.
+    virtual bool waitFence(base::unique_fd fenceFd) = 0;
+
+    virtual void clearWithColor(float red, float green, float blue, float alpha) = 0;
+    virtual void fillRegionWithColor(const Region& region, float red, float green, float blue,
+                                     float alpha) = 0;
+
+    virtual void setScissor(const Rect& region) = 0;
+    virtual void disableScissor() = 0;
+    virtual void genTextures(size_t count, uint32_t* names) = 0;
+    virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
+    virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
+    virtual status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
+                                               sp<Fence> fence, bool cleanCache) = 0;
+    // When binding a native buffer, it must be done before setViewportAndProjection
+    // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
+    virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
+    virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
+
+    // set-up
+    virtual void checkErrors() const = 0;
+    virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
+                                          ui::Transform::orientation_flags rotation) = 0;
+    virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+                                    const half4& color, float cornerRadius) = 0;
+    virtual void setupLayerTexturing(const Texture& texture) = 0;
+    virtual void setupLayerBlackedOut() = 0;
+    virtual void setupFillWithColor(float r, float g, float b, float a) = 0;
+    // Sets up the crop size for corner radius clipping.
+    //
+    // Having corner radius will force GPU composition on the layer and its children, drawing it
+    // with a special shader. The shader will receive the radius and the crop rectangle as input,
+    // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1.
+    // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop
+    // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
+    // in local layer coordinate space, so we have to take the layer transform into account when
+    // walking up the tree.
+    virtual void setupCornerRadiusCropSize(float width, float height) = 0;
+
+    // Set a color transform matrix that is applied in linear space right before OETF.
+    virtual void setColorTransform(const mat4& /* colorTransform */) = 0;
+    virtual void disableTexturing() = 0;
+    virtual void disableBlending() = 0;
+
+    // HDR and color management support
+    virtual void setSourceY410BT2020(bool enable) = 0;
+    virtual void setSourceDataSpace(ui::Dataspace source) = 0;
+    virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0;
+    virtual void setDisplayMaxLuminance(const float maxLuminance) = 0;
+
+    // drawing
+    virtual void drawMesh(const Mesh& mesh) = 0;
+
+    // queries
+    virtual size_t getMaxTextureSize() const = 0;
+    virtual size_t getMaxViewportDims() const = 0;
+
+    // ----- END DEPRECATED INTERFACE -----
+
+    // ----- BEGIN NEW INTERFACE -----
+
+    virtual bool isProtected() const = 0;
+    virtual bool supportsProtectedContent() const = 0;
+    virtual bool useProtectedContext(bool useProtectedContext) = 0;
+
+    // Renders layers for a particular display via GPU composition. This method
+    // should be called for every display that needs to be rendered via the GPU.
+    // @param display The display-wide settings that should be applied prior to
+    // drawing any layers.
+    // @param layers The layers to draw onto the display, in Z-order.
+    // @param buffer The buffer which will be drawn to. This buffer will be
+    // ready once displayFence fires.
+    // @param drawFence A pointer to a fence, which will fire when the buffer
+    // has been drawn to and is ready to be examined. The fence will be
+    // initialized by this method. The caller will be responsible for owning the
+    // fence.
+    // @return An error code indicating whether drawing was successful. For
+    // now, this always returns NO_ERROR.
+    // TODO(alecmouri): Consider making this a multi-display API, so that the
+    // caller deoes not need to handle multiple fences.
+    virtual status_t drawLayers(const DisplaySettings& display,
+                                const std::vector<LayerSettings>& layers,
+                                ANativeWindowBuffer* buffer, base::unique_fd* drawFence) = 0;
+
+    // TODO(alecmouri): Expose something like bindTexImage() so that devices
+    // that don't support native sync fences can get rid of code duplicated
+    // between BufferStateLayer and BufferQueueLayer for binding an external
+    // texture.
+
+    // TODO(alecmouri): Add API to help with managing a texture pool.
+};
+
+class BindNativeBufferAsFramebuffer {
+public:
+    BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer)
+          : mEngine(engine), mFramebuffer(mEngine.createFramebuffer()), mStatus(NO_ERROR) {
+        mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected())
+                ? mEngine.bindFrameBuffer(mFramebuffer.get())
+                : NO_MEMORY;
+    }
+    ~BindNativeBufferAsFramebuffer() {
+        mFramebuffer->setNativeWindowBuffer(nullptr, false);
+        mEngine.unbindFrameBuffer(mFramebuffer.get());
+    }
+    status_t getStatus() const { return mStatus; }
+
+private:
+    RenderEngine& mEngine;
+    std::unique_ptr<Framebuffer> mFramebuffer;
+    status_t mStatus;
+};
+
+namespace impl {
+
+// impl::RenderEngine contains common implementation that is graphics back-end agnostic.
+class RenderEngine : public renderengine::RenderEngine {
+public:
+    virtual ~RenderEngine() = 0;
+
+    bool useNativeFenceSync() const override;
+    bool useWaitSync() const override;
+
+protected:
+    RenderEngine(uint32_t featureFlags);
+    const uint32_t mFeatureFlags;
+};
+
+} // namespace impl
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/Texture.h b/libs/renderengine/include/renderengine/Texture.h
similarity index 94%
rename from services/surfaceflinger/RenderEngine/Texture.h
rename to libs/renderengine/include/renderengine/Texture.h
index 56b6b31..c69ace0 100644
--- a/services/surfaceflinger/RenderEngine/Texture.h
+++ b/libs/renderengine/include/renderengine/Texture.h
@@ -14,22 +14,17 @@
  * limitations under the License.
  */
 
-#include <math/mat4.h>
-#include <stdint.h>
-
 #ifndef SF_RENDER_ENGINE_TEXTURE_H
 #define SF_RENDER_ENGINE_TEXTURE_H
 
+#include <stdint.h>
+
+#include <math/mat4.h>
+
 namespace android {
+namespace renderengine {
 
 class Texture {
-    uint32_t mTextureName;
-    uint32_t mTextureTarget;
-    size_t mWidth;
-    size_t mHeight;
-    bool mFiltering;
-    mat4 mTextureMatrix;
-
 public:
     enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 };
 
@@ -50,7 +45,16 @@
     bool getFiltering() const;
     size_t getWidth() const;
     size_t getHeight() const;
+
+private:
+    uint32_t mTextureName;
+    uint32_t mTextureTarget;
+    size_t mWidth;
+    size_t mHeight;
+    bool mFiltering;
+    mat4 mTextureMatrix;
 };
 
-} /* namespace android */
+} // namespace renderengine
+} // namespace android
 #endif /* SF_RENDER_ENGINE_TEXTURE_H */
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/renderengine/include/renderengine/mock/Framebuffer.h
similarity index 63%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/renderengine/include/renderengine/mock/Framebuffer.h
index e6ac6bf..7695885 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/renderengine/include/renderengine/mock/Framebuffer.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,23 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+
+#include <gmock/gmock.h>
+#include <renderengine/Framebuffer.h>
 
 namespace android {
+namespace renderengine {
 namespace mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+class Framebuffer : public renderengine::Framebuffer {
+public:
+    Framebuffer();
+    ~Framebuffer() override;
+
+    MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool));
+};
 
 } // namespace mock
+} // namespace renderengine
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/renderengine/include/renderengine/mock/Image.h
similarity index 63%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/renderengine/include/renderengine/mock/Image.h
index e6ac6bf..2b0eed1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/renderengine/include/renderengine/mock/Image.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,23 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+
+#include <gmock/gmock.h>
+#include <renderengine/Image.h>
 
 namespace android {
+namespace renderengine {
 namespace mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+class Image : public renderengine::Image {
+public:
+    Image();
+    ~Image() override;
+
+    MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer* buffer, bool isProtected));
+};
 
 } // namespace mock
+} // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
new file mode 100644
index 0000000..4b86cfe
--- /dev/null
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/LayerSettings.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/RenderEngine.h>
+#include <renderengine/Texture.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Region.h>
+
+namespace android {
+namespace renderengine {
+namespace mock {
+
+class RenderEngine : public renderengine::RenderEngine {
+public:
+    RenderEngine();
+    ~RenderEngine() override;
+
+    MOCK_METHOD0(createFramebuffer, std::unique_ptr<renderengine::Framebuffer>());
+    MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>());
+    MOCK_CONST_METHOD0(primeCache, void());
+    MOCK_METHOD1(dump, void(std::string&));
+    MOCK_CONST_METHOD0(useNativeFenceSync, bool());
+    MOCK_CONST_METHOD0(useWaitSync, bool());
+    MOCK_CONST_METHOD0(isCurrent, bool());
+    MOCK_METHOD0(flush, base::unique_fd());
+    MOCK_METHOD0(finish, bool());
+    MOCK_METHOD1(waitFence, bool(base::unique_fd*));
+    bool waitFence(base::unique_fd fd) override { return waitFence(&fd); };
+    MOCK_METHOD4(clearWithColor, void(float, float, float, float));
+    MOCK_METHOD5(fillRegionWithColor, void(const Region&, float, float, float, float));
+    MOCK_METHOD1(setScissor, void(const Rect&));
+    MOCK_METHOD0(disableScissor, void());
+    MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
+    MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
+    MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
+    MOCK_METHOD4(bindExternalTextureBuffer, status_t(uint32_t, sp<GraphicBuffer>, sp<Fence>, bool));
+    MOCK_CONST_METHOD0(checkErrors, void());
+    MOCK_METHOD4(setViewportAndProjection,
+                 void(size_t, size_t, Rect, ui::Transform::orientation_flags));
+    MOCK_METHOD5(setupLayerBlending, void(bool, bool, bool, const half4&, float));
+    MOCK_METHOD1(setupLayerTexturing, void(const Texture&));
+    MOCK_METHOD0(setupLayerBlackedOut, void());
+    MOCK_METHOD4(setupFillWithColor, void(float, float, float, float));
+    MOCK_METHOD2(setupCornerRadiusCropSize, void(float, float));
+    MOCK_METHOD1(setColorTransform, void(const mat4&));
+    MOCK_METHOD1(setSaturationMatrix, void(const mat4&));
+    MOCK_METHOD0(disableTexturing, void());
+    MOCK_METHOD0(disableBlending, void());
+    MOCK_METHOD1(setSourceY410BT2020, void(bool));
+    MOCK_METHOD1(setSourceDataSpace, void(ui::Dataspace));
+    MOCK_METHOD1(setOutputDataSpace, void(ui::Dataspace));
+    MOCK_METHOD1(setDisplayMaxLuminance, void(const float));
+    MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*));
+    MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*));
+    MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&));
+    MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
+    MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
+    MOCK_CONST_METHOD0(isProtected, bool());
+    MOCK_CONST_METHOD0(supportsProtectedContent, bool());
+    MOCK_METHOD1(useProtectedContext, bool(bool));
+    MOCK_METHOD4(drawLayers,
+                 status_t(const DisplaySettings&, const std::vector<LayerSettings>&,
+                          ANativeWindowBuffer*, base::unique_fd*));
+};
+
+} // namespace mock
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
new file mode 100644
index 0000000..bd2055f
--- /dev/null
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 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 SF_RENDER_ENGINE_DESCRIPTION_H_
+#define SF_RENDER_ENGINE_DESCRIPTION_H_
+
+#include <renderengine/Texture.h>
+#include <ui/GraphicTypes.h>
+
+namespace android {
+namespace renderengine {
+
+/*
+ * This is the structure that holds the state of the rendering engine.
+ * This class is used to generate a corresponding GLSL program and set the
+ * appropriate uniform.
+ */
+struct Description {
+    enum class TransferFunction : int {
+        LINEAR,
+        SRGB,
+        ST2084,
+        HLG, // Hybrid Log-Gamma for HDR.
+    };
+
+    static TransferFunction dataSpaceToTransferFunction(ui::Dataspace dataSpace);
+
+    Description() = default;
+    ~Description() = default;
+
+    bool hasInputTransformMatrix() const;
+    bool hasOutputTransformMatrix() const;
+    bool hasColorMatrix() const;
+
+    // whether textures are premultiplied
+    bool isPremultipliedAlpha = false;
+    // whether this layer is marked as opaque
+    bool isOpaque = true;
+
+    // corner radius of the layer
+    float cornerRadius = 0;
+
+    // Size of the rounded rectangle we are cropping to
+    half2 cropSize;
+
+    // Texture this layer uses
+    Texture texture;
+    bool textureEnabled = false;
+
+    // color used when texturing is disabled or when setting alpha.
+    half4 color;
+
+    // true if the sampled pixel values are in Y410/BT2020 rather than RGBA
+    bool isY410BT2020 = false;
+
+    // transfer functions for the input/output
+    TransferFunction inputTransferFunction = TransferFunction::LINEAR;
+    TransferFunction outputTransferFunction = TransferFunction::LINEAR;
+
+    float displayMaxLuminance;
+
+    // projection matrix
+    mat4 projectionMatrix;
+
+    // The color matrix will be applied in linear space right before OETF.
+    mat4 colorMatrix;
+    mat4 inputTransformMatrix;
+    mat4 outputTransformMatrix;
+};
+
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/renderengine/mock/Framebuffer.cpp
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/renderengine/mock/Framebuffer.cpp
index e6ac6bf..fbdcaab 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/renderengine/mock/Framebuffer.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,17 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <renderengine/mock/Framebuffer.h>
 
 namespace android {
+namespace renderengine {
 namespace mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Framebuffer::Framebuffer() = default;
+Framebuffer::~Framebuffer() = default;
 
 } // namespace mock
+} // namespace renderengine
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp b/libs/renderengine/mock/Image.cpp
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
copy to libs/renderengine/mock/Image.cpp
index a98bece..57f4346 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
+++ b/libs/renderengine/mock/Image.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,22 +14,17 @@
  * limitations under the License.
  */
 
-#include "mock/RenderEngine/MockRenderEngine.h"
+#include <renderengine/mock/Image.h>
 
 namespace android {
-namespace RE {
+namespace renderengine {
 namespace mock {
 
-// Explicit default instantiation is recommended.
-RenderEngine::RenderEngine() = default;
-RenderEngine::~RenderEngine() = default;
-
-Surface::Surface() = default;
-Surface::~Surface() = default;
-
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
 Image::Image() = default;
 Image::~Image() = default;
 
 } // namespace mock
-} // namespace RE
+} // namespace renderengine
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp b/libs/renderengine/mock/RenderEngine.cpp
similarity index 70%
rename from services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
rename to libs/renderengine/mock/RenderEngine.cpp
index a98bece..261636d 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
+++ b/libs/renderengine/mock/RenderEngine.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,22 +14,17 @@
  * limitations under the License.
  */
 
-#include "mock/RenderEngine/MockRenderEngine.h"
+#include <renderengine/mock/RenderEngine.h>
 
 namespace android {
-namespace RE {
+namespace renderengine {
 namespace mock {
 
-// Explicit default instantiation is recommended.
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
 RenderEngine::RenderEngine() = default;
 RenderEngine::~RenderEngine() = default;
 
-Surface::Surface() = default;
-Surface::~Surface() = default;
-
-Image::Image() = default;
-Image::~Image() = default;
-
 } // namespace mock
-} // namespace RE
+} // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
new file mode 100644
index 0000000..9b483ef
--- /dev/null
+++ b/libs/renderengine/tests/Android.bp
@@ -0,0 +1,38 @@
+// Copyright 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "librenderengine_test",
+    defaults: ["surfaceflinger_defaults"],
+    test_suites: ["device-tests"],
+    srcs: [
+        "RenderEngineTest.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "librenderengine",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libEGL",
+        "libGLESv2",
+        "libgui",
+        "liblog",
+        "libnativewindow",
+        "libsync",
+        "libui",
+        "libutils",
+    ],
+}
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
new file mode 100644
index 0000000..0ee6153
--- /dev/null
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -0,0 +1,861 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <renderengine/RenderEngine.h>
+#include <sync/sync.h>
+#include <ui/PixelFormat.h>
+
+constexpr int DEFAULT_DISPLAY_WIDTH = 128;
+constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
+constexpr int DEFAULT_DISPLAY_OFFSET = 64;
+
+namespace android {
+
+struct RenderEngineTest : public ::testing::Test {
+    static sp<GraphicBuffer> allocateDefaultBuffer() {
+        return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
+                                 HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                         GRALLOC_USAGE_HW_RENDER,
+                                 "output");
+    }
+
+    // Allocates a 1x1 buffer to fill with a solid color
+    static sp<GraphicBuffer> allocateSourceBuffer(uint32_t width, uint32_t height) {
+        return new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                         GRALLOC_USAGE_HW_TEXTURE,
+                                 "input");
+    }
+
+    RenderEngineTest() { mBuffer = allocateDefaultBuffer(); }
+
+    ~RenderEngineTest() {
+        for (uint32_t texName : mTexNames) {
+            sRE->deleteTextures(1, &texName);
+        }
+    }
+
+    void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+                           uint8_t tolerance = 0) {
+        uint8_t* pixels;
+        mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                      reinterpret_cast<void**>(&pixels));
+
+        auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
+            uint8_t tmp = a >= b ? a - b : b - a;
+            return tmp <= tolerance;
+        };
+        int32_t maxFails = 10;
+        int32_t fails = 0;
+        for (int32_t j = 0; j < region.getHeight(); j++) {
+            const uint8_t* src =
+                    pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4;
+            for (int32_t i = 0; i < region.getWidth(); i++) {
+                const uint8_t expected[4] = {r, g, b, a};
+                bool equal = std::equal(src, src + 4, expected, colorCompare);
+                EXPECT_TRUE(equal)
+                        << "pixel @ (" << region.left + i << ", " << region.top + j << "): "
+                        << "expected (" << static_cast<uint32_t>(r) << ", "
+                        << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", "
+                        << static_cast<uint32_t>(a) << "), "
+                        << "got (" << static_cast<uint32_t>(src[0]) << ", "
+                        << static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2])
+                        << ", " << static_cast<uint32_t>(src[3]) << ")";
+                src += 4;
+                if (!equal && ++fails >= maxFails) {
+                    break;
+                }
+            }
+            if (fails >= maxFails) {
+                break;
+            }
+        }
+        mBuffer->unlock();
+    }
+
+    static Rect fullscreenRect() { return Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); }
+
+    static Rect offsetRect() {
+        return Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH,
+                    DEFAULT_DISPLAY_HEIGHT);
+    }
+
+    static Rect offsetRectAtZero() {
+        return Rect(DEFAULT_DISPLAY_WIDTH - DEFAULT_DISPLAY_OFFSET,
+                    DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
+    }
+
+    static void invokeDraw(renderengine::DisplaySettings settings,
+                           std::vector<renderengine::LayerSettings> layers,
+                           sp<GraphicBuffer> buffer) {
+        base::unique_fd fence;
+        status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), &fence);
+
+        int fd = fence.release();
+        if (fd >= 0) {
+            sync_wait(fd, -1);
+            close(fd);
+        }
+
+        ASSERT_EQ(NO_ERROR, status);
+    }
+
+    static void drawEmptyLayers() {
+        renderengine::DisplaySettings settings;
+        std::vector<renderengine::LayerSettings> layers;
+        // Meaningless buffer since we don't do any drawing
+        sp<GraphicBuffer> buffer = new GraphicBuffer();
+        invokeDraw(settings, layers, buffer);
+    }
+
+    template <typename SourceVariant>
+    void fillBuffer(half r, half g, half b, half a);
+
+    template <typename SourceVariant>
+    void fillRedBuffer();
+
+    template <typename SourceVariant>
+    void fillGreenBuffer();
+
+    template <typename SourceVariant>
+    void fillBlueBuffer();
+
+    template <typename SourceVariant>
+    void fillRedTransparentBuffer();
+
+    template <typename SourceVariant>
+    void fillRedOffsetBuffer();
+
+    template <typename SourceVariant>
+    void fillBufferPhysicalOffset();
+
+    template <typename SourceVariant>
+    void fillBufferCheckers(mat4 transform);
+
+    template <typename SourceVariant>
+    void fillBufferCheckersRotate0();
+
+    template <typename SourceVariant>
+    void fillBufferCheckersRotate90();
+
+    template <typename SourceVariant>
+    void fillBufferCheckersRotate180();
+
+    template <typename SourceVariant>
+    void fillBufferCheckersRotate270();
+
+    template <typename SourceVariant>
+    void fillBufferWithLayerTransform();
+
+    template <typename SourceVariant>
+    void fillBufferLayerTransform();
+
+    template <typename SourceVariant>
+    void fillBufferWithColorTransform();
+
+    template <typename SourceVariant>
+    void fillBufferColorTransform();
+
+    template <typename SourceVariant>
+    void fillRedBufferWithRoundedCorners();
+
+    template <typename SourceVariant>
+    void fillBufferWithRoundedCorners();
+
+    void fillRedBufferTextureTransform();
+
+    void fillBufferTextureTransform();
+
+    void fillRedBufferWithPremultiplyAlpha();
+
+    void fillBufferWithPremultiplyAlpha();
+
+    void fillRedBufferWithoutPremultiplyAlpha();
+
+    void fillBufferWithoutPremultiplyAlpha();
+
+    void fillGreenColorBufferThenClearRegion();
+
+    void clearLeftRegion();
+
+    void clearRegion();
+
+    // Dumb hack to get aroud the fact that tear-down for renderengine isn't
+    // well defined right now, so we can't create multiple instances
+    static std::unique_ptr<renderengine::RenderEngine> sRE;
+
+    sp<GraphicBuffer> mBuffer;
+
+    std::vector<uint32_t> mTexNames;
+};
+
+std::unique_ptr<renderengine::RenderEngine> RenderEngineTest::sRE =
+        renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888), 0);
+
+struct ColorSourceVariant {
+    static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
+                          RenderEngineTest* /*fixture*/) {
+        layer.source.solidColor = half3(r, g, b);
+    }
+};
+
+struct RelaxOpaqueBufferVariant {
+    static void setOpaqueBit(renderengine::LayerSettings& layer) {
+        layer.source.buffer.isOpaque = false;
+    }
+
+    static uint8_t getAlphaChannel() { return 255; }
+};
+
+struct ForceOpaqueBufferVariant {
+    static void setOpaqueBit(renderengine::LayerSettings& layer) {
+        layer.source.buffer.isOpaque = true;
+    }
+
+    static uint8_t getAlphaChannel() {
+        // The isOpaque bit will override the alpha channel, so this should be
+        // arbitrary.
+        return 10;
+    }
+};
+
+template <typename OpaquenessVariant>
+struct BufferSourceVariant {
+    static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
+                          RenderEngineTest* fixture) {
+        sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
+        uint32_t texName;
+        RenderEngineTest::sRE->genTextures(1, &texName);
+        fixture->mTexNames.push_back(texName);
+
+        uint8_t* pixels;
+        buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                  reinterpret_cast<void**>(&pixels));
+
+        for (int32_t j = 0; j < buf->getHeight(); j++) {
+            uint8_t* iter = pixels + (buf->getStride() * j) * 4;
+            for (int32_t i = 0; i < buf->getWidth(); i++) {
+                iter[0] = uint8_t(r * 255);
+                iter[1] = uint8_t(g * 255);
+                iter[2] = uint8_t(b * 255);
+                iter[3] = OpaquenessVariant::getAlphaChannel();
+                iter += 4;
+            }
+        }
+
+        buf->unlock();
+
+        layer.source.buffer.buffer = buf;
+        layer.source.buffer.textureName = texName;
+        OpaquenessVariant::setOpaqueBit(layer);
+    }
+};
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBuffer(half r, half g, half b, half a) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = fullscreenRect().toFloatRect();
+    SourceVariant::fillColor(layer, r, g, b, this);
+    layer.alpha = a;
+
+    layers.push_back(layer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedBuffer() {
+    fillBuffer<SourceVariant>(1.0f, 0.0f, 0.0f, 1.0f);
+    expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillGreenBuffer() {
+    fillBuffer<SourceVariant>(0.0f, 1.0f, 0.0f, 1.0f);
+    expectBufferColor(fullscreenRect(), 0, 255, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBlueBuffer() {
+    fillBuffer<SourceVariant>(0.0f, 0.0f, 1.0f, 1.0f);
+    expectBufferColor(fullscreenRect(), 0, 0, 255, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedTransparentBuffer() {
+    fillBuffer<SourceVariant>(1.0f, 0.0f, 0.0f, .2f);
+    expectBufferColor(fullscreenRect(), 51, 0, 0, 51);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedOffsetBuffer() {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = offsetRect();
+    settings.clip = offsetRectAtZero();
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = offsetRectAtZero().toFloatRect();
+    SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+    layer.alpha = 1.0f;
+
+    layers.push_back(layer);
+    invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferPhysicalOffset() {
+    fillRedOffsetBuffer<SourceVariant>();
+
+    expectBufferColor(Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH,
+                           DEFAULT_DISPLAY_HEIGHT),
+                      255, 0, 0, 255);
+    Rect offsetRegionLeft(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_HEIGHT);
+    Rect offsetRegionTop(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_OFFSET);
+
+    expectBufferColor(offsetRegionLeft, 0, 0, 0, 0);
+    expectBufferColor(offsetRegionTop, 0, 0, 0, 0);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckers(mat4 transform) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    // Here logical space is 2x2
+    settings.clip = Rect(2, 2);
+    settings.globalTransform = transform;
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layerOne;
+    Rect rectOne(0, 0, 1, 1);
+    layerOne.geometry.boundaries = rectOne.toFloatRect();
+    SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
+    layerOne.alpha = 1.0f;
+
+    renderengine::LayerSettings layerTwo;
+    Rect rectTwo(0, 1, 1, 2);
+    layerTwo.geometry.boundaries = rectTwo.toFloatRect();
+    SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
+    layerTwo.alpha = 1.0f;
+
+    renderengine::LayerSettings layerThree;
+    Rect rectThree(1, 0, 2, 1);
+    layerThree.geometry.boundaries = rectThree.toFloatRect();
+    SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this);
+    layerThree.alpha = 1.0f;
+
+    layers.push_back(layerOne);
+    layers.push_back(layerTwo);
+    layers.push_back(layerThree);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate0() {
+    fillBufferCheckers<SourceVariant>(mat4());
+    expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0,
+                      255);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+                           DEFAULT_DISPLAY_HEIGHT / 2),
+                      0, 0, 255, 255);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+                           DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                      0, 0, 0, 0);
+    expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+                           DEFAULT_DISPLAY_HEIGHT),
+                      0, 255, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate90() {
+    mat4 matrix = mat4(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1);
+    fillBufferCheckers<SourceVariant>(matrix);
+    expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 255, 0,
+                      255);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+                           DEFAULT_DISPLAY_HEIGHT / 2),
+                      255, 0, 0, 255);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+                           DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                      0, 0, 255, 255);
+    expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+                           DEFAULT_DISPLAY_HEIGHT),
+                      0, 0, 0, 0);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate180() {
+    mat4 matrix = mat4(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 2, 2, 0, 1);
+    fillBufferCheckers<SourceVariant>(matrix);
+    expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0,
+                      0);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+                           DEFAULT_DISPLAY_HEIGHT / 2),
+                      0, 255, 0, 255);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+                           DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                      255, 0, 0, 255);
+    expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+                           DEFAULT_DISPLAY_HEIGHT),
+                      0, 0, 255, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate270() {
+    mat4 matrix = mat4(0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1);
+    fillBufferCheckers<SourceVariant>(matrix);
+    expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 255,
+                      255);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+                           DEFAULT_DISPLAY_HEIGHT / 2),
+                      0, 0, 0, 0);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+                           DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                      0, 255, 0, 255);
+    expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+                           DEFAULT_DISPLAY_HEIGHT),
+                      255, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithLayerTransform() {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    // Here logical space is 2x2
+    settings.clip = Rect(2, 2);
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+    // Translate one pixel diagonally
+    layer.geometry.positionTransform = mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1);
+    SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+    layer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+    layer.alpha = 1.0f;
+
+    layers.push_back(layer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferLayerTransform() {
+    fillBufferWithLayerTransform<SourceVariant>();
+    expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0, 0);
+    expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+                           DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                      255, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransform() {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = Rect(1, 1);
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+    SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+    layer.alpha = 1.0f;
+
+    // construct a fake color matrix
+    // annihilate green and blue channels
+    settings.colorTransform = mat4::scale(vec4(1, 0, 0, 1));
+    // set red channel to red + green
+    layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+    layer.alpha = 1.0f;
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+    layers.push_back(layer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransform() {
+    fillBufferWithColorTransform<SourceVariant>();
+    expectBufferColor(fullscreenRect(), 191, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedBufferWithRoundedCorners() {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = fullscreenRect().toFloatRect();
+    layer.geometry.roundedCornersRadius = 5.0f;
+    layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+    SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+    layer.alpha = 1.0f;
+
+    layers.push_back(layer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithRoundedCorners() {
+    fillRedBufferWithRoundedCorners<SourceVariant>();
+    // Corners should be ignored...
+    expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 0);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, 0, DEFAULT_DISPLAY_WIDTH, 1), 0, 0, 0, 0);
+    expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT - 1, 1, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1,
+                           DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                      0, 0, 0, 0);
+    // ...And the non-rounded portion should be red.
+    // Other pixels may be anti-aliased, so let's not check those.
+    expectBufferColor(Rect(5, 5, DEFAULT_DISPLAY_WIDTH - 5, DEFAULT_DISPLAY_HEIGHT - 5), 255, 0, 0,
+                      255);
+}
+
+void RenderEngineTest::fillRedBufferTextureTransform() {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = Rect(1, 1);
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    // Here will allocate a checker board texture, but transform texture
+    // coordinates so that only the upper left is applied.
+    sp<GraphicBuffer> buf = allocateSourceBuffer(2, 2);
+    uint32_t texName;
+    RenderEngineTest::sRE->genTextures(1, &texName);
+    this->mTexNames.push_back(texName);
+
+    uint8_t* pixels;
+    buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+              reinterpret_cast<void**>(&pixels));
+    // Red top left, Green top right, Blue bottom left, Black bottom right
+    pixels[0] = 255;
+    pixels[1] = 0;
+    pixels[2] = 0;
+    pixels[3] = 255;
+    pixels[4] = 0;
+    pixels[5] = 255;
+    pixels[6] = 0;
+    pixels[7] = 255;
+    pixels[8] = 0;
+    pixels[9] = 0;
+    pixels[10] = 255;
+    pixels[11] = 255;
+    buf->unlock();
+
+    layer.source.buffer.buffer = buf;
+    layer.source.buffer.textureName = texName;
+    // Transform coordinates to only be inside the red quadrant.
+    layer.source.buffer.textureTransform = mat4::scale(vec4(0.2, 0.2, 1, 1));
+    layer.alpha = 1.0f;
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+    layers.push_back(layer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::fillBufferTextureTransform() {
+    fillRedBufferTextureTransform();
+    expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
+void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    // Here logical space is 1x1
+    settings.clip = Rect(1, 1);
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+    uint32_t texName;
+    RenderEngineTest::sRE->genTextures(1, &texName);
+    this->mTexNames.push_back(texName);
+
+    uint8_t* pixels;
+    buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+              reinterpret_cast<void**>(&pixels));
+    pixels[0] = 255;
+    pixels[1] = 0;
+    pixels[2] = 0;
+    pixels[3] = 255;
+    buf->unlock();
+
+    layer.source.buffer.buffer = buf;
+    layer.source.buffer.textureName = texName;
+    layer.source.buffer.usePremultipliedAlpha = true;
+    layer.alpha = 0.5f;
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+    layers.push_back(layer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::fillBufferWithPremultiplyAlpha() {
+    fillRedBufferWithPremultiplyAlpha();
+    expectBufferColor(fullscreenRect(), 128, 0, 0, 128);
+}
+
+void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    // Here logical space is 1x1
+    settings.clip = Rect(1, 1);
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+    uint32_t texName;
+    RenderEngineTest::sRE->genTextures(1, &texName);
+    this->mTexNames.push_back(texName);
+
+    uint8_t* pixels;
+    buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+              reinterpret_cast<void**>(&pixels));
+    pixels[0] = 255;
+    pixels[1] = 0;
+    pixels[2] = 0;
+    pixels[3] = 255;
+    buf->unlock();
+
+    layer.source.buffer.buffer = buf;
+    layer.source.buffer.textureName = texName;
+    layer.source.buffer.usePremultipliedAlpha = false;
+    layer.alpha = 0.5f;
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+    layers.push_back(layer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {
+    fillRedBufferWithoutPremultiplyAlpha();
+    expectBufferColor(fullscreenRect(), 128, 0, 0, 64, 1);
+}
+
+void RenderEngineTest::clearLeftRegion() {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    // Here logical space is 4x4
+    settings.clip = Rect(4, 4);
+    settings.globalTransform = mat4::scale(vec4(2, 4, 0, 1));
+    settings.clearRegion = Region(Rect(1, 1));
+    std::vector<renderengine::LayerSettings> layers;
+    // dummy layer, without bounds should not render anything
+    renderengine::LayerSettings layer;
+    layers.push_back(layer);
+    invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::clearRegion() {
+    // Reuse mBuffer
+    clearLeftRegion();
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255);
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+                           DEFAULT_DISPLAY_HEIGHT),
+                      0, 0, 0, 0);
+}
+
+TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
+    drawEmptyLayers();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
+    fillRedBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) {
+    fillGreenBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) {
+    fillBlueBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) {
+    fillRedTransparentBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) {
+    fillBufferPhysicalOffset<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) {
+    fillBufferCheckersRotate0<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) {
+    fillBufferCheckersRotate90<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) {
+    fillBufferCheckersRotate180<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) {
+    fillBufferCheckersRotate270<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) {
+    fillBufferLayerTransform<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
+    fillBufferLayerTransform<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
+    fillBufferWithRoundedCorners<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
+    fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) {
+    fillGreenBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) {
+    fillBlueBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) {
+    fillRedTransparentBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) {
+    fillBufferPhysicalOffset<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) {
+    fillBufferCheckersRotate0<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) {
+    fillBufferCheckersRotate90<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) {
+    fillBufferCheckersRotate180<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) {
+    fillBufferCheckersRotate270<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) {
+    fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
+    fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
+    fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
+    fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) {
+    fillGreenBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) {
+    fillBlueBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) {
+    fillRedTransparentBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) {
+    fillBufferPhysicalOffset<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) {
+    fillBufferCheckersRotate0<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) {
+    fillBufferCheckersRotate90<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) {
+    fillBufferCheckersRotate180<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) {
+    fillBufferCheckersRotate270<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) {
+    fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
+    fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
+    fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
+    fillBufferTextureTransform();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
+    fillBufferWithPremultiplyAlpha();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
+    fillBufferWithoutPremultiplyAlpha();
+}
+
+TEST_F(RenderEngineTest, drawLayers_clearRegion) {
+    clearRegion();
+}
+
+} // namespace android
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index a0e368c..d9a986e 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -318,7 +318,7 @@
         // If the sensor is protected by a permission we need to know if it is
         // a runtime one to determine whether we can use the permission cache.
         sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
-        if (binder != 0) {
+        if (binder != nullptr) {
             sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder);
             mRequiredPermissionRuntime = permCtrl->isRuntimePermission(
                     String16(mRequiredPermission));
diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp
index 6f68fb5..4438d45 100644
--- a/libs/sensor/SensorEventQueue.cpp
+++ b/libs/sensor/SensorEventQueue.cpp
@@ -29,6 +29,7 @@
 #include <sensor/ISensorEventConnection.h>
 
 #include <android/sensor.h>
+#include <hardware/sensors-base.h>
 
 using std::min;
 
@@ -37,7 +38,7 @@
 // ----------------------------------------------------------------------------
 
 SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection)
-    : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0),
+    : mSensorEventConnection(connection), mRecBuffer(nullptr), mAvailable(0), mConsumed(0),
       mNumAcksToSend(0) {
     mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT];
 }
@@ -82,9 +83,9 @@
 sp<Looper> SensorEventQueue::getLooper() const
 {
     Mutex::Autolock _l(mLock);
-    if (mLooper == 0) {
+    if (mLooper == nullptr) {
         mLooper = new Looper(true);
-        mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL);
+        mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, nullptr, nullptr);
     }
     return mLooper;
 }
@@ -97,7 +98,7 @@
     int events;
     int32_t result;
     do {
-        result = looper->pollOnce(-1, NULL, &events, NULL);
+        result = looper->pollOnce(-1, nullptr, &events, nullptr);
         if (result == ALOOPER_POLL_ERROR) {
             ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno);
             result = -EPIPE; // unknown error, so we make up one
@@ -188,6 +189,52 @@
     return;
 }
 
+ssize_t SensorEventQueue::filterEvents(ASensorEvent* events, size_t count) const {
+    // Check if this Sensor Event Queue is registered to receive each type of event. If it is not,
+    // then do not copy the event into the final buffer. Minimize the number of copy operations by
+    // finding consecutive sequences of events that the Sensor Event Queue should receive and only
+    // copying the events once an unregistered event type is reached.
+    bool intervalStartLocSet = false;
+    size_t intervalStartLoc = 0;
+    size_t eventsInInterval = 0;
+    ssize_t eventsCopied = 0;
+
+    for (size_t i = 0; i < count; i++) {
+        bool includeEvent =
+                (events[i].type != SENSOR_TYPE_ADDITIONAL_INFO || requestAdditionalInfo);
+
+        if (includeEvent) {
+            // Do not copy events yet since there may be more consecutive events that should be
+            // copied together. Track the start location and number of events in the current
+            // sequence.
+            if (!intervalStartLocSet) {
+                intervalStartLoc = i;
+                intervalStartLocSet = true;
+                eventsInInterval = 0;
+            }
+            eventsInInterval++;
+        }
+
+        // Shift the events from the already processed interval once an event that should not be
+        // included is reached or if this is the final event to be processed.
+        if (!includeEvent || (i + 1 == count)) {
+            // Only shift the events if the interval did not start with the first event. If the
+            // interval started with the first event, the events are already in their correct
+            // location.
+            if (intervalStartLoc > 0) {
+                memmove(&events[eventsCopied], &events[intervalStartLoc],
+                        eventsInInterval * sizeof(ASensorEvent));
+            }
+            eventsCopied += eventsInInterval;
+
+            // Reset the interval information
+            eventsInInterval = 0;
+            intervalStartLocSet = false;
+        }
+    }
+    return eventsCopied;
+}
+
 // ----------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index b9ae524..5840d51 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -62,7 +62,7 @@
         // to the wrong package and stats based on app ops may be slightly off.
         if (opPackageName.size() <= 0) {
             sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
-            if (binder != 0) {
+            if (binder != nullptr) {
                 const uid_t uid = IPCThreadState::self()->getCallingUid();
                 Vector<String16> packages;
                 interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
@@ -93,7 +93,7 @@
 }
 
 SensorManager::SensorManager(const String16& opPackageName)
-    : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
+    : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
     // okay we're not locked here, but it's not needed during construction
     assertStateLocked();
 }
@@ -128,13 +128,13 @@
     Mutex::Autolock _l(mLock);
     mSensorServer.clear();
     free(mSensorList);
-    mSensorList = NULL;
+    mSensorList = nullptr;
     mSensors.clear();
 }
 
 status_t SensorManager::assertStateLocked() {
     bool initSensorManager = false;
-    if (mSensorServer == NULL) {
+    if (mSensorServer == nullptr) {
         initSensorManager = true;
     } else {
         // Ping binder to check if sensorservice is alive.
@@ -164,7 +164,7 @@
         size_t count = mSensors.size();
         mSensorList =
                 static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
-        LOG_ALWAYS_FATAL_IF(mSensorList == NULL, "mSensorList NULL");
+        LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL");
 
         for (size_t i=0 ; i<count ; i++) {
             mSensorList[i] = mSensors.array() + i;
@@ -222,7 +222,7 @@
             }
         }
     }
-    return NULL;
+    return nullptr;
 }
 
 sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) {
@@ -232,10 +232,10 @@
     while (assertStateLocked() == NO_ERROR) {
         sp<ISensorEventConnection> connection =
                 mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName);
-        if (connection == NULL) {
+        if (connection == nullptr) {
             // SensorService just died or the app doesn't have required permissions.
             ALOGE("createEventQueue: connection is NULL.");
-            return NULL;
+            return nullptr;
         }
         queue = new SensorEventQueue(connection);
         break;
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
index 8176578..8c3fde0 100644
--- a/libs/sensor/include/sensor/SensorEventQueue.h
+++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -34,6 +34,7 @@
 // Concrete types for the NDK
 struct ASensorEventQueue {
     ALooper* looper;
+    bool requestAdditionalInfo;
 };
 
 // ----------------------------------------------------------------------------
@@ -92,6 +93,13 @@
     void sendAck(const ASensorEvent* events, int count);
 
     status_t injectSensorEvent(const ASensorEvent& event);
+
+    // Filters the given sensor events in place and returns the new number of events.
+    //
+    // The filtering is controlled by ASensorEventQueue.requestAdditionalInfo, and if this value is
+    // false, then all SENSOR_TYPE_ADDITIONAL_INFO sensor events will be removed.
+    ssize_t filterEvents(ASensorEvent* events, size_t count) const;
+
 private:
     sp<Looper> getLooper() const;
     sp<ISensorEventConnection> mSensorEventConnection;
diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp
index 9fd84bc..c9a7668 100644
--- a/libs/sensor/tests/Android.bp
+++ b/libs/sensor/tests/Android.bp
@@ -21,6 +21,7 @@
 
     srcs: [
         "Sensor_test.cpp",
+        "SensorEventQueue_test.cpp",
     ],
 
     shared_libs: [
diff --git a/libs/sensor/tests/SensorEventQueue_test.cpp b/libs/sensor/tests/SensorEventQueue_test.cpp
new file mode 100644
index 0000000..1eb5883
--- /dev/null
+++ b/libs/sensor/tests/SensorEventQueue_test.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2019 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 <stdint.h>
+
+#include <gtest/gtest.h>
+#include <utils/Errors.h>
+
+#include <android/sensor.h>
+#include <hardware/sensors-base.h>
+#include <sensor/SensorManager.h>
+#include <sensor/SensorEventQueue.h>
+
+namespace android {
+
+class SensorEventQueueTest : public ::testing::Test {
+protected:
+    typedef std::vector<int32_t> Events;
+
+    SensorEventQueueTest() {};
+
+    virtual void SetUp() override {
+        SensorManager& manager = SensorManager::getInstanceForPackage(String16("SensorEventQueueTest"));
+        mQueue = manager.createEventQueue();
+    }
+
+    void configureAdditionalInfo(bool enable) {
+        mQueue->requestAdditionalInfo = enable;
+    }
+
+    Events filterEvents(const Events &types) const {
+        // Convert the events into SensorEvent array
+        ASensorEvent* events = new ASensorEvent[types.size()];
+        for (size_t i = 0; i < types.size(); i++) {
+            events[i].type = types[i];
+        }
+
+        // Filter the events
+        ssize_t filteredCount = mQueue->filterEvents(events, types.size());
+
+        // Copy the result into an output vector
+        Events result;
+        for (size_t i = 0; i < filteredCount; i++) {
+            result.push_back(events[i].type);
+        }
+        delete[] events;
+
+        return result;
+    }
+
+    Events getExpectedEvents(const Events &events) const {
+        Events output;
+        for (size_t i = 0; i != events.size(); i++) {
+            // Copy events if the event queue is configured to receive them
+            if (events[i] != SENSOR_TYPE_ADDITIONAL_INFO || mQueue->requestAdditionalInfo) {
+                output.push_back(events[i]);
+            }
+        }
+        return output;
+    }
+
+    void runFilterTest(const Events& events) {
+        Events filtered = filterEvents(events);
+        Events expected = getExpectedEvents(events);
+        EXPECT_EQ(expected.size(), filtered.size());
+        EXPECT_EQ(expected, filtered);
+    }
+
+private:
+    sp<SensorEventQueue> mQueue;
+};
+
+TEST_F(SensorEventQueueTest, FilterZeroEvents) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_ReceiveAdditionalInfo) {
+    configureAdditionalInfo(true /* enable */);
+    runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_GYROSCOPE,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveAll) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveFirst) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_GYROSCOPE,
+                   SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveAllButOne) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveLast) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_GYROSCOPE,
+                   SENSOR_TYPE_MAGNETIC_FIELD,
+                   SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveConsecutive) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_MAGNETIC_FIELD,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ACCELEROMETER});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveInterleaved) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_GYROSCOPE,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_GYROSCOPE,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_ReconfigureAdditionalInfo) {
+    configureAdditionalInfo(false /* enable */);
+    const Events events = {SENSOR_TYPE_ACCELEROMETER,
+                           SENSOR_TYPE_GYROSCOPE,
+                           SENSOR_TYPE_ADDITIONAL_INFO,
+                           SENSOR_TYPE_MAGNETIC_FIELD,
+                           SENSOR_TYPE_ADDITIONAL_INFO};
+    runFilterTest(events);
+
+    // Update setting to request Additional Info
+    configureAdditionalInfo(true /* enable */);
+    runFilterTest(events);
+
+    // Update setting to stop requesting Additional Info
+    configureAdditionalInfo(true /* enable */);
+    runFilterTest(events);
+}
+
+} // namespace android
diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp
new file mode 100644
index 0000000..e0e3469
--- /dev/null
+++ b/libs/sensorprivacy/Android.bp
@@ -0,0 +1,47 @@
+// Copyright 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libsensorprivacy",
+
+    aidl: {
+        export_aidl_headers: true,
+        local_include_dirs: ["aidl"],
+    },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wzero-as-null-pointer-constant",
+    ],
+
+    srcs: [
+        "aidl/android/hardware/ISensorPrivacyListener.aidl",
+        "aidl/android/hardware/ISensorPrivacyManager.aidl",
+        "SensorPrivacyManager.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libcutils",
+        "libutils",
+        "liblog",
+        "libhardware",
+    ],
+
+    export_include_dirs: ["include"],
+
+    export_shared_lib_headers: ["libbinder"],
+}
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
new file mode 100644
index 0000000..f973cba
--- /dev/null
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mutex>
+#include <unistd.h>
+
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <sensorprivacy/SensorPrivacyManager.h>
+
+#include <utils/SystemClock.h>
+
+namespace android {
+
+SensorPrivacyManager::SensorPrivacyManager()
+{
+}
+
+sp<hardware::ISensorPrivacyManager> SensorPrivacyManager::getService()
+{
+    std::lock_guard<Mutex> scoped_lock(mLock);
+    int64_t startTime = 0;
+    sp<hardware::ISensorPrivacyManager> service = mService;
+    while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+        sp<IBinder> binder = defaultServiceManager()->checkService(String16("sensor_privacy"));
+        if (binder == nullptr) {
+            // Wait for the sensor privacy service to come back...
+            if (startTime == 0) {
+                startTime = uptimeMillis();
+                ALOGI("Waiting for sensor privacy service");
+            } else if ((uptimeMillis() - startTime) > 1000000) {
+                ALOGW("Waiting too long for sensor privacy service, giving up");
+                service = nullptr;
+                break;
+            }
+            usleep(25000);
+        } else {
+            service = interface_cast<hardware::ISensorPrivacyManager>(binder);
+            mService = service;
+        }
+    }
+    return service;
+}
+
+void SensorPrivacyManager::addSensorPrivacyListener(
+        const sp<hardware::ISensorPrivacyListener>& listener)
+{
+    sp<hardware::ISensorPrivacyManager> service = getService();
+    if (service != nullptr) {
+        service->addSensorPrivacyListener(listener);
+    }
+}
+
+void SensorPrivacyManager::removeSensorPrivacyListener(
+        const sp<hardware::ISensorPrivacyListener>& listener)
+{
+    sp<hardware::ISensorPrivacyManager> service = getService();
+    if (service != nullptr) {
+        service->removeSensorPrivacyListener(listener);
+    }
+}
+
+bool SensorPrivacyManager::isSensorPrivacyEnabled()
+{
+    sp<hardware::ISensorPrivacyManager> service = getService();
+    if (service != nullptr) {
+        bool result;
+        service->isSensorPrivacyEnabled(&result);
+        return result;
+    }
+    // if the SensorPrivacyManager is not available then assume sensor privacy is disabled
+    return false;
+}
+
+status_t SensorPrivacyManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient)
+{
+    sp<hardware::ISensorPrivacyManager> service = getService();
+    if (service != nullptr) {
+        return IInterface::asBinder(service)->linkToDeath(recipient);
+    }
+    return INVALID_OPERATION;
+}
+
+status_t SensorPrivacyManager::unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient)
+{
+    sp<hardware::ISensorPrivacyManager> service = getService();
+    if (service != nullptr) {
+        return IInterface::asBinder(service)->unlinkToDeath(recipient);
+    }
+    return INVALID_OPERATION;
+}
+
+}; // namespace android
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl
new file mode 100644
index 0000000..58177d8
--- /dev/null
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/**
+ * @hide
+ */
+oneway interface ISensorPrivacyListener {
+    void onSensorPrivacyChanged(boolean enabled);
+}
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
new file mode 100644
index 0000000..4c2d5db
--- /dev/null
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.hardware.ISensorPrivacyListener;
+
+/** @hide */
+interface ISensorPrivacyManager {
+    void addSensorPrivacyListener(in ISensorPrivacyListener listener);
+
+    void removeSensorPrivacyListener(in ISensorPrivacyListener listener);
+
+    boolean isSensorPrivacyEnabled();
+
+    void setSensorPrivacy(boolean enable);
+}
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
new file mode 100644
index 0000000..2546a68
--- /dev/null
+++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SENSOR_PRIVACY_MANAGER_H
+#define ANDROID_SENSOR_PRIVACY_MANAGER_H
+
+#include "android/hardware/ISensorPrivacyListener.h"
+#include "android/hardware/ISensorPrivacyManager.h"
+
+#include <utils/threads.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class SensorPrivacyManager
+{
+public:
+    SensorPrivacyManager();
+
+    void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
+    void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
+    bool isSensorPrivacyEnabled();
+
+    status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+    status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+
+private:
+    Mutex mLock;
+    sp<hardware::ISensorPrivacyManager> mService;
+    sp<hardware::ISensorPrivacyManager> getService();
+};
+
+
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_SENSOR_PRIVACY_MANAGER_H
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d25ad1a..00e227f 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -53,11 +53,16 @@
 
     srcs: [
         "ColorSpace.cpp",
+        "BufferHubBuffer.cpp",
+        "BufferHubEventFd.cpp",
+        "BufferHubMetadata.cpp",
         "DebugUtils.cpp",
         "Fence.cpp",
         "FenceTime.cpp",
         "FrameStats.cpp",
+        "Gralloc.cpp",
         "Gralloc2.cpp",
+        "Gralloc3.cpp",
         "GraphicBuffer.cpp",
         "GraphicBufferAllocator.cpp",
         "GraphicBufferMapper.cpp",
@@ -65,6 +70,8 @@
         "PixelFormat.cpp",
         "Rect.cpp",
         "Region.cpp",
+        "Size.cpp",
+        "Transform.cpp",
         "UiConfig.cpp",
     ],
 
@@ -73,11 +80,14 @@
     ],
 
     shared_libs: [
+        "android.frameworks.bufferhub@1.0",
         "android.hardware.graphics.allocator@2.0",
-        "android.hardware.graphics.common@1.1",
+        "android.hardware.graphics.allocator@3.0",
+        "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@2.1",
-        "android.hardware.configstore@1.0",
+        "android.hardware.graphics.mapper@3.0",
+        "android.hardware.configstore@1.2",
         "android.hardware.configstore-utils",
         "libbase",
         "libcutils",
@@ -92,7 +102,8 @@
     ],
 
     export_shared_lib_headers: [
-        "android.hardware.graphics.common@1.1",
+        "android.hardware.configstore@1.2",
+        "android.hardware.graphics.common@1.2",
     ],
 
     static_libs: [
@@ -101,9 +112,33 @@
         "libmath",
     ],
 
+    // bufferhub is not used when building libgui for vendors
+    target: {
+        vendor: {
+            cflags: ["-DLIBUI_IN_VNDK"],
+            exclude_srcs: [
+                "BufferHubBuffer.cpp",
+                "BufferHubEventFd.cpp",
+                "BufferHubMetadata.cpp",
+            ],
+            exclude_header_libs: [
+                "libbufferhub_headers",
+                "libdvr_headers",
+                "libnativewindow_headers",
+            ],
+            exclude_shared_libs: [
+                "android.frameworks.bufferhub@1.0",
+                "libpdx_default_transport",
+            ],
+        },
+    },
+
     header_libs: [
         "libbase_headers",
+        "libbufferhub_headers",
+        "libdvr_headers",
         "libnativebase_headers",
+        "libnativewindow_headers",
         "libhardware_headers",
         "libui_headers",
         "libpdx_headers",
@@ -128,6 +163,7 @@
     vendor_available: true,
     target: {
         vendor: {
+            cflags: ["-DLIBUI_IN_VNDK"],
             override_export_include_dirs: ["include_vndk"],
         },
     },
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
new file mode 100644
index 0000000..4b20772
--- /dev/null
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <poll.h>
+
+#include <android-base/unique_fd.h>
+#include <android/frameworks/bufferhub/1.0/IBufferHub.h>
+#include <log/log.h>
+#include <ui/BufferHubBuffer.h>
+#include <ui/BufferHubDefs.h>
+#include <utils/Trace.h>
+
+using ::android::base::unique_fd;
+using ::android::BufferHubDefs::AnyClientAcquired;
+using ::android::BufferHubDefs::AnyClientGained;
+using ::android::BufferHubDefs::IsClientAcquired;
+using ::android::BufferHubDefs::IsClientGained;
+using ::android::BufferHubDefs::IsClientPosted;
+using ::android::BufferHubDefs::IsClientReleased;
+using ::android::frameworks::bufferhub::V1_0::BufferHubStatus;
+using ::android::frameworks::bufferhub::V1_0::BufferTraits;
+using ::android::frameworks::bufferhub::V1_0::IBufferClient;
+using ::android::frameworks::bufferhub::V1_0::IBufferHub;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::graphics::common::V1_2::HardwareBufferDescription;
+
+namespace android {
+
+std::unique_ptr<BufferHubBuffer> BufferHubBuffer::Create(uint32_t width, uint32_t height,
+                                                         uint32_t layerCount, uint32_t format,
+                                                         uint64_t usage, size_t userMetadataSize) {
+    auto buffer = std::unique_ptr<BufferHubBuffer>(
+            new BufferHubBuffer(width, height, layerCount, format, usage, userMetadataSize));
+    return buffer->IsValid() ? std::move(buffer) : nullptr;
+}
+
+std::unique_ptr<BufferHubBuffer> BufferHubBuffer::Import(const native_handle_t* token) {
+    if (token == nullptr) {
+        ALOGE("%s: token cannot be nullptr!", __FUNCTION__);
+        return nullptr;
+    }
+
+    auto buffer = std::unique_ptr<BufferHubBuffer>(new BufferHubBuffer(token));
+    return buffer->IsValid() ? std::move(buffer) : nullptr;
+}
+
+BufferHubBuffer::BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount,
+                                 uint32_t format, uint64_t usage, size_t userMetadataSize) {
+    ATRACE_CALL();
+    ALOGD("%s: width=%u height=%u layerCount=%u, format=%u "
+          "usage=%" PRIx64 " mUserMetadataSize=%zu",
+          __FUNCTION__, width, height, layerCount, format, usage, userMetadataSize);
+
+    sp<IBufferHub> bufferhub = IBufferHub::getService();
+    if (bufferhub.get() == nullptr) {
+        ALOGE("%s: BufferHub service not found!", __FUNCTION__);
+        return;
+    }
+
+    AHardwareBuffer_Desc aDesc = {width, height,         layerCount,   format,
+                                  usage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
+    HardwareBufferDescription desc;
+    memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
+
+    BufferHubStatus ret;
+    sp<IBufferClient> client;
+    BufferTraits bufferTraits;
+    IBufferHub::allocateBuffer_cb alloc_cb = [&](const auto& status, const auto& outClient,
+                                                 const auto& traits) {
+        ret = status;
+        client = std::move(outClient);
+        bufferTraits = std::move(traits);
+    };
+
+    if (!bufferhub->allocateBuffer(desc, static_cast<uint32_t>(userMetadataSize), alloc_cb)
+                 .isOk()) {
+        ALOGE("%s: allocateBuffer transaction failed!", __FUNCTION__);
+        return;
+    } else if (ret != BufferHubStatus::NO_ERROR) {
+        ALOGE("%s: allocateBuffer failed with error %u.", __FUNCTION__, ret);
+        return;
+    } else if (client == nullptr) {
+        ALOGE("%s: allocateBuffer got null BufferClient.", __FUNCTION__);
+        return;
+    }
+
+    const int importRet = initWithBufferTraits(bufferTraits);
+    if (importRet < 0) {
+        ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-importRet));
+        client->close();
+    }
+    mBufferClient = std::move(client);
+}
+
+BufferHubBuffer::BufferHubBuffer(const native_handle_t* token) {
+    sp<IBufferHub> bufferhub = IBufferHub::getService();
+    if (bufferhub.get() == nullptr) {
+        ALOGE("%s: BufferHub service not found!", __FUNCTION__);
+        return;
+    }
+
+    BufferHubStatus ret;
+    sp<IBufferClient> client;
+    BufferTraits bufferTraits;
+    IBufferHub::importBuffer_cb import_cb = [&](const auto& status, const auto& outClient,
+                                                const auto& traits) {
+        ret = status;
+        client = std::move(outClient);
+        bufferTraits = std::move(traits);
+    };
+
+    // hidl_handle(native_handle_t*) simply creates a raw pointer reference withouth ownership
+    // transfer.
+    if (!bufferhub->importBuffer(hidl_handle(token), import_cb).isOk()) {
+        ALOGE("%s: importBuffer transaction failed!", __FUNCTION__);
+        return;
+    } else if (ret != BufferHubStatus::NO_ERROR) {
+        ALOGE("%s: importBuffer failed with error %u.", __FUNCTION__, ret);
+        return;
+    } else if (client == nullptr) {
+        ALOGE("%s: importBuffer got null BufferClient.", __FUNCTION__);
+        return;
+    }
+
+    const int importRet = initWithBufferTraits(bufferTraits);
+    if (importRet < 0) {
+        ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-importRet));
+        client->close();
+    }
+    mBufferClient = std::move(client);
+}
+
+BufferHubBuffer::~BufferHubBuffer() {
+    // Close buffer client to avoid possible race condition: user could first duplicate and hold
+    // token with the original buffer gone, and then try to import the token. The close function
+    // will explicitly invalidate the token to avoid this.
+    if (mBufferClient != nullptr) {
+        if (!mBufferClient->close().isOk()) {
+            ALOGE("%s: close BufferClient transaction failed!", __FUNCTION__);
+        }
+    }
+}
+
+int BufferHubBuffer::initWithBufferTraits(const BufferTraits& bufferTraits) {
+    ATRACE_CALL();
+
+    if (bufferTraits.bufferInfo.getNativeHandle() == nullptr) {
+        ALOGE("%s: missing buffer info handle.", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    if (bufferTraits.bufferHandle.getNativeHandle() == nullptr) {
+        ALOGE("%s: missing gralloc handle.", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    // Import fds. Dup fds because hidl_handle owns the fds.
+    unique_fd ashmemFd(fcntl(bufferTraits.bufferInfo->data[0], F_DUPFD_CLOEXEC, 0));
+    mMetadata = BufferHubMetadata::Import(std::move(ashmemFd));
+    if (!mMetadata.IsValid()) {
+        ALOGE("%s: Received an invalid metadata.", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    mEventFd = BufferHubEventFd(fcntl(bufferTraits.bufferInfo->data[1], F_DUPFD_CLOEXEC, 0));
+    if (!mEventFd.isValid()) {
+        ALOGE("%s: Received ad invalid event fd.", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    int bufferId = bufferTraits.bufferInfo->data[2];
+    if (bufferId < 0) {
+        ALOGE("%s: Received an invalid (negative) id.", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    uint32_t clientBitMask;
+    memcpy(&clientBitMask, &bufferTraits.bufferInfo->data[3], sizeof(clientBitMask));
+    if (clientBitMask == 0U) {
+        ALOGE("%s: Received an invalid client state mask.", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    uint32_t userMetadataSize;
+    memcpy(&userMetadataSize, &bufferTraits.bufferInfo->data[4], sizeof(userMetadataSize));
+    if (mMetadata.user_metadata_size() != userMetadataSize) {
+        ALOGE("%s: user metadata size not match: expected %u, actual %zu.", __FUNCTION__,
+              userMetadataSize, mMetadata.user_metadata_size());
+        return -EINVAL;
+    }
+
+    size_t metadataSize = static_cast<size_t>(mMetadata.metadata_size());
+    if (metadataSize < BufferHubDefs::kMetadataHeaderSize) {
+        ALOGE("%s: metadata too small: %zu", __FUNCTION__, metadataSize);
+        return -EINVAL;
+    }
+
+    // Populate shortcuts to the atomics in metadata.
+    auto metadata_header = mMetadata.metadata_header();
+    buffer_state_ = &metadata_header->buffer_state;
+    fence_state_ = &metadata_header->fence_state;
+    active_clients_bit_mask_ = &metadata_header->active_clients_bit_mask;
+    // The C++ standard recommends (but does not require) that lock-free atomic operations are
+    // also address-free, that is, suitable for communication between processes using shared
+    // memory.
+    LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(buffer_state_) ||
+                                !std::atomic_is_lock_free(fence_state_) ||
+                                !std::atomic_is_lock_free(active_clients_bit_mask_),
+                        "Atomic variables in ashmen are not lock free.");
+
+    // Import the buffer: We only need to hold on the native_handle_t here so that
+    // GraphicBuffer instance can be created in future.
+    mBufferHandle = std::move(bufferTraits.bufferHandle);
+    memcpy(&mBufferDesc, &bufferTraits.bufferDesc, sizeof(AHardwareBuffer_Desc));
+
+    mId = bufferId;
+    mClientStateMask = clientBitMask;
+
+    // TODO(b/112012161) Set up shared fences.
+    ALOGD("%s: id=%d, buffer_state=%" PRIx32 ".", __FUNCTION__, mId,
+          buffer_state_->load(std::memory_order_acquire));
+    return 0;
+}
+
+int BufferHubBuffer::Gain() {
+    uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
+    if (IsClientGained(current_buffer_state, mClientStateMask)) {
+        ALOGV("%s: Buffer is already gained by this client %" PRIx32 ".", __FUNCTION__,
+              mClientStateMask);
+        return 0;
+    }
+    do {
+        if (AnyClientGained(current_buffer_state & (~mClientStateMask)) ||
+            AnyClientAcquired(current_buffer_state)) {
+            ALOGE("%s: Buffer is in use, id=%d mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
+                  __FUNCTION__, mId, mClientStateMask, current_buffer_state);
+            return -EBUSY;
+        }
+        // Change the buffer state to gained state, whose value happens to be the same as
+        // mClientStateMask.
+    } while (!buffer_state_->compare_exchange_weak(current_buffer_state, mClientStateMask,
+                                                   std::memory_order_acq_rel,
+                                                   std::memory_order_acquire));
+    // TODO(b/119837586): Update fence state and return GPU fence.
+    return 0;
+}
+
+int BufferHubBuffer::Post() {
+    uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
+    uint32_t updated_buffer_state = (~mClientStateMask) & BufferHubDefs::kHighBitsMask;
+    do {
+        if (!IsClientGained(current_buffer_state, mClientStateMask)) {
+            ALOGE("%s: Cannot post a buffer that is not gained by this client. buffer_id=%d "
+                  "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
+                  __FUNCTION__, mId, mClientStateMask, current_buffer_state);
+            return -EBUSY;
+        }
+        // Set the producer client buffer state to released, other clients' buffer state to posted.
+        // Post to all existing and non-existing clients.
+    } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state,
+                                                   std::memory_order_acq_rel,
+                                                   std::memory_order_acquire));
+    // TODO(b/119837586): Update fence state and return GPU fence if needed.
+    return 0;
+}
+
+int BufferHubBuffer::Acquire() {
+    uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
+    if (IsClientAcquired(current_buffer_state, mClientStateMask)) {
+        ALOGV("%s: Buffer is already acquired by this client %" PRIx32 ".", __FUNCTION__,
+              mClientStateMask);
+        return 0;
+    }
+    uint32_t updated_buffer_state = 0U;
+    do {
+        if (!IsClientPosted(current_buffer_state, mClientStateMask)) {
+            ALOGE("%s: Cannot acquire a buffer that is not in posted state. buffer_id=%d "
+                  "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
+                  __FUNCTION__, mId, mClientStateMask, current_buffer_state);
+            return -EBUSY;
+        }
+        // Change the buffer state for this consumer from posted to acquired.
+        updated_buffer_state = current_buffer_state ^ mClientStateMask;
+    } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state,
+                                                   std::memory_order_acq_rel,
+                                                   std::memory_order_acquire));
+    // TODO(b/119837586): Update fence state and return GPU fence.
+    return 0;
+}
+
+int BufferHubBuffer::Release() {
+    uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
+    if (IsClientReleased(current_buffer_state, mClientStateMask)) {
+        ALOGV("%s: Buffer is already released by this client %" PRIx32 ".", __FUNCTION__,
+              mClientStateMask);
+        return 0;
+    }
+    uint32_t updated_buffer_state = 0U;
+    do {
+        updated_buffer_state = current_buffer_state & (~mClientStateMask);
+    } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state,
+                                                   std::memory_order_acq_rel,
+                                                   std::memory_order_acquire));
+    // TODO(b/119837586): Update fence state and return GPU fence if needed.
+    return 0;
+}
+
+bool BufferHubBuffer::IsReleased() const {
+    return (buffer_state_->load(std::memory_order_acquire) &
+            active_clients_bit_mask_->load(std::memory_order_acquire)) == 0;
+}
+
+bool BufferHubBuffer::IsValid() const {
+    return mBufferHandle.getNativeHandle() != nullptr && mId >= 0 && mClientStateMask != 0U &&
+            mEventFd.get() >= 0 && mMetadata.IsValid() && mBufferClient != nullptr;
+}
+
+native_handle_t* BufferHubBuffer::Duplicate() {
+    if (mBufferClient == nullptr) {
+        ALOGE("%s: missing BufferClient!", __FUNCTION__);
+        return nullptr;
+    }
+
+    hidl_handle token;
+    BufferHubStatus ret;
+    IBufferClient::duplicate_cb dup_cb = [&](const auto& outToken, const auto& status) {
+        token = std::move(outToken);
+        ret = status;
+    };
+
+    if (!mBufferClient->duplicate(dup_cb).isOk()) {
+        ALOGE("%s: duplicate transaction failed!", __FUNCTION__);
+        return nullptr;
+    } else if (ret != BufferHubStatus::NO_ERROR) {
+        ALOGE("%s: duplicate failed with error %u.", __FUNCTION__, ret);
+        return nullptr;
+    } else if (token.getNativeHandle() == nullptr) {
+        ALOGE("%s: duplicate got null token.", __FUNCTION__);
+        return nullptr;
+    }
+
+    return native_handle_clone(token.getNativeHandle());
+}
+
+} // namespace android
diff --git a/libs/ui/BufferHubEventFd.cpp b/libs/ui/BufferHubEventFd.cpp
new file mode 100644
index 0000000..bffc2ca
--- /dev/null
+++ b/libs/ui/BufferHubEventFd.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/eventfd.h>
+
+#include <log/log.h>
+#include <ui/BufferHubEventFd.h>
+
+namespace android {
+
+BufferHubEventFd::BufferHubEventFd() : mFd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) {}
+
+BufferHubEventFd::BufferHubEventFd(int fd) : mFd(fd) {}
+
+status_t BufferHubEventFd::signal() const {
+    if (!isValid()) {
+        ALOGE("%s: cannot signal an invalid eventfd.", __FUNCTION__);
+        return DEAD_OBJECT;
+    }
+
+    eventfd_write(mFd.get(), 1);
+    return OK;
+}
+
+status_t BufferHubEventFd::clear() const {
+    if (!isValid()) {
+        ALOGE("%s: cannot clear an invalid eventfd.", __FUNCTION__);
+        return DEAD_OBJECT;
+    }
+
+    eventfd_t value;
+    eventfd_read(mFd.get(), &value);
+    return OK;
+}
+
+} // namespace android
diff --git a/libs/ui/BufferHubMetadata.cpp b/libs/ui/BufferHubMetadata.cpp
new file mode 100644
index 0000000..816707d
--- /dev/null
+++ b/libs/ui/BufferHubMetadata.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <sys/mman.h>
+#include <limits>
+
+#include <cutils/ashmem.h>
+#include <log/log.h>
+#include <ui/BufferHubMetadata.h>
+
+namespace android {
+
+namespace {
+
+static const int kAshmemProt = PROT_READ | PROT_WRITE;
+
+} // namespace
+
+using BufferHubDefs::kMetadataHeaderSize;
+using BufferHubDefs::MetadataHeader;
+
+/* static */
+BufferHubMetadata BufferHubMetadata::Create(size_t userMetadataSize) {
+    // The size the of metadata buffer is used as the "width" parameter during allocation. Thus it
+    // cannot overflow uint32_t.
+    if (userMetadataSize >= (std::numeric_limits<uint32_t>::max() - kMetadataHeaderSize)) {
+        ALOGE("BufferHubMetadata::Create: metadata size too big: %zu.", userMetadataSize);
+        return {};
+    }
+
+    const size_t metadataSize = userMetadataSize + kMetadataHeaderSize;
+    int fd = ashmem_create_region(/*name=*/"BufferHubMetadata", metadataSize);
+    if (fd < 0) {
+        ALOGE("BufferHubMetadata::Create: failed to create ashmem region.");
+        return {};
+    }
+
+    // Hand over the ownership of the fd to a unique_fd immediately after the successful
+    // return of ashmem_create_region. The ashmemFd is going to own the fd and to prevent fd
+    // leaks during error handling.
+    unique_fd ashmemFd{fd};
+
+    if (ashmem_set_prot_region(ashmemFd.get(), kAshmemProt) != 0) {
+        ALOGE("BufferHubMetadata::Create: failed to set protect region.");
+        return {};
+    }
+
+    return BufferHubMetadata::Import(std::move(ashmemFd));
+}
+
+/* static */
+BufferHubMetadata BufferHubMetadata::Import(unique_fd ashmemFd) {
+    if (!ashmem_valid(ashmemFd.get())) {
+        ALOGE("BufferHubMetadata::Import: invalid ashmem fd.");
+        return {};
+    }
+
+    size_t metadataSize = static_cast<size_t>(ashmem_get_size_region(ashmemFd.get()));
+    size_t userMetadataSize = metadataSize - kMetadataHeaderSize;
+
+    // Note that here the buffer state is mapped from shared memory as an atomic object. The
+    // std::atomic's constructor will not be called so that the original value stored in the memory
+    // region can be preserved.
+    auto metadataHeader = static_cast<MetadataHeader*>(mmap(nullptr, metadataSize, kAshmemProt,
+                                                            MAP_SHARED, ashmemFd.get(),
+                                                            /*offset=*/0));
+    if (metadataHeader == nullptr) {
+        ALOGE("BufferHubMetadata::Import: failed to map region.");
+        return {};
+    }
+
+    return BufferHubMetadata(userMetadataSize, std::move(ashmemFd), metadataHeader);
+}
+
+BufferHubMetadata::BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd,
+                                     MetadataHeader* metadataHeader)
+      : mUserMetadataSize(userMetadataSize),
+        mAshmemFd(std::move(ashmemFd)),
+        mMetadataHeader(metadataHeader) {}
+
+BufferHubMetadata::~BufferHubMetadata() {
+    if (mMetadataHeader != nullptr) {
+        int ret = munmap(mMetadataHeader, metadata_size());
+        ALOGE_IF(ret != 0,
+                 "BufferHubMetadata::~BufferHubMetadata: failed to unmap ashmem, error=%d.", errno);
+        mMetadataHeader = nullptr;
+    }
+}
+
+} // namespace android
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 61df02d..ee06d93 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -234,6 +234,9 @@
         case ColorMode::BT2020:
             return std::string("ColorMode::BT2020");
 
+        case ColorMode::DISPLAY_BT2020:
+            return std::string("ColorMode::DISPLAY_BT2020");
+
         case ColorMode::BT2100_PQ:
             return std::string("ColorMode::BT2100_PQ");
 
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index 1414766..340231d 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -33,18 +33,6 @@
 
 const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE);
 
-void* FenceTime::operator new(size_t byteCount) noexcept {
-    void *p = nullptr;
-    if (posix_memalign(&p, alignof(FenceTime), byteCount)) {
-        return nullptr;
-    }
-    return p;
-}
-
-void FenceTime::operator delete(void *p) {
-    free(p);
-}
-
 FenceTime::FenceTime(const sp<Fence>& fence)
   : mState(((fence.get() != nullptr) && fence->isValid()) ?
             State::VALID : State::INVALID),
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/ui/Gralloc.cpp
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/ui/Gralloc.cpp
index e6ac6bf..8e09a13 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/ui/Gralloc.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#define LOG_TAG "Gralloc"
+
+#include <ui/Gralloc.h>
 
 namespace android {
-namespace mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+GrallocMapper::~GrallocMapper() {}
 
-} // namespace mock
+GrallocAllocator::~GrallocAllocator() {}
+
 } // namespace android
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 37cf617..92ea07c 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -27,9 +27,15 @@
 #include <sync/sync.h>
 #pragma clang diagnostic pop
 
-namespace android {
+using android::hardware::graphics::allocator::V2_0::IAllocator;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using android::hardware::graphics::common::V1_1::PixelFormat;
+using android::hardware::graphics::mapper::V2_0::BufferDescriptor;
+using android::hardware::graphics::mapper::V2_0::Error;
+using android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+using android::hardware::graphics::mapper::V2_1::IMapper;
 
-namespace Gralloc2 {
+namespace android {
 
 namespace {
 
@@ -42,9 +48,6 @@
         for (const auto bit : hardware::hidl_enum_range<BufferUsage>()) {
             bits = bits | bit;
         }
-        // TODO(b/72323293, b/72703005): Remove these additional bits
-        bits = bits | (1 << 10) | (1 << 13);
-
         return bits;
     }();
     return valid10UsageBits;
@@ -62,17 +65,26 @@
     return valid11UsageBits;
 }
 
+static inline IMapper::Rect sGralloc2Rect(const Rect& rect) {
+    IMapper::Rect outRect{};
+    outRect.left = rect.left;
+    outRect.top = rect.top;
+    outRect.width = rect.width();
+    outRect.height = rect.height();
+    return outRect;
+}
+
 }  // anonymous namespace
 
-void Mapper::preload() {
+void Gralloc2Mapper::preload() {
     android::hardware::preloadPassthroughService<hardware::graphics::mapper::V2_0::IMapper>();
 }
 
-Mapper::Mapper()
-{
+Gralloc2Mapper::Gralloc2Mapper() {
     mMapper = hardware::graphics::mapper::V2_0::IMapper::getService();
     if (mMapper == nullptr) {
-        LOG_ALWAYS_FATAL("gralloc-mapper is missing");
+        ALOGW("mapper 2.x is not supported");
+        return;
     }
     if (mMapper->isRemote()) {
         LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
@@ -82,30 +94,37 @@
     mMapperV2_1 = IMapper::castFrom(mMapper);
 }
 
-Gralloc2::Error Mapper::validateBufferDescriptorInfo(
-        const IMapper::BufferDescriptorInfo& descriptorInfo) const {
+bool Gralloc2Mapper::isLoaded() const {
+    return mMapper != nullptr;
+}
+
+status_t Gralloc2Mapper::validateBufferDescriptorInfo(
+        IMapper::BufferDescriptorInfo* descriptorInfo) const {
     uint64_t validUsageBits = getValid10UsageBits();
     if (mMapperV2_1 != nullptr) {
         validUsageBits = validUsageBits | getValid11UsageBits();
     }
 
-    if (descriptorInfo.usage & ~validUsageBits) {
+    if (descriptorInfo->usage & ~validUsageBits) {
         ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
-              descriptorInfo.usage & ~validUsageBits);
-        return Error::BAD_VALUE;
+              descriptorInfo->usage & ~validUsageBits);
+        return BAD_VALUE;
     }
-    return Error::NONE;
+    return NO_ERROR;
 }
 
-Error Mapper::createDescriptor(
-        const IMapper::BufferDescriptorInfo& descriptorInfo,
-        BufferDescriptor* outDescriptor) const
-{
-    Error error = validateBufferDescriptorInfo(descriptorInfo);
-    if (error != Error::NONE) {
-        return error;
+status_t Gralloc2Mapper::createDescriptor(void* bufferDescriptorInfo,
+                                          void* outBufferDescriptor) const {
+    IMapper::BufferDescriptorInfo* descriptorInfo =
+            static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo);
+    BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor);
+
+    status_t status = validateBufferDescriptorInfo(descriptorInfo);
+    if (status != NO_ERROR) {
+        return status;
     }
 
+    Error error;
     auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor)
                    {
                        error = tmpError;
@@ -118,24 +137,23 @@
 
     hardware::Return<void> ret;
     if (mMapperV2_1 != nullptr) {
-        ret = mMapperV2_1->createDescriptor_2_1(descriptorInfo, hidl_cb);
+        ret = mMapperV2_1->createDescriptor_2_1(*descriptorInfo, hidl_cb);
     } else {
         const hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo info = {
-            descriptorInfo.width,
-            descriptorInfo.height,
-            descriptorInfo.layerCount,
-            static_cast<hardware::graphics::common::V1_0::PixelFormat>(descriptorInfo.format),
-            descriptorInfo.usage,
+                descriptorInfo->width,
+                descriptorInfo->height,
+                descriptorInfo->layerCount,
+                static_cast<hardware::graphics::common::V1_0::PixelFormat>(descriptorInfo->format),
+                descriptorInfo->usage,
         };
         ret = mMapper->createDescriptor(info, hidl_cb);
     }
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
 }
 
-Error Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
-        buffer_handle_t* outBufferHandle) const
-{
+status_t Gralloc2Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+                                      buffer_handle_t* outBufferHandle) const {
     Error error;
     auto ret = mMapper->importBuffer(rawHandle,
             [&](const auto& tmpError, const auto& tmpBuffer)
@@ -148,11 +166,10 @@
                 *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
             });
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
 }
 
-void Mapper::freeBuffer(buffer_handle_t bufferHandle) const
-{
+void Gralloc2Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
     auto ret = mMapper->freeBuffer(buffer);
 
@@ -161,23 +178,29 @@
             buffer, error);
 }
 
-Error Mapper::validateBufferSize(buffer_handle_t bufferHandle,
-        const IMapper::BufferDescriptorInfo& descriptorInfo,
-        uint32_t stride) const
-{
+status_t Gralloc2Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+                                            uint32_t height, android::PixelFormat format,
+                                            uint32_t layerCount, uint64_t usage,
+                                            uint32_t stride) const {
     if (mMapperV2_1 == nullptr) {
-        return Error::NONE;
+        return NO_ERROR;
     }
 
+    IMapper::BufferDescriptorInfo descriptorInfo = {};
+    descriptorInfo.width = width;
+    descriptorInfo.height = height;
+    descriptorInfo.layerCount = layerCount;
+    descriptorInfo.format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+    descriptorInfo.usage = usage;
+
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
     auto ret = mMapperV2_1->validateBufferSize(buffer, descriptorInfo, stride);
 
-    return (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError);
 }
 
-void Mapper::getTransportSize(buffer_handle_t bufferHandle,
-        uint32_t* outNumFds, uint32_t* outNumInts) const
-{
+void Gralloc2Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                                      uint32_t* outNumInts) const {
     *outNumFds = uint32_t(bufferHandle->numFds);
     *outNumInts = uint32_t(bufferHandle->numInts);
 
@@ -198,19 +221,24 @@
                 *outNumInts = tmpNumInts;
             });
 
-    if (!ret.isOk()) {
-        error = kTransactionError;
-    }
-    ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d",
-            buffer, error);
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error);
 }
 
-Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
-        const IMapper::Rect& accessRegion,
-        int acquireFence, void** outData) const
-{
+status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                              int32_t* outBytesPerStride) const {
+    if (outBytesPerPixel) {
+        *outBytesPerPixel = -1;
+    }
+    if (outBytesPerStride) {
+        *outBytesPerStride = -1;
+    }
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
 
+    IMapper::Rect accessRegion = sGralloc2Rect(bounds);
+
     // put acquireFence in a hidl_handle
     hardware::hidl_handle acquireFenceHandle;
     NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
@@ -237,15 +265,19 @@
         close(acquireFence);
     }
 
-    return (ret.isOk()) ? error : kTransactionError;
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error);
+
+    return static_cast<status_t>(error);
 }
 
-Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
-        const IMapper::Rect& accessRegion,
-        int acquireFence, YCbCrLayout* outLayout) const
-{
+status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, android_ycbcr* ycbcr) const {
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
 
+    IMapper::Rect accessRegion = sGralloc2Rect(bounds);
+
     // put acquireFence in a hidl_handle
     hardware::hidl_handle acquireFenceHandle;
     NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
@@ -255,6 +287,7 @@
         acquireFenceHandle = h;
     }
 
+    YCbCrLayout layout;
     Error error;
     auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion,
             acquireFenceHandle,
@@ -265,19 +298,27 @@
                     return;
                 }
 
-                *outLayout = tmpLayout;
+                layout = tmpLayout;
             });
 
+    if (error == Error::NONE) {
+        ycbcr->y = layout.y;
+        ycbcr->cb = layout.cb;
+        ycbcr->cr = layout.cr;
+        ycbcr->ystride = static_cast<size_t>(layout.yStride);
+        ycbcr->cstride = static_cast<size_t>(layout.cStride);
+        ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
+    }
+
     // we own acquireFence even on errors
     if (acquireFence >= 0) {
         close(acquireFence);
     }
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
 }
 
-int Mapper::unlock(buffer_handle_t bufferHandle) const
-{
+int Gralloc2Mapper::unlock(buffer_handle_t bufferHandle) const {
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
 
     int releaseFence = -1;
@@ -302,10 +343,7 @@
                 }
             });
 
-    if (!ret.isOk()) {
-        error = kTransactionError;
-    }
-
+    error = (ret.isOk()) ? error : kTransactionError;
     if (error != Error::NONE) {
         ALOGE("unlock(%p) failed with %d", buffer, error);
     }
@@ -313,17 +351,19 @@
     return releaseFence;
 }
 
-Allocator::Allocator(const Mapper& mapper)
-    : mMapper(mapper)
-{
+Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) {
     mAllocator = IAllocator::getService();
     if (mAllocator == nullptr) {
-        LOG_ALWAYS_FATAL("gralloc-alloc is missing");
+        ALOGW("allocator 2.x is not supported");
+        return;
     }
 }
 
-std::string Allocator::dumpDebugInfo() const
-{
+bool Gralloc2Allocator::isLoaded() const {
+    return mAllocator != nullptr;
+}
+
+std::string Gralloc2Allocator::dumpDebugInfo() const {
     std::string debugInfo;
 
     mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) {
@@ -333,40 +373,51 @@
     return debugInfo;
 }
 
-Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count,
-        uint32_t* outStride, buffer_handle_t* outBufferHandles) const
-{
-    Error error;
-    auto ret = mAllocator->allocate(descriptor, count,
-            [&](const auto& tmpError, const auto& tmpStride,
-                const auto& tmpBuffers) {
-                error = tmpError;
-                if (tmpError != Error::NONE) {
-                    return;
-                }
+status_t Gralloc2Allocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+                                     uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                                     uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+    IMapper::BufferDescriptorInfo descriptorInfo = {};
+    descriptorInfo.width = width;
+    descriptorInfo.height = height;
+    descriptorInfo.layerCount = layerCount;
+    descriptorInfo.format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+    descriptorInfo.usage = usage;
 
-                // import buffers
-                for (uint32_t i = 0; i < count; i++) {
-                    error = mMapper.importBuffer(tmpBuffers[i],
-                            &outBufferHandles[i]);
-                    if (error != Error::NONE) {
-                        for (uint32_t j = 0; j < i; j++) {
-                            mMapper.freeBuffer(outBufferHandles[j]);
-                            outBufferHandles[j] = nullptr;
-                        }
-                        return;
-                    }
-                }
+    BufferDescriptor descriptor;
+    status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
+                                              static_cast<void*>(&descriptor));
+    if (error != NO_ERROR) {
+        return error;
+    }
 
-                *outStride = tmpStride;
-            });
+    auto ret = mAllocator->allocate(descriptor, bufferCount,
+                                    [&](const auto& tmpError, const auto& tmpStride,
+                                        const auto& tmpBuffers) {
+                                        error = static_cast<status_t>(tmpError);
+                                        if (tmpError != Error::NONE) {
+                                            return;
+                                        }
+
+                                        // import buffers
+                                        for (uint32_t i = 0; i < bufferCount; i++) {
+                                            error = mMapper.importBuffer(tmpBuffers[i],
+                                                                         &outBufferHandles[i]);
+                                            if (error != NO_ERROR) {
+                                                for (uint32_t j = 0; j < i; j++) {
+                                                    mMapper.freeBuffer(outBufferHandles[j]);
+                                                    outBufferHandles[j] = nullptr;
+                                                }
+                                                return;
+                                            }
+                                        }
+
+                                        *outStride = tmpStride;
+                                    });
 
     // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
     hardware::IPCThreadState::self()->flushCommands();
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
 }
 
-} // namespace Gralloc2
-
 } // namespace android
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
new file mode 100644
index 0000000..306a74b
--- /dev/null
+++ b/libs/ui/Gralloc3.cpp
@@ -0,0 +1,379 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Gralloc3"
+
+#include <hidl/ServiceManagement.h>
+#include <hwbinder/IPCThreadState.h>
+#include <ui/Gralloc3.h>
+
+#include <inttypes.h>
+#include <log/log.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wzero-length-array"
+#include <sync/sync.h>
+#pragma clang diagnostic pop
+
+using android::hardware::graphics::allocator::V3_0::IAllocator;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using android::hardware::graphics::mapper::V3_0::BufferDescriptor;
+using android::hardware::graphics::mapper::V3_0::Error;
+using android::hardware::graphics::mapper::V3_0::IMapper;
+using android::hardware::graphics::mapper::V3_0::YCbCrLayout;
+
+namespace android {
+
+namespace {
+
+static constexpr Error kTransactionError = Error::NO_RESOURCES;
+
+uint64_t getValidUsageBits() {
+    static const uint64_t validUsageBits = []() -> uint64_t {
+        uint64_t bits = 0;
+        for (const auto bit :
+             hardware::hidl_enum_range<hardware::graphics::common::V1_0::BufferUsage>()) {
+            bits = bits | bit;
+        }
+        for (const auto bit :
+             hardware::hidl_enum_range<hardware::graphics::common::V1_1::BufferUsage>()) {
+            bits = bits | bit;
+        }
+        return bits;
+    }();
+    return validUsageBits;
+}
+
+static inline IMapper::Rect sGralloc3Rect(const Rect& rect) {
+    IMapper::Rect outRect{};
+    outRect.left = rect.left;
+    outRect.top = rect.top;
+    outRect.width = rect.width();
+    outRect.height = rect.height();
+    return outRect;
+}
+static inline void sBufferDescriptorInfo(uint32_t width, uint32_t height,
+                                         android::PixelFormat format, uint32_t layerCount,
+                                         uint64_t usage,
+                                         IMapper::BufferDescriptorInfo* outDescriptorInfo) {
+    outDescriptorInfo->width = width;
+    outDescriptorInfo->height = height;
+    outDescriptorInfo->layerCount = layerCount;
+    outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+    outDescriptorInfo->usage = usage;
+}
+
+} // anonymous namespace
+
+void Gralloc3Mapper::preload() {
+    android::hardware::preloadPassthroughService<IMapper>();
+}
+
+Gralloc3Mapper::Gralloc3Mapper() {
+    mMapper = IMapper::getService();
+    if (mMapper == nullptr) {
+        ALOGW("mapper 3.x is not supported");
+        return;
+    }
+    if (mMapper->isRemote()) {
+        LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
+    }
+}
+
+bool Gralloc3Mapper::isLoaded() const {
+    return mMapper != nullptr;
+}
+
+status_t Gralloc3Mapper::validateBufferDescriptorInfo(
+        IMapper::BufferDescriptorInfo* descriptorInfo) const {
+    uint64_t validUsageBits = getValidUsageBits();
+
+    if (descriptorInfo->usage & ~validUsageBits) {
+        ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
+              descriptorInfo->usage & ~validUsageBits);
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
+status_t Gralloc3Mapper::createDescriptor(void* bufferDescriptorInfo,
+                                          void* outBufferDescriptor) const {
+    IMapper::BufferDescriptorInfo* descriptorInfo =
+            static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo);
+    BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor);
+
+    status_t status = validateBufferDescriptorInfo(descriptorInfo);
+    if (status != NO_ERROR) {
+        return status;
+    }
+
+    Error error;
+    auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+        *outDescriptor = tmpDescriptor;
+    };
+
+    hardware::Return<void> ret = mMapper->createDescriptor(*descriptorInfo, hidl_cb);
+
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+status_t Gralloc3Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+                                      buffer_handle_t* outBufferHandle) const {
+    Error error;
+    auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+        *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
+    });
+
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+void Gralloc3Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+    auto ret = mMapper->freeBuffer(buffer);
+
+    auto error = (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+    ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d", buffer, error);
+}
+
+status_t Gralloc3Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+                                            uint32_t height, android::PixelFormat format,
+                                            uint32_t layerCount, uint64_t usage,
+                                            uint32_t stride) const {
+    IMapper::BufferDescriptorInfo descriptorInfo;
+    sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+    auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride);
+
+    return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError);
+}
+
+void Gralloc3Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                                      uint32_t* outNumInts) const {
+    *outNumFds = uint32_t(bufferHandle->numFds);
+    *outNumInts = uint32_t(bufferHandle->numInts);
+
+    Error error;
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+    auto ret = mMapper->getTransportSize(buffer,
+                                         [&](const auto& tmpError, const auto& tmpNumFds,
+                                             const auto& tmpNumInts) {
+                                             error = tmpError;
+                                             if (error != Error::NONE) {
+                                                 return;
+                                             }
+                                             *outNumFds = tmpNumFds;
+                                             *outNumInts = tmpNumInts;
+                                         });
+
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error);
+}
+
+status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                              int32_t* outBytesPerStride) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    IMapper::Rect accessRegion = sGralloc3Rect(bounds);
+
+    // put acquireFence in a hidl_handle
+    hardware::hidl_handle acquireFenceHandle;
+    NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+    if (acquireFence >= 0) {
+        auto h = native_handle_init(acquireFenceStorage, 1, 0);
+        h->data[0] = acquireFence;
+        acquireFenceHandle = h;
+    }
+
+    Error error;
+    auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
+                             [&](const auto& tmpError, const auto& tmpData,
+                                 const auto& tmpBytesPerPixel, const auto& tmpBytesPerStride) {
+                                 error = tmpError;
+                                 if (error != Error::NONE) {
+                                     return;
+                                 }
+                                 *outData = tmpData;
+                                 if (outBytesPerPixel) {
+                                     *outBytesPerPixel = tmpBytesPerPixel;
+                                 }
+                                 if (outBytesPerStride) {
+                                     *outBytesPerStride = tmpBytesPerStride;
+                                 }
+                             });
+
+    // we own acquireFence even on errors
+    if (acquireFence >= 0) {
+        close(acquireFence);
+    }
+
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error);
+
+    return static_cast<status_t>(error);
+}
+
+status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, android_ycbcr* ycbcr) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    IMapper::Rect accessRegion = sGralloc3Rect(bounds);
+
+    // put acquireFence in a hidl_handle
+    hardware::hidl_handle acquireFenceHandle;
+    NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+    if (acquireFence >= 0) {
+        auto h = native_handle_init(acquireFenceStorage, 1, 0);
+        h->data[0] = acquireFence;
+        acquireFenceHandle = h;
+    }
+
+    YCbCrLayout layout;
+    Error error;
+    auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle,
+                                  [&](const auto& tmpError, const auto& tmpLayout) {
+                                      error = tmpError;
+                                      if (error != Error::NONE) {
+                                          return;
+                                      }
+
+                                      layout = tmpLayout;
+                                  });
+
+    if (error == Error::NONE) {
+        ycbcr->y = layout.y;
+        ycbcr->cb = layout.cb;
+        ycbcr->cr = layout.cr;
+        ycbcr->ystride = static_cast<size_t>(layout.yStride);
+        ycbcr->cstride = static_cast<size_t>(layout.cStride);
+        ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
+    }
+
+    // we own acquireFence even on errors
+    if (acquireFence >= 0) {
+        close(acquireFence);
+    }
+
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+int Gralloc3Mapper::unlock(buffer_handle_t bufferHandle) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    int releaseFence = -1;
+    Error error;
+    auto ret = mMapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+
+        auto fenceHandle = tmpReleaseFence.getNativeHandle();
+        if (fenceHandle && fenceHandle->numFds == 1) {
+            int fd = dup(fenceHandle->data[0]);
+            if (fd >= 0) {
+                releaseFence = fd;
+            } else {
+                ALOGD("failed to dup unlock release fence");
+                sync_wait(fenceHandle->data[0], -1);
+            }
+        }
+    });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("unlock(%p) failed with %d", buffer, error);
+    }
+
+    return releaseFence;
+}
+
+Gralloc3Allocator::Gralloc3Allocator(const Gralloc3Mapper& mapper) : mMapper(mapper) {
+    mAllocator = IAllocator::getService();
+    if (mAllocator == nullptr) {
+        ALOGW("allocator 3.x is not supported");
+        return;
+    }
+}
+
+bool Gralloc3Allocator::isLoaded() const {
+    return mAllocator != nullptr;
+}
+
+std::string Gralloc3Allocator::dumpDebugInfo() const {
+    std::string debugInfo;
+
+    mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
+
+    return debugInfo;
+}
+
+status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
+                                     uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                                     uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+    IMapper::BufferDescriptorInfo descriptorInfo;
+    sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+
+    BufferDescriptor descriptor;
+    status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
+                                              static_cast<void*>(&descriptor));
+    if (error != NO_ERROR) {
+        return error;
+    }
+
+    auto ret = mAllocator->allocate(descriptor, bufferCount,
+                                    [&](const auto& tmpError, const auto& tmpStride,
+                                        const auto& tmpBuffers) {
+                                        error = static_cast<status_t>(tmpError);
+                                        if (tmpError != Error::NONE) {
+                                            return;
+                                        }
+
+                                        // import buffers
+                                        for (uint32_t i = 0; i < bufferCount; i++) {
+                                            error = mMapper.importBuffer(tmpBuffers[i],
+                                                                         &outBufferHandles[i]);
+                                            if (error != NO_ERROR) {
+                                                for (uint32_t j = 0; j < i; j++) {
+                                                    mMapper.freeBuffer(outBufferHandles[j]);
+                                                    outBufferHandles[j] = nullptr;
+                                                }
+                                                return;
+                                            }
+                                        }
+                                        *outStride = tmpStride;
+                                    });
+
+    // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
+    hardware::IPCThreadState::self()->flushCommands();
+
+    return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
+}
+
+} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 4aa9f62..15597eb 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -22,7 +22,10 @@
 
 #include <grallocusage/GrallocUsageConversion.h>
 
-#include <ui/DetachedBufferHandle.h>
+#ifndef LIBUI_IN_VNDK
+#include <ui/BufferHubBuffer.h>
+#endif // LIBUI_IN_VNDK
+
 #include <ui/Gralloc2.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
@@ -90,6 +93,22 @@
                                 inUsage, inStride);
 }
 
+#ifndef LIBUI_IN_VNDK
+GraphicBuffer::GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer) : GraphicBuffer() {
+    if (buffer == nullptr) {
+        mInitCheck = BAD_VALUE;
+        return;
+    }
+
+    mInitCheck = initWithHandle(buffer->DuplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE,
+                                buffer->desc().width, buffer->desc().height,
+                                static_cast<PixelFormat>(buffer->desc().format),
+                                buffer->desc().layers, buffer->desc().usage, buffer->desc().stride);
+    mBufferId = buffer->id();
+    mBufferHubBuffer = std::move(buffer);
+}
+#endif // LIBUI_IN_VNDK
+
 GraphicBuffer::~GraphicBuffer()
 {
     if (handle) {
@@ -217,15 +236,15 @@
     return NO_ERROR;
 }
 
-status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr)
-{
+status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr, int32_t* outBytesPerPixel,
+                             int32_t* outBytesPerStride) {
     const Rect lockBounds(width, height);
-    status_t res = lock(inUsage, lockBounds, vaddr);
+    status_t res = lock(inUsage, lockBounds, vaddr, outBytesPerPixel, outBytesPerStride);
     return res;
 }
 
-status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr)
-{
+status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr,
+                             int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
     if (rect.left < 0 || rect.right  > width ||
         rect.top  < 0 || rect.bottom > height) {
         ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
@@ -233,7 +252,10 @@
                 width, height);
         return BAD_VALUE;
     }
-    status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr);
+
+    status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr, outBytesPerPixel,
+                                          outBytesPerStride);
+
     return res;
 }
 
@@ -287,8 +309,10 @@
                 width, height);
         return BAD_VALUE;
     }
-    status_t res = getBufferMapper().lockAsync(handle, inProducerUsage,
-            inConsumerUsage, rect, vaddr, fenceFd);
+
+    int32_t bytesPerPixel, bytesPerStride;
+    status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, inConsumerUsage, rect,
+                                               vaddr, fenceFd, &bytesPerPixel, &bytesPerStride);
     return res;
 }
 
@@ -484,23 +508,11 @@
     return NO_ERROR;
 }
 
-bool GraphicBuffer::isDetachedBuffer() const {
-    return mDetachedBufferHandle && mDetachedBufferHandle->isValid();
+#ifndef LIBUI_IN_VNDK
+bool GraphicBuffer::isBufferHubBuffer() const {
+    return mBufferHubBuffer != nullptr;
 }
-
-status_t GraphicBuffer::setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> channel) {
-    if (isDetachedBuffer()) {
-        ALOGW("setDetachedBuffer: there is already a BufferHub channel associated with this "
-              "GraphicBuffer. Replacing the old one.");
-    }
-
-    mDetachedBufferHandle = std::move(channel);
-    return NO_ERROR;
-}
-
-std::unique_ptr<DetachedBufferHandle> GraphicBuffer::takeDetachedBufferHandle() {
-    return std::move(mDetachedBufferHandle);
-}
+#endif // LIBUI_IN_VNDK
 
 // ---------------------------------------------------------------------------
 
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index eaba1ed..5a67dc4 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -24,72 +24,73 @@
 
 #include <grallocusage/GrallocUsageConversion.h>
 
+#include <android-base/stringprintf.h>
 #include <log/log.h>
 #include <utils/Singleton.h>
-#include <utils/String8.h>
 #include <utils/Trace.h>
 
+#include <ui/Gralloc.h>
 #include <ui/Gralloc2.h>
+#include <ui/Gralloc3.h>
 #include <ui/GraphicBufferMapper.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
 
+using base::StringAppendF;
+
 ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferAllocator )
 
 Mutex GraphicBufferAllocator::sLock;
 KeyedVector<buffer_handle_t,
     GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
 
-GraphicBufferAllocator::GraphicBufferAllocator()
-  : mMapper(GraphicBufferMapper::getInstance()),
-    mAllocator(std::make_unique<Gralloc2::Allocator>(
-                mMapper.getGrallocMapper()))
-{
+GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) {
+    mAllocator = std::make_unique<const Gralloc3Allocator>(
+            reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper()));
+    if (!mAllocator->isLoaded()) {
+        mAllocator = std::make_unique<const Gralloc2Allocator>(
+                reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper()));
+    }
+
+    if (!mAllocator->isLoaded()) {
+        LOG_ALWAYS_FATAL("gralloc-allocator is missing");
+    }
 }
 
 GraphicBufferAllocator::~GraphicBufferAllocator() {}
 
-void GraphicBufferAllocator::dump(String8& result) const
-{
+void GraphicBufferAllocator::dump(std::string& result) const {
     Mutex::Autolock _l(sLock);
     KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
     size_t total = 0;
-    const size_t SIZE = 4096;
-    char buffer[SIZE];
-    snprintf(buffer, SIZE, "Allocated buffers:\n");
-    result.append(buffer);
+    result.append("Allocated buffers:\n");
     const size_t c = list.size();
     for (size_t i=0 ; i<c ; i++) {
         const alloc_rec_t& rec(list.valueAt(i));
         if (rec.size) {
-            snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64
-                    " | %s\n",
-                    list.keyAt(i), rec.size/1024.0,
-                    rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
-                    rec.usage, rec.requestorName.c_str());
+            StringAppendF(&result,
+                          "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n",
+                          list.keyAt(i), rec.size / 1024.0, rec.width, rec.stride, rec.height,
+                          rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
         } else {
-            snprintf(buffer, SIZE, "%10p: unknown     | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64
-                    " | %s\n",
-                    list.keyAt(i),
-                    rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
-                    rec.usage, rec.requestorName.c_str());
+            StringAppendF(&result,
+                          "%10p: unknown     | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n",
+                          list.keyAt(i), rec.width, rec.stride, rec.height, rec.layerCount,
+                          rec.format, rec.usage, rec.requestorName.c_str());
         }
-        result.append(buffer);
         total += rec.size;
     }
-    snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0);
-    result.append(buffer);
+    StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", total / 1024.0);
 
-    std::string deviceDump = mAllocator->dumpDebugInfo();
-    result.append(deviceDump.c_str(), deviceDump.size());
+    result.append(mAllocator->dumpDebugInfo());
 }
 
 void GraphicBufferAllocator::dumpToSystemLog()
 {
-    String8 s;
+    std::string s;
     GraphicBufferAllocator::getInstance().dump(s);
-    ALOGD("%s", s.string());
+    ALOGD("%s", s.c_str());
 }
 
 status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
@@ -108,15 +109,12 @@
     if (layerCount < 1)
         layerCount = 1;
 
-    Gralloc2::IMapper::BufferDescriptorInfo info = {};
-    info.width = width;
-    info.height = height;
-    info.layerCount = layerCount;
-    info.format = static_cast<Gralloc2::PixelFormat>(format);
-    info.usage = usage;
+    // TODO(b/72323293, b/72703005): Remove these invalid bits from callers
+    usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
 
-    Gralloc2::Error error = mAllocator->allocate(info, stride, handle);
-    if (error == Gralloc2::Error::NONE) {
+    status_t error =
+            mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle);
+    if (error == NO_ERROR) {
         Mutex::Autolock _l(sLock);
         KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
         uint32_t bpp = bytesPerPixel(format);
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 2d8e582..79c333f 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -32,7 +32,9 @@
 #include <utils/Log.h>
 #include <utils/Trace.h>
 
+#include <ui/Gralloc.h>
 #include <ui/Gralloc2.h>
+#include <ui/Gralloc3.h>
 #include <ui/GraphicBuffer.h>
 
 #include <system/graphics.h>
@@ -43,12 +45,19 @@
 ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper )
 
 void GraphicBufferMapper::preloadHal() {
-    Gralloc2::Mapper::preload();
+    Gralloc2Mapper::preload();
+    Gralloc3Mapper::preload();
 }
 
-GraphicBufferMapper::GraphicBufferMapper()
-  : mMapper(std::make_unique<const Gralloc2::Mapper>())
-{
+GraphicBufferMapper::GraphicBufferMapper() {
+    mMapper = std::make_unique<const Gralloc3Mapper>();
+    if (!mMapper->isLoaded()) {
+        mMapper = std::make_unique<const Gralloc2Mapper>();
+    }
+
+    if (!mMapper->isLoaded()) {
+        LOG_ALWAYS_FATAL("gralloc-mapper is missing");
+    }
 }
 
 status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
@@ -59,22 +68,15 @@
     ATRACE_CALL();
 
     buffer_handle_t bufferHandle;
-    Gralloc2::Error error = mMapper->importBuffer(
-            hardware::hidl_handle(rawHandle), &bufferHandle);
-    if (error != Gralloc2::Error::NONE) {
+    status_t error = mMapper->importBuffer(hardware::hidl_handle(rawHandle), &bufferHandle);
+    if (error != NO_ERROR) {
         ALOGW("importBuffer(%p) failed: %d", rawHandle, error);
-        return static_cast<status_t>(error);
+        return error;
     }
 
-    Gralloc2::IMapper::BufferDescriptorInfo info = {};
-    info.width = width;
-    info.height = height;
-    info.layerCount = layerCount;
-    info.format = static_cast<Gralloc2::PixelFormat>(format);
-    info.usage = usage;
-
-    error = mMapper->validateBufferSize(bufferHandle, info, stride);
-    if (error != Gralloc2::Error::NONE) {
+    error = mMapper->validateBufferSize(bufferHandle, width, height, format, layerCount, usage,
+                                        stride);
+    if (error != NO_ERROR) {
         ALOGE("validateBufferSize(%p) failed: %d", rawHandle, error);
         freeBuffer(bufferHandle);
         return static_cast<status_t>(error);
@@ -100,19 +102,10 @@
     return NO_ERROR;
 }
 
-static inline Gralloc2::IMapper::Rect asGralloc2Rect(const Rect& rect) {
-    Gralloc2::IMapper::Rect outRect{};
-    outRect.left = rect.left;
-    outRect.top = rect.top;
-    outRect.width = rect.width();
-    outRect.height = rect.height();
-    return outRect;
-}
-
-status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage,
-        const Rect& bounds, void** vaddr)
-{
-    return lockAsync(handle, usage, bounds, vaddr, -1);
+status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds,
+                                   void** vaddr, int32_t* outBytesPerPixel,
+                                   int32_t* outBytesPerStride) {
+    return lockAsync(handle, usage, bounds, vaddr, -1, outBytesPerPixel, outBytesPerStride);
 }
 
 status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage,
@@ -132,27 +125,23 @@
     return error;
 }
 
-status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
-        uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd)
-{
-    return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd);
+status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds,
+                                        void** vaddr, int fenceFd, int32_t* outBytesPerPixel,
+                                        int32_t* outBytesPerStride) {
+    return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd, outBytesPerPixel,
+                     outBytesPerStride);
 }
 
-status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
-        uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
-        void** vaddr, int fenceFd)
-{
+status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage,
+                                        uint64_t consumerUsage, const Rect& bounds, void** vaddr,
+                                        int fenceFd, int32_t* outBytesPerPixel,
+                                        int32_t* outBytesPerStride) {
     ATRACE_CALL();
 
     const uint64_t usage = static_cast<uint64_t>(
             android_convertGralloc1To0Usage(producerUsage, consumerUsage));
-    Gralloc2::Error error = mMapper->lock(handle, usage,
-            asGralloc2Rect(bounds), fenceFd, vaddr);
-
-    ALOGW_IF(error != Gralloc2::Error::NONE, "lock(%p, ...) failed: %d",
-            handle, error);
-
-    return static_cast<status_t>(error);
+    return mMapper->lock(handle, usage, bounds, fenceFd, vaddr, outBytesPerPixel,
+                         outBytesPerStride);
 }
 
 status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle,
@@ -160,19 +149,7 @@
 {
     ATRACE_CALL();
 
-    Gralloc2::YCbCrLayout layout;
-    Gralloc2::Error error = mMapper->lock(handle, usage,
-            asGralloc2Rect(bounds), fenceFd, &layout);
-    if (error == Gralloc2::Error::NONE) {
-        ycbcr->y = layout.y;
-        ycbcr->cb = layout.cb;
-        ycbcr->cr = layout.cr;
-        ycbcr->ystride = static_cast<size_t>(layout.yStride);
-        ycbcr->cstride = static_cast<size_t>(layout.cStride);
-        ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
-    }
-
-    return static_cast<status_t>(error);
+    return mMapper->lock(handle, usage, bounds, fenceFd, ycbcr);
 }
 
 status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd)
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index d8702e5..13fed3a 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -72,6 +72,14 @@
     return *this;
 }
 
+Rect& Rect::inset(int32_t _left, int32_t _top, int32_t _right, int32_t _bottom) {
+    this->left += _left;
+    this->top += _top;
+    this->right -= _right;
+    this->bottom -= _bottom;
+    return *this;
+}
+
 const Rect Rect::operator +(const Point& rhs) const {
     const Rect result(left + rhs.x, top + rhs.y, right + rhs.x, bottom + rhs.y);
     return result;
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index fe4ae6c..3bd3748 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -19,8 +19,9 @@
 #include <inttypes.h>
 #include <limits.h>
 
+#include <android-base/stringprintf.h>
+
 #include <utils/Log.h>
-#include <utils/String8.h>
 #include <utils/CallStack.h>
 
 #include <ui/Rect.h>
@@ -41,6 +42,8 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
+using base::StringAppendF;
+
 enum {
     op_nand = region_operator<Rect>::op_nand,
     op_and  = region_operator<Rect>::op_and,
@@ -325,6 +328,20 @@
     return *this;
 }
 
+Region& Region::scaleSelf(float sx, float sy) {
+    size_t count = mStorage.size();
+    Rect* rects = mStorage.editArray();
+    while (count) {
+        rects->left = static_cast<int32_t>(rects->left * sx + 0.5f);
+        rects->right = static_cast<int32_t>(rects->right * sx + 0.5f);
+        rects->top = static_cast<int32_t>(rects->top * sy + 0.5f);
+        rects->bottom = static_cast<int32_t>(rects->bottom * sy + 0.5f);
+        rects++;
+        count--;
+    }
+    return *this;
+}
+
 // ----------------------------------------------------------------------------
 
 const Region Region::merge(const Rect& rhs) const {
@@ -854,16 +871,14 @@
 
 // ----------------------------------------------------------------------------
 
-void Region::dump(String8& out, const char* what, uint32_t /* flags */) const
-{
+void Region::dump(std::string& out, const char* what, uint32_t /* flags */) const {
     const_iterator head = begin();
     const_iterator const tail = end();
 
-    out.appendFormat("  Region %s (this=%p, count=%" PRIdPTR ")\n",
-            what, this, tail - head);
+    StringAppendF(&out, "  Region %s (this=%p, count=%" PRIdPTR ")\n", what, this, tail - head);
     while (head != tail) {
-        out.appendFormat("    [%3d, %3d, %3d, %3d]\n", head->left, head->top,
-                head->right, head->bottom);
+        StringAppendF(&out, "    [%3d, %3d, %3d, %3d]\n", head->left, head->top, head->right,
+                      head->bottom);
         ++head;
     }
 }
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/ui/Size.cpp
similarity index 63%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/ui/Size.cpp
index e6ac6bf..d2996d1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/ui/Size.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -14,14 +14,11 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <ui/Size.h>
 
-namespace android {
-namespace mock {
+namespace android::ui {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+const Size Size::INVALID{-1, -1};
+const Size Size::EMPTY{0, 0};
 
-} // namespace mock
-} // namespace android
+} // namespace android::ui
diff --git a/services/surfaceflinger/Transform.cpp b/libs/ui/Transform.cpp
similarity index 74%
rename from services/surfaceflinger/Transform.cpp
rename to libs/ui/Transform.cpp
index e05ed53..d13942d 100644
--- a/services/surfaceflinger/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -16,18 +16,14 @@
 
 #include <math.h>
 
+#include <android-base/stringprintf.h>
 #include <cutils/compiler.h>
-#include <utils/String8.h>
 #include <ui/Region.h>
-
-#include "Transform.h"
-#include "clz.h"
-
-// ---------------------------------------------------------------------------
+#include <ui/Transform.h>
+#include <utils/String8.h>
 
 namespace android {
-
-// ---------------------------------------------------------------------------
+namespace ui {
 
 Transform::Transform() {
     reset();
@@ -41,8 +37,7 @@
     set(orientation, 0, 0);
 }
 
-Transform::~Transform() {
-}
+Transform::~Transform() = default;
 
 static const float EPSILON = 0.0f;
 
@@ -67,7 +62,7 @@
     const mat33& A(mMatrix);
     const mat33& B(rhs.mMatrix);
           mat33& D(r.mMatrix);
-    for (int i=0 ; i<3 ; i++) {
+    for (size_t i = 0; i < 3; i++) {
         const float v0 = A[0][i];
         const float v1 = A[1][i];
         const float v2 = A[2][i];
@@ -83,6 +78,12 @@
     return r;
 }
 
+Transform& Transform::operator=(const Transform& other) {
+    mMatrix = other.mMatrix;
+    mType = other.mType;
+    return *this;
+}
+
 const vec3& Transform::operator [] (size_t i) const {
     return mMatrix[i];
 }
@@ -95,12 +96,20 @@
     return mMatrix[2][1];
 }
 
+float Transform::sx() const {
+    return mMatrix[0][0];
+}
+
+float Transform::sy() const {
+    return mMatrix[1][1];
+}
+
 void Transform::reset() {
     mType = IDENTITY;
-    for(int i=0 ; i<3 ; i++) {
+    for(size_t i = 0; i < 3; i++) {
         vec3& v(mMatrix[i]);
-        for (int j=0 ; j<3 ; j++)
-            v[j] = ((i==j) ? 1.0f : 0.0f);
+        for (size_t j = 0; j < 3; j++)
+            v[j] = ((i == j) ? 1.0f : 0.0f);
     }
 }
 
@@ -137,7 +146,7 @@
     Transform H, V, R;
     if (flags & ROT_90) {
         // w & h are inverted when rotating by 90 degrees
-        swap(w, h);
+        std::swap(w, h);
     }
 
     if (flags & FLIP_H) {
@@ -210,15 +219,15 @@
     rb = transform(rb);
 
     if (roundOutwards) {
-        r.left   = floorf(min(lt[0], rt[0], lb[0], rb[0]));
-        r.top    = floorf(min(lt[1], rt[1], lb[1], rb[1]));
-        r.right  = ceilf(max(lt[0], rt[0], lb[0], rb[0]));
-        r.bottom = ceilf(max(lt[1], rt[1], lb[1], rb[1]));
+        r.left   = static_cast<int32_t>(floorf(std::min({lt[0], rt[0], lb[0], rb[0]})));
+        r.top    = static_cast<int32_t>(floorf(std::min({lt[1], rt[1], lb[1], rb[1]})));
+        r.right  = static_cast<int32_t>(ceilf(std::max({lt[0], rt[0], lb[0], rb[0]})));
+        r.bottom = static_cast<int32_t>(ceilf(std::max({lt[1], rt[1], lb[1], rb[1]})));
     } else {
-        r.left   = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
-        r.top    = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
-        r.right  = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
-        r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
+        r.left   = static_cast<int32_t>(floorf(std::min({lt[0], rt[0], lb[0], rb[0]}) + 0.5f));
+        r.top    = static_cast<int32_t>(floorf(std::min({lt[1], rt[1], lb[1], rb[1]}) + 0.5f));
+        r.right  = static_cast<int32_t>(floorf(std::max({lt[0], rt[0], lb[0], rb[0]}) + 0.5f));
+        r.bottom = static_cast<int32_t>(floorf(std::max({lt[1], rt[1], lb[1], rb[1]}) + 0.5f));
     }
 
     return r;
@@ -237,10 +246,10 @@
     rb = transform(rb);
 
     FloatRect r;
-    r.left = min(lt[0], rt[0], lb[0], rb[0]);
-    r.top = min(lt[1], rt[1], lb[1], rb[1]);
-    r.right = max(lt[0], rt[0], lb[0], rb[0]);
-    r.bottom = max(lt[1], rt[1], lb[1], rb[1]);
+    r.left = std::min({lt[0], rt[0], lb[0], rb[0]});
+    r.top = std::min({lt[1], rt[1], lb[1], rb[1]});
+    r.right = std::max({lt[0], rt[0], lb[0], rb[0]});
+    r.bottom = std::max({lt[1], rt[1], lb[1], rb[1]});
 
     return r;
 }
@@ -259,8 +268,8 @@
             out.set(transform(reg.bounds()));
         }
     } else {
-        int xpos = floorf(tx() + 0.5f);
-        int ypos = floorf(ty() + 0.5f);
+        int xpos = static_cast<int>(floorf(tx() + 0.5f));
+        int ypos = static_cast<int>(floorf(ty() + 0.5f));
         out = reg.translate(xpos, ypos);
     }
     return out;
@@ -343,7 +352,7 @@
         const float x = M[2][0];
         const float y = M[2][1];
 
-        const float idet = 1.0 / (a*d - b*c);
+        const float idet = 1.0f / (a*d - b*c);
         result.mMatrix[0][0] =  d*idet;
         result.mMatrix[0][1] = -c*idet;
         result.mMatrix[1][0] = -b*idet;
@@ -372,60 +381,48 @@
     return (getOrientation() & ROT_INVALID) ? false : true;
 }
 
-void Transform::dump(const char* name) const
-{
-    type(); // updates the type
+void Transform::dump(std::string& out, const char* name) const {
+    using android::base::StringAppendF;
 
-    String8 flags, type;
-    const mat33& m(mMatrix);
-    uint32_t orient = mType >> 8;
+    type(); // Ensure the information in mType is up to date
 
-    if (orient&ROT_INVALID) {
-        flags.append("ROT_INVALID ");
+    const uint32_t type = mType;
+    const uint32_t orient = type >> 8;
+
+    StringAppendF(&out, "%s 0x%08x (", name, orient);
+
+    if (orient & ROT_INVALID) {
+        out.append("ROT_INVALID ");
     } else {
-        if (orient&ROT_90) {
-            flags.append("ROT_90 ");
+        if (orient & ROT_90) {
+            out.append("ROT_90 ");
         } else {
-            flags.append("ROT_0 ");
+            out.append("ROT_0 ");
         }
-        if (orient&FLIP_V)
-            flags.append("FLIP_V ");
-        if (orient&FLIP_H)
-            flags.append("FLIP_H ");
+        if (orient & FLIP_V) out.append("FLIP_V ");
+        if (orient & FLIP_H) out.append("FLIP_H ");
     }
 
-    if (!(mType&(SCALE|ROTATE|TRANSLATE)))
-        type.append("IDENTITY ");
-    if (mType&SCALE)
-        type.append("SCALE ");
-    if (mType&ROTATE)
-        type.append("ROTATE ");
-    if (mType&TRANSLATE)
-        type.append("TRANSLATE ");
+    StringAppendF(&out, ") 0x%02x (", type);
 
-    ALOGD("%s 0x%08x (%s, %s)", name, mType, flags.string(), type.string());
-    ALOGD("%.4f  %.4f  %.4f", m[0][0], m[1][0], m[2][0]);
-    ALOGD("%.4f  %.4f  %.4f", m[0][1], m[1][1], m[2][1]);
-    ALOGD("%.4f  %.4f  %.4f", m[0][2], m[1][2], m[2][2]);
-}
+    if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY ");
+    if (type & SCALE) out.append("SCALE ");
+    if (type & ROTATE) out.append("ROTATE ");
+    if (type & TRANSLATE) out.append("TRANSLATE ");
 
-Transform::orientation_flags Transform::fromRotation(ISurfaceComposer::Rotation rotation) {
-    // Convert to surfaceflinger's internal rotation type.
-    switch (rotation) {
-        case ISurfaceComposer::eRotateNone:
-            return Transform::ROT_0;
-        case ISurfaceComposer::eRotate90:
-            return Transform::ROT_90;
-        case ISurfaceComposer::eRotate180:
-            return Transform::ROT_180;
-        case ISurfaceComposer::eRotate270:
-            return Transform::ROT_270;
-        default:
-            ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation);
-            return Transform::ROT_0;
+    out.append(")\n");
+
+    for (size_t i = 0; i < 3; i++) {
+        StringAppendF(&out, "    %.4f  %.4f  %.4f\n", static_cast<double>(mMatrix[0][i]),
+                      static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i]));
     }
 }
 
-// ---------------------------------------------------------------------------
+void Transform::dump(const char* name) const {
+    std::string out;
+    dump(out, name);
+    ALOGD("%s", out.c_str());
+}
 
-}; // namespace android
+}  // namespace ui
+}  // namespace android
diff --git a/libs/ui/UiConfig.cpp b/libs/ui/UiConfig.cpp
index 7730690..0ac863d 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/ui/UiConfig.cpp
@@ -18,8 +18,7 @@
 
 namespace android {
 
-void appendUiConfigString(String8& configStr)
-{
+void appendUiConfigString(std::string& configStr) {
     static const char* config =
             " [libui]";
     configStr.append(config);
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
new file mode 100644
index 0000000..0b6d75a
--- /dev/null
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BUFFER_HUB_BUFFER_H_
+#define ANDROID_BUFFER_HUB_BUFFER_H_
+
+#include <android/frameworks/bufferhub/1.0/IBufferClient.h>
+#include <android/hardware_buffer.h>
+#include <cutils/native_handle.h>
+#include <ui/BufferHubDefs.h>
+#include <ui/BufferHubEventFd.h>
+#include <ui/BufferHubMetadata.h>
+
+namespace android {
+
+class BufferHubBuffer {
+public:
+    // Allocates a standalone BufferHubBuffer.
+    static std::unique_ptr<BufferHubBuffer> Create(uint32_t width, uint32_t height,
+                                                   uint32_t layerCount, uint32_t format,
+                                                   uint64_t usage, size_t userMetadataSize);
+
+    // Imports the given token to a BufferHubBuffer. Not taking ownership of the token. Caller
+    // should close and destroy the token after calling this function regardless of output.
+    // TODO(b/122543147): use a movable wrapper for token
+    static std::unique_ptr<BufferHubBuffer> Import(const native_handle_t* token);
+
+    BufferHubBuffer(const BufferHubBuffer&) = delete;
+    void operator=(const BufferHubBuffer&) = delete;
+
+    virtual ~BufferHubBuffer();
+
+    // Gets ID of the buffer client. All BufferHubBuffer clients derived from the same buffer in
+    // BufferHub share the same buffer id.
+    int id() const { return mId; }
+
+    // Returns the buffer description, which is guaranteed to be faithful values from BufferHub.
+    const AHardwareBuffer_Desc& desc() const { return mBufferDesc; }
+
+    // Duplicate the underlying Gralloc buffer handle. Caller is responsible to free the handle
+    // after use.
+    native_handle_t* DuplicateHandle() {
+        return native_handle_clone(mBufferHandle.getNativeHandle());
+    }
+
+    const BufferHubEventFd& eventFd() const { return mEventFd; }
+
+    // Returns the current value of MetadataHeader::buffer_state.
+    uint32_t buffer_state() const { return buffer_state_->load(std::memory_order_acquire); }
+
+    // A state mask which is unique to a buffer hub client among all its siblings sharing the same
+    // concrete graphic buffer.
+    uint32_t client_state_mask() const { return mClientStateMask; }
+
+    size_t user_metadata_size() const { return mMetadata.user_metadata_size(); }
+
+    // Returns true if the BufferClient is still alive.
+    bool IsConnected() const { return mBufferClient->ping().isOk(); }
+
+    // Returns true if the buffer is valid: non-null buffer handle, valid id, valid client bit mask,
+    // valid metadata and valid buffer client
+    bool IsValid() const;
+
+    // Gains the buffer for exclusive write permission. Read permission is implied once a buffer is
+    // gained.
+    // The buffer can be gained as long as there is no other client in acquired or gained state.
+    int Gain();
+
+    // Posts the gained buffer for other buffer clients to use the buffer.
+    // The buffer can be posted iff the buffer state for this client is gained.
+    // After posting the buffer, this client is put to released state and does not have access to
+    // the buffer for this cycle of the usage of the buffer.
+    int Post();
+
+    // Acquires the buffer for shared read permission.
+    // The buffer can be acquired iff the buffer state for this client is posted.
+    int Acquire();
+
+    // Releases the buffer.
+    // The buffer can be released from any buffer state.
+    // After releasing the buffer, this client no longer have any permissions to the buffer for the
+    // current cycle of the usage of the buffer.
+    int Release();
+
+    // Returns whether the buffer is released by all active clients or not.
+    bool IsReleased() const;
+
+    // Creates a token that stands for this BufferHubBuffer client and could be used for Import to
+    // create another BufferHubBuffer. The new BufferHubBuffer will share the same underlying
+    // gralloc buffer and ashmem region for metadata. Note that the caller owns the token and
+    // should free it after use.
+    // Returns a valid token on success, nullptr on failure.
+    // TODO(b/122543147): use a movable wrapper for token
+    native_handle_t* Duplicate();
+
+private:
+    BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
+                    uint64_t usage, size_t userMetadataSize);
+
+    BufferHubBuffer(const native_handle_t* token);
+
+    int initWithBufferTraits(const frameworks::bufferhub::V1_0::BufferTraits& bufferTraits);
+
+    // Global id for the buffer that is consistent across processes.
+    int mId = 0;
+
+    // Client state mask of this BufferHubBuffer object. It is unique amoung all
+    // clients/users of the buffer.
+    uint32_t mClientStateMask = 0U;
+
+    // Stores ground truth of the buffer.
+    AHardwareBuffer_Desc mBufferDesc;
+
+    // Wraps the gralloc buffer handle of this buffer.
+    hardware::hidl_handle mBufferHandle;
+
+    // Event fd used for signalling buffer state changes. Shared by all clients of the same buffer.
+    BufferHubEventFd mEventFd;
+
+    // An ashmem-based metadata object. The same shared memory are mapped to the
+    // bufferhubd daemon and all buffer clients.
+    BufferHubMetadata mMetadata;
+    // Shortcuts to the atomics inside the header of mMetadata.
+    std::atomic<uint32_t>* buffer_state_ = nullptr;
+    std::atomic<uint32_t>* fence_state_ = nullptr;
+    std::atomic<uint32_t>* active_clients_bit_mask_ = nullptr;
+
+    // HwBinder backend
+    sp<frameworks::bufferhub::V1_0::IBufferClient> mBufferClient;
+};
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_HUB_BUFFER_H_
diff --git a/libs/ui/include/ui/BufferHubDefs.h b/libs/ui/include/ui/BufferHubDefs.h
new file mode 100644
index 0000000..ff970cb
--- /dev/null
+++ b/libs/ui/include/ui/BufferHubDefs.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BUFFER_HUB_DEFS_H_
+#define ANDROID_BUFFER_HUB_DEFS_H_
+
+#include <atomic>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpacked"
+// TODO(b/118893702): remove dependency once DvrNativeBufferMetadata moved out of libdvr
+#include <dvr/dvr_api.h>
+#pragma clang diagnostic pop
+
+namespace android {
+
+namespace BufferHubDefs {
+
+// Single buffer clients (up to 16) ownership signal.
+// 32-bit atomic unsigned int.
+// Each client takes 2 bits. The first bit locates in the first 16 bits of
+// buffer_state; the second bit locates in the last 16 bits of buffer_state.
+// Client states:
+// Gained state 11. Exclusive write state.
+// Posted state 10.
+// Acquired state 01. Shared read state.
+// Released state 00.
+//
+//  MSB                        LSB
+//   |                          |
+//   v                          v
+// [C15|...|C1|C0|C15| ... |C1|C0]
+
+// Maximum number of clients a buffer can have.
+static constexpr int kMaxNumberOfClients = 16;
+
+// Definition of bit masks.
+//  MSB                            LSB
+//   | kHighBitsMask | kLowbitsMask |
+//   v               v              v
+// [b31|   ...   |b16|b15|   ...  |b0]
+
+// The location of lower 16 bits in the 32-bit buffer state.
+static constexpr uint32_t kLowbitsMask = (1U << kMaxNumberOfClients) - 1U;
+
+// The location of higher 16 bits in the 32-bit buffer state.
+static constexpr uint32_t kHighBitsMask = ~kLowbitsMask;
+
+// The client bit mask of the first client.
+static constexpr uint32_t kFirstClientBitMask = (1U << kMaxNumberOfClients) + 1U;
+
+// Returns true if any of the client is in gained state.
+static inline bool AnyClientGained(uint32_t state) {
+    uint32_t high_bits = state >> kMaxNumberOfClients;
+    uint32_t low_bits = state & kLowbitsMask;
+    return high_bits == low_bits && low_bits != 0U;
+}
+
+// Returns true if the input client is in gained state.
+static inline bool IsClientGained(uint32_t state, uint32_t client_bit_mask) {
+    return state == client_bit_mask;
+}
+
+// Returns true if any of the client is in posted state.
+static inline bool AnyClientPosted(uint32_t state) {
+    uint32_t high_bits = state >> kMaxNumberOfClients;
+    uint32_t low_bits = state & kLowbitsMask;
+    uint32_t posted_or_acquired = high_bits ^ low_bits;
+    return posted_or_acquired & high_bits;
+}
+
+// Returns true if the input client is in posted state.
+static inline bool IsClientPosted(uint32_t state, uint32_t client_bit_mask) {
+    uint32_t client_bits = state & client_bit_mask;
+    if (client_bits == 0U) return false;
+    uint32_t low_bits = client_bits & kLowbitsMask;
+    return low_bits == 0U;
+}
+
+// Return true if any of the client is in acquired state.
+static inline bool AnyClientAcquired(uint32_t state) {
+    uint32_t high_bits = state >> kMaxNumberOfClients;
+    uint32_t low_bits = state & kLowbitsMask;
+    uint32_t posted_or_acquired = high_bits ^ low_bits;
+    return posted_or_acquired & low_bits;
+}
+
+// Return true if the input client is in acquired state.
+static inline bool IsClientAcquired(uint32_t state, uint32_t client_bit_mask) {
+    uint32_t client_bits = state & client_bit_mask;
+    if (client_bits == 0U) return false;
+    uint32_t high_bits = client_bits & kHighBitsMask;
+    return high_bits == 0U;
+}
+
+// Returns true if the input client is in released state.
+static inline bool IsClientReleased(uint32_t state, uint32_t client_bit_mask) {
+    return (state & client_bit_mask) == 0U;
+}
+
+// Returns the next available buffer client's client_state_masks.
+// @params union_bits. Union of all existing clients' client_state_masks.
+static inline uint32_t FindNextAvailableClientStateMask(uint32_t union_bits) {
+    uint32_t low_union = union_bits & kLowbitsMask;
+    if (low_union == kLowbitsMask) return 0U;
+    uint32_t incremented = low_union + 1U;
+    uint32_t difference = incremented ^ low_union;
+    uint32_t new_low_bit = (difference + 1U) >> 1;
+    return new_low_bit + (new_low_bit << kMaxNumberOfClients);
+}
+
+struct __attribute__((aligned(8))) MetadataHeader {
+    // Internal data format, which can be updated as long as the size, padding and field alignment
+    // of the struct is consistent within the same ABI. As this part is subject for future updates,
+    // it's not stable cross Android version, so don't have it visible from outside of the Android
+    // platform (include Apps and vendor HAL).
+
+    // Every client takes up one bit from the higher 32 bits and one bit from the lower 32 bits in
+    // buffer_state.
+    std::atomic<uint32_t> buffer_state;
+
+    // Every client takes up one bit in fence_state. Only the lower 32 bits are valid. The upper 32
+    // bits are there for easier manipulation, but the value should be ignored.
+    std::atomic<uint32_t> fence_state;
+
+    // Every client takes up one bit from the higher 32 bits and one bit from the lower 32 bits in
+    // active_clients_bit_mask.
+    std::atomic<uint32_t> active_clients_bit_mask;
+
+    // Explicit padding 4 bytes.
+    uint32_t padding;
+
+    // The index of the buffer queue where the buffer belongs to.
+    uint64_t queue_index;
+
+    // Public data format, which should be updated with caution. See more details in dvr_api.h
+    DvrNativeBufferMetadata metadata;
+};
+
+static_assert(sizeof(MetadataHeader) == 128, "Unexpected MetadataHeader size");
+static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader);
+
+/**
+ * android.frameworks.bufferhub@1.0::BufferTraits.bufferInfo is an opaque handle. See
+ * https://cs.corp.google.com/android/frameworks/hardware/interfaces/bufferhub/1.0/types.hal for
+ * more details about android.frameworks.bufferhub@1.0::BufferTraits.
+ *
+ * This definition could be changed, but implementation of BufferHubService::buildBufferInfo
+ * (frameworks/native/services/bufferhub), VtsHalBufferHubV1_0TargetTest
+ * (frameworks/hardware/interfaces/bufferhub) and BufferHubBuffer::readBufferTraits (libui) will
+ * also need to be updated.
+ *
+ * It's definition should follow the following format:
+ * {
+ *   NumFds = 2,
+ *   NumInts = 3,
+ *   data[0] = Ashmem fd for BufferHubMetadata,
+ *   data[1] = event fd,
+ *   data[2] = buffer id,
+ *   data[3] = client state bit mask,
+ *   data[4] = user metadata size,
+ * }
+ */
+static constexpr int kBufferInfoNumFds = 2;
+static constexpr int kBufferInfoNumInts = 3;
+
+} // namespace BufferHubDefs
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_HUB_DEFS_H_
diff --git a/libs/ui/include/ui/BufferHubEventFd.h b/libs/ui/include/ui/BufferHubEventFd.h
new file mode 100644
index 0000000..8772304
--- /dev/null
+++ b/libs/ui/include/ui/BufferHubEventFd.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BUFFER_HUB_EVENT_FD_H_
+#define ANDROID_BUFFER_HUB_EVENT_FD_H_
+
+#include <android-base/unique_fd.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class BufferHubEventFd {
+public:
+    /**
+     * Constructs a valid event fd.
+     */
+    BufferHubEventFd();
+
+    /**
+     * Constructs from a valid event fd. Caller is responsible for the validity of the fd. Takes
+     * ownership.
+     */
+    BufferHubEventFd(int fd);
+
+    /**
+     * Returns whether this BufferHubEventFd holds a valid event_fd.
+     */
+    bool isValid() const { return get() >= 0; }
+
+    /**
+     * Returns the fd number of the BufferHubEventFd object. Note that there is no ownership
+     * transfer.
+     */
+    int get() const { return mFd.get(); }
+
+    /**
+     * Signals the eventfd.
+     */
+    status_t signal() const;
+
+    /**
+     * Clears the signal from this eventfd if it is signaled.
+     */
+    status_t clear() const;
+
+private:
+    base::unique_fd mFd;
+};
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_HUB_EVENT_FD_H_
diff --git a/libs/ui/include/ui/BufferHubMetadata.h b/libs/ui/include/ui/BufferHubMetadata.h
new file mode 100644
index 0000000..2121894
--- /dev/null
+++ b/libs/ui/include/ui/BufferHubMetadata.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BUFFER_HUB_METADATA_H_
+#define ANDROID_BUFFER_HUB_METADATA_H_
+
+#include <android-base/unique_fd.h>
+#include <ui/BufferHubDefs.h>
+
+namespace android {
+
+namespace {
+using base::unique_fd;
+} // namespace
+
+class BufferHubMetadata {
+public:
+    // Creates a new BufferHubMetadata backed by an ashmem region.
+    //
+    // @param userMetadataSize Size in bytes of the user defined metadata. The entire metadata
+    //        shared memory region to be allocated is the size of canonical
+    //        BufferHubDefs::MetadataHeader plus userMetadataSize.
+    static BufferHubMetadata Create(size_t userMetadataSize);
+
+    // Imports an existing BufferHubMetadata from an ashmem FD.
+    //
+    // @param ashmemFd Ashmem file descriptor representing an ashmem region.
+    static BufferHubMetadata Import(unique_fd ashmemFd);
+
+    BufferHubMetadata() = default;
+
+    BufferHubMetadata(BufferHubMetadata&& other) { *this = std::move(other); }
+
+    ~BufferHubMetadata();
+
+    BufferHubMetadata& operator=(BufferHubMetadata&& other) {
+        if (this != &other) {
+            mUserMetadataSize = other.mUserMetadataSize;
+            other.mUserMetadataSize = 0;
+
+            mAshmemFd = std::move(other.mAshmemFd);
+
+            // The old raw mMetadataHeader pointer must be cleared, otherwise the destructor will
+            // automatically mummap() the shared memory.
+            mMetadataHeader = other.mMetadataHeader;
+            other.mMetadataHeader = nullptr;
+        }
+        return *this;
+    }
+
+    // Returns true if the metadata is valid, i.e. the metadata has a valid ashmem fd and the ashmem
+    // has been mapped into virtual address space.
+    bool IsValid() const { return mAshmemFd.get() != -1 && mMetadataHeader != nullptr; }
+
+    size_t user_metadata_size() const { return mUserMetadataSize; }
+    size_t metadata_size() const { return mUserMetadataSize + BufferHubDefs::kMetadataHeaderSize; }
+
+    const unique_fd& ashmem_fd() const { return mAshmemFd; }
+    BufferHubDefs::MetadataHeader* metadata_header() { return mMetadataHeader; }
+
+private:
+    BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd,
+                      BufferHubDefs::MetadataHeader* metadataHeader);
+
+    BufferHubMetadata(const BufferHubMetadata&) = delete;
+    void operator=(const BufferHubMetadata&) = delete;
+
+    size_t mUserMetadataSize = 0;
+    unique_fd mAshmemFd;
+    BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr;
+};
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_HUB_METADATA_H_
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/ui/include/ui/ConfigStoreTypes.h
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/ui/include/ui/ConfigStoreTypes.h
index e6ac6bf..37a2bd5 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/ui/include/ui/ConfigStoreTypes.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
 
+#include <android/hardware/configstore/1.2/types.h>
+
+// android::ui::* in this header file will alias different types as
+// the HIDL interface is updated.
 namespace android {
-namespace mock {
+namespace ui {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+using android::hardware::configstore::V1_2::DisplayPrimaries;
 
-} // namespace mock
-} // namespace android
+}  // namespace ui
+}  // namespace android
diff --git a/libs/ui/include/ui/DetachedBufferHandle.h b/libs/ui/include/ui/DetachedBufferHandle.h
deleted file mode 100644
index f3c328d..0000000
--- a/libs/ui/include/ui/DetachedBufferHandle.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_DETACHED_BUFFER_HUB_HANDLE_H
-#define ANDROID_DETACHED_BUFFER_HUB_HANDLE_H
-
-#include <pdx/channel_handle.h>
-
-#include <memory>
-
-namespace android {
-
-// A wrapper that holds a pdx::LocalChannelHandle object. From the handle, a BufferHub buffer can be
-// created. Current implementation assumes that the underlying transport is using libpdx (thus
-// holding a pdx::LocalChannelHandle object), but future implementation can change it to a Binder
-// backend if ever needed.
-class DetachedBufferHandle {
-public:
-    static std::unique_ptr<DetachedBufferHandle> Create(pdx::LocalChannelHandle handle) {
-        return std::unique_ptr<DetachedBufferHandle>(new DetachedBufferHandle(std::move(handle)));
-    }
-
-    // Accessors to get or take the internal pdx::LocalChannelHandle.
-    pdx::LocalChannelHandle& handle() { return mHandle; }
-    const pdx::LocalChannelHandle& handle() const { return mHandle; }
-
-    // Returns whether the DetachedBufferHandle holds a BufferHub channel.
-    bool isValid() const { return mHandle.valid(); }
-
-private:
-    // Constructs a DetachedBufferHandle from a pdx::LocalChannelHandle.
-    explicit DetachedBufferHandle(pdx::LocalChannelHandle handle) : mHandle(std::move(handle)) {}
-
-    pdx::LocalChannelHandle mHandle;
-};
-
-} // namespace android
-
-#endif // ANDROID_DETACHED_BUFFER_HUB_HANDLE_H
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 94caf6b..8976d2d 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -35,6 +35,8 @@
     bool secure{false};
     nsecs_t appVsyncOffset{0};
     nsecs_t presentationDeadline{0};
+    uint32_t viewportW{0};
+    uint32_t viewportH{0};
 };
 
 /* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
diff --git a/libs/ui/include/ui/DisplayedFrameStats.h b/libs/ui/include/ui/DisplayedFrameStats.h
new file mode 100644
index 0000000..7a70ea1
--- /dev/null
+++ b/libs/ui/include/ui/DisplayedFrameStats.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+namespace android {
+
+struct DisplayedFrameStats {
+    /* The number of frames represented by this sample. */
+    uint64_t numFrames = 0;
+    /* A histogram counting how many times a pixel of a given value was displayed onscreen for
+     * FORMAT_COMPONENT_0. The buckets of the histogram are evenly weighted, the number of buckets
+     * is device specific. eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that
+     * 10 red pixels were displayed onscreen in range 0x00->0x3F, 6 red pixels
+     * were displayed onscreen in range 0x40->0x7F, etc.
+     */
+    std::vector<uint64_t> component_0_sample = {};
+    /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_1. */
+    std::vector<uint64_t> component_1_sample = {};
+    /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_2. */
+    std::vector<uint64_t> component_2_sample = {};
+    /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_3. */
+    std::vector<uint64_t> component_3_sample = {};
+};
+
+} // namespace android
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index 871fcf2..a5a1fcb 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -113,11 +113,6 @@
 
     void signalForTest(nsecs_t signalTime);
 
-    // Override new and delete since this needs 8-byte alignment, which
-    // is not guaranteed on x86.
-    static void* operator new(size_t nbytes) noexcept;
-    static void operator delete(void *p);
-
 private:
     // For tests only. If forceValidForTest is true, then getSignalTime will
     // never return SIGNAL_TIME_INVALID and isValid will always return true.
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 6a7479a..4cd9a0b 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -28,7 +28,7 @@
     float getHeight() const { return bottom - top; }
 
     FloatRect intersect(const FloatRect& other) const {
-        return {
+        FloatRect intersection = {
             // Inline to avoid tromping on other min/max defines or adding a
             // dependency on STL
             (left > other.left) ? left : other.left,
@@ -36,6 +36,10 @@
             (right < other.right) ? right : other.right,
             (bottom < other.bottom) ? bottom : other.bottom
         };
+        if (intersection.getWidth() < 0 || intersection.getHeight() < 0) {
+            return {0, 0, 0, 0};
+        }
+        return intersection;
     }
 
     float left = 0.0f;
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
new file mode 100644
index 0000000..1b8a930
--- /dev/null
+++ b/libs/ui/include/ui/Gralloc.h
@@ -0,0 +1,93 @@
+/*
+ * 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_UI_GRALLOC_H
+#define ANDROID_UI_GRALLOC_H
+
+#include <string>
+
+#include <hidl/HidlSupport.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+// A wrapper to IMapper
+class GrallocMapper {
+public:
+    virtual ~GrallocMapper();
+
+    virtual bool isLoaded() const = 0;
+
+    virtual status_t createDescriptor(void* bufferDescriptorInfo,
+                                      void* outBufferDescriptor) const = 0;
+
+    // Import a buffer that is from another HAL, another process, or is
+    // cloned.
+    //
+    // The returned handle must be freed with freeBuffer.
+    virtual status_t importBuffer(const hardware::hidl_handle& rawHandle,
+                                  buffer_handle_t* outBufferHandle) const = 0;
+
+    virtual void freeBuffer(buffer_handle_t bufferHandle) const = 0;
+
+    virtual status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+                                        uint32_t height, android::PixelFormat format,
+                                        uint32_t layerCount, uint64_t usage,
+                                        uint32_t stride) const = 0;
+
+    virtual void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                                  uint32_t* outNumInts) const = 0;
+
+    // The ownership of acquireFence is always transferred to the callee, even
+    // on errors.
+    virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                          int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                          int32_t* outBytesPerStride) const = 0;
+
+    // The ownership of acquireFence is always transferred to the callee, even
+    // on errors.
+    virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                          int acquireFence, android_ycbcr* ycbcr) const = 0;
+
+    // unlock returns a fence sync object (or -1) and the fence sync object is
+    // owned by the caller
+    virtual int unlock(buffer_handle_t bufferHandle) const = 0;
+};
+
+// A wrapper to IAllocator
+class GrallocAllocator {
+public:
+    virtual ~GrallocAllocator();
+
+    virtual bool isLoaded() const = 0;
+
+    virtual std::string dumpDebugInfo() const = 0;
+
+    /*
+     * The returned buffers are already imported and must not be imported
+     * again.  outBufferHandles must point to a space that can contain at
+     * least "bufferCount" buffer_handle_t.
+     */
+    virtual status_t allocate(uint32_t width, uint32_t height, PixelFormat format,
+                              uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                              uint32_t* outStride, buffer_handle_t* outBufferHandles) const = 0;
+};
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC_H
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 5a8dbda..4ef9b51 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -23,119 +23,72 @@
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <android/hardware/graphics/mapper/2.1/IMapper.h>
+#include <ui/Gralloc.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
 #include <utils/StrongPointer.h>
 
 namespace android {
 
-namespace Gralloc2 {
-
-using hardware::graphics::allocator::V2_0::IAllocator;
-using hardware::graphics::common::V1_1::BufferUsage;
-using hardware::graphics::common::V1_1::PixelFormat;
-using hardware::graphics::mapper::V2_1::IMapper;
-using hardware::graphics::mapper::V2_0::BufferDescriptor;
-using hardware::graphics::mapper::V2_0::Error;
-using hardware::graphics::mapper::V2_0::YCbCrLayout;
-
-// A wrapper to IMapper
-class Mapper {
+class Gralloc2Mapper : public GrallocMapper {
 public:
     static void preload();
 
-    Mapper();
+    Gralloc2Mapper();
 
-    Error createDescriptor(
-            const IMapper::BufferDescriptorInfo& descriptorInfo,
-            BufferDescriptor* outDescriptor) const;
+    bool isLoaded() const override;
 
-    // Import a buffer that is from another HAL, another process, or is
-    // cloned.
-    //
-    // The returned handle must be freed with freeBuffer.
-    Error importBuffer(const hardware::hidl_handle& rawHandle,
-            buffer_handle_t* outBufferHandle) const;
+    status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
 
-    void freeBuffer(buffer_handle_t bufferHandle) const;
+    status_t importBuffer(const hardware::hidl_handle& rawHandle,
+                          buffer_handle_t* outBufferHandle) const override;
 
-    Error validateBufferSize(buffer_handle_t bufferHandle,
-            const IMapper::BufferDescriptorInfo& descriptorInfo,
-            uint32_t stride) const;
+    void freeBuffer(buffer_handle_t bufferHandle) const override;
 
-    void getTransportSize(buffer_handle_t bufferHandle,
-            uint32_t* outNumFds, uint32_t* outNumInts) const;
+    status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
+                                android::PixelFormat format, uint32_t layerCount, uint64_t usage,
+                                uint32_t stride) const override;
 
-    // The ownership of acquireFence is always transferred to the callee, even
-    // on errors.
-    Error lock(buffer_handle_t bufferHandle, uint64_t usage,
-            const IMapper::Rect& accessRegion,
-            int acquireFence, void** outData) const;
+    void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                          uint32_t* outNumInts) const override;
 
-    // The ownership of acquireFence is always transferred to the callee, even
-    // on errors.
-    Error lock(buffer_handle_t bufferHandle, uint64_t usage,
-            const IMapper::Rect& accessRegion,
-            int acquireFence, YCbCrLayout* outLayout) const;
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                  int32_t* outBytesPerStride) const override;
 
-    // unlock returns a fence sync object (or -1) and the fence sync object is
-    // owned by the caller
-    int unlock(buffer_handle_t bufferHandle) const;
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, android_ycbcr* ycbcr) const override;
+
+    int unlock(buffer_handle_t bufferHandle) const override;
 
 private:
     // Determines whether the passed info is compatible with the mapper.
-    Error validateBufferDescriptorInfo(
-            const IMapper::BufferDescriptorInfo& descriptorInfo) const;
+    status_t validateBufferDescriptorInfo(
+            hardware::graphics::mapper::V2_1::IMapper::BufferDescriptorInfo* descriptorInfo) const;
 
     sp<hardware::graphics::mapper::V2_0::IMapper> mMapper;
-    sp<IMapper> mMapperV2_1;
+    sp<hardware::graphics::mapper::V2_1::IMapper> mMapperV2_1;
 };
 
-// A wrapper to IAllocator
-class Allocator {
+class Gralloc2Allocator : public GrallocAllocator {
 public:
     // An allocator relies on a mapper, and that mapper must be alive at all
     // time.
-    Allocator(const Mapper& mapper);
+    Gralloc2Allocator(const Gralloc2Mapper& mapper);
 
-    std::string dumpDebugInfo() const;
+    bool isLoaded() const override;
 
-    /*
-     * The returned buffers are already imported and must not be imported
-     * again.  outBufferHandles must point to a space that can contain at
-     * least "count" buffer_handle_t.
-     */
-    Error allocate(BufferDescriptor descriptor, uint32_t count,
-            uint32_t* outStride, buffer_handle_t* outBufferHandles) const;
+    std::string dumpDebugInfo() const override;
 
-    Error allocate(BufferDescriptor descriptor,
-            uint32_t* outStride, buffer_handle_t* outBufferHandle) const
-    {
-        return allocate(descriptor, 1, outStride, outBufferHandle);
-    }
-
-    Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t count,
-            uint32_t* outStride, buffer_handle_t* outBufferHandles) const
-    {
-        BufferDescriptor descriptor;
-        Error error = mMapper.createDescriptor(descriptorInfo, &descriptor);
-        if (error == Error::NONE) {
-            error = allocate(descriptor, count, outStride, outBufferHandles);
-        }
-        return error;
-    }
-
-    Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
-            uint32_t* outStride, buffer_handle_t* outBufferHandle) const
-    {
-        return allocate(descriptorInfo, 1, outStride, outBufferHandle);
-    }
+    status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+                      uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+                      buffer_handle_t* outBufferHandles) const override;
 
 private:
-    const Mapper& mMapper;
-    sp<IAllocator> mAllocator;
+    const Gralloc2Mapper& mMapper;
+    sp<hardware::graphics::allocator::V2_0::IAllocator> mAllocator;
 };
 
-} // namespace Gralloc2
-
 } // namespace android
 
 #endif // ANDROID_UI_GRALLOC2_H
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
new file mode 100644
index 0000000..879e96e
--- /dev/null
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -0,0 +1,92 @@
+/*
+ * 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_UI_GRALLOC3_H
+#define ANDROID_UI_GRALLOC3_H
+
+#include <string>
+
+#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
+#include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <ui/Gralloc.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Gralloc3Mapper : public GrallocMapper {
+public:
+    static void preload();
+
+    Gralloc3Mapper();
+
+    bool isLoaded() const override;
+
+    status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
+
+    status_t importBuffer(const hardware::hidl_handle& rawHandle,
+                          buffer_handle_t* outBufferHandle) const override;
+
+    void freeBuffer(buffer_handle_t bufferHandle) const override;
+
+    status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
+                                android::PixelFormat format, uint32_t layerCount, uint64_t usage,
+                                uint32_t stride) const override;
+
+    void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                          uint32_t* outNumInts) const override;
+
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                  int32_t* outBytesPerStride) const override;
+
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, android_ycbcr* ycbcr) const override;
+
+    int unlock(buffer_handle_t bufferHandle) const override;
+
+private:
+    // Determines whether the passed info is compatible with the mapper.
+    status_t validateBufferDescriptorInfo(
+            hardware::graphics::mapper::V3_0::IMapper::BufferDescriptorInfo* descriptorInfo) const;
+
+    sp<hardware::graphics::mapper::V3_0::IMapper> mMapper;
+};
+
+class Gralloc3Allocator : public GrallocAllocator {
+public:
+    // An allocator relies on a mapper, and that mapper must be alive at all
+    // time.
+    Gralloc3Allocator(const Gralloc3Mapper& mapper);
+
+    bool isLoaded() const override;
+
+    std::string dumpDebugInfo() const override;
+
+    status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+                      uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+                      buffer_handle_t* outBufferHandles) const override;
+
+private:
+    const Gralloc3Mapper& mMapper;
+    sp<hardware::graphics::allocator::V3_0::IAllocator> mAllocator;
+};
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC3_H
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 315db11..18a27a0 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -34,7 +34,10 @@
 
 namespace android {
 
-class DetachedBufferHandle;
+#ifndef LIBUI_IN_VNDK
+class BufferHubBuffer;
+#endif // LIBUI_IN_VNDK
+
 class GraphicBufferMapper;
 
 // ===========================================================================
@@ -134,6 +137,11 @@
     GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
             uint32_t inUsage, std::string requestorName = "<Unknown>");
 
+#ifndef LIBUI_IN_VNDK
+    // Create a GraphicBuffer from an existing BufferHubBuffer.
+    GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer);
+#endif // LIBUI_IN_VNDK
+
     // return status
     status_t initCheck() const;
 
@@ -145,6 +153,7 @@
     uint32_t getLayerCount() const      { return static_cast<uint32_t>(layerCount); }
     Rect getBounds() const              { return Rect(width, height); }
     uint64_t getId() const              { return mId; }
+    int32_t getBufferId() const { return mBufferId; }
 
     uint32_t getGenerationNumber() const { return mGenerationNumber; }
     void setGenerationNumber(uint32_t generation) {
@@ -160,8 +169,12 @@
     bool needsReallocation(uint32_t inWidth, uint32_t inHeight,
             PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage);
 
-    status_t lock(uint32_t inUsage, void** vaddr);
-    status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr);
+    // For the following two lock functions, if bytesPerStride or bytesPerPixel
+    // are unknown or variable, -1 will be returned
+    status_t lock(uint32_t inUsage, void** vaddr, int32_t* outBytesPerPixel = nullptr,
+                  int32_t* outBytesPerStride = nullptr);
+    status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr,
+                  int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
     // For HAL_PIXEL_FORMAT_YCbCr_420_888
     status_t lockYCbCr(uint32_t inUsage, android_ycbcr *ycbcr);
     status_t lockYCbCr(uint32_t inUsage, const Rect& rect,
@@ -189,10 +202,10 @@
     status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
     status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
 
-    // Sets and takes DetachedBuffer. Should only be called from BufferHub.
-    bool isDetachedBuffer() const;
-    status_t setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> detachedBuffer);
-    std::unique_ptr<DetachedBufferHandle> takeDetachedBufferHandle();
+#ifndef LIBUI_IN_VNDK
+    // Returns whether this GraphicBuffer is backed by BufferHubBuffer.
+    bool isBufferHubBuffer() const;
+#endif // LIBUI_IN_VNDK
 
 private:
     ~GraphicBuffer();
@@ -239,21 +252,21 @@
 
     uint64_t mId;
 
+    // System unique buffer ID. Note that this is different from mId, which is process unique. For
+    // GraphicBuffer backed by BufferHub, the mBufferId is a system unique identifier that stays the
+    // same cross process for the same chunck of underlying memory. Also note that this only applies
+    // to GraphicBuffers that are backed by BufferHub.
+    int32_t mBufferId = -1;
+
     // Stores the generation number of this buffer. If this number does not
     // match the BufferQueue's internal generation number (set through
     // IGBP::setGenerationNumber), attempts to attach the buffer will fail.
     uint32_t mGenerationNumber;
 
-    // Stores a BufferHub handle that can be used to re-attach this GraphicBuffer back into a
-    // BufferHub producer/consumer set. In terms of GraphicBuffer's relationship with BufferHub,
-    // there are three different modes:
-    // 1. Legacy mode: GraphicBuffer is not backed by BufferHub and mDetachedBufferHandle must be
-    //    invalid.
-    // 2. Detached mode: GraphicBuffer is backed by BufferHub, but not part of a producer/consumer
-    //    set. In this mode, mDetachedBufferHandle must be valid.
-    // 3. Attached mode: GraphicBuffer is backed by BufferHub and it's part of a producer/consumer
-    //    set. In this mode, mDetachedBufferHandle must be invalid.
-    std::unique_ptr<DetachedBufferHandle> mDetachedBufferHandle;
+#ifndef LIBUI_IN_VNDK
+    // Stores a BufferHubBuffer that handles buffer signaling, identification.
+    std::unique_ptr<BufferHubBuffer> mBufferHubBuffer;
+#endif // LIBUI_IN_VNDK
 };
 
 }; // namespace android
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 14a865e..3a547b6 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -34,12 +34,8 @@
 
 namespace android {
 
-namespace Gralloc2 {
-class Allocator;
-}
-
+class GrallocAllocator;
 class GraphicBufferMapper;
-class String8;
 
 class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
 {
@@ -53,7 +49,7 @@
 
     status_t free(buffer_handle_t handle);
 
-    void dump(String8& res) const;
+    void dump(std::string& res) const;
     static void dumpToSystemLog();
 
 private:
@@ -76,7 +72,7 @@
     ~GraphicBufferAllocator();
 
     GraphicBufferMapper& mMapper;
-    const std::unique_ptr<const Gralloc2::Allocator> mAllocator;
+    std::unique_ptr<const GrallocAllocator> mAllocator;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 7cf003d..072926f 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -35,10 +35,7 @@
 
 // ---------------------------------------------------------------------------
 
-namespace Gralloc2 {
-class Mapper;
-}
-
+class GrallocMapper;
 class Rect;
 
 class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
@@ -59,20 +56,21 @@
     void getTransportSize(buffer_handle_t handle,
             uint32_t* outTransportNumFds, uint32_t* outTransportNumInts);
 
-    status_t lock(buffer_handle_t handle,
-            uint32_t usage, const Rect& bounds, void** vaddr);
+    status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr,
+                  int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
 
     status_t lockYCbCr(buffer_handle_t handle,
             uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr);
 
     status_t unlock(buffer_handle_t handle);
 
-    status_t lockAsync(buffer_handle_t handle,
-            uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd);
+    status_t lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr,
+                       int fenceFd, int32_t* outBytesPerPixel = nullptr,
+                       int32_t* outBytesPerStride = nullptr);
 
-    status_t lockAsync(buffer_handle_t handle,
-            uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
-            void** vaddr, int fenceFd);
+    status_t lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage,
+                       const Rect& bounds, void** vaddr, int fenceFd,
+                       int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
 
     status_t lockAsyncYCbCr(buffer_handle_t handle,
             uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr,
@@ -80,9 +78,8 @@
 
     status_t unlockAsync(buffer_handle_t handle, int *fenceFd);
 
-    const Gralloc2::Mapper& getGrallocMapper() const
-    {
-        return *mMapper;
+    const GrallocMapper& getGrallocMapper() const {
+        return reinterpret_cast<const GrallocMapper&>(*mMapper);
     }
 
 private:
@@ -90,7 +87,7 @@
 
     GraphicBufferMapper();
 
-    const std::unique_ptr<const Gralloc2::Mapper> mMapper;
+    std::unique_ptr<const GrallocMapper> mMapper;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 0fa819d..4b03e44 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/common/1.2/types.h>
 #include <system/graphics.h>
 
 // android::ui::* in this header file will alias different types as
@@ -24,11 +25,11 @@
 namespace android {
 namespace ui {
 
-using android::hardware::graphics::common::V1_0::Hdr;
-using android::hardware::graphics::common::V1_1::ColorMode;
-using android::hardware::graphics::common::V1_1::Dataspace;
-using android::hardware::graphics::common::V1_1::PixelFormat;
 using android::hardware::graphics::common::V1_1::RenderIntent;
+using android::hardware::graphics::common::V1_2::ColorMode;
+using android::hardware::graphics::common::V1_2::Dataspace;
+using android::hardware::graphics::common::V1_2::Hdr;
+using android::hardware::graphics::common::V1_2::PixelFormat;
 
 }  // namespace ui
 }  // namespace android
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 0bec0b7..1768805 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -24,6 +24,7 @@
 
 #include <ui/FloatRect.h>
 #include <ui/Point.h>
+#include <ui/Size.h>
 
 #include <android/rect.h>
 
@@ -78,6 +79,13 @@
         bottom = static_cast<int32_t>(floatRect.bottom + 0.5f);
     }
 
+    inline explicit Rect(const ui::Size& size) {
+        left = 0;
+        top = 0;
+        right = size.width;
+        bottom = size.height;
+    }
+
     void makeInvalid();
 
     inline void clear() {
@@ -106,6 +114,8 @@
         return bottom - top;
     }
 
+    ui::Size getSize() const { return ui::Size(getWidth(), getHeight()); }
+
     __attribute__((no_sanitize("signed-integer-overflow")))
     inline Rect getBounds() const {
         return Rect(right - left, bottom - top);
@@ -120,7 +130,7 @@
         right = rb.x;
         bottom = rb.y;
     }
-    
+
     // the following 4 functions return the 4 corners of the rect as Point
     Point leftTop() const {
         return Point(left, top);
@@ -175,6 +185,11 @@
     Rect& offsetTo(int32_t x, int32_t y);
     Rect& offsetBy(int32_t x, int32_t y);
 
+    /**
+     * Insets the rectangle on all sides specified by the insets.
+     */
+    Rect& inset(int32_t _left, int32_t _top, int32_t _right, int32_t _bottom);
+
     bool intersect(const Rect& with, Rect* result) const;
 
     // Create a new Rect by transforming this one using a graphics HAL
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 7788452..79642ae 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -25,12 +25,13 @@
 #include <ui/Rect.h>
 #include <utils/Flattenable.h>
 
+#include <android-base/macros.h>
+
+#include <string>
+
 namespace android {
 // ---------------------------------------------------------------------------
 
-class String8;
-
-// ---------------------------------------------------------------------------
 class Region : public LightFlattenable<Region>
 {
 public:
@@ -87,17 +88,19 @@
 
             // these translate rhs first
             Region&     translateSelf(int dx, int dy);
+            Region&     scaleSelf(float sx, float sy);
             Region&     orSelf(const Region& rhs, int dx, int dy);
             Region&     xorSelf(const Region& rhs, int dx, int dy);
             Region&     andSelf(const Region& rhs, int dx, int dy);
             Region&     subtractSelf(const Region& rhs, int dx, int dy);
 
+
             // these translate rhs first
-    const   Region      translate(int dx, int dy) const;
-    const   Region      merge(const Region& rhs, int dx, int dy) const;
-    const   Region      mergeExclusive(const Region& rhs, int dx, int dy) const;
-    const   Region      intersect(const Region& rhs, int dx, int dy) const;
-    const   Region      subtract(const Region& rhs, int dx, int dy) const;
+    const   Region      translate(int dx, int dy) const WARN_UNUSED;
+    const   Region      merge(const Region& rhs, int dx, int dy) const WARN_UNUSED;
+    const   Region      mergeExclusive(const Region& rhs, int dx, int dy) const WARN_UNUSED;
+    const   Region      intersect(const Region& rhs, int dx, int dy) const WARN_UNUSED;
+    const   Region      subtract(const Region& rhs, int dx, int dy) const WARN_UNUSED;
 
     // convenience operators overloads
     inline  const Region      operator | (const Region& rhs) const;
@@ -140,8 +143,8 @@
             status_t    flatten(void* buffer, size_t size) const;
             status_t    unflatten(void const* buffer, size_t size);
 
-    void        dump(String8& out, const char* what, uint32_t flags=0) const;
-    void        dump(const char* what, uint32_t flags=0) const;
+            void        dump(std::string& out, const char* what, uint32_t flags=0) const;
+            void        dump(const char* what, uint32_t flags=0) const;
 
 private:
     class rasterizer;
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
new file mode 100644
index 0000000..c39d8af
--- /dev/null
+++ b/libs/ui/include/ui/Size.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2019 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 <algorithm>
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+namespace android {
+namespace ui {
+
+// Forward declare a few things.
+struct Size;
+bool operator==(const Size& lhs, const Size& rhs);
+
+/**
+ * A simple value type representing a two-dimensional size
+ */
+struct Size {
+    int32_t width;
+    int32_t height;
+
+    // Special values
+    static const Size INVALID;
+    static const Size EMPTY;
+
+    // ------------------------------------------------------------------------
+    // Construction
+    // ------------------------------------------------------------------------
+
+    Size() : Size(INVALID) {}
+    template <typename T>
+    Size(T&& w, T&& h)
+          : width(Size::clamp<int32_t, T>(std::forward<T>(w))),
+            height(Size::clamp<int32_t, T>(std::forward<T>(h))) {}
+
+    // ------------------------------------------------------------------------
+    // Accessors
+    // ------------------------------------------------------------------------
+
+    int32_t getWidth() const { return width; }
+    int32_t getHeight() const { return height; }
+
+    template <typename T>
+    void setWidth(T&& v) {
+        width = Size::clamp<int32_t, T>(std::forward<T>(v));
+    }
+    template <typename T>
+    void setHeight(T&& v) {
+        height = Size::clamp<int32_t, T>(std::forward<T>(v));
+    }
+
+    // ------------------------------------------------------------------------
+    // Assignment
+    // ------------------------------------------------------------------------
+
+    void set(const Size& size) { *this = size; }
+    template <typename T>
+    void set(T&& w, T&& h) {
+        set(Size(std::forward<T>(w), std::forward<T>(h)));
+    }
+
+    // Sets the value to INVALID
+    void makeInvalid() { set(INVALID); }
+
+    // Sets the value to EMPTY
+    void clear() { set(EMPTY); }
+
+    // ------------------------------------------------------------------------
+    // Semantic checks
+    // ------------------------------------------------------------------------
+
+    // Valid means non-negative width and height
+    bool isValid() const { return width >= 0 && height >= 0; }
+
+    // Empty means zero width and height
+    bool isEmpty() const { return *this == EMPTY; }
+
+    // ------------------------------------------------------------------------
+    // Clamp Helpers
+    // ------------------------------------------------------------------------
+
+    // Note: We use only features available in C++11 here for compatibility with
+    // external targets which include this file directly or indirectly and which
+    // themselves use C++11.
+
+    // C++11 compatible replacement for std::remove_cv_reference_t [C++20]
+    template <typename T>
+    using remove_cv_reference_t =
+            typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+    // Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
+    // clamping the input value to the output range if necessary.
+    template <typename ToType, typename FromType>
+    static Size::remove_cv_reference_t<ToType> clamp(
+            typename std::enable_if<
+                    std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded &&
+                            std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded,
+                    FromType&&>::type v) {
+        static constexpr auto toHighest = std::numeric_limits<remove_cv_reference_t<ToType>>::max();
+        static constexpr auto toLowest =
+                std::numeric_limits<remove_cv_reference_t<ToType>>::lowest();
+        static constexpr auto fromHighest =
+                std::numeric_limits<remove_cv_reference_t<FromType>>::max();
+        static constexpr auto fromLowest =
+                std::numeric_limits<remove_cv_reference_t<FromType>>::lowest();
+
+        // A clamp is needed if the range of FromType is not a subset of the range of ToType
+        static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest);
+
+        // If a clamp is not needed, the conversion is just a trivial cast.
+        if (!isClampNeeded) {
+            return static_cast<ToType>(v);
+        }
+
+        // Otherwise we leverage implicit conversion to safely compare values of
+        // different types, to ensure we return a value clamped to the range of
+        // ToType.
+        return v < toLowest ? toLowest : (v > toHighest ? toHighest : static_cast<ToType>(v));
+    }
+};
+
+// ------------------------------------------------------------------------
+// Comparisons
+// ------------------------------------------------------------------------
+
+inline bool operator==(const Size& lhs, const Size& rhs) {
+    return lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+inline bool operator!=(const Size& lhs, const Size& rhs) {
+    return !operator==(lhs, rhs);
+}
+
+inline bool operator<(const Size& lhs, const Size& rhs) {
+    // Orders by increasing width, then height.
+    if (lhs.width != rhs.width) return lhs.width < rhs.width;
+    return lhs.height < rhs.height;
+}
+
+} // namespace ui
+} // namespace android
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
new file mode 100644
index 0000000..dcb26cf
--- /dev/null
+++ b/libs/ui/include/ui/Transform.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TRANSFORM_H
+#define ANDROID_TRANSFORM_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <string>
+
+#include <hardware/hardware.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+#include <ui/Point.h>
+#include <ui/Rect.h>
+
+namespace android {
+
+class Region;
+
+namespace ui {
+
+class Transform {
+public:
+    Transform();
+    Transform(const Transform&  other);
+    explicit Transform(uint32_t orientation);
+    ~Transform();
+
+    enum orientation_flags {
+        ROT_0   = 0x00000000,
+        FLIP_H  = HAL_TRANSFORM_FLIP_H,
+        FLIP_V  = HAL_TRANSFORM_FLIP_V,
+        ROT_90  = HAL_TRANSFORM_ROT_90,
+        ROT_180 = FLIP_H|FLIP_V,
+        ROT_270 = ROT_180|ROT_90,
+        ROT_INVALID = 0x80
+    };
+
+    enum type_mask : uint32_t {
+        IDENTITY            = 0,
+        TRANSLATE           = 0x1,
+        ROTATE              = 0x2,
+        SCALE               = 0x4,
+        UNKNOWN             = 0x8
+    };
+
+    // query the transform
+    bool        preserveRects() const;
+    uint32_t    getType() const;
+    uint32_t    getOrientation() const;
+
+    const vec3& operator [] (size_t i) const;  // returns column i
+    float   tx() const;
+    float   ty() const;
+    float   sx() const;
+    float   sy() const;
+
+    // modify the transform
+    void        reset();
+    void        set(float tx, float ty);
+    void        set(float a, float b, float c, float d);
+    status_t    set(uint32_t flags, float w, float h);
+
+    // transform data
+    Rect    makeBounds(int w, int h) const;
+    vec2    transform(int x, int y) const;
+    Region  transform(const Region& reg) const;
+    Rect    transform(const Rect& bounds,
+                      bool roundOutwards = false) const;
+    FloatRect transform(const FloatRect& bounds) const;
+    Transform& operator = (const Transform& other);
+    Transform operator * (const Transform& rhs) const;
+    // assumes the last row is < 0 , 0 , 1 >
+    vec2 transform(const vec2& v) const;
+    vec3 transform(const vec3& v) const;
+
+    Transform inverse() const;
+
+    // for debugging
+    void dump(std::string& result, const char* name) const;
+    void dump(const char* name) const;
+
+private:
+    struct mat33 {
+        vec3 v[3];
+        inline const vec3& operator [] (size_t i) const { return v[i]; }
+        inline vec3& operator [] (size_t i) { return v[i]; }
+    };
+
+    enum { UNKNOWN_TYPE = 0x80000000 };
+
+    uint32_t type() const;
+    static bool absIsOne(float f);
+    static bool isZero(float f);
+
+    mat33               mMatrix;
+    mutable uint32_t    mType;
+};
+
+}  // namespace ui
+}  // namespace android
+
+#endif /* ANDROID_TRANSFORM_H */
diff --git a/libs/ui/include/ui/UiConfig.h b/libs/ui/include/ui/UiConfig.h
index fcf8ed5..d1d6014 100644
--- a/libs/ui/include/ui/UiConfig.h
+++ b/libs/ui/include/ui/UiConfig.h
@@ -17,12 +17,12 @@
 #ifndef ANDROID_UI_CONFIG_H
 #define ANDROID_UI_CONFIG_H
 
-#include <utils/String8.h>
+#include <string>
 
 namespace android {
 
 // Append the libui configuration details to configStr.
-void appendUiConfigString(String8& configStr);
+void appendUiConfigString(std::string& configStr);
 
 }; // namespace android
 
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/ui/include_vndk/ui/ConfigStoreTypes.h
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/ui/include_vndk/ui/ConfigStoreTypes.h
index e6ac6bf..37a2bd5 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/ui/include_vndk/ui/ConfigStoreTypes.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
 
+#include <android/hardware/configstore/1.2/types.h>
+
+// android::ui::* in this header file will alias different types as
+// the HIDL interface is updated.
 namespace android {
-namespace mock {
+namespace ui {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+using android::hardware::configstore::V1_2::DisplayPrimaries;
 
-} // namespace mock
-} // namespace android
+}  // namespace ui
+}  // namespace android
diff --git a/libs/ui/include_vndk/ui/DisplayedFrameStats.h b/libs/ui/include_vndk/ui/DisplayedFrameStats.h
new file mode 120000
index 0000000..6014e19
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayedFrameStats.h
@@ -0,0 +1 @@
+../../include/ui/DisplayedFrameStats.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/Size.h b/libs/ui/include_vndk/ui/Size.h
new file mode 120000
index 0000000..fd2b21b
--- /dev/null
+++ b/libs/ui/include_vndk/ui/Size.h
@@ -0,0 +1 @@
+../../include/ui/Size.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/Transform.h b/libs/ui/include_vndk/ui/Transform.h
new file mode 120000
index 0000000..60633c2
--- /dev/null
+++ b/libs/ui/include_vndk/ui/Transform.h
@@ -0,0 +1 @@
+../../include/ui/Transform.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index aef6428..373fa4f 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -30,7 +30,51 @@
 
 cc_test {
     name: "GraphicBuffer_test",
-    shared_libs: ["libpdx_default_transport", "libui", "libutils"],
+    header_libs: [
+        "libdvr_headers",
+        "libnativewindow_headers",
+    ],
+    shared_libs: [
+        "android.frameworks.bufferhub@1.0",
+        "libcutils",
+        "libhidlbase",
+        "libhwbinder",
+        "libui",
+        "libutils",
+    ],
     srcs: ["GraphicBuffer_test.cpp"],
     cflags: ["-Wall", "-Werror"],
 }
+
+cc_test {
+    name: "BufferHub_test",
+    header_libs: [
+        "libdvr_headers",
+        "libnativewindow_headers",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+    shared_libs: [
+        "android.frameworks.bufferhub@1.0",
+        "libcutils",
+        "libhidlbase",
+        "libhwbinder",
+        "liblog",
+        "libui",
+        "libutils"
+    ],
+    srcs: [
+        "BufferHubBuffer_test.cpp",
+        "BufferHubEventFd_test.cpp",
+        "BufferHubMetadata_test.cpp",
+    ],
+    cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
+    name: "Size_test",
+    shared_libs: ["libui"],
+    srcs: ["Size_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
new file mode 100644
index 0000000..3bcd935
--- /dev/null
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -0,0 +1,469 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferHubBufferTest"
+
+#include <errno.h>
+#include <sys/epoll.h>
+
+#include <android/hardware_buffer.h>
+#include <cutils/native_handle.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/ServiceManagement.h>
+#include <hwbinder/IPCThreadState.h>
+#include <ui/BufferHubBuffer.h>
+#include <ui/BufferHubEventFd.h>
+
+namespace android {
+
+namespace {
+
+using ::android::BufferHubDefs::AnyClientAcquired;
+using ::android::BufferHubDefs::AnyClientGained;
+using ::android::BufferHubDefs::AnyClientPosted;
+using ::android::BufferHubDefs::IsClientAcquired;
+using ::android::BufferHubDefs::IsClientGained;
+using ::android::BufferHubDefs::IsClientPosted;
+using ::android::BufferHubDefs::IsClientReleased;
+using ::android::BufferHubDefs::kMetadataHeaderSize;
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+const int kWidth = 640;
+const int kHeight = 480;
+const int kLayerCount = 1;
+const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+const int kUsage = 0;
+const AHardwareBuffer_Desc kDesc = {kWidth, kHeight,        kLayerCount,  kFormat,
+                                    kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
+const size_t kUserMetadataSize = 1;
+
+class BufferHubBufferTest : public ::testing::Test {
+protected:
+    void SetUp() override { android::hardware::ProcessState::self()->startThreadPool(); }
+};
+
+bool cmpAHardwareBufferDesc(const AHardwareBuffer_Desc& desc, const AHardwareBuffer_Desc& other) {
+    // Not comparing stride because it's unknown before allocation
+    return desc.format == other.format && desc.height == other.height &&
+            desc.layers == other.layers && desc.usage == other.usage && desc.width == other.width;
+}
+
+class BufferHubBufferStateTransitionTest : public BufferHubBufferTest {
+protected:
+    void SetUp() override {
+        BufferHubBufferTest::SetUp();
+        CreateTwoClientsOfABuffer();
+    }
+
+    std::unique_ptr<BufferHubBuffer> b1;
+    uint32_t b1ClientMask = 0U;
+    std::unique_ptr<BufferHubBuffer> b2;
+    uint32_t b2ClientMask = 0U;
+
+private:
+    // Creates b1 and b2 as the clients of the same buffer for testing.
+    void CreateTwoClientsOfABuffer();
+};
+
+void BufferHubBufferStateTransitionTest::CreateTwoClientsOfABuffer() {
+    b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
+    ASSERT_THAT(b1, NotNull());
+    b1ClientMask = b1->client_state_mask();
+    ASSERT_NE(b1ClientMask, 0U);
+
+    native_handle_t* token = b1->Duplicate();
+    ASSERT_THAT(token, NotNull());
+
+    // TODO(b/122543147): use a movalbe wrapper for token
+    b2 = BufferHubBuffer::Import(token);
+    native_handle_close(token);
+    native_handle_delete(token);
+    ASSERT_THAT(b2, NotNull());
+
+    b2ClientMask = b2->client_state_mask();
+    ASSERT_NE(b2ClientMask, 0U);
+    ASSERT_NE(b2ClientMask, b1ClientMask);
+}
+
+TEST_F(BufferHubBufferTest, CreateBufferFails) {
+    // Buffer Creation will fail: BLOB format requires height to be 1.
+    auto b1 = BufferHubBuffer::Create(kWidth, /*height=*/2, kLayerCount,
+                                      /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, kUserMetadataSize);
+
+    EXPECT_THAT(b1, IsNull());
+
+    // Buffer Creation will fail: user metadata size too large.
+    auto b2 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                      /*userMetadataSize=*/std::numeric_limits<size_t>::max());
+
+    EXPECT_THAT(b2, IsNull());
+
+    // Buffer Creation will fail: user metadata size too large.
+    const size_t userMetadataSize = std::numeric_limits<size_t>::max() - kMetadataHeaderSize;
+    auto b3 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                      userMetadataSize);
+
+    EXPECT_THAT(b3, IsNull());
+}
+
+TEST_F(BufferHubBufferTest, CreateBuffer) {
+    auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                      kUserMetadataSize);
+    ASSERT_THAT(b1, NotNull());
+    EXPECT_TRUE(b1->IsConnected());
+    EXPECT_TRUE(b1->IsValid());
+    EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), kDesc));
+    EXPECT_EQ(b1->user_metadata_size(), kUserMetadataSize);
+}
+
+TEST_F(BufferHubBufferTest, DuplicateAndImportBuffer) {
+    auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                      kUserMetadataSize);
+    ASSERT_THAT(b1, NotNull());
+    EXPECT_TRUE(b1->IsValid());
+
+    native_handle_t* token = b1->Duplicate();
+    EXPECT_TRUE(token);
+
+    // The detached buffer should still be valid.
+    EXPECT_TRUE(b1->IsConnected());
+    EXPECT_TRUE(b1->IsValid());
+
+    std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(token);
+    native_handle_close(token);
+    native_handle_delete(token);
+
+    ASSERT_THAT(b2, NotNull());
+    EXPECT_TRUE(b2->IsValid());
+
+    EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), b2->desc()));
+    EXPECT_EQ(b1->user_metadata_size(), b2->user_metadata_size());
+
+    // These two buffer instances are based on the same physical buffer under the
+    // hood, so they should share the same id.
+    EXPECT_EQ(b1->id(), b2->id());
+    // We use client_state_mask() to tell those two instances apart.
+    EXPECT_NE(b1->client_state_mask(), b2->client_state_mask());
+
+    // Both buffer instances should be in released state currently.
+    EXPECT_TRUE(b1->IsReleased());
+    EXPECT_TRUE(b2->IsReleased());
+
+    // The event fd should behave like duped event fds.
+    const BufferHubEventFd& eventFd1 = b1->eventFd();
+    ASSERT_GE(eventFd1.get(), 0);
+    const BufferHubEventFd& eventFd2 = b2->eventFd();
+    ASSERT_GE(eventFd2.get(), 0);
+
+    base::unique_fd epollFd(epoll_create(64));
+    ASSERT_GE(epollFd.get(), 0);
+
+    // Add eventFd1 to epoll set, and signal eventFd2.
+    epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+    ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e), 0) << strerror(errno);
+
+    std::array<epoll_event, 1> events;
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+    eventFd2.signal();
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+    // The epoll fd is edge triggered, so it only responds to the eventFd once.
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+    eventFd2.signal();
+    eventFd2.clear();
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubBufferTest, ImportFreedBuffer) {
+    auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                      kUserMetadataSize);
+    ASSERT_THAT(b1, NotNull());
+    EXPECT_TRUE(b1->IsValid());
+
+    native_handle_t* token = b1->Duplicate();
+    EXPECT_TRUE(token);
+
+    // Explicitly destroy b1. Backend buffer should be freed and token becomes invalid
+    b1.reset();
+
+    // TODO(b/122543147): use a movalbe wrapper for token
+    std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(token);
+    native_handle_close(token);
+    native_handle_delete(token);
+
+    // Import should fail with INVALID_TOKEN
+    EXPECT_THAT(b2, IsNull());
+}
+
+// nullptr must not crash the service
+TEST_F(BufferHubBufferTest, ImportNullToken) {
+    auto b1 = BufferHubBuffer::Import(nullptr);
+    EXPECT_THAT(b1, IsNull());
+}
+
+TEST_F(BufferHubBufferTest, ImportInvalidToken) {
+    native_handle_t* token = native_handle_create(/*numFds=*/0, /*numInts=*/1);
+    token->data[0] = 0;
+
+    auto b1 = BufferHubBuffer::Import(token);
+    native_handle_delete(token);
+
+    EXPECT_THAT(b1, IsNull());
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) {
+    ASSERT_TRUE(b1->IsReleased());
+
+    // Successful gaining the buffer should change the buffer state bit of b1 to
+    // gained state, other client state bits to released state.
+    EXPECT_EQ(b1->Gain(), 0);
+    EXPECT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask));
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromGainedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    auto current_buffer_state = b1->buffer_state();
+    ASSERT_TRUE(IsClientGained(current_buffer_state, b1ClientMask));
+
+    // Gaining from gained state by the same client should not return error.
+    EXPECT_EQ(b1->Gain(), 0);
+
+    // Gaining from gained state by another client should return error.
+    EXPECT_EQ(b2->Gain(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromAcquiredState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_EQ(b2->Acquire(), 0);
+    ASSERT_TRUE(AnyClientAcquired(b1->buffer_state()));
+
+    // Gaining from acquired state should fail.
+    EXPECT_EQ(b1->Gain(), -EBUSY);
+    EXPECT_EQ(b2->Gain(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromOtherClientInPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+
+    // Gaining a buffer who has other posted client should succeed.
+    EXPECT_EQ(b1->Gain(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromSelfInPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+
+    // A posted client should be able to gain the buffer when there is no other clients in
+    // acquired state.
+    EXPECT_EQ(b2->Gain(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromOtherInGainedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask));
+
+    EXPECT_EQ(b2->Post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromSelfInGainedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask));
+
+    EXPECT_EQ(b1->Post(), 0);
+    auto current_buffer_state = b1->buffer_state();
+    EXPECT_TRUE(IsClientReleased(current_buffer_state, b1ClientMask));
+    EXPECT_TRUE(IsClientPosted(current_buffer_state, b2ClientMask));
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+
+    // Post from posted state should fail.
+    EXPECT_EQ(b1->Post(), -EBUSY);
+    EXPECT_EQ(b2->Post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromAcquiredState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_EQ(b2->Acquire(), 0);
+    ASSERT_TRUE(AnyClientAcquired(b1->buffer_state()));
+
+    // Posting from acquired state should fail.
+    EXPECT_EQ(b1->Post(), -EBUSY);
+    EXPECT_EQ(b2->Post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) {
+    ASSERT_TRUE(b1->IsReleased());
+
+    // Posting from released state should fail.
+    EXPECT_EQ(b1->Post(), -EBUSY);
+    EXPECT_EQ(b2->Post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask));
+
+    // Acquire from posted state should pass.
+    EXPECT_EQ(b2->Acquire(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromOtherInPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask));
+
+    // Acquire from released state should fail, although there are other clients
+    // in posted state.
+    EXPECT_EQ(b1->Acquire(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInAcquiredState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_EQ(b2->Acquire(), 0);
+    auto current_buffer_state = b1->buffer_state();
+    ASSERT_TRUE(IsClientAcquired(current_buffer_state, b2ClientMask));
+
+    // Acquiring from acquired state by the same client should not error out.
+    EXPECT_EQ(b2->Acquire(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromReleasedState) {
+    ASSERT_TRUE(b1->IsReleased());
+
+    // Acquiring form released state should fail.
+    EXPECT_EQ(b1->Acquire(), -EBUSY);
+    EXPECT_EQ(b2->Acquire(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromGainedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_TRUE(AnyClientGained(b1->buffer_state()));
+
+    // Acquiring from gained state should fail.
+    EXPECT_EQ(b1->Acquire(), -EBUSY);
+    EXPECT_EQ(b2->Acquire(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) {
+    ASSERT_TRUE(b1->IsReleased());
+
+    EXPECT_EQ(b1->Release(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) {
+    ASSERT_TRUE(b1->IsReleased());
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_TRUE(AnyClientGained(b1->buffer_state()));
+
+    EXPECT_EQ(b1->Release(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+
+    EXPECT_EQ(b2->Release(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInAcquiredState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_EQ(b2->Acquire(), 0);
+    ASSERT_TRUE(AnyClientAcquired(b1->buffer_state()));
+
+    EXPECT_EQ(b2->Release(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, BasicUsage) {
+    // 1 producer buffer and 1 consumer buffer initialised in testcase setup.
+    // Test if this set of basic operation succeed:
+    // Producer post three times to the consumer, and released by consumer.
+    for (int i = 0; i < 3; ++i) {
+        ASSERT_EQ(b1->Gain(), 0);
+        ASSERT_EQ(b1->Post(), 0);
+        ASSERT_EQ(b2->Acquire(), 0);
+        ASSERT_EQ(b2->Release(), 0);
+    }
+}
+
+TEST_F(BufferHubBufferTest, createNewConsumerAfterGain) {
+    // Create a poducer buffer and gain.
+    std::unique_ptr<BufferHubBuffer> b1 =
+            BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                    kUserMetadataSize);
+    ASSERT_THAT(b1, NotNull());
+    ASSERT_EQ(b1->Gain(), 0);
+
+    // Create a consumer of the buffer and test if the consumer can acquire the
+    // buffer if producer posts.
+    // TODO(b/122543147): use a movalbe wrapper for token
+    native_handle_t* token = b1->Duplicate();
+    ASSERT_TRUE(token);
+
+    std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(token);
+    native_handle_close(token);
+    native_handle_delete(token);
+
+    ASSERT_THAT(b2, NotNull());
+    ASSERT_NE(b1->client_state_mask(), b2->client_state_mask());
+
+    ASSERT_EQ(b1->Post(), 0);
+    EXPECT_EQ(b2->Acquire(), 0);
+}
+
+TEST_F(BufferHubBufferTest, createNewConsumerAfterPost) {
+    // Create a poducer buffer and post.
+    std::unique_ptr<BufferHubBuffer> b1 =
+            BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+                                    kUserMetadataSize);
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+
+    // Create a consumer of the buffer and test if the consumer can acquire the
+    // buffer if producer posts.
+    // TODO(b/122543147): use a movalbe wrapper for token
+    native_handle_t* token = b1->Duplicate();
+    ASSERT_TRUE(token);
+
+    std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(token);
+    native_handle_close(token);
+    native_handle_delete(token);
+
+    ASSERT_THAT(b2, NotNull());
+    ASSERT_NE(b1->client_state_mask(), b2->client_state_mask());
+
+    EXPECT_EQ(b2->Acquire(), 0);
+}
+
+} // namespace
+
+} // namespace android
diff --git a/libs/ui/tests/BufferHubEventFd_test.cpp b/libs/ui/tests/BufferHubEventFd_test.cpp
new file mode 100644
index 0000000..2c9aa57
--- /dev/null
+++ b/libs/ui/tests/BufferHubEventFd_test.cpp
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferHubEventFdTest"
+
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include <array>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <ui/BufferHubEventFd.h>
+
+namespace android {
+
+namespace {
+
+const int kTimeout = 100;
+const std::chrono::milliseconds kTimeoutMs(kTimeout);
+
+using ::testing::Contains;
+using BufferHubEventFdTest = ::testing::Test;
+
+} // namespace
+
+TEST_F(BufferHubEventFdTest, EventFd_testSingleEpollFd) {
+    BufferHubEventFd eventFd;
+    ASSERT_TRUE(eventFd.isValid());
+
+    base::unique_fd epollFd(epoll_create(64));
+    epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+
+    ASSERT_GE(epollFd.get(), 0);
+    ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+    std::array<epoll_event, 1> events;
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+    eventFd.signal();
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+    // The epoll fd is edge triggered, so it only responds to the eventFd once.
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testClear) {
+    BufferHubEventFd eventFd;
+    ASSERT_TRUE(eventFd.isValid());
+
+    base::unique_fd epollFd(epoll_create(64));
+    epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+
+    ASSERT_GE(epollFd.get(), 0);
+    ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+    eventFd.signal();
+    eventFd.clear();
+
+    std::array<epoll_event, 1> events;
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testDupEventFd) {
+    BufferHubEventFd eventFd;
+    ASSERT_TRUE(eventFd.isValid());
+
+    base::unique_fd epollFd(epoll_create(64));
+    epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+
+    ASSERT_GE(epollFd.get(), 0);
+    ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+    // Technically, the dupliated eventFd and the original eventFd are pointing
+    // to the same kernel object. This test signals the duplicated eventFd but epolls the origianl
+    // eventFd.
+    BufferHubEventFd dupedEventFd(dup(eventFd.get()));
+    ASSERT_GE(dupedEventFd.get(), 0);
+
+    std::array<epoll_event, 1> events;
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+    dupedEventFd.signal();
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+    // The epoll fd is edge triggered, so it only responds to the eventFd once.
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+    dupedEventFd.signal();
+
+    dupedEventFd.clear();
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testTwoEpollFds) {
+    BufferHubEventFd eventFd;
+    ASSERT_TRUE(eventFd.isValid());
+
+    base::unique_fd epollFd1(epoll_create(64));
+    base::unique_fd epollFd2(epoll_create(64));
+    epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+
+    ASSERT_GE(epollFd1.get(), 0);
+    ASSERT_GE(epollFd2.get(), 0);
+
+    // Register the same eventFd to two EpollFds.
+    ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+    ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+    std::array<epoll_event, 1> events;
+    EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0);
+    EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0);
+
+    eventFd.signal();
+    EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1);
+    EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 1);
+
+    // The epoll fd is edge triggered, so it only responds to the eventFd once.
+    EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0);
+    EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0);
+
+    eventFd.signal();
+    EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1);
+
+    eventFd.clear();
+    EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0);
+    EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testTwoEventFds) {
+    BufferHubEventFd eventFd1;
+    BufferHubEventFd eventFd2;
+
+    ASSERT_TRUE(eventFd1.isValid());
+    ASSERT_TRUE(eventFd2.isValid());
+
+    base::unique_fd epollFd(epoll_create(64));
+    epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}};
+    epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}};
+
+    ASSERT_GE(epollFd.get(), 0);
+    ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0);
+    ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0);
+
+    std::array<epoll_event, 2> events;
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+    // Signal one by one.
+    eventFd1.signal();
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+    EXPECT_EQ(events[0].data.u32, e1.data.u32);
+
+    eventFd2.signal();
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+    EXPECT_EQ(events[0].data.u32, e2.data.u32);
+
+    // Signal both.
+    eventFd1.signal();
+    eventFd2.signal();
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 2);
+
+    uint32_t u32s[] = {events[0].data.u32, events[1].data.u32};
+    EXPECT_THAT(u32s, Contains(e1.data.u32));
+    EXPECT_THAT(u32s, Contains(e2.data.u32));
+
+    // The epoll fd is edge triggered, so it only responds to the eventFd once.
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+    eventFd1.signal();
+    eventFd2.signal();
+    eventFd2.clear();
+    EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testPollingThreadWithTwoEventFds) {
+    BufferHubEventFd eventFd1;
+    BufferHubEventFd eventFd2;
+
+    ASSERT_TRUE(eventFd1.isValid());
+    ASSERT_TRUE(eventFd2.isValid());
+
+    base::unique_fd epollFd(epoll_create(64));
+    epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}};
+    epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}};
+
+    ASSERT_GE(epollFd.get(), 0);
+    ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0);
+    ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0);
+
+    int countEvent1 = 0;
+    int countEvent2 = 0;
+    std::atomic<bool> stop{false};
+    std::mutex mx;
+    std::condition_variable cv;
+
+    std::thread pollingThread([&] {
+        std::array<epoll_event, 2> events;
+        while (true) {
+            if (stop.load()) {
+                break;
+            }
+            int ret = epoll_wait(epollFd.get(), events.data(), events.size(), kTimeout);
+            ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed.");
+
+            std::lock_guard<std::mutex> lock(mx);
+            for (int i = 0; i < ret; i++) {
+                if (events[i].data.u32 == e1.data.u32) {
+                    countEvent1++;
+                    cv.notify_one();
+                } else if (events[i].data.u32 == e2.data.u32) {
+                    countEvent2++;
+                    cv.notify_one();
+                }
+            }
+        }
+    });
+
+    {
+        std::unique_lock<std::mutex> lock(mx);
+
+        eventFd1.signal();
+        EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 1; }));
+
+        eventFd1.signal();
+        EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 2; }));
+
+        eventFd2.signal();
+        EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 1; }));
+
+        eventFd1.clear();
+        eventFd2.clear();
+        EXPECT_EQ(countEvent1, 2);
+        EXPECT_EQ(countEvent2, 1);
+
+        eventFd1.signal();
+        EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 3; }));
+
+        eventFd2.signal();
+        EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 2; }));
+    }
+
+    stop.store(true);
+    pollingThread.join();
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testTwoPollingThreads) {
+    BufferHubEventFd eventFd;
+    ASSERT_TRUE(eventFd.isValid());
+
+    base::unique_fd epollFd1(epoll_create(64));
+    base::unique_fd epollFd2(epoll_create(64));
+    epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+
+    ASSERT_GE(epollFd1.get(), 0);
+    ASSERT_GE(epollFd2.get(), 0);
+
+    // Register the same eventFd to two EpollFds.
+    ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+    ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+    int countEpoll1 = 0;
+    int countEpoll2 = 0;
+    std::atomic<bool> stop{false};
+    std::mutex mx;
+    std::condition_variable cv;
+
+    std::thread pollingThread1([&] {
+        std::array<epoll_event, 1> events;
+        while (!stop.load()) {
+            int ret = epoll_wait(epollFd1.get(), events.data(), events.size(), kTimeout);
+            ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed.");
+
+            if (ret > 0) {
+                std::lock_guard<std::mutex> lock(mx);
+                countEpoll1++;
+                cv.notify_one();
+            }
+        }
+    });
+
+    std::thread pollingThread2([&] {
+        std::array<epoll_event, 1> events;
+        while (!stop.load()) {
+            int ret = epoll_wait(epollFd2.get(), events.data(), events.size(), kTimeout);
+            ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed.");
+
+            if (ret > 0) {
+                std::lock_guard<std::mutex> lock(mx);
+                countEpoll2++;
+                cv.notify_one();
+            }
+        }
+    });
+
+    {
+        std::unique_lock<std::mutex> lock(mx);
+
+        eventFd.signal();
+        EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 1; }));
+        EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 1; }));
+
+        eventFd.signal();
+        EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 2; }));
+        EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 2; }));
+
+        eventFd.clear();
+        EXPECT_EQ(countEpoll1, 2);
+        EXPECT_EQ(countEpoll2, 2);
+
+        eventFd.signal();
+        EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 3; }));
+        EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 3; }));
+    }
+
+    stop.store(true);
+    pollingThread1.join();
+    pollingThread2.join();
+}
+
+} // namespace android
diff --git a/libs/ui/tests/BufferHubMetadata_test.cpp b/libs/ui/tests/BufferHubMetadata_test.cpp
new file mode 100644
index 0000000..b7f0b4b
--- /dev/null
+++ b/libs/ui/tests/BufferHubMetadata_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <ui/BufferHubMetadata.h>
+
+namespace android {
+namespace dvr {
+
+constexpr size_t kEmptyUserMetadataSize = 0;
+
+class BufferHubMetadataTest : public ::testing::Test {};
+
+TEST_F(BufferHubMetadataTest, Create_UserMetdataSizeTooBig) {
+  BufferHubMetadata m1 =
+      BufferHubMetadata::Create(std::numeric_limits<uint32_t>::max());
+  EXPECT_FALSE(m1.IsValid());
+}
+
+TEST_F(BufferHubMetadataTest, Create_Success) {
+  BufferHubMetadata m1 = BufferHubMetadata::Create(kEmptyUserMetadataSize);
+  EXPECT_TRUE(m1.IsValid());
+  EXPECT_NE(m1.metadata_header(), nullptr);
+}
+
+TEST_F(BufferHubMetadataTest, Import_Success) {
+  BufferHubMetadata m1 = BufferHubMetadata::Create(kEmptyUserMetadataSize);
+  EXPECT_TRUE(m1.IsValid());
+  EXPECT_NE(m1.metadata_header(), nullptr);
+
+  unique_fd h2 = unique_fd(dup(m1.ashmem_fd().get()));
+  EXPECT_NE(h2.get(), -1);
+
+  BufferHubMetadata m2 = BufferHubMetadata::Import(std::move(h2));
+  EXPECT_EQ(h2.get(), -1);
+  EXPECT_TRUE(m1.IsValid());
+  BufferHubDefs::MetadataHeader* mh1 = m1.metadata_header();
+  EXPECT_NE(mh1, nullptr);
+
+  // Check if the newly allocated buffer is initialized in released state (i.e.
+  // state equals to 0U).
+  EXPECT_TRUE(mh1->buffer_state.load() == 0U);
+
+  EXPECT_TRUE(m2.IsValid());
+  BufferHubDefs::MetadataHeader* mh2 = m2.metadata_header();
+  EXPECT_NE(mh2, nullptr);
+
+  // Check if the newly allocated buffer is initialized in released state (i.e.
+  // state equals to 0U).
+  EXPECT_TRUE(mh2->buffer_state.load() == 0U);
+}
+
+TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) {
+  BufferHubMetadata m1 = BufferHubMetadata::Create(sizeof(int));
+  EXPECT_TRUE(m1.IsValid());
+  EXPECT_NE(m1.metadata_header(), nullptr);
+  EXPECT_NE(m1.ashmem_fd().get(), -1);
+  EXPECT_EQ(m1.user_metadata_size(), sizeof(int));
+
+  BufferHubMetadata m2 = std::move(m1);
+
+  // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
+  EXPECT_EQ(m1.metadata_header(), nullptr);
+  EXPECT_NE(m2.metadata_header(), nullptr);
+
+  EXPECT_EQ(m1.ashmem_fd().get(), -1);
+  EXPECT_NE(m2.ashmem_fd().get(), -1);
+
+  EXPECT_EQ(m1.user_metadata_size(), 0U);
+  EXPECT_EQ(m2.user_metadata_size(), sizeof(int));
+
+  BufferHubMetadata m3{std::move(m2)};
+
+  // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
+  EXPECT_EQ(m2.metadata_header(), nullptr);
+  EXPECT_NE(m3.metadata_header(), nullptr);
+
+  EXPECT_EQ(m2.ashmem_fd().get(), -1);
+  EXPECT_NE(m3.ashmem_fd().get(), -1);
+
+  EXPECT_EQ(m2.user_metadata_size(), 0U);
+  EXPECT_EQ(m3.user_metadata_size(), sizeof(int));
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp
index eb679ac..5b46454 100644
--- a/libs/ui/tests/GraphicBuffer_test.cpp
+++ b/libs/ui/tests/GraphicBuffer_test.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "GraphicBufferTest"
 
-#include <ui/DetachedBufferHandle.h>
+#include <ui/BufferHubBuffer.h>
 #include <ui/GraphicBuffer.h>
 
 #include <gtest/gtest.h>
@@ -35,30 +35,43 @@
 
 class GraphicBufferTest : public testing::Test {};
 
-TEST_F(GraphicBufferTest, DetachedBuffer) {
-    sp<GraphicBuffer> buffer(
+TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) {
+    std::unique_ptr<BufferHubBuffer> b1 =
+            BufferHubBuffer::Create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
+                                    kTestUsage, /*userMetadataSize=*/0);
+    ASSERT_NE(b1, nullptr);
+    EXPECT_TRUE(b1->IsValid());
+
+    sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1)));
+    EXPECT_TRUE(gb->isBufferHubBuffer());
+
+    EXPECT_EQ(gb->getWidth(), kTestWidth);
+    EXPECT_EQ(gb->getHeight(), kTestHeight);
+    EXPECT_EQ(static_cast<uint32_t>(gb->getPixelFormat()), kTestFormat);
+    EXPECT_EQ(gb->getUsage(), kTestUsage);
+    EXPECT_EQ(gb->getLayerCount(), kTestLayerCount);
+}
+
+TEST_F(GraphicBufferTest, InvalidBufferIdForNoneBufferHubBuffer) {
+    sp<GraphicBuffer> gb(
             new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount, kTestUsage));
+    EXPECT_FALSE(gb->isBufferHubBuffer());
+    EXPECT_EQ(gb->getBufferId(), -1);
+}
 
-    // Currently a newly allocated GraphicBuffer is in legacy mode, i.e. not associated with
-    // BufferHub. But this may change in the future.
-    EXPECT_FALSE(buffer->isDetachedBuffer());
+TEST_F(GraphicBufferTest, BufferIdMatchesBufferHubBufferId) {
+    std::unique_ptr<BufferHubBuffer> b1 =
+            BufferHubBuffer::Create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
+                                    kTestUsage, /*userMetadataSize=*/0);
+    EXPECT_NE(b1, nullptr);
+    EXPECT_TRUE(b1->IsValid());
 
-    pdx::LocalChannelHandle channel{nullptr, 1234};
-    EXPECT_TRUE(channel.valid());
+    int b1_id = b1->id();
+    EXPECT_GE(b1_id, 0);
 
-    std::unique_ptr<DetachedBufferHandle> handle = DetachedBufferHandle::Create(std::move(channel));
-    EXPECT_FALSE(channel.valid());
-    EXPECT_TRUE(handle->isValid());
-    EXPECT_TRUE(handle->handle().valid());
-
-    buffer->setDetachedBufferHandle(std::move(handle));
-    EXPECT_TRUE(handle == nullptr);
-    EXPECT_TRUE(buffer->isDetachedBuffer());
-
-    handle = buffer->takeDetachedBufferHandle();
-    EXPECT_TRUE(handle != nullptr);
-    EXPECT_TRUE(handle->isValid());
-    EXPECT_FALSE(buffer->isDetachedBuffer());
+    sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1)));
+    EXPECT_TRUE(gb->isBufferHubBuffer());
+    EXPECT_EQ(gb->getBufferId(), b1_id);
 }
 
 } // namespace android
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
new file mode 100644
index 0000000..69e1ac8
--- /dev/null
+++ b/libs/ui/tests/Size_test.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2019 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 "SizeTest"
+
+#include <cmath>
+#include <cstdlib>
+
+#include <ui/Size.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace ui {
+
+TEST(SizeTest, BasicConstructionAndEqualityComparison) {
+    Size s(123, 456);
+
+    EXPECT_EQ(123, s.width);
+    EXPECT_EQ(123, s.getWidth());
+
+    EXPECT_EQ(456, s.height);
+    EXPECT_EQ(456, s.getHeight());
+
+    EXPECT_EQ(Size(123, 456), s);
+    EXPECT_NE(Size(456, 123), s);
+}
+
+TEST(SizeTest, BasicLessThanComparison) {
+    EXPECT_TRUE(Size(0, 1) < Size(2, 3));
+    EXPECT_FALSE(Size(2, 3) < Size(0, 1));
+
+    EXPECT_TRUE(Size(0, 3) < Size(2, 1));
+    EXPECT_FALSE(Size(2, 1) < Size(0, 3));
+
+    EXPECT_TRUE(Size(0, 1) < Size(0, 3));
+    EXPECT_FALSE(Size(0, 3) < Size(0, 1));
+
+    EXPECT_FALSE(Size(1, 1) < Size(1, 1));
+}
+
+TEST(SizeTest, ValidAndEmpty) {
+    {
+        Size s;
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(-1, -1);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(1, -1000);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(-1000, 1);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(-1000, -1000);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        const auto& s = Size::INVALID;
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(123, 456);
+        s.makeInvalid();
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(0, 0);
+        EXPECT_TRUE(s.isValid());
+        EXPECT_TRUE(s.isEmpty());
+    }
+
+    {
+        const auto& s = Size::EMPTY;
+        EXPECT_TRUE(s.isValid());
+        EXPECT_TRUE(s.isEmpty());
+    }
+
+    {
+        Size s(123, 456);
+        s.clear();
+        EXPECT_TRUE(s.isValid());
+        EXPECT_TRUE(s.isEmpty());
+    }
+
+    {
+        Size s(123, 456);
+        EXPECT_TRUE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+}
+
+TEST(SizeTest, Set) {
+    {
+        Size s;
+        s.setWidth(0);
+        EXPECT_EQ(Size(0, -1), s);
+    }
+
+    {
+        Size s;
+        s.setHeight(0);
+        EXPECT_EQ(Size(-1, 0), s);
+    }
+
+    {
+        Size s;
+        s.set(123, 456);
+        EXPECT_EQ(Size(123, 456), s);
+    }
+}
+
+template <typename T, typename U>
+void ClampTest(T input, U expected) {
+    // The constructor, set(), setWidth() and setHeight() all allow arbitrary
+    // conversions from other numeric types, and implement clamping if necessary.
+
+    EXPECT_EQ(Size(expected, expected), Size(input, input));
+
+    {
+        Size s;
+        s.set(input, input);
+        EXPECT_EQ(Size(expected, expected), s);
+    }
+
+    {
+        Size s;
+        s.setWidth(input);
+        EXPECT_EQ(expected, s.width);
+    }
+
+    {
+        Size s;
+        s.setHeight(input);
+        EXPECT_EQ(expected, s.height);
+    }
+}
+
+TEST(SizeTest, Int8RangeIsNotClamped) {
+    ClampTest(std::numeric_limits<int8_t>::max(), std::numeric_limits<int8_t>::max());
+    ClampTest(int8_t(0), int8_t(0));
+    ClampTest(std::numeric_limits<int8_t>::lowest(), std::numeric_limits<int8_t>::lowest());
+}
+
+TEST(SizeTest, FloatRangeIsClamped) {
+    ClampTest(std::numeric_limits<float>::max(), std::numeric_limits<int32_t>::max());
+    ClampTest(float(0), int32_t(0));
+    ClampTest(std::numeric_limits<float>::lowest(), std::numeric_limits<int32_t>::lowest());
+}
+
+} // namespace ui
+} // namespace android
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
new file mode 100644
index 0000000..e95a080
--- /dev/null
+++ b/libs/vibrator/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libvibrator",
+
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libutils",
+    ],
+
+    header_libs: [
+        "libaudio_system_headers",
+    ],
+
+    aidl: {
+        include_dirs: ["frameworks/base/core/java"],
+        local_include_dirs: ["include/"],
+        export_aidl_headers: true,
+    },
+
+    srcs: [
+        ":libvibrator_aidl",
+        "*.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wno-unused-parameter",
+    ],
+
+    export_include_dirs: ["include"],
+}
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
new file mode 100644
index 0000000..f6fc19e
--- /dev/null
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vibrator/ExternalVibration.h>
+
+#include <binder/Parcel.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+void writeAudioAttributes(const audio_attributes_t& attrs, android::Parcel* out) {
+    out->writeInt32(attrs.usage);
+    out->writeInt32(attrs.content_type);
+    out->writeInt32(attrs.source);
+    out->writeInt32(attrs.flags);
+}
+
+void readAudioAttributes(audio_attributes_t* attrs, const android::Parcel* in) {
+    attrs->usage = static_cast<audio_usage_t>(in->readInt32());
+    attrs->content_type = static_cast<audio_content_type_t>(in->readInt32());
+    attrs->source = static_cast<audio_source_t>(in->readInt32());
+    attrs->flags = static_cast<audio_flags_mask_t>(in->readInt32());
+}
+
+namespace android {
+namespace os {
+
+ExternalVibration::ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs,
+            sp<IExternalVibrationController> controller) :
+    mUid(uid), mPkg(pkg), mAttrs(attrs), mController(controller) { }
+
+status_t ExternalVibration::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(mUid);
+    parcel->writeString16(String16(mPkg.c_str()));
+    writeAudioAttributes(mAttrs, parcel);
+    parcel->writeStrongBinder(IInterface::asBinder(mController));
+    parcel->writeStrongBinder(mToken);
+    return OK;
+}
+status_t ExternalVibration::readFromParcel(const Parcel* parcel) {
+    mUid = parcel->readInt32();
+    String8 pkgStr8 = String8(parcel->readString16());
+    mPkg = pkgStr8.c_str();
+    readAudioAttributes(&mAttrs, parcel);
+    mController = IExternalVibrationController::asInterface(parcel->readStrongBinder());
+    mToken = parcel->readStrongBinder();
+    return OK;
+}
+
+inline bool ExternalVibration::operator==(const ExternalVibration& rhs) const {
+    return mToken == rhs.mToken;
+}
+
+} // namespace os
+} // namespace android
diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h
new file mode 100644
index 0000000..c6eb3d1
--- /dev/null
+++ b/libs/vibrator/include/vibrator/ExternalVibration.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 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_EXTERNAL_VIBRATION_H
+#define ANDROID_EXTERNAL_VIBRATION_H
+
+#include <android/os/IExternalVibrationController.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/Parcelable.h>
+#include <system/audio.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace os {
+
+class ExternalVibration : public Parcelable, public virtual RefBase {
+public :
+    ExternalVibration() = default;
+    ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs,
+            sp<IExternalVibrationController> controller);
+    virtual ~ExternalVibration() = default;
+    ExternalVibration(const ExternalVibration&) = default;
+
+    bool operator==(const ExternalVibration&) const;
+
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
+
+    int32_t getUid() const { return mUid; }
+    std::string getPackage() const { return mPkg; }
+    audio_attributes_t getAudioAttributes() const { return mAttrs; }
+    sp<IExternalVibrationController> getController() { return mController; }
+
+
+private:
+    int32_t mUid;
+    std::string mPkg;
+    audio_attributes_t mAttrs;
+    sp<IExternalVibrationController> mController;
+    sp<IBinder> mToken = new BBinder();
+};
+
+} // namespace android
+} // namespace os
+
+#endif // ANDROID_EXTERNAL_VIBRATION_H
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index 69b6422..fa92830 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -12,20 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-sourceFiles = [
-    "buffer_hub_client.cpp",
-    "buffer_hub_rpc.cpp",
-    "detached_buffer.cpp",
-    "ion_buffer.cpp",
-]
+cc_library_headers {
+    name: "libbufferhub_headers",
+    export_include_dirs: ["include"],
+    vendor_available: true,  // TODO(b/112338314): Does shouldn't be available to vendor.
+}
 
-localIncludeFiles = [
-    "include",
+sourceFiles = [
+    "buffer_hub_base.cpp",
+    "buffer_hub_rpc.cpp",
+    "consumer_buffer.cpp",
+    "ion_buffer.cpp",
+    "producer_buffer.cpp",
 ]
 
 sharedLibraries = [
     "libbase",
-    "libbinder",
     "libcutils",
     "libhardware",
     "liblog",
@@ -36,6 +38,7 @@
 ]
 
 headerLibraries = [
+    "libbufferhub_headers",
     "libdvr_headers",
     "libnativebase_headers",
 ]
@@ -49,13 +52,16 @@
         "-Wall",
         "-Werror",
     ],
-    export_include_dirs: localIncludeFiles,
     shared_libs: sharedLibraries,
     header_libs: headerLibraries,
     name: "libbufferhub",
     export_header_lib_headers: [
+        "libbufferhub_headers",
         "libnativebase_headers",
     ],
+
+    // TODO(b/117568153): Temporarily opt out using libcrt.
+    no_libcrt: true,
 }
 
 cc_test {
@@ -64,5 +70,7 @@
     shared_libs: sharedLibraries,
     header_libs: headerLibraries,
     name: "buffer_hub-test",
-}
 
+    // TODO(b/117568153): Temporarily opt out using libcrt.
+    no_libcrt: true,
+}
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index e247398..a47a98f 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -1,11 +1,11 @@
 #include <gtest/gtest.h>
 #include <poll.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/bufferhub_rpc.h>
-#include <private/dvr/detached_buffer.h>
+#include <private/dvr/consumer_buffer.h>
+#include <private/dvr/producer_buffer.h>
 #include <sys/epoll.h>
 #include <sys/eventfd.h>
-#include <ui/DetachedBufferHandle.h>
+#include <ui/BufferHubDefs.h>
 
 #include <mutex>
 #include <thread>
@@ -19,102 +19,86 @@
     return result;                            \
   })()
 
-using android::GraphicBuffer;
-using android::sp;
-using android::dvr::BufferConsumer;
-using android::dvr::BufferProducer;
-using android::dvr::DetachedBuffer;
-using android::dvr::BufferHubDefs::IsBufferAcquired;
-using android::dvr::BufferHubDefs::IsBufferGained;
-using android::dvr::BufferHubDefs::IsBufferPosted;
-using android::dvr::BufferHubDefs::IsBufferReleased;
-using android::dvr::BufferHubDefs::kConsumerStateMask;
-using android::dvr::BufferHubDefs::kMetadataHeaderSize;
-using android::dvr::BufferHubDefs::kProducerStateBit;
-using android::pdx::LocalChannelHandle;
+using android::BufferHubDefs::AnyClientAcquired;
+using android::BufferHubDefs::AnyClientGained;
+using android::BufferHubDefs::AnyClientPosted;
+using android::BufferHubDefs::IsClientAcquired;
+using android::BufferHubDefs::IsClientPosted;
+using android::BufferHubDefs::IsClientReleased;
+using android::BufferHubDefs::kFirstClientBitMask;
+using android::dvr::ConsumerBuffer;
+using android::dvr::ProducerBuffer;
 using android::pdx::LocalHandle;
 using android::pdx::Status;
 
 const int kWidth = 640;
 const int kHeight = 480;
-const int kLayerCount = 1;
 const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
 const int kUsage = 0;
-const size_t kUserMetadataSize = 0;
-const uint64_t kContext = 42;
-const size_t kMaxConsumerCount = 63;
+// Maximum number of consumers for the buffer that only has one producer in the
+// test.
+const size_t kMaxConsumerCount =
+    android::BufferHubDefs::kMaxNumberOfClients - 1;
 const int kPollTimeoutMs = 100;
 
 using LibBufferHubTest = ::testing::Test;
 
 TEST_F(LibBufferHubTest, TestBasicUsage) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
-  ASSERT_TRUE(c.get() != nullptr);
+  std::unique_ptr<ConsumerBuffer> c1 =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c1.get() != nullptr);
   // Check that consumers can spawn other consumers.
-  std::unique_ptr<BufferConsumer> c2 =
-      BufferConsumer::Import(c->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c2 =
+      ConsumerBuffer::Import(c1->CreateConsumer());
   ASSERT_TRUE(c2.get() != nullptr);
 
-  // Producer state mask is unique, i.e. 1.
-  EXPECT_EQ(p->buffer_state_bit(), kProducerStateBit);
-  // Consumer state mask cannot have producer bit on.
-  EXPECT_EQ(c->buffer_state_bit() & kProducerStateBit, 0U);
-  // Consumer state mask must be a single, i.e. power of 2.
-  EXPECT_NE(c->buffer_state_bit(), 0U);
-  EXPECT_EQ(c->buffer_state_bit() & (c->buffer_state_bit() - 1), 0U);
-  // Consumer state mask cannot have producer bit on.
-  EXPECT_EQ(c2->buffer_state_bit() & kProducerStateBit, 0U);
-  // Consumer state mask must be a single, i.e. power of 2.
-  EXPECT_NE(c2->buffer_state_bit(), 0U);
-  EXPECT_EQ(c2->buffer_state_bit() & (c2->buffer_state_bit() - 1), 0U);
-  // Each consumer should have unique bit.
-  EXPECT_EQ(c->buffer_state_bit() & c2->buffer_state_bit(), 0U);
+  // Checks the state masks of client p, c1 and c2.
+  EXPECT_EQ(p->client_state_mask(), kFirstClientBitMask);
+  EXPECT_EQ(c1->client_state_mask(), kFirstClientBitMask << 1);
+  EXPECT_EQ(c2->client_state_mask(), kFirstClientBitMask << 2);
 
   // Initial state: producer not available, consumers not available.
   EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-  EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
   EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
 
-  EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
+  EXPECT_EQ(0, p->GainAsync());
+  EXPECT_EQ(0, p->Post(LocalHandle()));
 
   // New state: producer not available, consumers available.
   EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-  EXPECT_EQ(1, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(1, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
   EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
 
-  uint64_t context;
   LocalHandle fence;
-  EXPECT_EQ(0, c->Acquire(&fence, &context));
-  EXPECT_EQ(kContext, context);
-  EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, c1->Acquire(&fence));
+  EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
   EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
 
-  EXPECT_EQ(0, c2->Acquire(&fence, &context));
-  EXPECT_EQ(kContext, context);
+  EXPECT_EQ(0, c2->Acquire(&fence));
   EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
-  EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
 
-  EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_EQ(0, c1->Release(LocalHandle()));
   EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
   EXPECT_EQ(0, c2->Discard());
-
   EXPECT_EQ(1, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+
   EXPECT_EQ(0, p->Gain(&fence));
   EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-  EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
   EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
 }
 
 TEST_F(LibBufferHubTest, TestEpoll) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
 
   LocalHandle epoll_fd{epoll_create1(EPOLL_CLOEXEC)};
@@ -146,8 +130,9 @@
   // No events should be signaled initially.
   ASSERT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 0));
 
-  // Post the producer and check for consumer signal.
-  EXPECT_EQ(0, p->Post({}, kContext));
+  // Gain and post the producer and check for consumer signal.
+  EXPECT_EQ(0, p->GainAsync());
+  EXPECT_EQ(0, p->Post({}));
   ASSERT_EQ(1, epoll_wait(epoll_fd.Get(), events.data(), events.size(),
                           kPollTimeoutMs));
   ASSERT_TRUE(events[0].events & EPOLLIN);
@@ -177,21 +162,21 @@
 }
 
 TEST_F(LibBufferHubTest, TestStateMask) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
 
   // It's ok to create up to kMaxConsumerCount consumer buffers.
-  uint64_t buffer_state_bits = p->buffer_state_bit();
-  std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs;
+  uint32_t client_state_masks = p->client_state_mask();
+  std::array<std::unique_ptr<ConsumerBuffer>, kMaxConsumerCount> cs;
   for (size_t i = 0; i < kMaxConsumerCount; i++) {
-    cs[i] = BufferConsumer::Import(p->CreateConsumer());
+    cs[i] = ConsumerBuffer::Import(p->CreateConsumer());
     ASSERT_TRUE(cs[i].get() != nullptr);
     // Expect all buffers have unique state mask.
-    EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0U);
-    buffer_state_bits |= cs[i]->buffer_state_bit();
+    EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U);
+    client_state_masks |= cs[i]->client_state_mask();
   }
-  EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask);
+  EXPECT_EQ(client_state_masks, ~0U);
 
   // The 64th creation will fail with out-of-memory error.
   auto state = p->CreateConsumer();
@@ -199,97 +184,84 @@
 
   // Release any consumer should allow us to re-create.
   for (size_t i = 0; i < kMaxConsumerCount; i++) {
-    buffer_state_bits &= ~cs[i]->buffer_state_bit();
+    client_state_masks &= ~cs[i]->client_state_mask();
     cs[i] = nullptr;
-    cs[i] = BufferConsumer::Import(p->CreateConsumer());
+    cs[i] = ConsumerBuffer::Import(p->CreateConsumer());
     ASSERT_TRUE(cs[i].get() != nullptr);
     // The released state mask will be reused.
-    EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0U);
-    buffer_state_bits |= cs[i]->buffer_state_bit();
-    EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask);
+    EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U);
+    client_state_masks |= cs[i]->client_state_mask();
   }
 }
 
 TEST_F(LibBufferHubTest, TestStateTransitions) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
 
-  uint64_t context;
   LocalHandle fence;
+  EXPECT_EQ(0, p->GainAsync());
 
-  // The producer buffer starts in gained state.
-
-  // Acquire, release, and gain in gained state should fail.
-  EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
-  EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
-  EXPECT_EQ(-EALREADY, p->Gain(&fence));
+  // Acquire in gained state should fail.
+  EXPECT_EQ(-EBUSY, c->Acquire(&fence));
 
   // Post in gained state should succeed.
-  EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
+  EXPECT_EQ(0, p->Post(LocalHandle()));
 
-  // Post, release, and gain in posted state should fail.
-  EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext));
-  EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
+  // Post and gain in posted state should fail.
+  EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
   EXPECT_EQ(-EBUSY, p->Gain(&fence));
 
   // Acquire in posted state should succeed.
-  EXPECT_LE(0, c->Acquire(&fence, &context));
+  EXPECT_EQ(0, c->Acquire(&fence));
 
   // Acquire, post, and gain in acquired state should fail.
-  EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
-  EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext));
+  EXPECT_EQ(-EBUSY, c->Acquire(&fence));
+  EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
   EXPECT_EQ(-EBUSY, p->Gain(&fence));
 
   // Release in acquired state should succeed.
   EXPECT_EQ(0, c->Release(LocalHandle()));
   EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
 
-  // Release, acquire, and post in released state should fail.
-  EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
-  EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
-  EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext));
+  // Acquire and post in released state should fail.
+  EXPECT_EQ(-EBUSY, c->Acquire(&fence));
+  EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
 
   // Gain in released state should succeed.
   EXPECT_EQ(0, p->Gain(&fence));
 
-  // Acquire, release, and gain in gained state should fail.
-  EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
-  EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
-  EXPECT_EQ(-EALREADY, p->Gain(&fence));
+  // Acquire in gained state should fail.
+  EXPECT_EQ(-EBUSY, c->Acquire(&fence));
 }
 
 TEST_F(LibBufferHubTest, TestAsyncStateTransitions) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
 
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
+  EXPECT_EQ(0, p->GainAsync());
 
-  // The producer buffer starts in gained state.
-
-  // Acquire, release, and gain in gained state should fail.
+  // Acquire in gained state should fail.
   EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
-  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
-  EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
 
   // Post in gained state should succeed.
   EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
   EXPECT_EQ(p->buffer_state(), c->buffer_state());
-  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+  EXPECT_TRUE(AnyClientPosted(p->buffer_state()));
 
-  // Post, release, and gain in posted state should fail.
+  // Post and gain in posted state should fail.
   EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
-  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
   EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
 
@@ -298,7 +270,7 @@
   EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
   EXPECT_EQ(p->buffer_state(), c->buffer_state());
-  EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+  EXPECT_TRUE(AnyClientAcquired(p->buffer_state()));
 
   // Acquire, post, and gain in acquired state should fail.
   EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
@@ -311,10 +283,9 @@
   EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence));
   EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
   EXPECT_EQ(p->buffer_state(), c->buffer_state());
-  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+  EXPECT_TRUE(p->is_released());
 
-  // Release, acquire, and post in released state should fail.
-  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+  // Acquire and post in released state should fail.
   EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
   EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
@@ -323,75 +294,114 @@
   EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
   EXPECT_EQ(p->buffer_state(), c->buffer_state());
-  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+  EXPECT_TRUE(AnyClientGained(p->buffer_state()));
 
-  // Acquire, release, and gain in gained state should fail.
+  // Acquire and gain in gained state should fail.
   EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
   EXPECT_FALSE(invalid_fence.IsValid());
-  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
-  EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
-  EXPECT_FALSE(invalid_fence.IsValid());
 }
 
-TEST_F(LibBufferHubTest, TestZeroConsumer) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+TEST_F(LibBufferHubTest, TestGainTwiceByTheSameProducer) {
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
 
+  ASSERT_EQ(0, p->GainAsync());
+  ASSERT_EQ(0, p->GainAsync());
+}
+
+TEST_F(LibBufferHubTest, TestGainPostedBuffer) {
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+  ASSERT_EQ(0, p->GainAsync());
+  ASSERT_EQ(0, p->Post(LocalHandle()));
+  ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
+
+  // Gain in posted state should only succeed with gain_posted_buffer = true.
+  LocalHandle invalid_fence;
+  EXPECT_EQ(-EBUSY, p->Gain(&invalid_fence, false));
+  EXPECT_EQ(0, p->Gain(&invalid_fence, true));
+}
+
+TEST_F(LibBufferHubTest, TestGainPostedBufferAsync) {
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+  ASSERT_EQ(0, p->GainAsync());
+  ASSERT_EQ(0, p->Post(LocalHandle()));
+  ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
+
+  // GainAsync in posted state should only succeed with gain_posted_buffer
+  // equals true.
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
+  EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence, false));
+  EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence, true));
+}
 
-  // Newly created.
-  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
-  EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
-  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+TEST_F(LibBufferHubTest, TestGainPostedBuffer_noConsumer) {
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  ASSERT_EQ(0, p->GainAsync());
+  ASSERT_EQ(0, p->Post(LocalHandle()));
+  // Producer state bit is in released state after post, other clients shall be
+  // in posted state although there is no consumer of this buffer yet.
+  ASSERT_TRUE(IsClientReleased(p->buffer_state(), p->client_state_mask()));
+  ASSERT_TRUE(p->is_released());
+  ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
 
-  // The buffer should stay in posted stay until a consumer picks it up.
-  EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-
-  // A new consumer should still be able to acquire the buffer immediately.
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
-  ASSERT_TRUE(c.get() != nullptr);
-  EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
-  EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+  // Gain in released state should succeed.
+  LocalHandle invalid_fence;
+  EXPECT_EQ(0, p->Gain(&invalid_fence, false));
 }
 
 TEST_F(LibBufferHubTest, TestMaxConsumers) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
+  uint32_t producer_state_mask = p->client_state_mask();
 
-  std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs;
-  for (size_t i = 0; i < kMaxConsumerCount; i++) {
-    cs[i] = BufferConsumer::Import(p->CreateConsumer());
+  std::array<std::unique_ptr<ConsumerBuffer>, kMaxConsumerCount> cs;
+  for (size_t i = 0; i < kMaxConsumerCount; ++i) {
+    cs[i] = ConsumerBuffer::Import(p->CreateConsumer());
     ASSERT_TRUE(cs[i].get() != nullptr);
-    EXPECT_TRUE(IsBufferGained(cs[i]->buffer_state()));
+    EXPECT_TRUE(cs[i]->is_released());
+    EXPECT_NE(producer_state_mask, cs[i]->client_state_mask());
   }
 
+  EXPECT_EQ(0, p->GainAsync());
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
 
   // Post the producer should trigger all consumers to be available.
   EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
-  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
-  for (size_t i = 0; i < kMaxConsumerCount; i++) {
+  EXPECT_TRUE(IsClientReleased(p->buffer_state(), p->client_state_mask()));
+  for (size_t i = 0; i < kMaxConsumerCount; ++i) {
     EXPECT_TRUE(
-        IsBufferPosted(cs[i]->buffer_state(), cs[i]->buffer_state_bit()));
+        IsClientPosted(cs[i]->buffer_state(), cs[i]->client_state_mask()));
     EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(kPollTimeoutMs)));
     EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence));
-    EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+    EXPECT_TRUE(
+        IsClientAcquired(p->buffer_state(), cs[i]->client_state_mask()));
   }
 
   // All consumers have to release before the buffer is considered to be
   // released.
   for (size_t i = 0; i < kMaxConsumerCount; i++) {
-    EXPECT_FALSE(IsBufferReleased(p->buffer_state()));
+    EXPECT_FALSE(p->is_released());
     EXPECT_EQ(0, cs[i]->ReleaseAsync(&metadata, invalid_fence));
   }
 
   EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+  EXPECT_TRUE(p->is_released());
 
   // Buffer state cross all clients must be consistent.
   for (size_t i = 0; i < kMaxConsumerCount; i++) {
@@ -400,58 +410,62 @@
 }
 
 TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferGained) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+  EXPECT_EQ(0, p->GainAsync());
+  EXPECT_TRUE(AnyClientGained(p->buffer_state()));
 
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
-  EXPECT_TRUE(IsBufferGained(c->buffer_state()));
+  EXPECT_TRUE(AnyClientGained(c->buffer_state()));
 
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
 
   // Post the gained buffer should signal already created consumer.
   EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
-  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+  EXPECT_TRUE(AnyClientPosted(p->buffer_state()));
   EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
   EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
-  EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+  EXPECT_TRUE(AnyClientAcquired(c->buffer_state()));
 }
 
-TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+TEST_F(LibBufferHubTest, TestCreateTheFirstConsumerAfterPostingBuffer) {
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+  EXPECT_EQ(0, p->GainAsync());
+  EXPECT_TRUE(AnyClientGained(p->buffer_state()));
 
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
 
   // Post the gained buffer before any consumer gets created.
   EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
-  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+  EXPECT_TRUE(p->is_released());
+  EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
 
-  // Newly created consumer should be automatically sigalled.
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  // Newly created consumer will be signalled for the posted buffer although it
+  // is created after producer posting.
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
-  EXPECT_TRUE(IsBufferPosted(c->buffer_state()));
+  EXPECT_TRUE(IsClientPosted(c->buffer_state(), c->client_state_mask()));
   EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
-  EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
 }
 
 TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
 
-  std::unique_ptr<BufferConsumer> c1 =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c1 =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c1.get() != nullptr);
 
+  EXPECT_EQ(0, p->GainAsync());
   DvrNativeBufferMetadata metadata;
   LocalHandle invalid_fence;
 
@@ -466,17 +480,17 @@
   // need to wait until the releasd is confirmed before creating another
   // consumer.
   EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+  EXPECT_TRUE(p->is_released());
 
   // Create another consumer immediately after the release, should not make the
   // buffer un-released.
-  std::unique_ptr<BufferConsumer> c2 =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c2 =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c2.get() != nullptr);
 
-  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+  EXPECT_TRUE(p->is_released());
   EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
-  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+  EXPECT_TRUE(AnyClientGained(p->buffer_state()));
 }
 
 TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
@@ -484,23 +498,21 @@
     int64_t field1;
     int64_t field2;
   };
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
-
+  EXPECT_EQ(0, p->GainAsync());
   Metadata m = {1, 3};
-  EXPECT_EQ(0, p->Post(LocalHandle(), m));
+  EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(Metadata)));
   EXPECT_LE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
-
   LocalHandle fence;
   Metadata m2 = {};
-  EXPECT_EQ(0, c->Acquire(&fence, &m2));
+  EXPECT_EQ(0, c->Acquire(&fence, &m2, sizeof(m2)));
   EXPECT_EQ(m.field1, m2.field1);
   EXPECT_EQ(m.field2, m2.field2);
-
   EXPECT_EQ(0, c->Release(LocalHandle()));
   EXPECT_LT(0, RETRY_EINTR(p->Poll(0)));
 }
@@ -515,23 +527,23 @@
     int64_t field2;
     int64_t field3;
   };
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   // It is illegal to post metadata larger than originally requested during
   // buffer allocation.
   OverSizedMetadata evil_meta = {};
-  EXPECT_NE(0, p->Post(LocalHandle(), evil_meta));
+  EXPECT_NE(0, p->Post(LocalHandle(), &evil_meta, sizeof(OverSizedMetadata)));
   EXPECT_GE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
 
   // It is ok to post metadata smaller than originally requested during
   // buffer allocation.
-  int64_t sequence = 42;
-  EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
+  EXPECT_EQ(0, p->Post(LocalHandle()));
 }
 
 TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) {
@@ -544,15 +556,16 @@
     int64_t field2;
     int64_t field3;
   };
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   Metadata m = {1, 3};
-  EXPECT_EQ(0, p->Post(LocalHandle(), m));
+  EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(m)));
 
   LocalHandle fence;
   int64_t sequence;
@@ -560,53 +573,56 @@
 
   // It is illegal to acquire metadata larger than originally requested during
   // buffer allocation.
-  EXPECT_NE(0, c->Acquire(&fence, &e));
+  EXPECT_NE(0, c->Acquire(&fence, &e, sizeof(e)));
 
   // It is ok to acquire metadata smaller than originally requested during
   // buffer allocation.
-  EXPECT_EQ(0, c->Acquire(&fence, &sequence));
+  EXPECT_EQ(0, c->Acquire(&fence, &sequence, sizeof(sequence)));
   EXPECT_EQ(m.field1, sequence);
 }
 
 TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   int64_t sequence = 3;
-  EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
+  EXPECT_EQ(0, p->Post(LocalHandle(), &sequence, sizeof(sequence)));
 
   LocalHandle fence;
   EXPECT_EQ(0, c->Acquire(&fence));
 }
 
 TEST_F(LibBufferHubTest, TestWithNoMeta) {
-  std::unique_ptr<BufferProducer> p =
-      BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+  std::unique_ptr<ProducerBuffer> p =
+      ProducerBuffer::Create(kWidth, kHeight, kFormat, kUsage);
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   LocalHandle fence;
 
-  EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+  EXPECT_EQ(0, p->Post(LocalHandle()));
   EXPECT_EQ(0, c->Acquire(&fence));
 }
 
 TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) {
-  std::unique_ptr<BufferProducer> p =
-      BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+  std::unique_ptr<ProducerBuffer> p =
+      ProducerBuffer::Create(kWidth, kHeight, kFormat, kUsage);
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   int64_t sequence = 3;
-  EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+  EXPECT_NE(0, p->Post(LocalHandle(), &sequence, sizeof(sequence)));
 }
 
 namespace {
@@ -619,12 +635,13 @@
 }  // namespace
 
 TEST_F(LibBufferHubTest, TestAcquireFence) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, /*metadata_size=*/0);
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, p->GainAsync());
 
   DvrNativeBufferMetadata meta;
   LocalHandle f1(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
@@ -680,59 +697,112 @@
 }
 
 TEST_F(LibBufferHubTest, TestOrphanedAcquire) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
-  std::unique_ptr<BufferConsumer> c1 =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c1 =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(c1.get() != nullptr);
-  const uint64_t consumer_state_bit1 = c1->buffer_state_bit();
+  const uint32_t client_state_mask1 = c1->client_state_mask();
 
+  EXPECT_EQ(0, p->GainAsync());
   DvrNativeBufferMetadata meta;
   EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
 
   LocalHandle fence;
   EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
-  EXPECT_LE(0, c1->AcquireAsync(&meta, &fence));
-  // Destroy the consumer now will make it orphaned and the buffer is still
-  // acquired.
+  EXPECT_EQ(0, c1->AcquireAsync(&meta, &fence));
+
+  // Destroy the consumer who has acquired but not released the buffer.
   c1 = nullptr;
-  EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-
-  std::unique_ptr<BufferConsumer> c2 =
-      BufferConsumer::Import(p->CreateConsumer());
-  ASSERT_TRUE(c2.get() != nullptr);
-  const uint64_t consumer_state_bit2 = c2->buffer_state_bit();
-  EXPECT_NE(consumer_state_bit1, consumer_state_bit2);
-
-  // The new consumer is available for acquire.
-  EXPECT_LT(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
-  EXPECT_LE(0, c2->AcquireAsync(&meta, &fence));
-  // Releasing the consumer makes the buffer gainable.
-  EXPECT_EQ(0, c2->ReleaseAsync(&meta, LocalHandle()));
 
   // The buffer is now available for the producer to gain.
   EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
 
-  // But if another consumer is created in released state.
-  std::unique_ptr<BufferConsumer> c3 =
-      BufferConsumer::Import(p->CreateConsumer());
-  ASSERT_TRUE(c3.get() != nullptr);
-  const uint64_t consumer_state_bit3 = c3->buffer_state_bit();
-  EXPECT_NE(consumer_state_bit2, consumer_state_bit3);
-  // The consumer buffer is not acquirable.
-  EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
-  EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &fence));
+  // Newly added consumer is not able to acquire the buffer.
+  std::unique_ptr<ConsumerBuffer> c2 =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c2.get() != nullptr);
+  const uint32_t client_state_mask2 = c2->client_state_mask();
+  EXPECT_NE(client_state_mask1, client_state_mask2);
+  EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(-EBUSY, c2->AcquireAsync(&meta, &fence));
 
-  // Producer should be able to gain no matter what.
-  EXPECT_EQ(0, p->GainAsync(&meta, &fence));
+  // Producer should be able to gain.
+  EXPECT_EQ(0, p->GainAsync(&meta, &fence, false));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireLastPosted) {
+  std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<ConsumerBuffer> c1 =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c1.get() != nullptr);
+  const uint32_t client_state_mask1 = c1->client_state_mask();
+
+  EXPECT_EQ(0, p->GainAsync());
+  DvrNativeBufferMetadata meta;
+  EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
+  EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
+
+  // c2 is created when the buffer is in posted state. buffer state for c1 is
+  // posted. Thus, c2 should be automatically set to posted and able to acquire.
+  std::unique_ptr<ConsumerBuffer> c2 =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c2.get() != nullptr);
+  const uint32_t client_state_mask2 = c2->client_state_mask();
+  EXPECT_NE(client_state_mask1, client_state_mask2);
+  EXPECT_LT(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+  LocalHandle invalid_fence;
+  EXPECT_EQ(0, c2->AcquireAsync(&meta, &invalid_fence));
+
+  EXPECT_EQ(0, c1->AcquireAsync(&meta, &invalid_fence));
+
+  // c3 is created when the buffer is in acquired state. buffer state for c1 and
+  // c2 are acquired. Thus, c3 should be automatically set to posted and able to
+  // acquire.
+  std::unique_ptr<ConsumerBuffer> c3 =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c3.get() != nullptr);
+  const uint32_t client_state_mask3 = c3->client_state_mask();
+  EXPECT_NE(client_state_mask1, client_state_mask3);
+  EXPECT_NE(client_state_mask2, client_state_mask3);
+  EXPECT_LT(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(0, c3->AcquireAsync(&meta, &invalid_fence));
+
+  // Releasing c2 and c3 in normal ways.
+  EXPECT_EQ(0, c2->Release(LocalHandle()));
+  EXPECT_EQ(0, c3->ReleaseAsync(&meta, LocalHandle()));
+
+  // Destroy the c1 who has not released the buffer.
+  c1 = nullptr;
+
+  // The buffer is now available for the producer to gain.
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+
+  // C4 is created in released state. Thus, it cannot gain the just posted
+  // buffer.
+  std::unique_ptr<ConsumerBuffer> c4 =
+      ConsumerBuffer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c4.get() != nullptr);
+  const uint32_t client_state_mask4 = c4->client_state_mask();
+  EXPECT_NE(client_state_mask3, client_state_mask4);
+  EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
+  EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &invalid_fence));
+
+  // Producer should be able to gain.
+  EXPECT_EQ(0, p->GainAsync(&meta, &invalid_fence));
 }
 
 TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) {
-  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+  // TODO(b/112338294) rewrite test after migration
+  return;
+
+  /* std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
-  std::unique_ptr<BufferConsumer> c =
-      BufferConsumer::Import(p->CreateConsumer());
+  std::unique_ptr<ConsumerBuffer> c =
+      ConsumerBuffer::Import(p->CreateConsumer());
   ASSERT_TRUE(p.get() != nullptr);
   ASSERT_TRUE(c.get() != nullptr);
 
@@ -741,6 +811,7 @@
   int p_id = p->id();
 
   // Detach in posted state should fail.
+  EXPECT_EQ(0, p->GainAsync());
   EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
   EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
   auto s1 = p->Detach();
@@ -789,159 +860,37 @@
   // is gone.
   EXPECT_EQ(s3.error(), EPIPE);
 
-  // Detached buffer handle can be use to construct a new DetachedBuffer object.
-  auto d = DetachedBuffer::Import(std::move(handle));
+  // Detached buffer handle can be use to construct a new BufferHubBuffer
+  // object.
+  auto d = BufferHubBuffer::Import(std::move(handle));
   EXPECT_FALSE(handle.valid());
   EXPECT_TRUE(d->IsConnected());
   EXPECT_TRUE(d->IsValid());
 
-  ASSERT_TRUE(d->buffer() != nullptr);
-  EXPECT_EQ(d->buffer()->initCheck(), 0);
-  EXPECT_EQ(d->id(), p_id);
+  EXPECT_EQ(d->id(), p_id); */
 }
 
-TEST_F(LibBufferHubTest, TestCreateDetachedBufferFails) {
-  // Buffer Creation will fail: BLOB format requires height to be 1.
-  auto b1 = DetachedBuffer::Create(kWidth, /*height=2*/ 2, kLayerCount,
-                                   /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage,
-                                   kUserMetadataSize);
+TEST_F(LibBufferHubTest, TestDetach) {
+  // TODO(b/112338294) rewrite test after migration
+  return;
 
-  EXPECT_FALSE(b1->IsConnected());
-  EXPECT_FALSE(b1->IsValid());
-  EXPECT_TRUE(b1->buffer() == nullptr);
-
-  // Buffer Creation will fail: user metadata size too large.
-  auto b2 = DetachedBuffer::Create(
-      kWidth, kHeight, kLayerCount, kFormat, kUsage,
-      /*user_metadata_size=*/std::numeric_limits<size_t>::max());
-
-  EXPECT_FALSE(b2->IsConnected());
-  EXPECT_FALSE(b2->IsValid());
-  EXPECT_TRUE(b2->buffer() == nullptr);
-
-  // Buffer Creation will fail: user metadata size too large.
-  auto b3 = DetachedBuffer::Create(
-      kWidth, kHeight, kLayerCount, kFormat, kUsage,
-      /*user_metadata_size=*/std::numeric_limits<size_t>::max() -
-          kMetadataHeaderSize);
-
-  EXPECT_FALSE(b3->IsConnected());
-  EXPECT_FALSE(b3->IsValid());
-  EXPECT_TRUE(b3->buffer() == nullptr);
-}
-
-TEST_F(LibBufferHubTest, TestCreateDetachedBuffer) {
-  auto b1 = DetachedBuffer::Create(kWidth, kHeight, kLayerCount, kFormat,
-                                   kUsage, kUserMetadataSize);
-  int b1_id = b1->id();
-
-  EXPECT_TRUE(b1->IsConnected());
-  EXPECT_TRUE(b1->IsValid());
-  ASSERT_TRUE(b1->buffer() != nullptr);
-  EXPECT_NE(b1->id(), 0);
-  EXPECT_EQ(b1->buffer()->initCheck(), 0);
-  EXPECT_FALSE(b1->buffer()->isDetachedBuffer());
-
-  // Takes a standalone GraphicBuffer which still holds on an
-  // PDX::LocalChannelHandle towards BufferHub.
-  sp<GraphicBuffer> g1 = b1->TakeGraphicBuffer();
-  ASSERT_TRUE(g1 != nullptr);
-  EXPECT_TRUE(g1->isDetachedBuffer());
-
-  EXPECT_FALSE(b1->IsConnected());
-  EXPECT_FALSE(b1->IsValid());
-  EXPECT_TRUE(b1->buffer() == nullptr);
-
-  sp<GraphicBuffer> g2 = b1->TakeGraphicBuffer();
-  ASSERT_TRUE(g2 == nullptr);
-
-  auto h1 = g1->takeDetachedBufferHandle();
-  ASSERT_TRUE(h1 != nullptr);
-  ASSERT_TRUE(h1->isValid());
-  EXPECT_FALSE(g1->isDetachedBuffer());
-
-  auto b2 = DetachedBuffer::Import(std::move(h1->handle()));
-  ASSERT_FALSE(h1->isValid());
-  EXPECT_TRUE(b2->IsConnected());
-  EXPECT_TRUE(b2->IsValid());
-
-  ASSERT_TRUE(b2->buffer() != nullptr);
-  EXPECT_EQ(b2->buffer()->initCheck(), 0);
-
-  // The newly created DetachedBuffer should share the original buffer_id.
-  EXPECT_EQ(b2->id(), b1_id);
-  EXPECT_FALSE(b2->buffer()->isDetachedBuffer());
-}
-
-TEST_F(LibBufferHubTest, TestPromoteDetachedBuffer) {
-  auto b1 = DetachedBuffer::Create(kWidth, kHeight, kLayerCount, kFormat,
-                                   kUsage, kUserMetadataSize);
-  int b1_id = b1->id();
-  EXPECT_TRUE(b1->IsValid());
-
-  auto status_or_handle = b1->Promote();
-  EXPECT_TRUE(status_or_handle);
-
-  // The detached buffer should have hangup.
-  EXPECT_GT(RETRY_EINTR(b1->Poll(kPollTimeoutMs)), 0);
-  auto status_or_int = b1->GetEventMask(POLLHUP);
-  EXPECT_TRUE(status_or_int.ok());
-  EXPECT_EQ(status_or_int.get(), POLLHUP);
-
-  // The buffer client is still considered as connected but invalid.
-  EXPECT_TRUE(b1->IsConnected());
-  EXPECT_FALSE(b1->IsValid());
-
-  // Gets the channel handle for the producer.
-  LocalChannelHandle h1 = status_or_handle.take();
-  EXPECT_TRUE(h1.valid());
-
-  std::unique_ptr<BufferProducer> p1 = BufferProducer::Import(std::move(h1));
-  EXPECT_FALSE(h1.valid());
-  ASSERT_TRUE(p1 != nullptr);
-  int p1_id = p1->id();
-
-  // A newly promoted ProducerBuffer should inherit the same buffer id.
-  EXPECT_EQ(b1_id, p1_id);
-  EXPECT_TRUE(IsBufferGained(p1->buffer_state()));
-}
-
-TEST_F(LibBufferHubTest, TestDetachThenPromote) {
-  std::unique_ptr<BufferProducer> p1 = BufferProducer::Create(
+  /* std::unique_ptr<ProducerBuffer> p1 = ProducerBuffer::Create(
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p1.get() != nullptr);
   int p1_id = p1->id();
 
-  // Detached the producer.
+  // Detached the producer from gained state.
+  EXPECT_EQ(0, p1->GainAsync());
   auto status_or_handle = p1->Detach();
   EXPECT_TRUE(status_or_handle.ok());
   LocalChannelHandle h1 = status_or_handle.take();
   EXPECT_TRUE(h1.valid());
 
-  // Detached buffer handle can be use to construct a new DetachedBuffer object.
-  auto b1 = DetachedBuffer::Import(std::move(h1));
+  // Detached buffer handle can be use to construct a new BufferHubBuffer
+  // object.
+  auto b1 = BufferHubBuffer::Import(std::move(h1));
   EXPECT_FALSE(h1.valid());
   EXPECT_TRUE(b1->IsValid());
   int b1_id = b1->id();
-  EXPECT_EQ(b1_id, p1_id);
-
-  // Promote the detached buffer.
-  status_or_handle = b1->Promote();
-  // The buffer client is still considered as connected but invalid.
-  EXPECT_TRUE(b1->IsConnected());
-  EXPECT_FALSE(b1->IsValid());
-  EXPECT_TRUE(status_or_handle.ok());
-
-  // Gets the channel handle for the producer.
-  LocalChannelHandle h2 = status_or_handle.take();
-  EXPECT_TRUE(h2.valid());
-
-  std::unique_ptr<BufferProducer> p2 = BufferProducer::Import(std::move(h2));
-  EXPECT_FALSE(h2.valid());
-  ASSERT_TRUE(p2 != nullptr);
-  int p2_id = p2->id();
-
-  // A newly promoted ProducerBuffer should inherit the same buffer id.
-  EXPECT_EQ(b1_id, p2_id);
-  EXPECT_TRUE(IsBufferGained(p2->buffer_state()));
+  EXPECT_EQ(b1_id, p1_id); */
 }
diff --git a/libs/vr/libbufferhub/buffer_hub_base.cpp b/libs/vr/libbufferhub/buffer_hub_base.cpp
new file mode 100644
index 0000000..8497f3e
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_base.cpp
@@ -0,0 +1,229 @@
+#include <poll.h>
+#include <sys/epoll.h>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/buffer_hub_base.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Status;
+using android::pdx::default_transport::ClientChannel;
+using android::pdx::default_transport::ClientChannelFactory;
+
+namespace android {
+namespace dvr {
+
+BufferHubBase::BufferHubBase(LocalChannelHandle channel_handle)
+    : Client{pdx::default_transport::ClientChannel::Create(
+          std::move(channel_handle))},
+      id_(-1),
+      cid_(-1) {}
+BufferHubBase::BufferHubBase(const std::string& endpoint_path)
+    : Client{pdx::default_transport::ClientChannelFactory::Create(
+          endpoint_path)},
+      id_(-1),
+      cid_(-1) {}
+
+BufferHubBase::~BufferHubBase() {
+  // buffer_state and fence_state are not reset here. They will be used to
+  // clean up epoll fd if necessary in ProducerChannel::RemoveConsumer method.
+  if (metadata_header_ != nullptr) {
+    metadata_buffer_.Unlock();
+  }
+}
+
+Status<LocalChannelHandle> BufferHubBase::CreateConsumer() {
+  Status<LocalChannelHandle> status =
+      InvokeRemoteMethod<BufferHubRPC::NewConsumer>();
+  ALOGE_IF(!status,
+           "BufferHub::CreateConsumer: Failed to create consumer channel: %s",
+           status.GetErrorMessage().c_str());
+  return status;
+}
+
+int BufferHubBase::ImportBuffer() {
+  ATRACE_NAME("BufferHubBase::ImportBuffer");
+
+  Status<BufferDescription<LocalHandle>> status =
+      InvokeRemoteMethod<BufferHubRPC::GetBuffer>();
+  if (!status) {
+    ALOGE("BufferHubBase::ImportBuffer: Failed to get buffer: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  } else if (status.get().id() < 0) {
+    ALOGE("BufferHubBase::ImportBuffer: Received an invalid id!");
+    return -EIO;
+  }
+
+  auto buffer_desc = status.take();
+
+  // Stash the buffer id to replace the value in id_.
+  const int new_id = buffer_desc.id();
+
+  // Import the buffer.
+  IonBuffer ion_buffer;
+  ALOGD_IF(TRACE, "BufferHubBase::ImportBuffer: id=%d.", buffer_desc.id());
+
+  if (const int ret = buffer_desc.ImportBuffer(&ion_buffer))
+    return ret;
+
+  // Import the metadata.
+  IonBuffer metadata_buffer;
+  if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) {
+    ALOGE("Failed to import metadata buffer, error=%d", ret);
+    return ret;
+  }
+  size_t metadata_buf_size = metadata_buffer.width();
+  if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) {
+    ALOGE("BufferHubBase::ImportBuffer: metadata buffer too small: %zu",
+          metadata_buf_size);
+    return -ENOMEM;
+  }
+
+  // If all imports succee, replace the previous buffer and id.
+  buffer_ = std::move(ion_buffer);
+  metadata_buffer_ = std::move(metadata_buffer);
+  metadata_buf_size_ = metadata_buf_size;
+  user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize;
+
+  void* metadata_ptr = nullptr;
+  if (const int ret =
+          metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0,
+                                /*y=*/0, metadata_buf_size_,
+                                /*height=*/1, &metadata_ptr)) {
+    ALOGE("BufferHubBase::ImportBuffer: Failed to lock metadata.");
+    return ret;
+  }
+
+  // Set up shared fences.
+  shared_acquire_fence_ = buffer_desc.take_acquire_fence();
+  shared_release_fence_ = buffer_desc.take_release_fence();
+  if (!shared_acquire_fence_ || !shared_release_fence_) {
+    ALOGE("BufferHubBase::ImportBuffer: Failed to import shared fences.");
+    return -EIO;
+  }
+
+  metadata_header_ =
+      reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr);
+  if (user_metadata_size_) {
+    user_metadata_ptr_ =
+        reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_ptr) +
+                                BufferHubDefs::kMetadataHeaderSize);
+  } else {
+    user_metadata_ptr_ = nullptr;
+  }
+
+  id_ = new_id;
+  cid_ = buffer_desc.buffer_cid();
+  client_state_mask_ = buffer_desc.client_state_mask();
+
+  // Note that here the buffer_state, fence_state and active_clients_bit_mask
+  // are mapped from shared memory as an atomic object. The std::atomic's
+  // constructor will not be called so that the original value stored in the
+  // memory region will be preserved.
+  buffer_state_ = &metadata_header_->buffer_state;
+  ALOGD_IF(TRACE,
+           "BufferHubBase::ImportBuffer: id=%d, buffer_state=%" PRIx32 ".",
+           id(), buffer_state_->load(std::memory_order_acquire));
+  fence_state_ = &metadata_header_->fence_state;
+  ALOGD_IF(TRACE,
+           "BufferHubBase::ImportBuffer: id=%d, fence_state=%" PRIx32 ".", id(),
+           fence_state_->load(std::memory_order_acquire));
+  active_clients_bit_mask_ = &metadata_header_->active_clients_bit_mask;
+  ALOGD_IF(
+      TRACE,
+      "BufferHubBase::ImportBuffer: id=%d, active_clients_bit_mask=%" PRIx32
+      ".",
+      id(), active_clients_bit_mask_->load(std::memory_order_acquire));
+
+  return 0;
+}
+
+int BufferHubBase::CheckMetadata(size_t user_metadata_size) const {
+  if (user_metadata_size && !user_metadata_ptr_) {
+    ALOGE("BufferHubBase::CheckMetadata: doesn't support custom metadata.");
+    return -EINVAL;
+  }
+  if (user_metadata_size > user_metadata_size_) {
+    ALOGE("BufferHubBase::CheckMetadata: too big: %zu, maximum: %zu.",
+          user_metadata_size, user_metadata_size_);
+    return -E2BIG;
+  }
+  return 0;
+}
+
+int BufferHubBase::UpdateSharedFence(const LocalHandle& new_fence,
+                                     const LocalHandle& shared_fence) {
+  if (pending_fence_fd_.Get() != new_fence.Get()) {
+    // First, replace the old fd if there was already one. Skipping if the new
+    // one is the same as the old.
+    if (pending_fence_fd_.IsValid()) {
+      const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL,
+                                pending_fence_fd_.Get(), nullptr);
+      ALOGW_IF(ret,
+               "BufferHubBase::UpdateSharedFence: failed to remove old fence "
+               "fd from epoll set, error: %s.",
+               strerror(errno));
+    }
+
+    if (new_fence.IsValid()) {
+      // If ready fence is valid, we put that into the epoll set.
+      epoll_event event;
+      event.events = EPOLLIN;
+      event.data.u32 = client_state_mask();
+      pending_fence_fd_ = new_fence.Duplicate();
+      if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(),
+                    &event) < 0) {
+        const int error = errno;
+        ALOGE(
+            "BufferHubBase::UpdateSharedFence: failed to add new fence fd "
+            "into epoll set, error: %s.",
+            strerror(error));
+        return -error;
+      }
+      // Set bit in fence state to indicate that there is a fence from this
+      // producer or consumer.
+      fence_state_->fetch_or(client_state_mask());
+    } else {
+      // Unset bit in fence state to indicate that there is no fence, so that
+      // when consumer to acquire or producer to acquire, it knows no need to
+      // check fence for this buffer.
+      fence_state_->fetch_and(~client_state_mask());
+    }
+  }
+
+  return 0;
+}
+
+int BufferHubBase::Poll(int timeout_ms) {
+  ATRACE_NAME("BufferHubBase::Poll");
+  pollfd p = {event_fd(), POLLIN, 0};
+  return poll(&p, 1, timeout_ms);
+}
+
+int BufferHubBase::Lock(int usage, int x, int y, int width, int height,
+                        void** address) {
+  return buffer_.Lock(usage, x, y, width, height, address);
+}
+
+int BufferHubBase::Unlock() { return buffer_.Unlock(); }
+
+int BufferHubBase::GetBlobReadWritePointer(size_t size, void** addr) {
+  int width = static_cast<int>(size);
+  int height = 1;
+  int ret = Lock(usage(), 0, 0, width, height, addr);
+  if (ret == 0)
+    Unlock();
+  return ret;
+}
+
+void BufferHubBase::GetBlobFds(int* fds, size_t* fds_count,
+                               size_t max_fds_count) const {
+  size_t numFds = static_cast<size_t>(native_handle()->numFds);
+  *fds_count = std::min(max_fds_count, numFds);
+  std::copy(native_handle()->data, native_handle()->data + *fds_count, fds);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
deleted file mode 100644
index 159f2bd..0000000
--- a/libs/vr/libbufferhub/buffer_hub_client.cpp
+++ /dev/null
@@ -1,650 +0,0 @@
-#include <private/dvr/buffer_hub_client.h>
-
-#include <log/log.h>
-#include <poll.h>
-#include <sys/epoll.h>
-#include <utils/Trace.h>
-
-#include <mutex>
-
-#include <pdx/default_transport/client_channel.h>
-#include <pdx/default_transport/client_channel_factory.h>
-
-#include "include/private/dvr/bufferhub_rpc.h"
-
-using android::pdx::LocalChannelHandle;
-using android::pdx::LocalHandle;
-using android::pdx::Status;
-using android::pdx::default_transport::ClientChannel;
-using android::pdx::default_transport::ClientChannelFactory;
-
-namespace android {
-namespace dvr {
-
-BufferHubClient::BufferHubClient()
-    : Client(ClientChannelFactory::Create(BufferHubRPC::kClientPath)) {}
-
-BufferHubClient::BufferHubClient(LocalChannelHandle channel_handle)
-    : Client(ClientChannel::Create(std::move(channel_handle))) {}
-
-bool BufferHubClient::IsValid() const {
-  return IsConnected() && GetChannelHandle().valid();
-}
-
-LocalChannelHandle BufferHubClient::TakeChannelHandle() {
-  if (IsConnected()) {
-    return std::move(GetChannelHandle());
-  } else {
-    return {};
-  }
-}
-
-BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle)
-    : Client{pdx::default_transport::ClientChannel::Create(
-          std::move(channel_handle))},
-      id_(-1) {}
-BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path)
-    : Client{pdx::default_transport::ClientChannelFactory::Create(
-          endpoint_path)},
-      id_(-1) {}
-
-BufferHubBuffer::~BufferHubBuffer() {
-  if (metadata_header_ != nullptr) {
-    metadata_buffer_.Unlock();
-  }
-}
-
-Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
-  Status<LocalChannelHandle> status =
-      InvokeRemoteMethod<BufferHubRPC::NewConsumer>();
-  ALOGE_IF(!status,
-           "BufferHub::CreateConsumer: Failed to create consumer channel: %s",
-           status.GetErrorMessage().c_str());
-  return status;
-}
-
-int BufferHubBuffer::ImportBuffer() {
-  ATRACE_NAME("BufferHubBuffer::ImportBuffer");
-
-  Status<BufferDescription<LocalHandle>> status =
-      InvokeRemoteMethod<BufferHubRPC::GetBuffer>();
-  if (!status) {
-    ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s",
-          status.GetErrorMessage().c_str());
-    return -status.error();
-  } else if (status.get().id() < 0) {
-    ALOGE("BufferHubBuffer::ImportBuffer: Received an invalid id!");
-    return -EIO;
-  }
-
-  auto buffer_desc = status.take();
-
-  // Stash the buffer id to replace the value in id_.
-  const int new_id = buffer_desc.id();
-
-  // Import the buffer.
-  IonBuffer ion_buffer;
-  ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d.", buffer_desc.id());
-
-  if (const int ret = buffer_desc.ImportBuffer(&ion_buffer))
-    return ret;
-
-  // Import the metadata.
-  IonBuffer metadata_buffer;
-  if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) {
-    ALOGE("Failed to import metadata buffer, error=%d", ret);
-    return ret;
-  }
-  size_t metadata_buf_size = metadata_buffer.width();
-  if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) {
-    ALOGE("BufferHubBuffer::ImportBuffer: metadata buffer too small: %zu",
-          metadata_buf_size);
-    return -ENOMEM;
-  }
-
-  // If all imports succee, replace the previous buffer and id.
-  buffer_ = std::move(ion_buffer);
-  metadata_buffer_ = std::move(metadata_buffer);
-  metadata_buf_size_ = metadata_buf_size;
-  user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize;
-
-  void* metadata_ptr = nullptr;
-  if (const int ret =
-          metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0,
-                                /*y=*/0, metadata_buf_size_,
-                                /*height=*/1, &metadata_ptr)) {
-    ALOGE("BufferHubBuffer::ImportBuffer: Failed to lock metadata.");
-    return ret;
-  }
-
-  // Set up shared fences.
-  shared_acquire_fence_ = buffer_desc.take_acquire_fence();
-  shared_release_fence_ = buffer_desc.take_release_fence();
-  if (!shared_acquire_fence_ || !shared_release_fence_) {
-    ALOGE("BufferHubBuffer::ImportBuffer: Failed to import shared fences.");
-    return -EIO;
-  }
-
-  metadata_header_ =
-      reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr);
-  if (user_metadata_size_) {
-    user_metadata_ptr_ =
-        reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_ptr) +
-                                BufferHubDefs::kMetadataHeaderSize);
-  } else {
-    user_metadata_ptr_ = nullptr;
-  }
-
-  id_ = new_id;
-  buffer_state_bit_ = buffer_desc.buffer_state_bit();
-
-  // Note that here the buffer state is mapped from shared memory as an atomic
-  // object. The std::atomic's constructor will not be called so that the
-  // original value stored in the memory region will be preserved.
-  buffer_state_ = &metadata_header_->buffer_state;
-  ALOGD_IF(TRACE,
-           "BufferHubBuffer::ImportBuffer: id=%d, buffer_state=%" PRIx64 ".",
-           id(), buffer_state_->load());
-  fence_state_ = &metadata_header_->fence_state;
-  ALOGD_IF(TRACE,
-           "BufferHubBuffer::ImportBuffer: id=%d, fence_state=%" PRIx64 ".",
-           id(), fence_state_->load());
-
-  return 0;
-}
-
-inline int BufferHubBuffer::CheckMetadata(size_t user_metadata_size) const {
-  if (user_metadata_size && !user_metadata_ptr_) {
-    ALOGE("BufferHubBuffer::CheckMetadata: doesn't support custom metadata.");
-    return -EINVAL;
-  }
-  if (user_metadata_size > user_metadata_size_) {
-    ALOGE("BufferHubBuffer::CheckMetadata: too big: %zu, maximum: %zu.",
-          user_metadata_size, user_metadata_size_);
-    return -E2BIG;
-  }
-  return 0;
-}
-
-int BufferHubBuffer::UpdateSharedFence(const LocalHandle& new_fence,
-                                       const LocalHandle& shared_fence) {
-  if (pending_fence_fd_.Get() != new_fence.Get()) {
-    // First, replace the old fd if there was already one. Skipping if the new
-    // one is the same as the old.
-    if (pending_fence_fd_.IsValid()) {
-      const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL,
-                                pending_fence_fd_.Get(), nullptr);
-      ALOGW_IF(ret,
-               "BufferHubBuffer::UpdateSharedFence: failed to remove old fence "
-               "fd from epoll set, error: %s.",
-               strerror(errno));
-    }
-
-    if (new_fence.IsValid()) {
-      // If ready fence is valid, we put that into the epoll set.
-      epoll_event event;
-      event.events = EPOLLIN;
-      event.data.u64 = buffer_state_bit();
-      pending_fence_fd_ = new_fence.Duplicate();
-      if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(),
-                    &event) < 0) {
-        const int error = errno;
-        ALOGE(
-            "BufferHubBuffer::UpdateSharedFence: failed to add new fence fd "
-            "into epoll set, error: %s.",
-            strerror(error));
-        return -error;
-      }
-      // Set bit in fence state to indicate that there is a fence from this
-      // producer or consumer.
-      fence_state_->fetch_or(buffer_state_bit());
-    } else {
-      // Unset bit in fence state to indicate that there is no fence, so that
-      // when consumer to acquire or producer to acquire, it knows no need to
-      // check fence for this buffer.
-      fence_state_->fetch_and(~buffer_state_bit());
-    }
-  }
-
-  return 0;
-}
-
-int BufferHubBuffer::Poll(int timeout_ms) {
-  ATRACE_NAME("BufferHubBuffer::Poll");
-  pollfd p = {event_fd(), POLLIN, 0};
-  return poll(&p, 1, timeout_ms);
-}
-
-int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height,
-                          void** address) {
-  return buffer_.Lock(usage, x, y, width, height, address);
-}
-
-int BufferHubBuffer::Unlock() { return buffer_.Unlock(); }
-
-int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) {
-  int width = static_cast<int>(size);
-  int height = 1;
-  int ret = Lock(usage(), 0, 0, width, height, addr);
-  if (ret == 0)
-    Unlock();
-  return ret;
-}
-
-int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) {
-  return GetBlobReadWritePointer(size, addr);
-}
-
-void BufferHubBuffer::GetBlobFds(int* fds, size_t* fds_count,
-                                 size_t max_fds_count) const {
-  size_t numFds = static_cast<size_t>(native_handle()->numFds);
-  *fds_count = std::min(max_fds_count, numFds);
-  std::copy(native_handle()->data, native_handle()->data + *fds_count, fds);
-}
-
-BufferConsumer::BufferConsumer(LocalChannelHandle channel)
-    : BASE(std::move(channel)) {
-  const int ret = ImportBuffer();
-  if (ret < 0) {
-    ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s",
-          strerror(-ret));
-    Close(ret);
-  }
-}
-
-std::unique_ptr<BufferConsumer> BufferConsumer::Import(
-    LocalChannelHandle channel) {
-  ATRACE_NAME("BufferConsumer::Import");
-  ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value());
-  return BufferConsumer::Create(std::move(channel));
-}
-
-std::unique_ptr<BufferConsumer> BufferConsumer::Import(
-    Status<LocalChannelHandle> status) {
-  return Import(status ? status.take()
-                       : LocalChannelHandle{nullptr, -status.error()});
-}
-
-int BufferConsumer::LocalAcquire(DvrNativeBufferMetadata* out_meta,
-                                 LocalHandle* out_fence) {
-  if (!out_meta)
-    return -EINVAL;
-
-  // Only check producer bit and this consumer buffer's particular consumer bit.
-  // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit
-  // is not set.
-  uint64_t buffer_state = buffer_state_->load();
-  if (!BufferHubDefs::IsBufferPosted(buffer_state, buffer_state_bit())) {
-    ALOGE("BufferConsumer::LocalAcquire: not posted, id=%d state=%" PRIx64
-          " buffer_state_bit=%" PRIx64 ".",
-          id(), buffer_state, buffer_state_bit());
-    return -EBUSY;
-  }
-
-  // Copy the canonical metadata.
-  void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
-  memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata));
-  // Fill in the user_metadata_ptr in address space of the local process.
-  if (out_meta->user_metadata_size) {
-    out_meta->user_metadata_ptr =
-        reinterpret_cast<uint64_t>(user_metadata_ptr_);
-  } else {
-    out_meta->user_metadata_ptr = 0;
-  }
-
-  uint64_t fence_state = fence_state_->load();
-  // If there is an acquire fence from producer, we need to return it.
-  if (fence_state & BufferHubDefs::kProducerStateBit) {
-    *out_fence = shared_acquire_fence_.Duplicate();
-  }
-
-  // Set the consumer bit unique to this consumer.
-  BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, buffer_state_bit());
-  return 0;
-}
-
-int BufferConsumer::Acquire(LocalHandle* ready_fence) {
-  return Acquire(ready_fence, nullptr, 0);
-}
-
-int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta,
-                            size_t user_metadata_size) {
-  ATRACE_NAME("BufferConsumer::Acquire");
-
-  if (const int error = CheckMetadata(user_metadata_size))
-    return error;
-
-  DvrNativeBufferMetadata canonical_meta;
-  if (const int error = LocalAcquire(&canonical_meta, ready_fence))
-    return error;
-
-  if (meta && user_metadata_size) {
-    void* metadata_src =
-        reinterpret_cast<void*>(canonical_meta.user_metadata_ptr);
-    if (metadata_src) {
-      memcpy(meta, metadata_src, user_metadata_size);
-    } else {
-      ALOGW("BufferConsumer::Acquire: no user-defined metadata.");
-    }
-  }
-
-  auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerAcquire>();
-  if (!status)
-    return -status.error();
-  return 0;
-}
-
-int BufferConsumer::AcquireAsync(DvrNativeBufferMetadata* out_meta,
-                                 LocalHandle* out_fence) {
-  ATRACE_NAME("BufferConsumer::AcquireAsync");
-
-  if (const int error = LocalAcquire(out_meta, out_fence))
-    return error;
-
-  auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode);
-  if (!status)
-    return -status.error();
-  return 0;
-}
-
-int BufferConsumer::LocalRelease(const DvrNativeBufferMetadata* meta,
-                                 const LocalHandle& release_fence) {
-  if (const int error = CheckMetadata(meta->user_metadata_size))
-    return error;
-
-  // Check invalid state transition.
-  uint64_t buffer_state = buffer_state_->load();
-  if (!BufferHubDefs::IsBufferAcquired(buffer_state)) {
-    ALOGE("BufferConsumer::LocalRelease: not acquired id=%d state=%" PRIx64 ".",
-          id(), buffer_state);
-    return -EBUSY;
-  }
-
-  // On release, only the user requested metadata is copied back into the shared
-  // memory for metadata. Since there are multiple consumers, it doesn't make
-  // sense to send the canonical metadata back to the producer. However, one of
-  // the consumer can still choose to write up to user_metadata_size bytes of
-  // data into user_metadata_ptr.
-  if (meta->user_metadata_ptr && meta->user_metadata_size) {
-    void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
-    memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
-  }
-
-  // Send out the release fence through the shared epoll fd. Note that during
-  // releasing the producer is not expected to be polling on the fence.
-  if (const int error = UpdateSharedFence(release_fence, shared_release_fence_))
-    return error;
-
-  // For release operation, the client don't need to change the state as it's
-  // bufferhubd's job to flip the produer bit once all consumers are released.
-  return 0;
-}
-
-int BufferConsumer::Release(const LocalHandle& release_fence) {
-  ATRACE_NAME("BufferConsumer::Release");
-
-  DvrNativeBufferMetadata meta;
-  if (const int error = LocalRelease(&meta, release_fence))
-    return error;
-
-  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
-      BorrowedFence(release_fence.Borrow())));
-}
-
-int BufferConsumer::ReleaseAsync() {
-  DvrNativeBufferMetadata meta;
-  return ReleaseAsync(&meta, LocalHandle());
-}
-
-int BufferConsumer::ReleaseAsync(const DvrNativeBufferMetadata* meta,
-                                 const LocalHandle& release_fence) {
-  ATRACE_NAME("BufferConsumer::ReleaseAsync");
-
-  if (const int error = LocalRelease(meta, release_fence))
-    return error;
-
-  return ReturnStatusOrError(
-      SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
-}
-
-int BufferConsumer::Discard() { return Release(LocalHandle()); }
-
-int BufferConsumer::SetIgnore(bool ignore) {
-  return ReturnStatusOrError(
-      InvokeRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(ignore));
-}
-
-BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format,
-                               uint64_t usage, size_t user_metadata_size)
-    : BASE(BufferHubRPC::kClientPath) {
-  ATRACE_NAME("BufferProducer::BufferProducer");
-  ALOGD_IF(TRACE,
-           "BufferProducer::BufferProducer: fd=%d width=%u height=%u format=%u "
-           "usage=%" PRIx64 " user_metadata_size=%zu",
-           event_fd(), width, height, format, usage, user_metadata_size);
-
-  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
-      width, height, format, usage, user_metadata_size);
-  if (!status) {
-    ALOGE(
-        "BufferProducer::BufferProducer: Failed to create producer buffer: %s",
-        status.GetErrorMessage().c_str());
-    Close(-status.error());
-    return;
-  }
-
-  const int ret = ImportBuffer();
-  if (ret < 0) {
-    ALOGE(
-        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
-        strerror(-ret));
-    Close(ret);
-  }
-}
-
-BufferProducer::BufferProducer(uint64_t usage, size_t size)
-    : BASE(BufferHubRPC::kClientPath) {
-  ATRACE_NAME("BufferProducer::BufferProducer");
-  ALOGD_IF(TRACE, "BufferProducer::BufferProducer: usage=%" PRIx64 " size=%zu",
-           usage, size);
-  const int width = static_cast<int>(size);
-  const int height = 1;
-  const int format = HAL_PIXEL_FORMAT_BLOB;
-  const size_t user_metadata_size = 0;
-
-  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
-      width, height, format, usage, user_metadata_size);
-  if (!status) {
-    ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s",
-          status.GetErrorMessage().c_str());
-    Close(-status.error());
-    return;
-  }
-
-  const int ret = ImportBuffer();
-  if (ret < 0) {
-    ALOGE(
-        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
-        strerror(-ret));
-    Close(ret);
-  }
-}
-
-BufferProducer::BufferProducer(LocalChannelHandle channel)
-    : BASE(std::move(channel)) {
-  const int ret = ImportBuffer();
-  if (ret < 0) {
-    ALOGE(
-        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
-        strerror(-ret));
-    Close(ret);
-  }
-}
-
-int BufferProducer::LocalPost(const DvrNativeBufferMetadata* meta,
-                              const LocalHandle& ready_fence) {
-  if (const int error = CheckMetadata(meta->user_metadata_size))
-    return error;
-
-  // Check invalid state transition.
-  uint64_t buffer_state = buffer_state_->load();
-  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
-    ALOGE("BufferProducer::LocalPost: not gained, id=%d state=%" PRIx64 ".",
-          id(), buffer_state);
-    return -EBUSY;
-  }
-
-  // Copy the canonical metadata.
-  void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
-  memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata));
-  // Copy extra user requested metadata.
-  if (meta->user_metadata_ptr && meta->user_metadata_size) {
-    void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
-    memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
-  }
-
-  // Send out the acquire fence through the shared epoll fd. Note that during
-  // posting no consumer is not expected to be polling on the fence.
-  if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_))
-    return error;
-
-  // Set the producer bit atomically to transit into posted state.
-  BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL,
-                                   BufferHubDefs::kProducerStateBit);
-  return 0;
-}
-
-int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta,
-                         size_t user_metadata_size) {
-  ATRACE_NAME("BufferProducer::Post");
-
-  // Populate cononical metadata for posting.
-  DvrNativeBufferMetadata canonical_meta;
-  canonical_meta.user_metadata_ptr = reinterpret_cast<uint64_t>(meta);
-  canonical_meta.user_metadata_size = user_metadata_size;
-
-  if (const int error = LocalPost(&canonical_meta, ready_fence))
-    return error;
-
-  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
-      BorrowedFence(ready_fence.Borrow())));
-}
-
-int BufferProducer::PostAsync(const DvrNativeBufferMetadata* meta,
-                              const LocalHandle& ready_fence) {
-  ATRACE_NAME("BufferProducer::PostAsync");
-
-  if (const int error = LocalPost(meta, ready_fence))
-    return error;
-
-  return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode));
-}
-
-int BufferProducer::LocalGain(DvrNativeBufferMetadata* out_meta,
-                              LocalHandle* out_fence) {
-  uint64_t buffer_state = buffer_state_->load();
-  ALOGD_IF(TRACE, "BufferProducer::LocalGain: buffer=%d, state=%" PRIx64 ".",
-           id(), buffer_state);
-
-  if (!out_meta)
-    return -EINVAL;
-
-  if (!BufferHubDefs::IsBufferReleased(buffer_state)) {
-    if (BufferHubDefs::IsBufferGained(buffer_state)) {
-      // We don't want to log error when gaining a newly allocated
-      // buffer.
-      ALOGI("BufferProducer::LocalGain: already gained id=%d.", id());
-      return -EALREADY;
-    }
-    ALOGE("BufferProducer::LocalGain: not released id=%d state=%" PRIx64 ".",
-          id(), buffer_state);
-    return -EBUSY;
-  }
-
-  // Canonical metadata is undefined on Gain. Except for user_metadata and
-  // release_fence_mask. Fill in the user_metadata_ptr in address space of the
-  // local process.
-  if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) {
-    out_meta->user_metadata_size =
-        metadata_header_->metadata.user_metadata_size;
-    out_meta->user_metadata_ptr =
-        reinterpret_cast<uint64_t>(user_metadata_ptr_);
-  } else {
-    out_meta->user_metadata_size = 0;
-    out_meta->user_metadata_ptr = 0;
-  }
-
-  uint64_t fence_state = fence_state_->load();
-  // If there is an release fence from consumer, we need to return it.
-  if (fence_state & BufferHubDefs::kConsumerStateMask) {
-    *out_fence = shared_release_fence_.Duplicate();
-    out_meta->release_fence_mask =
-        fence_state & BufferHubDefs::kConsumerStateMask;
-  }
-
-  // Clear out all bits and the buffer is now back to gained state.
-  buffer_state_->store(0ULL);
-  return 0;
-}
-
-int BufferProducer::Gain(LocalHandle* release_fence) {
-  ATRACE_NAME("BufferProducer::Gain");
-
-  DvrNativeBufferMetadata meta;
-  if (const int error = LocalGain(&meta, release_fence))
-    return error;
-
-  auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
-  if (!status)
-    return -status.error();
-  return 0;
-}
-
-int BufferProducer::GainAsync(DvrNativeBufferMetadata* out_meta,
-                              LocalHandle* release_fence) {
-  ATRACE_NAME("BufferProducer::GainAsync");
-
-  if (const int error = LocalGain(out_meta, release_fence))
-    return error;
-
-  return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
-}
-
-int BufferProducer::GainAsync() {
-  DvrNativeBufferMetadata meta;
-  LocalHandle fence;
-  return GainAsync(&meta, &fence);
-}
-
-std::unique_ptr<BufferProducer> BufferProducer::Import(
-    LocalChannelHandle channel) {
-  ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value());
-  return BufferProducer::Create(std::move(channel));
-}
-
-std::unique_ptr<BufferProducer> BufferProducer::Import(
-    Status<LocalChannelHandle> status) {
-  return Import(status ? status.take()
-                       : LocalChannelHandle{nullptr, -status.error()});
-}
-
-Status<LocalChannelHandle> BufferProducer::Detach() {
-  uint64_t buffer_state = buffer_state_->load();
-  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
-    // Can only detach a BufferProducer when it's in gained state.
-    ALOGW("BufferProducer::Detach: The buffer (id=%d, state=0x%" PRIx64
-          ") is not in gained state.",
-          id(), buffer_state);
-    return {};
-  }
-
-  Status<LocalChannelHandle> status =
-      InvokeRemoteMethod<BufferHubRPC::ProducerBufferDetach>();
-  ALOGE_IF(!status,
-           "BufferProducer::Detach: Failed to detach buffer (id=%d): %s.", id(),
-           status.GetErrorMessage().c_str());
-  return status;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libbufferhub/consumer_buffer.cpp b/libs/vr/libbufferhub/consumer_buffer.cpp
new file mode 100644
index 0000000..b6ca64e
--- /dev/null
+++ b/libs/vr/libbufferhub/consumer_buffer.cpp
@@ -0,0 +1,213 @@
+#include <private/dvr/consumer_buffer.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+ConsumerBuffer::ConsumerBuffer(LocalChannelHandle channel)
+    : BASE(std::move(channel)) {
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE("ConsumerBuffer::ConsumerBuffer: Failed to import buffer: %s",
+          strerror(-ret));
+    Close(ret);
+  }
+}
+
+std::unique_ptr<ConsumerBuffer> ConsumerBuffer::Import(
+    LocalChannelHandle channel) {
+  ATRACE_NAME("ConsumerBuffer::Import");
+  ALOGD_IF(TRACE, "ConsumerBuffer::Import: channel=%d", channel.value());
+  return ConsumerBuffer::Create(std::move(channel));
+}
+
+std::unique_ptr<ConsumerBuffer> ConsumerBuffer::Import(
+    Status<LocalChannelHandle> status) {
+  return Import(status ? status.take()
+                       : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int ConsumerBuffer::LocalAcquire(DvrNativeBufferMetadata* out_meta,
+                                 LocalHandle* out_fence) {
+  if (!out_meta)
+    return -EINVAL;
+
+  // The buffer can be acquired iff the buffer state for this client is posted.
+  uint32_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+                                     client_state_mask())) {
+    ALOGE(
+        "%s: Failed to acquire the buffer. The buffer is not posted, id=%d "
+        "state=%" PRIx32 " client_state_mask=%" PRIx32 ".",
+        __FUNCTION__, id(), current_buffer_state, client_state_mask());
+    return -EBUSY;
+  }
+
+  // Change the buffer state for this consumer from posted to acquired.
+  uint32_t updated_buffer_state = current_buffer_state ^ client_state_mask();
+  while (!buffer_state_->compare_exchange_weak(
+      current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+      std::memory_order_acquire)) {
+    ALOGD(
+        "%s Failed to acquire the buffer. Current buffer state was changed to "
+        "%" PRIx32
+        " when trying to acquire the buffer and modify the buffer state to "
+        "%" PRIx32 ". About to try again if the buffer is still posted.",
+        __FUNCTION__, current_buffer_state, updated_buffer_state);
+    if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+                                       client_state_mask())) {
+      ALOGE(
+          "%s: Failed to acquire the buffer. The buffer is no longer posted, "
+          "id=%d state=%" PRIx32 " client_state_mask=%" PRIx32 ".",
+          __FUNCTION__, id(), current_buffer_state, client_state_mask());
+      return -EBUSY;
+    }
+    // The failure of compare_exchange_weak updates current_buffer_state.
+    updated_buffer_state = current_buffer_state ^ client_state_mask();
+  }
+
+  // Copy the canonical metadata.
+  void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
+  memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata));
+  // Fill in the user_metadata_ptr in address space of the local process.
+  if (out_meta->user_metadata_size) {
+    out_meta->user_metadata_ptr =
+        reinterpret_cast<uint64_t>(user_metadata_ptr_);
+  } else {
+    out_meta->user_metadata_ptr = 0;
+  }
+
+  uint32_t fence_state = fence_state_->load(std::memory_order_acquire);
+  // If there is an acquire fence from producer, we need to return it.
+  // The producer state bit mask is kFirstClientBitMask for now.
+  if (fence_state & BufferHubDefs::kFirstClientBitMask) {
+    *out_fence = shared_acquire_fence_.Duplicate();
+  }
+
+  return 0;
+}
+
+int ConsumerBuffer::Acquire(LocalHandle* ready_fence) {
+  return Acquire(ready_fence, nullptr, 0);
+}
+
+int ConsumerBuffer::Acquire(LocalHandle* ready_fence, void* meta,
+                            size_t user_metadata_size) {
+  ATRACE_NAME("ConsumerBuffer::Acquire");
+
+  if (const int error = CheckMetadata(user_metadata_size))
+    return error;
+
+  DvrNativeBufferMetadata canonical_meta;
+  if (const int error = LocalAcquire(&canonical_meta, ready_fence))
+    return error;
+
+  if (meta && user_metadata_size) {
+    void* metadata_src =
+        reinterpret_cast<void*>(canonical_meta.user_metadata_ptr);
+    if (metadata_src) {
+      memcpy(meta, metadata_src, user_metadata_size);
+    } else {
+      ALOGW("ConsumerBuffer::Acquire: no user-defined metadata.");
+    }
+  }
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerAcquire>();
+  if (!status)
+    return -status.error();
+  return 0;
+}
+
+int ConsumerBuffer::AcquireAsync(DvrNativeBufferMetadata* out_meta,
+                                 LocalHandle* out_fence) {
+  ATRACE_NAME("ConsumerBuffer::AcquireAsync");
+
+  if (const int error = LocalAcquire(out_meta, out_fence))
+    return error;
+
+  auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode);
+  if (!status)
+    return -status.error();
+  return 0;
+}
+
+int ConsumerBuffer::LocalRelease(const DvrNativeBufferMetadata* meta,
+                                 const LocalHandle& release_fence) {
+  if (const int error = CheckMetadata(meta->user_metadata_size))
+    return error;
+
+  // Set the buffer state of this client to released if it is not already in
+  // released state.
+  uint32_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  if (BufferHubDefs::IsClientReleased(current_buffer_state,
+                                      client_state_mask())) {
+    return 0;
+  }
+  uint32_t updated_buffer_state = current_buffer_state & (~client_state_mask());
+  while (!buffer_state_->compare_exchange_weak(
+      current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+      std::memory_order_acquire)) {
+    ALOGD(
+        "%s: Failed to release the buffer. Current buffer state was changed to "
+        "%" PRIx32
+        " when trying to release the buffer and modify the buffer state to "
+        "%" PRIx32 ". About to try again.",
+        __FUNCTION__, current_buffer_state, updated_buffer_state);
+    // The failure of compare_exchange_weak updates current_buffer_state.
+    updated_buffer_state = current_buffer_state & (~client_state_mask());
+  }
+
+  // On release, only the user requested metadata is copied back into the shared
+  // memory for metadata. Since there are multiple consumers, it doesn't make
+  // sense to send the canonical metadata back to the producer. However, one of
+  // the consumer can still choose to write up to user_metadata_size bytes of
+  // data into user_metadata_ptr.
+  if (meta->user_metadata_ptr && meta->user_metadata_size) {
+    void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
+    memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
+  }
+
+  // Send out the release fence through the shared epoll fd. Note that during
+  // releasing the producer is not expected to be polling on the fence.
+  if (const int error = UpdateSharedFence(release_fence, shared_release_fence_))
+    return error;
+
+  return 0;
+}
+
+int ConsumerBuffer::Release(const LocalHandle& release_fence) {
+  ATRACE_NAME("ConsumerBuffer::Release");
+
+  DvrNativeBufferMetadata meta;
+  if (const int error = LocalRelease(&meta, release_fence))
+    return error;
+
+  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
+      BorrowedFence(release_fence.Borrow())));
+}
+
+int ConsumerBuffer::ReleaseAsync() {
+  DvrNativeBufferMetadata meta;
+  return ReleaseAsync(&meta, LocalHandle());
+}
+
+int ConsumerBuffer::ReleaseAsync(const DvrNativeBufferMetadata* meta,
+                                 const LocalHandle& release_fence) {
+  ATRACE_NAME("ConsumerBuffer::ReleaseAsync");
+
+  if (const int error = LocalRelease(meta, release_fence))
+    return error;
+
+  return ReturnStatusOrError(
+      SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
+}
+
+int ConsumerBuffer::Discard() { return Release(LocalHandle()); }
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/detached_buffer.cpp b/libs/vr/libbufferhub/detached_buffer.cpp
deleted file mode 100644
index 6fae16d..0000000
--- a/libs/vr/libbufferhub/detached_buffer.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-#include <private/dvr/detached_buffer.h>
-
-#include <pdx/file_handle.h>
-#include <ui/DetachedBufferHandle.h>
-
-#include <poll.h>
-
-using android::pdx::LocalChannelHandle;
-using android::pdx::LocalHandle;
-using android::pdx::Status;
-
-namespace android {
-namespace dvr {
-
-DetachedBuffer::DetachedBuffer(uint32_t width, uint32_t height,
-                               uint32_t layer_count, uint32_t format,
-                               uint64_t usage, size_t user_metadata_size) {
-  ATRACE_NAME("DetachedBuffer::DetachedBuffer");
-  ALOGD_IF(TRACE,
-           "DetachedBuffer::DetachedBuffer: width=%u height=%u layer_count=%u, "
-           "format=%u usage=%" PRIx64 " user_metadata_size=%zu",
-           width, height, layer_count, format, usage, user_metadata_size);
-
-  auto status = client_.InvokeRemoteMethod<DetachedBufferRPC::Create>(
-      width, height, layer_count, format, usage, user_metadata_size);
-  if (!status) {
-    ALOGE(
-        "DetachedBuffer::DetachedBuffer: Failed to create detached buffer: %s",
-        status.GetErrorMessage().c_str());
-    client_.Close(-status.error());
-  }
-
-  const int ret = ImportGraphicBuffer();
-  if (ret < 0) {
-    ALOGE("DetachedBuffer::DetachedBuffer: Failed to import buffer: %s",
-          strerror(-ret));
-    client_.Close(ret);
-  }
-}
-
-DetachedBuffer::DetachedBuffer(LocalChannelHandle channel_handle)
-    : client_(std::move(channel_handle)) {
-  const int ret = ImportGraphicBuffer();
-  if (ret < 0) {
-    ALOGE("DetachedBuffer::DetachedBuffer: Failed to import buffer: %s",
-          strerror(-ret));
-    client_.Close(ret);
-  }
-}
-
-int DetachedBuffer::ImportGraphicBuffer() {
-  ATRACE_NAME("DetachedBuffer::DetachedBuffer");
-
-  auto status = client_.InvokeRemoteMethod<DetachedBufferRPC::Import>();
-  if (!status) {
-    ALOGE("DetachedBuffer::DetachedBuffer: Failed to import GraphicBuffer: %s",
-          status.GetErrorMessage().c_str());
-    return -status.error();
-  }
-
-  BufferDescription<LocalHandle> buffer_desc = status.take();
-  if (buffer_desc.id() < 0) {
-    ALOGE("DetachedBuffer::DetachedBuffer: Received an invalid id!");
-    return -EIO;
-  }
-
-  // Stash the buffer id to replace the value in id_.
-  const int buffer_id = buffer_desc.id();
-
-  // Import the buffer.
-  IonBuffer ion_buffer;
-  ALOGD_IF(TRACE, "DetachedBuffer::DetachedBuffer: id=%d.", buffer_id);
-
-  if (const int ret = buffer_desc.ImportBuffer(&ion_buffer)) {
-    ALOGE("Failed to import GraphicBuffer, error=%d", ret);
-    return ret;
-  }
-
-  // If all imports succeed, replace the previous buffer and id.
-  id_ = buffer_id;
-  buffer_ = std::move(ion_buffer);
-  return 0;
-}
-
-int DetachedBuffer::Poll(int timeout_ms) {
-  ATRACE_NAME("DetachedBuffer::Poll");
-  pollfd p = {client_.event_fd(), POLLIN, 0};
-  return poll(&p, 1, timeout_ms);
-}
-
-Status<LocalChannelHandle> DetachedBuffer::Promote() {
-  ATRACE_NAME("DetachedBuffer::Promote");
-  ALOGD_IF(TRACE, "DetachedBuffer::Promote: id=%d.", id_);
-
-  auto status_or_handle =
-      client_.InvokeRemoteMethod<DetachedBufferRPC::Promote>();
-  if (status_or_handle.ok()) {
-    // Invalidate the buffer.
-    buffer_ = {};
-  } else {
-    ALOGE("DetachedBuffer::Promote: Failed to promote buffer (id=%d): %s.", id_,
-          status_or_handle.GetErrorMessage().c_str());
-  }
-  return status_or_handle;
-}
-
-sp<GraphicBuffer> DetachedBuffer::TakeGraphicBuffer() {
-  if (!client_.IsValid() || !buffer_.buffer()) {
-    ALOGE("DetachedBuffer::TakeGraphicBuffer: Invalid buffer.");
-    return nullptr;
-  }
-
-  // Technically this should never happen.
-  LOG_FATAL_IF(
-      buffer_.buffer()->isDetachedBuffer(),
-      "DetachedBuffer::TakeGraphicBuffer: GraphicBuffer is already detached.");
-
-  sp<GraphicBuffer> buffer = std::move(buffer_.buffer());
-  buffer->setDetachedBufferHandle(
-      DetachedBufferHandle::Create(client_.TakeChannelHandle()));
-  return buffer;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h
new file mode 100644
index 0000000..889763a
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h
@@ -0,0 +1,176 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_BASE_H_
+#define ANDROID_DVR_BUFFER_HUB_BASE_H_
+
+#include <vector>
+
+#include <private/dvr/bufferhub_rpc.h>
+
+namespace android {
+namespace dvr {
+
+// Base class of two types of BufferHub clients: dvr::ProducerBuffer and
+// dvr::ConsumerBuffer.
+class BufferHubBase : public pdx::Client {
+ public:
+  using LocalHandle = pdx::LocalHandle;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  template <typename T>
+  using Status = pdx::Status<T>;
+
+  // Create a new consumer channel that is attached to the producer. Returns
+  // a file descriptor for the new channel or a negative error code.
+  Status<LocalChannelHandle> CreateConsumer();
+
+  // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
+  int Poll(int timeout_ms);
+
+  // Locks the area specified by (x, y, width, height) for a specific usage. If
+  // the usage is software then |addr| will be updated to point to the address
+  // of the buffer in virtual memory. The caller should only access/modify the
+  // pixels in the specified area. anything else is undefined behavior.
+  int Lock(int usage, int x, int y, int width, int height, void** addr);
+
+  // Must be called after Lock() when the caller has finished changing the
+  // buffer.
+  int Unlock();
+
+  // Gets a blob buffer that was created with ProducerBuffer::CreateBlob.
+  // Locking and Unlocking is handled internally. There's no need to Unlock
+  // after calling this method.
+  int GetBlobReadWritePointer(size_t size, void** addr);
+
+  // Returns a dup'd file descriptor for accessing the blob shared memory. The
+  // caller takes ownership of the file descriptor and must close it or pass on
+  // ownership. Some GPU API extensions can take file descriptors to bind shared
+  // memory gralloc buffers to GPU buffer objects.
+  LocalHandle GetBlobFd() const {
+    // Current GPU vendor puts the buffer allocation in one FD. If we change GPU
+    // vendors and this is the wrong fd, late-latching and EDS will very clearly
+    // stop working and we will need to correct this. The alternative is to use
+    // a GL context in the pose service to allocate this buffer or to use the
+    // ION API directly instead of gralloc.
+    return LocalHandle(dup(native_handle()->data[0]));
+  }
+
+  // Get up to |max_fds_count| file descriptors for accessing the blob shared
+  // memory. |fds_count| will contain the actual number of file descriptors.
+  void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const;
+
+  using Client::event_fd;
+
+  Status<int> GetEventMask(int events) {
+    if (auto* client_channel = GetChannel()) {
+      return client_channel->GetEventMask(events);
+    } else {
+      return pdx::ErrorStatus(EINVAL);
+    }
+  }
+
+  std::vector<pdx::ClientChannel::EventSource> GetEventSources() const {
+    if (auto* client_channel = GetChannel()) {
+      return client_channel->GetEventSources();
+    } else {
+      return {};
+    }
+  }
+
+  native_handle_t* native_handle() const {
+    return const_cast<native_handle_t*>(buffer_.handle());
+  }
+
+  IonBuffer* buffer() { return &buffer_; }
+  const IonBuffer* buffer() const { return &buffer_; }
+
+  // Gets ID of the buffer client. All BufferHub clients derived from the same
+  // buffer in bufferhubd share the same buffer id.
+  int id() const { return id_; }
+
+  // Gets the channel id of the buffer client. Each BufferHub client has its
+  // system unique channel id.
+  int cid() const { return cid_; }
+
+  // Returns the buffer buffer state.
+  uint32_t buffer_state() {
+    return buffer_state_->load(std::memory_order_acquire);
+  };
+
+  // Returns whether the buffer is already released by all current clients.
+  bool is_released() {
+    return (buffer_state() &
+            active_clients_bit_mask_->load(std::memory_order_acquire)) == 0;
+  }
+
+  // A state mask which is unique to a buffer hub client among all its siblings
+  // sharing the same concrete graphic buffer.
+  uint32_t client_state_mask() const { return client_state_mask_; }
+
+  // The following methods return settings of the first buffer. Currently,
+  // it is only possible to create multi-buffer BufferHubBases with the same
+  // settings.
+  uint32_t width() const { return buffer_.width(); }
+  uint32_t height() const { return buffer_.height(); }
+  uint32_t stride() const { return buffer_.stride(); }
+  uint32_t format() const { return buffer_.format(); }
+  uint32_t usage() const { return buffer_.usage(); }
+  uint32_t layer_count() const { return buffer_.layer_count(); }
+
+  uint64_t GetQueueIndex() const { return metadata_header_->queue_index; }
+  void SetQueueIndex(uint64_t index) { metadata_header_->queue_index = index; }
+
+ protected:
+  explicit BufferHubBase(LocalChannelHandle channel);
+  explicit BufferHubBase(const std::string& endpoint_path);
+  virtual ~BufferHubBase();
+
+  // Initialization helper.
+  int ImportBuffer();
+
+  // Check invalid metadata operation. Returns 0 if requested metadata is valid.
+  int CheckMetadata(size_t user_metadata_size) const;
+
+  // Send out the new fence by updating the shared fence (shared_release_fence
+  // for producer and shared_acquire_fence for consumer). Note that during this
+  // should only be used in LocalPost() or LocalRelease, and the shared fence
+  // shouldn't be poll'ed by the other end.
+  int UpdateSharedFence(const LocalHandle& new_fence,
+                        const LocalHandle& shared_fence);
+
+  // IonBuffer that is shared between bufferhubd, producer, and consumers.
+  size_t metadata_buf_size_{0};
+  size_t user_metadata_size_{0};
+  BufferHubDefs::MetadataHeader* metadata_header_ = nullptr;
+  void* user_metadata_ptr_ = nullptr;
+  std::atomic<uint32_t>* buffer_state_ = nullptr;
+  std::atomic<uint32_t>* fence_state_ = nullptr;
+  std::atomic<uint32_t>* active_clients_bit_mask_ = nullptr;
+
+  LocalHandle shared_acquire_fence_;
+  LocalHandle shared_release_fence_;
+
+  // A local fence fd that holds the ownership of the fence fd on Post (for
+  // producer) and Release (for consumer).
+  LocalHandle pending_fence_fd_;
+
+ private:
+  BufferHubBase(const BufferHubBase&) = delete;
+  void operator=(const BufferHubBase&) = delete;
+
+  // Global id for the buffer that is consistent across processes. It is meant
+  // for logging and debugging purposes only and should not be used for lookup
+  // or any other functional purpose as a security precaution.
+  int id_;
+
+  // Channel id.
+  int cid_;
+
+  // Client bit mask which indicates the locations of this client object in the
+  // buffer_state_.
+  uint32_t client_state_mask_{0U};
+  IonBuffer buffer_;
+  IonBuffer metadata_buffer_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFER_HUB_BASE_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
deleted file mode 100644
index 0ea77c8..0000000
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
+++ /dev/null
@@ -1,347 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_
-#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_
-
-#include <hardware/gralloc.h>
-#include <pdx/channel_handle.h>
-#include <pdx/client.h>
-#include <pdx/file_handle.h>
-#include <pdx/status.h>
-
-#include <vector>
-
-#include <private/dvr/ion_buffer.h>
-
-#include "bufferhub_rpc.h"
-
-namespace android {
-namespace dvr {
-
-class BufferHubClient : public pdx::Client {
- public:
-  BufferHubClient();
-  explicit BufferHubClient(pdx::LocalChannelHandle channel_handle);
-
-  bool IsValid() const;
-  pdx::LocalChannelHandle TakeChannelHandle();
-
-  using pdx::Client::Close;
-  using pdx::Client::GetChannel;
-  using pdx::Client::InvokeRemoteMethod;
-  using pdx::Client::IsConnected;
-  using pdx::Client::event_fd;
-};
-
-class BufferHubBuffer : public pdx::Client {
- public:
-  using LocalHandle = pdx::LocalHandle;
-  using LocalChannelHandle = pdx::LocalChannelHandle;
-  template <typename T>
-  using Status = pdx::Status<T>;
-
-  // Create a new consumer channel that is attached to the producer. Returns
-  // a file descriptor for the new channel or a negative error code.
-  Status<LocalChannelHandle> CreateConsumer();
-
-  // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
-  int Poll(int timeout_ms);
-
-  // Locks the area specified by (x, y, width, height) for a specific usage. If
-  // the usage is software then |addr| will be updated to point to the address
-  // of the buffer in virtual memory. The caller should only access/modify the
-  // pixels in the specified area. anything else is undefined behavior.
-  int Lock(int usage, int x, int y, int width, int height, void** addr);
-
-  // Must be called after Lock() when the caller has finished changing the
-  // buffer.
-  int Unlock();
-
-  // Gets a blob buffer that was created with BufferProducer::CreateBlob.
-  // Locking and Unlocking is handled internally. There's no need to Unlock
-  // after calling this method.
-  int GetBlobReadWritePointer(size_t size, void** addr);
-
-  // Gets a blob buffer that was created with BufferProducer::CreateBlob.
-  // Locking and Unlocking is handled internally. There's no need to Unlock
-  // after calling this method.
-  int GetBlobReadOnlyPointer(size_t size, void** addr);
-
-  // Returns a dup'd file descriptor for accessing the blob shared memory. The
-  // caller takes ownership of the file descriptor and must close it or pass on
-  // ownership. Some GPU API extensions can take file descriptors to bind shared
-  // memory gralloc buffers to GPU buffer objects.
-  LocalHandle GetBlobFd() const {
-    // Current GPU vendor puts the buffer allocation in one FD. If we change GPU
-    // vendors and this is the wrong fd, late-latching and EDS will very clearly
-    // stop working and we will need to correct this. The alternative is to use
-    // a GL context in the pose service to allocate this buffer or to use the
-    // ION API directly instead of gralloc.
-    return LocalHandle(dup(native_handle()->data[0]));
-  }
-
-  // Get up to |max_fds_count| file descriptors for accessing the blob shared
-  // memory. |fds_count| will contain the actual number of file descriptors.
-  void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const;
-
-  using Client::event_fd;
-
-  Status<int> GetEventMask(int events) {
-    if (auto* client_channel = GetChannel()) {
-      return client_channel->GetEventMask(events);
-    } else {
-      return pdx::ErrorStatus(EINVAL);
-    }
-  }
-
-  std::vector<pdx::ClientChannel::EventSource> GetEventSources() const {
-    if (auto* client_channel = GetChannel()) {
-      return client_channel->GetEventSources();
-    } else {
-      return {};
-    }
-  }
-
-  native_handle_t* native_handle() const {
-    return const_cast<native_handle_t*>(buffer_.handle());
-  }
-
-  IonBuffer* buffer() { return &buffer_; }
-  const IonBuffer* buffer() const { return &buffer_; }
-
-  int id() const { return id_; }
-
-  // Returns the buffer buffer state.
-  uint64_t buffer_state() { return buffer_state_->load(); };
-
-  // A state mask which is unique to a buffer hub client among all its siblings
-  // sharing the same concrete graphic buffer.
-  uint64_t buffer_state_bit() const { return buffer_state_bit_; }
-
-  // The following methods return settings of the first buffer. Currently,
-  // it is only possible to create multi-buffer BufferHubBuffers with the same
-  // settings.
-  uint32_t width() const { return buffer_.width(); }
-  uint32_t height() const { return buffer_.height(); }
-  uint32_t stride() const { return buffer_.stride(); }
-  uint32_t format() const { return buffer_.format(); }
-  uint32_t usage() const { return buffer_.usage(); }
-  uint32_t layer_count() const { return buffer_.layer_count(); }
-
-  uint64_t GetQueueIndex() const { return metadata_header_->queue_index; }
-  void SetQueueIndex(uint64_t index) { metadata_header_->queue_index = index; }
-
- protected:
-  explicit BufferHubBuffer(LocalChannelHandle channel);
-  explicit BufferHubBuffer(const std::string& endpoint_path);
-  virtual ~BufferHubBuffer();
-
-  // Initialization helper.
-  int ImportBuffer();
-
-  // Check invalid metadata operation. Returns 0 if requested metadata is valid.
-  int CheckMetadata(size_t user_metadata_size) const;
-
-  // Send out the new fence by updating the shared fence (shared_release_fence
-  // for producer and shared_acquire_fence for consumer). Note that during this
-  // should only be used in LocalPost() or LocalRelease, and the shared fence
-  // shouldn't be poll'ed by the other end.
-  int UpdateSharedFence(const LocalHandle& new_fence,
-                        const LocalHandle& shared_fence);
-
-  // IonBuffer that is shared between bufferhubd, producer, and consumers.
-  size_t metadata_buf_size_{0};
-  size_t user_metadata_size_{0};
-  BufferHubDefs::MetadataHeader* metadata_header_{nullptr};
-  void* user_metadata_ptr_{nullptr};
-  std::atomic<uint64_t>* buffer_state_{nullptr};
-  std::atomic<uint64_t>* fence_state_{nullptr};
-
-  LocalHandle shared_acquire_fence_;
-  LocalHandle shared_release_fence_;
-
-  // A local fence fd that holds the ownership of the fence fd on Post (for
-  // producer) and Release (for consumer).
-  LocalHandle pending_fence_fd_;
-
- private:
-  BufferHubBuffer(const BufferHubBuffer&) = delete;
-  void operator=(const BufferHubBuffer&) = delete;
-
-  // Global id for the buffer that is consistent across processes. It is meant
-  // for logging and debugging purposes only and should not be used for lookup
-  // or any other functional purpose as a security precaution.
-  int id_;
-  uint64_t buffer_state_bit_{0ULL};
-  IonBuffer buffer_;
-  IonBuffer metadata_buffer_;
-};
-
-// This represents a writable buffer. Calling Post notifies all clients and
-// makes the buffer read-only. Call Gain to acquire write access. A buffer
-// may have many consumers.
-//
-// The user of BufferProducer is responsible with making sure that the Post() is
-// done with the correct metadata type and size. The user is also responsible
-// for making sure that remote ends (BufferConsumers) are also using the correct
-// metadata when acquiring the buffer. The API guarantees that a Post() with a
-// metadata of wrong size will fail. However, it currently does not do any
-// type checking.
-// The API also assumes that metadata is a serializable type (plain old data).
-class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> {
- public:
-  // Imports a bufferhub producer channel, assuming ownership of its handle.
-  static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel);
-  static std::unique_ptr<BufferProducer> Import(
-      Status<LocalChannelHandle> status);
-
-  // Asynchronously posts a buffer. The fence and metadata are passed to
-  // consumer via shared fd and shared memory.
-  int PostAsync(const DvrNativeBufferMetadata* meta,
-                const LocalHandle& ready_fence);
-
-  // Post this buffer, passing |ready_fence| to the consumers. The bytes in
-  // |meta| are passed unaltered to the consumers. The producer must not modify
-  // the buffer until it is re-gained.
-  // This returns zero or a negative unix error code.
-  int Post(const LocalHandle& ready_fence, const void* meta,
-           size_t user_metadata_size);
-
-  template <typename Meta,
-            typename = typename std::enable_if<std::is_void<Meta>::value>::type>
-  int Post(const LocalHandle& ready_fence) {
-    return Post(ready_fence, nullptr, 0);
-  }
-  template <typename Meta, typename = typename std::enable_if<
-                               !std::is_void<Meta>::value>::type>
-  int Post(const LocalHandle& ready_fence, const Meta& meta) {
-    return Post(ready_fence, &meta, sizeof(meta));
-  }
-
-  // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it
-  // must be waited on before using the buffer. If it is not valid then the
-  // buffer is free for immediate use. This call will only succeed if the buffer
-  // is in the released state.
-  // This returns zero or a negative unix error code.
-  int Gain(LocalHandle* release_fence);
-  int GainAsync();
-
-  // Asynchronously marks a released buffer as gained. This method is similar to
-  // the synchronous version above, except that it does not wait for BufferHub
-  // to acknowledge success or failure. Because of the asynchronous nature of
-  // the underlying message, no error is returned if this method is called when
-  // the buffer is in an incorrect state. Returns zero if sending the message
-  // succeeded, or a negative errno code if local error check fails.
-  int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
-
-  // Detaches a ProducerBuffer from an existing producer/consumer set. Can only
-  // be called when a producer buffer has exclusive access to the buffer (i.e.
-  // in the gain'ed state). On the successful return of the IPC call, a new
-  // LocalChannelHandle representing a detached buffer will be returned and all
-  // existing producer and consumer channels will be closed. Further IPCs
-  // towards those channels will return error.
-  Status<LocalChannelHandle> Detach();
-
- private:
-  friend BASE;
-
-  // Constructors are automatically exposed through BufferProducer::Create(...)
-  // static template methods inherited from ClientBase, which take the same
-  // arguments as the constructors.
-
-  // Constructs a buffer with the given geometry and parameters.
-  BufferProducer(uint32_t width, uint32_t height, uint32_t format,
-                 uint64_t usage, size_t metadata_size = 0);
-
-  // Constructs a blob (flat) buffer with the given usage flags.
-  BufferProducer(uint64_t usage, size_t size);
-
-  // Imports the given file handle to a producer channel, taking ownership.
-  explicit BufferProducer(LocalChannelHandle channel);
-
-  // Local state transition helpers.
-  int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
-  int LocalPost(const DvrNativeBufferMetadata* meta,
-                const LocalHandle& ready_fence);
-};
-
-// This is a connection to a producer buffer, which can be located in another
-// application. When that buffer is Post()ed, this fd will be signaled and
-// Acquire allows read access. The user is responsible for making sure that
-// Acquire is called with the correct metadata structure. The only guarantee the
-// API currently provides is that an Acquire() with metadata of the wrong size
-// will fail.
-class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> {
- public:
-  // This call assumes ownership of |fd|.
-  static std::unique_ptr<BufferConsumer> Import(LocalChannelHandle channel);
-  static std::unique_ptr<BufferConsumer> Import(
-      Status<LocalChannelHandle> status);
-
-  // Attempt to retrieve a post event from buffer hub. If successful,
-  // |ready_fence| will be set to a fence to wait on until the buffer is ready.
-  // This call will only succeed after the fd is signalled. This call may be
-  // performed as an alternative to the Acquire() with metadata. In such cases
-  // the metadata is not read.
-  //
-  // This returns zero or negative unix error code.
-  int Acquire(LocalHandle* ready_fence);
-
-  // Attempt to retrieve a post event from buffer hub. If successful,
-  // |ready_fence| is set to a fence signaling that the contents of the buffer
-  // are available. This call will only succeed if the buffer is in the posted
-  // state.
-  // Returns zero on success, or a negative errno code otherwise.
-  int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size);
-
-  // Attempt to retrieve a post event from buffer hub. If successful,
-  // |ready_fence| is set to a fence to wait on until the buffer is ready. This
-  // call will only succeed after the fd is signaled. This returns zero or a
-  // negative unix error code.
-  template <typename Meta>
-  int Acquire(LocalHandle* ready_fence, Meta* meta) {
-    return Acquire(ready_fence, meta, sizeof(*meta));
-  }
-
-  // Asynchronously acquires a bufer.
-  int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
-
-  // This should be called after a successful Acquire call. If the fence is
-  // valid the fence determines the buffer usage, otherwise the buffer is
-  // released immediately.
-  // This returns zero or a negative unix error code.
-  int Release(const LocalHandle& release_fence);
-  int ReleaseAsync();
-
-  // Asynchronously releases a buffer. Similar to the synchronous version above,
-  // except that it does not wait for BufferHub to reply with success or error.
-  // The fence and metadata are passed to consumer via shared fd and shared
-  // memory.
-  int ReleaseAsync(const DvrNativeBufferMetadata* meta,
-                   const LocalHandle& release_fence);
-
-  // May be called after or instead of Acquire to indicate that the consumer
-  // does not need to access the buffer this cycle. This returns zero or a
-  // negative unix error code.
-  int Discard();
-
-  // When set, this consumer is no longer notified when this buffer is
-  // available. The system behaves as if Discard() is immediately called
-  // whenever the buffer is posted. If ignore is set to true while a buffer is
-  // pending, it will act as if Discard() was also called.
-  // This returns zero or a negative unix error code.
-  int SetIgnore(bool ignore);
-
- private:
-  friend BASE;
-
-  explicit BufferConsumer(LocalChannelHandle channel);
-
-  // Local state transition helpers.
-  int LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
-  int LocalRelease(const DvrNativeBufferMetadata* meta,
-                   const LocalHandle& release_fence);
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_BUFFER_HUB_CLIENT_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
new file mode 100644
index 0000000..bab7367
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
@@ -0,0 +1,164 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_DEFS_H_
+#define ANDROID_DVR_BUFFER_HUB_DEFS_H_
+
+#include <dvr/dvr_api.h>
+#include <hardware/gralloc.h>
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <private/dvr/native_handle_wrapper.h>
+#include <ui/BufferHubDefs.h>
+
+namespace android {
+namespace dvr {
+
+namespace BufferHubDefs {
+
+static constexpr uint32_t kMetadataFormat = HAL_PIXEL_FORMAT_BLOB;
+static constexpr uint32_t kMetadataUsage =
+    GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
+
+// See more details in libs/ui/include/ui/BufferHubDefs.h
+static constexpr int kMaxNumberOfClients =
+    android::BufferHubDefs::kMaxNumberOfClients;
+static constexpr uint32_t kLowbitsMask = android::BufferHubDefs::kLowbitsMask;
+static constexpr uint32_t kHighBitsMask = android::BufferHubDefs::kHighBitsMask;
+static constexpr uint32_t kFirstClientBitMask =
+    android::BufferHubDefs::kFirstClientBitMask;
+
+static inline bool AnyClientGained(uint32_t state) {
+  return android::BufferHubDefs::AnyClientGained(state);
+}
+
+static inline bool IsClientGained(uint32_t state, uint32_t client_bit_mask) {
+  return android::BufferHubDefs::IsClientGained(state, client_bit_mask);
+}
+
+static inline bool AnyClientPosted(uint32_t state) {
+  return android::BufferHubDefs::AnyClientPosted(state);
+}
+
+static inline bool IsClientPosted(uint32_t state, uint32_t client_bit_mask) {
+  return android::BufferHubDefs::IsClientPosted(state, client_bit_mask);
+}
+
+static inline bool AnyClientAcquired(uint32_t state) {
+  return android::BufferHubDefs::AnyClientAcquired(state);
+}
+
+static inline bool IsClientAcquired(uint32_t state, uint32_t client_bit_mask) {
+  return android::BufferHubDefs::IsClientAcquired(state, client_bit_mask);
+}
+
+static inline bool IsClientReleased(uint32_t state, uint32_t client_bit_mask) {
+  return android::BufferHubDefs::IsClientReleased(state, client_bit_mask);
+}
+
+// Returns the next available buffer client's client_state_masks.
+// @params union_bits. Union of all existing clients' client_state_masks.
+static inline uint32_t FindNextAvailableClientStateMask(uint32_t union_bits) {
+  return android::BufferHubDefs::FindNextAvailableClientStateMask(union_bits);
+}
+
+using MetadataHeader = android::BufferHubDefs::MetadataHeader;
+static constexpr size_t kMetadataHeaderSize =
+    android::BufferHubDefs::kMetadataHeaderSize;
+
+}  // namespace BufferHubDefs
+
+template <typename FileHandleType>
+class BufferTraits {
+ public:
+  BufferTraits() = default;
+  BufferTraits(const native_handle_t* buffer_handle,
+               const FileHandleType& metadata_handle, int id,
+               uint32_t client_state_mask, uint64_t metadata_size,
+               uint32_t width, uint32_t height, uint32_t layer_count,
+               uint32_t format, uint64_t usage, uint32_t stride,
+               const FileHandleType& acquire_fence_fd,
+               const FileHandleType& release_fence_fd)
+      : id_(id),
+        client_state_mask_(client_state_mask),
+        metadata_size_(metadata_size),
+        width_(width),
+        height_(height),
+        layer_count_(layer_count),
+        format_(format),
+        usage_(usage),
+        stride_(stride),
+        buffer_handle_(buffer_handle),
+        metadata_handle_(metadata_handle.Borrow()),
+        acquire_fence_fd_(acquire_fence_fd.Borrow()),
+        release_fence_fd_(release_fence_fd.Borrow()) {}
+
+  BufferTraits(BufferTraits&& other) = default;
+  BufferTraits& operator=(BufferTraits&& other) = default;
+
+  // ID of the buffer client. All BufferHubBuffer clients derived from the same
+  // buffer in bufferhubd share the same buffer id.
+  int id() const { return id_; }
+
+  // State mask of the buffer client. Each BufferHubBuffer client backed by the
+  // same buffer channel has uniqued state bit among its siblings. For a
+  // producer buffer the bit must be kFirstClientBitMask; for a consumer the bit
+  // must be one of the kConsumerStateMask.
+  uint32_t client_state_mask() const { return client_state_mask_; }
+  uint64_t metadata_size() const { return metadata_size_; }
+
+  uint32_t width() { return width_; }
+  uint32_t height() { return height_; }
+  uint32_t layer_count() { return layer_count_; }
+  uint32_t format() { return format_; }
+  uint64_t usage() { return usage_; }
+  uint32_t stride() { return stride_; }
+
+  const NativeHandleWrapper<FileHandleType>& buffer_handle() const {
+    return buffer_handle_;
+  }
+
+  NativeHandleWrapper<FileHandleType> take_buffer_handle() {
+    return std::move(buffer_handle_);
+  }
+  FileHandleType take_metadata_handle() { return std::move(metadata_handle_); }
+  FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); }
+  FileHandleType take_release_fence() { return std::move(release_fence_fd_); }
+
+ private:
+  // BufferHub specific traits.
+  int id_ = -1;
+  uint32_t client_state_mask_;
+  uint64_t metadata_size_;
+
+  // Traits for a GraphicBuffer.
+  uint32_t width_;
+  uint32_t height_;
+  uint32_t layer_count_;
+  uint32_t format_;
+  uint64_t usage_;
+  uint32_t stride_;
+
+  // Native handle for the graphic buffer.
+  NativeHandleWrapper<FileHandleType> buffer_handle_;
+
+  // File handle of an ashmem that holds buffer metadata.
+  FileHandleType metadata_handle_;
+
+  // Pamameters for shared fences.
+  FileHandleType acquire_fence_fd_;
+  FileHandleType release_fence_fd_;
+
+  PDX_SERIALIZABLE_MEMBERS(BufferTraits<FileHandleType>, id_,
+                           client_state_mask_, metadata_size_, stride_, width_,
+                           height_, layer_count_, format_, usage_,
+                           buffer_handle_, metadata_handle_, acquire_fence_fd_,
+                           release_fence_fd_);
+
+  BufferTraits(const BufferTraits&) = delete;
+  void operator=(const BufferTraits&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFER_HUB_DEFS_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
index 04f4fb4..f1cd0b4 100644
--- a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -1,11 +1,11 @@
 #ifndef ANDROID_DVR_BUFFERHUB_RPC_H_
 #define ANDROID_DVR_BUFFERHUB_RPC_H_
 
+#include "buffer_hub_defs.h"
+
 #include <cutils/native_handle.h>
-#include <sys/types.h>
 #include <ui/BufferQueueDefs.h>
 
-#include <dvr/dvr_api.h>
 #include <pdx/channel_handle.h>
 #include <pdx/file_handle.h>
 #include <pdx/rpc/remote_method.h>
@@ -15,71 +15,6 @@
 namespace android {
 namespace dvr {
 
-namespace BufferHubDefs {
-
-static constexpr uint32_t kMetadataFormat = HAL_PIXEL_FORMAT_BLOB;
-static constexpr uint32_t kMetadataUsage =
-    GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
-
-// Single producuer multiple (up to 63) consumers ownership signal.
-// 64-bit atomic unsigned int.
-//
-// MSB           LSB
-//  |             |
-//  v             v
-// [P|C62|...|C1|C0]
-// Gain'ed state:     [0|..|0|0] -> Exclusively Writable.
-// Post'ed state:     [1|..|0|0]
-// Acquired'ed state: [1|..|X|X] -> At least one bit is set in lower 63 bits
-// Released'ed state: [0|..|X|X] -> At least one bit is set in lower 63 bits
-static constexpr uint64_t kProducerStateBit = 1ULL << 63;
-static constexpr uint64_t kConsumerStateMask = (1ULL << 63) - 1;
-
-static inline void ModifyBufferState(std::atomic<uint64_t>* buffer_state,
-                                     uint64_t clear_mask, uint64_t set_mask) {
-  uint64_t old_state;
-  uint64_t new_state;
-  do {
-    old_state = buffer_state->load();
-    new_state = (old_state & ~clear_mask) | set_mask;
-  } while (!buffer_state->compare_exchange_weak(old_state, new_state));
-}
-
-static inline bool IsBufferGained(uint64_t state) { return state == 0; }
-
-static inline bool IsBufferPosted(uint64_t state,
-                                  uint64_t consumer_bit = kConsumerStateMask) {
-  return (state & kProducerStateBit) && !(state & consumer_bit);
-}
-
-static inline bool IsBufferAcquired(uint64_t state) {
-  return (state & kProducerStateBit) && (state & kConsumerStateMask);
-}
-
-static inline bool IsBufferReleased(uint64_t state) {
-  return !(state & kProducerStateBit) && (state & kConsumerStateMask);
-}
-
-struct __attribute__((packed, aligned(8))) MetadataHeader {
-  // Internal data format, which can be updated as long as the size, padding and
-  // field alignment of the struct is consistent within the same ABI. As this
-  // part is subject for future updates, it's not stable cross Android version,
-  // so don't have it visible from outside of the Android platform (include Apps
-  // and vendor HAL).
-  std::atomic<uint64_t> buffer_state;
-  std::atomic<uint64_t> fence_state;
-  uint64_t queue_index;
-
-  // Public data format, which should be updated with caution. See more details
-  // in dvr_api.h
-  DvrNativeBufferMetadata metadata;
-};
-
-static_assert(sizeof(MetadataHeader) == 128, "Unexpected MetadataHeader size");
-static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader);
-
-}  // namespace BufferHubDefs
-
 template <typename FileHandleType>
 class NativeBufferHandle {
  public:
@@ -164,11 +99,12 @@
  public:
   BufferDescription() = default;
   BufferDescription(const IonBuffer& buffer, const IonBuffer& metadata, int id,
-                    uint64_t buffer_state_bit,
+                    int buffer_cid, uint32_t client_state_mask,
                     const FileHandleType& acquire_fence_fd,
                     const FileHandleType& release_fence_fd)
       : id_(id),
-        buffer_state_bit_(buffer_state_bit),
+        buffer_cid_(buffer_cid),
+        client_state_mask_(client_state_mask),
         buffer_(buffer, id),
         metadata_(metadata, id),
         acquire_fence_fd_(acquire_fence_fd.Borrow()),
@@ -177,14 +113,17 @@
   BufferDescription(BufferDescription&& other) noexcept = default;
   BufferDescription& operator=(BufferDescription&& other) noexcept = default;
 
-  // ID of the buffer client. All BufferHubBuffer clients derived from the same
-  // buffer in bufferhubd share the same buffer id.
+  // ID of the buffer client. All BufferHub clients derived from the same buffer
+  // in bufferhubd share the same buffer id.
   int id() const { return id_; }
-  // State mask of the buffer client. Each BufferHubBuffer client backed by the
-  // same buffer channel has uniqued state bit among its siblings. For a
-  // producer buffer the bit must be kProducerStateBit; for a consumer the bit
-  // must be one of the kConsumerStateMask.
-  uint64_t buffer_state_bit() const { return buffer_state_bit_; }
+
+  // Channel ID of the buffer client. Each BufferHub client has its system
+  // unique channel id.
+  int buffer_cid() const { return buffer_cid_; }
+
+  // State mask of the buffer client. Each BufferHub client backed by the
+  // same buffer channel has uniqued state bit among its siblings.
+  uint32_t client_state_mask() const { return client_state_mask_; }
   FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); }
   FileHandleType take_release_fence() { return std::move(release_fence_fd_); }
 
@@ -193,7 +132,8 @@
 
  private:
   int id_{-1};
-  uint64_t buffer_state_bit_{0};
+  int buffer_cid_{-1};
+  uint32_t client_state_mask_{0U};
   // Two IonBuffers: one for the graphic buffer and one for metadata.
   NativeBufferHandle<FileHandleType> buffer_;
   NativeBufferHandle<FileHandleType> metadata_;
@@ -202,8 +142,8 @@
   FileHandleType acquire_fence_fd_;
   FileHandleType release_fence_fd_;
 
-  PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_,
-                           buffer_state_bit_, buffer_, metadata_,
+  PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_, buffer_cid_,
+                           client_state_mask_, buffer_, metadata_,
                            acquire_fence_fd_, release_fence_fd_);
 
   BufferDescription(const BufferDescription&) = delete;
@@ -372,19 +312,15 @@
     kOpProducerGain,
     kOpConsumerAcquire,
     kOpConsumerRelease,
-    kOpConsumerSetIgnore,
-    kOpProducerBufferDetach,
     kOpConsumerBufferDetach,
-    kOpDetachedBufferCreate,
-    kOpDetachedBufferPromote,
     kOpCreateProducerQueue,
     kOpCreateConsumerQueue,
     kOpGetQueueInfo,
     kOpProducerQueueAllocateBuffers,
+    kOpProducerQueueInsertBuffer,
     kOpProducerQueueRemoveBuffer,
     kOpConsumerQueueImportBuffers,
     // TODO(b/77153033): Separate all those RPC operations into subclasses.
-    kOpDetachedBufferBase = 1000,
   };
 
   // Aliases.
@@ -405,9 +341,6 @@
   PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire, LocalFence(Void));
   PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease,
                     void(LocalFence release_fence));
-  PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore));
-  PDX_REMOTE_METHOD(ProducerBufferDetach, kOpProducerBufferDetach,
-                    LocalChannelHandle(Void));
 
   // Detaches a ConsumerBuffer from an existing producer/consumer set. Can only
   // be called when the consumer is the only consumer and it has exclusive
@@ -430,31 +363,14 @@
                     std::vector<std::pair<LocalChannelHandle, size_t>>(
                         uint32_t width, uint32_t height, uint32_t layer_count,
                         uint32_t format, uint64_t usage, size_t buffer_count));
+  PDX_REMOTE_METHOD(ProducerQueueInsertBuffer, kOpProducerQueueInsertBuffer,
+                    size_t(int buffer_cid));
   PDX_REMOTE_METHOD(ProducerQueueRemoveBuffer, kOpProducerQueueRemoveBuffer,
                     void(size_t slot));
   PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers,
                     std::vector<std::pair<LocalChannelHandle, size_t>>(Void));
 };
 
-struct DetachedBufferRPC final : public BufferHubRPC {
- private:
-  enum {
-    kOpCreate = kOpDetachedBufferBase,
-    kOpImport,
-    kOpPromote,
-  };
-
- public:
-  PDX_REMOTE_METHOD(Create, kOpCreate,
-                    void(uint32_t width, uint32_t height, uint32_t layer_count,
-                         uint32_t format, uint64_t usage,
-                         size_t user_metadata_size));
-  PDX_REMOTE_METHOD(Import, kOpImport, BufferDescription<LocalHandle>(Void));
-  PDX_REMOTE_METHOD(Promote, kOpPromote, LocalChannelHandle(Void));
-
-  PDX_REMOTE_API(API, Create, Promote);
-};
-
 }  // namespace dvr
 }  // namespace android
 
diff --git a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
new file mode 100644
index 0000000..726f035
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
@@ -0,0 +1,73 @@
+#ifndef ANDROID_DVR_CONSUMER_BUFFER_H_
+#define ANDROID_DVR_CONSUMER_BUFFER_H_
+
+#include <private/dvr/buffer_hub_base.h>
+
+namespace android {
+namespace dvr {
+
+// This is a connection to a producer buffer, which can be located in another
+// application. When that buffer is Post()ed, this fd will be signaled and
+// Acquire allows read access. The user is responsible for making sure that
+// Acquire is called with the correct metadata structure. The only guarantee the
+// API currently provides is that an Acquire() with metadata of the wrong size
+// will fail.
+class ConsumerBuffer : public pdx::ClientBase<ConsumerBuffer, BufferHubBase> {
+ public:
+  // This call assumes ownership of |fd|.
+  static std::unique_ptr<ConsumerBuffer> Import(LocalChannelHandle channel);
+  static std::unique_ptr<ConsumerBuffer> Import(
+      Status<LocalChannelHandle> status);
+
+  // Attempt to retrieve a post event from buffer hub. If successful,
+  // |ready_fence| will be set to a fence to wait on until the buffer is ready.
+  // This call will only succeed after the fd is signalled. This call may be
+  // performed as an alternative to the Acquire() with metadata. In such cases
+  // the metadata is not read.
+  //
+  // This returns zero or negative unix error code.
+  int Acquire(LocalHandle* ready_fence);
+
+  // Attempt to retrieve a post event from buffer hub. If successful,
+  // |ready_fence| is set to a fence signaling that the contents of the buffer
+  // are available. This call will only succeed if the buffer is in the posted
+  // state.
+  // Returns zero on success, or a negative errno code otherwise.
+  int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size);
+
+  // Asynchronously acquires a bufer.
+  int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
+
+  // Releases the buffer from any buffer state. If the fence is valid the fence
+  // determines the buffer usage, otherwise the buffer is released immediately.
+  // This returns zero or a negative unix error code.
+  int Release(const LocalHandle& release_fence);
+  int ReleaseAsync();
+
+  // Asynchronously releases a buffer. Similar to the synchronous version above,
+  // except that it does not wait for BufferHub to reply with success or error.
+  // The fence and metadata are passed to consumer via shared fd and shared
+  // memory.
+  int ReleaseAsync(const DvrNativeBufferMetadata* meta,
+                   const LocalHandle& release_fence);
+
+  // May be called after or instead of Acquire to indicate that the consumer
+  // does not need to access the buffer this cycle. This returns zero or a
+  // negative unix error code.
+  int Discard();
+
+ private:
+  friend BASE;
+
+  explicit ConsumerBuffer(LocalChannelHandle channel);
+
+  // Local state transition helpers.
+  int LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
+  int LocalRelease(const DvrNativeBufferMetadata* meta,
+                   const LocalHandle& release_fence);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_CONSUMER_BUFFER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h b/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h
deleted file mode 100644
index 6d0b502..0000000
--- a/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h
+++ /dev/null
@@ -1,82 +0,0 @@
-#ifndef ANDROID_DVR_DETACHED_BUFFER_H_
-#define ANDROID_DVR_DETACHED_BUFFER_H_
-
-#include <private/dvr/buffer_hub_client.h>
-
-namespace android {
-namespace dvr {
-
-class DetachedBuffer {
- public:
-  // Allocates a standalone DetachedBuffer not associated with any producer
-  // consumer set.
-  static std::unique_ptr<DetachedBuffer> Create(uint32_t width, uint32_t height,
-                                                uint32_t layer_count,
-                                                uint32_t format, uint64_t usage,
-                                                size_t user_metadata_size) {
-    return std::unique_ptr<DetachedBuffer>(new DetachedBuffer(
-        width, height, layer_count, format, usage, user_metadata_size));
-  }
-
-  // Imports the given channel handle to a DetachedBuffer, taking ownership.
-  static std::unique_ptr<DetachedBuffer> Import(
-      pdx::LocalChannelHandle channel_handle) {
-    return std::unique_ptr<DetachedBuffer>(
-        new DetachedBuffer(std::move(channel_handle)));
-  }
-
-  DetachedBuffer(const DetachedBuffer&) = delete;
-  void operator=(const DetachedBuffer&) = delete;
-
-  const sp<GraphicBuffer>& buffer() const { return buffer_.buffer(); }
-
-  int id() const { return id_; }
-
-  // Returns true if the buffer holds an open PDX channels towards bufferhubd.
-  bool IsConnected() const { return client_.IsValid(); }
-
-  // Returns true if the buffer holds an valid gralloc buffer handle that's
-  // availble for the client to read from and/or write into.
-  bool IsValid() const { return buffer_.IsValid(); }
-
-  // Returns the event mask for all the events that are pending on this buffer
-  // (see sys/poll.h for all possible bits).
-  pdx::Status<int> GetEventMask(int events) {
-    if (auto* channel = client_.GetChannel()) {
-      return channel->GetEventMask(events);
-    } else {
-      return pdx::ErrorStatus(EINVAL);
-    }
-  }
-
-  // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
-  int Poll(int timeout_ms);
-
-  // Promotes a DetachedBuffer to become a ProducerBuffer. Once promoted the
-  // DetachedBuffer channel will be closed automatically on successful IPC
-  // return. Further IPCs towards this channel will return error.
-  pdx::Status<pdx::LocalChannelHandle> Promote();
-
-  // Takes the underlying graphic buffer out of this DetachedBuffer. This call
-  // immediately invalidates this DetachedBuffer object and transfers the
-  // underlying pdx::LocalChannelHandle into the GraphicBuffer.
-  sp<GraphicBuffer> TakeGraphicBuffer();
-
- private:
-  DetachedBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
-                 uint32_t format, uint64_t usage, size_t user_metadata_size);
-
-  DetachedBuffer(pdx::LocalChannelHandle channel_handle);
-
-  int ImportGraphicBuffer();
-
-  // Global id for the buffer that is consistent across processes.
-  int id_;
-  IonBuffer buffer_;
-  BufferHubClient client_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_DETACHED_BUFFER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
index 860f08a..ed38e7f 100644
--- a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
+++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
@@ -24,7 +24,7 @@
   IonBuffer& operator=(IonBuffer&& other) noexcept;
 
   // Returns check this IonBuffer holds a valid Gralloc buffer.
-  bool IsValid() const { return buffer_ && buffer_->initCheck() == NO_ERROR; }
+  bool IsValid() const { return buffer_ && buffer_->initCheck() == OK; }
 
   // Frees the underlying native handle and leaves the instance initialized to
   // empty.
diff --git a/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h
new file mode 100644
index 0000000..a5c6ca2
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h
@@ -0,0 +1,102 @@
+#ifndef ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_
+#define ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_
+
+#include <cutils/native_handle.h>
+#include <log/log.h>
+#include <pdx/rpc/serializable.h>
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+// A PDX-friendly wrapper to maintain the life cycle of a native_handle_t
+// object.
+//
+// See https://source.android.com/devices/architecture/hidl/types#handle_t for
+// more information about native_handle_t.
+template <typename FileHandleType>
+class NativeHandleWrapper {
+ public:
+  NativeHandleWrapper() = default;
+  NativeHandleWrapper(NativeHandleWrapper&& other) = default;
+  NativeHandleWrapper& operator=(NativeHandleWrapper&& other) = default;
+
+  // Create a new NativeHandleWrapper by duplicating the handle.
+  explicit NativeHandleWrapper(const native_handle_t* handle) {
+    const int fd_count = handle->numFds;
+    const int int_count = handle->numInts;
+
+    // Populate the fd and int vectors: native_handle->data[] is an array of fds
+    // followed by an array of opaque ints.
+    for (int i = 0; i < fd_count; i++) {
+      fds_.emplace_back(FileHandleType::AsDuplicate(handle->data[i]));
+    }
+    for (int i = 0; i < int_count; i++) {
+      ints_.push_back(handle->data[fd_count + i]);
+    }
+  }
+
+  size_t int_count() const { return ints_.size(); }
+  size_t fd_count() const { return fds_.size(); }
+  bool IsValid() const { return ints_.size() != 0 || fds_.size() != 0; }
+
+  // Duplicate a native handle from the wrapper.
+  native_handle_t* DuplicateHandle() const {
+    if (!IsValid()) {
+      return nullptr;
+    }
+
+    // numFds + numInts ints.
+    std::vector<FileHandleType> fds;
+    for (const auto& fd : fds_) {
+      if (!fd.IsValid()) {
+        return nullptr;
+      }
+      fds.emplace_back(fd.Duplicate());
+    }
+
+    return FromFdsAndInts(std::move(fds), ints_);
+  }
+
+  // Takes the native handle out of the wrapper.
+  native_handle_t* TakeHandle() {
+    if (!IsValid()) {
+      return nullptr;
+    }
+
+    return FromFdsAndInts(std::move(fds_), std::move(ints_));
+  }
+
+ private:
+  NativeHandleWrapper(const NativeHandleWrapper&) = delete;
+  void operator=(const NativeHandleWrapper&) = delete;
+
+  static native_handle_t* FromFdsAndInts(std::vector<FileHandleType> fds,
+                                         std::vector<int> ints) {
+    native_handle_t* handle = native_handle_create(fds.size(), ints.size());
+    if (!handle) {
+      ALOGE("NativeHandleWrapper::TakeHandle: Failed to create new handle.");
+      return nullptr;
+    }
+
+    // numFds + numInts ints.
+    for (int i = 0; i < handle->numFds; i++) {
+      handle->data[i] = fds[i].Release();
+    }
+    memcpy(&handle->data[handle->numFds], ints.data(),
+           sizeof(int) * handle->numInts);
+
+    return handle;
+  }
+
+  std::vector<int> ints_;
+  std::vector<FileHandleType> fds_;
+
+  PDX_SERIALIZABLE_MEMBERS(NativeHandleWrapper<FileHandleType>, ints_, fds_);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h
new file mode 100644
index 0000000..7ec345c
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h
@@ -0,0 +1,103 @@
+#ifndef ANDROID_DVR_PRODUCER_BUFFER_H_
+#define ANDROID_DVR_PRODUCER_BUFFER_H_
+
+#include <private/dvr/buffer_hub_base.h>
+
+namespace android {
+namespace dvr {
+
+// This represents a writable buffer. Calling Post notifies all clients and
+// makes the buffer read-only. Call Gain to acquire write access. A buffer
+// may have many consumers.
+//
+// The user of ProducerBuffer is responsible with making sure that the Post() is
+// done with the correct metadata type and size. The user is also responsible
+// for making sure that remote ends (ConsumerBuffers) are also using the correct
+// metadata when acquiring the buffer. The API guarantees that a Post() with a
+// metadata of wrong size will fail. However, it currently does not do any
+// type checking.
+// The API also assumes that metadata is a serializable type (plain old data).
+class ProducerBuffer : public pdx::ClientBase<ProducerBuffer, BufferHubBase> {
+ public:
+  // Imports a bufferhub producer channel, assuming ownership of its handle.
+  static std::unique_ptr<ProducerBuffer> Import(LocalChannelHandle channel);
+  static std::unique_ptr<ProducerBuffer> Import(
+      Status<LocalChannelHandle> status);
+
+  // Asynchronously posts a buffer. The fence and metadata are passed to
+  // consumer via shared fd and shared memory.
+  int PostAsync(const DvrNativeBufferMetadata* meta,
+                const LocalHandle& ready_fence);
+
+  // Post this buffer, passing |ready_fence| to the consumers. The bytes in
+  // |meta| are passed unaltered to the consumers. The producer must not modify
+  // the buffer until it is re-gained.
+  // This returns zero or a negative unix error code.
+  int Post(const LocalHandle& ready_fence, const void* meta,
+           size_t user_metadata_size);
+
+  int Post(const LocalHandle& ready_fence) {
+    return Post(ready_fence, nullptr, 0);
+  }
+
+  // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it
+  // must be waited on before using the buffer. If it is not valid then the
+  // buffer is free for immediate use. This call will succeed if the buffer
+  // is in the released state, or in posted state and gain_posted_buffer is
+  // true.
+  //
+  // @param release_fence output fence.
+  // @param gain_posted_buffer whether to gain posted buffer or not.
+  // @return This returns zero or a negative unix error code.
+  int Gain(LocalHandle* release_fence, bool gain_posted_buffer = false);
+
+  // Asynchronously marks a released buffer as gained. This method is similar to
+  // the synchronous version above, except that it does not wait for BufferHub
+  // to acknowledge success or failure. Because of the asynchronous nature of
+  // the underlying message, no error is returned if this method is called when
+  // the buffer is in an incorrect state. Returns zero if sending the message
+  // succeeded, or a negative errno code if local error check fails.
+  // TODO(b/112007999): gain_posted_buffer true is only used to prevent
+  // libdvrtracking from starving when there are non-responding clients. This
+  // gain_posted_buffer param can be removed once libdvrtracking start to use
+  // the new AHardwareBuffer API.
+  int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence,
+                bool gain_posted_buffer = false);
+  int GainAsync();
+
+  // Detaches a ProducerBuffer from an existing producer/consumer set. Can only
+  // be called when a producer buffer has exclusive access to the buffer (i.e.
+  // in the gain'ed state). On the successful return of the IPC call, a new
+  // LocalChannelHandle representing a detached buffer will be returned and all
+  // existing producer and consumer channels will be closed. Further IPCs
+  // towards those channels will return error.
+  Status<LocalChannelHandle> Detach();
+
+ private:
+  friend BASE;
+
+  // Constructors are automatically exposed through ProducerBuffer::Create(...)
+  // static template methods inherited from ClientBase, which take the same
+  // arguments as the constructors.
+
+  // Constructs a buffer with the given geometry and parameters.
+  ProducerBuffer(uint32_t width, uint32_t height, uint32_t format,
+                 uint64_t usage, size_t metadata_size = 0);
+
+  // Constructs a blob (flat) buffer with the given usage flags.
+  ProducerBuffer(uint64_t usage, size_t size);
+
+  // Imports the given file handle to a producer channel, taking ownership.
+  explicit ProducerBuffer(LocalChannelHandle channel);
+
+  // Local state transition helpers.
+  int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence,
+                bool gain_posted_buffer = false);
+  int LocalPost(const DvrNativeBufferMetadata* meta,
+                const LocalHandle& ready_fence);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PRODUCER_BUFFER_H_
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
index 1295531..1965410 100644
--- a/libs/vr/libbufferhub/ion_buffer.cpp
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -205,7 +205,7 @@
 
   status_t err =
       buffer_->lock(usage, Rect(x, y, x + width, y + height), address);
-  if (err != NO_ERROR)
+  if (err != OK)
     return -EINVAL;
   else
     return 0;
@@ -220,7 +220,7 @@
 
   status_t err =
       buffer_->lockYCbCr(usage, Rect(x, y, x + width, y + height), yuv);
-  if (err != NO_ERROR)
+  if (err != OK)
     return -EINVAL;
   else
     return 0;
@@ -231,7 +231,7 @@
   ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle());
 
   status_t err = buffer_->unlock();
-  if (err != NO_ERROR)
+  if (err != OK)
     return -EINVAL;
   else
     return 0;
diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp
new file mode 100644
index 0000000..edfdddf
--- /dev/null
+++ b/libs/vr/libbufferhub/producer_buffer.cpp
@@ -0,0 +1,312 @@
+#include <private/dvr/producer_buffer.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+ProducerBuffer::ProducerBuffer(uint32_t width, uint32_t height, uint32_t format,
+                               uint64_t usage, size_t user_metadata_size)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("ProducerBuffer::ProducerBuffer");
+  ALOGD_IF(TRACE,
+           "ProducerBuffer::ProducerBuffer: fd=%d width=%u height=%u format=%u "
+           "usage=%" PRIx64 " user_metadata_size=%zu",
+           event_fd(), width, height, format, usage, user_metadata_size);
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+      width, height, format, usage, user_metadata_size);
+  if (!status) {
+    ALOGE(
+        "ProducerBuffer::ProducerBuffer: Failed to create producer buffer: %s",
+        status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+ProducerBuffer::ProducerBuffer(uint64_t usage, size_t size)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("ProducerBuffer::ProducerBuffer");
+  ALOGD_IF(TRACE, "ProducerBuffer::ProducerBuffer: usage=%" PRIx64 " size=%zu",
+           usage, size);
+  const int width = static_cast<int>(size);
+  const int height = 1;
+  const int format = HAL_PIXEL_FORMAT_BLOB;
+  const size_t user_metadata_size = 0;
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+      width, height, format, usage, user_metadata_size);
+  if (!status) {
+    ALOGE("ProducerBuffer::ProducerBuffer: Failed to create blob: %s",
+          status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+ProducerBuffer::ProducerBuffer(LocalChannelHandle channel)
+    : BASE(std::move(channel)) {
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+int ProducerBuffer::LocalPost(const DvrNativeBufferMetadata* meta,
+                              const LocalHandle& ready_fence) {
+  if (const int error = CheckMetadata(meta->user_metadata_size))
+    return error;
+
+  // The buffer can be posted iff the buffer state for this client is gained.
+  uint32_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  if (!BufferHubDefs::IsClientGained(current_buffer_state,
+                                     client_state_mask())) {
+    ALOGE("%s: not gained, id=%d state=%" PRIx32 ".", __FUNCTION__, id(),
+          current_buffer_state);
+    return -EBUSY;
+  }
+
+  // Set the producer client buffer state to released, that of all other clients
+  // (both existing and non-existing clients) to posted.
+  uint32_t updated_buffer_state =
+      (~client_state_mask()) & BufferHubDefs::kHighBitsMask;
+  while (!buffer_state_->compare_exchange_weak(
+      current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+      std::memory_order_acquire)) {
+    ALOGD(
+        "%s: Failed to post the buffer. Current buffer state was changed to "
+        "%" PRIx32
+        " when trying to post the buffer and modify the buffer state to "
+        "%" PRIx32
+        ". About to try again if the buffer is still gained by this client.",
+        __FUNCTION__, current_buffer_state, updated_buffer_state);
+    if (!BufferHubDefs::IsClientGained(current_buffer_state,
+                                       client_state_mask())) {
+      ALOGE(
+          "%s: Failed to post the buffer. The buffer is no longer gained, "
+          "id=%d state=%" PRIx32 ".",
+          __FUNCTION__, id(), current_buffer_state);
+      return -EBUSY;
+    }
+  }
+
+  // Copy the canonical metadata.
+  void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
+  memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata));
+  // Copy extra user requested metadata.
+  if (meta->user_metadata_ptr && meta->user_metadata_size) {
+    void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
+    memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
+  }
+
+  // Send out the acquire fence through the shared epoll fd. Note that during
+  // posting no consumer is not expected to be polling on the fence.
+  if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_))
+    return error;
+
+  return 0;
+}
+
+int ProducerBuffer::Post(const LocalHandle& ready_fence, const void* meta,
+                         size_t user_metadata_size) {
+  ATRACE_NAME("ProducerBuffer::Post");
+
+  // Populate cononical metadata for posting.
+  DvrNativeBufferMetadata canonical_meta;
+  canonical_meta.user_metadata_ptr = reinterpret_cast<uint64_t>(meta);
+  canonical_meta.user_metadata_size = user_metadata_size;
+
+  if (const int error = LocalPost(&canonical_meta, ready_fence))
+    return error;
+
+  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
+      BorrowedFence(ready_fence.Borrow())));
+}
+
+int ProducerBuffer::PostAsync(const DvrNativeBufferMetadata* meta,
+                              const LocalHandle& ready_fence) {
+  ATRACE_NAME("ProducerBuffer::PostAsync");
+
+  if (const int error = LocalPost(meta, ready_fence))
+    return error;
+
+  return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode));
+}
+
+int ProducerBuffer::LocalGain(DvrNativeBufferMetadata* out_meta,
+                              LocalHandle* out_fence, bool gain_posted_buffer) {
+  if (!out_meta)
+    return -EINVAL;
+
+  uint32_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  ALOGD_IF(TRACE, "%s: buffer=%d, state=%" PRIx32 ".", __FUNCTION__, id(),
+           current_buffer_state);
+
+  if (BufferHubDefs::IsClientGained(current_buffer_state,
+                                    client_state_mask())) {
+    ALOGV("%s: already gained id=%d.", __FUNCTION__, id());
+    return 0;
+  }
+  if (BufferHubDefs::AnyClientAcquired(current_buffer_state) ||
+      BufferHubDefs::AnyClientGained(current_buffer_state) ||
+      (BufferHubDefs::AnyClientPosted(
+           current_buffer_state &
+           active_clients_bit_mask_->load(std::memory_order_acquire)) &&
+       !gain_posted_buffer)) {
+    ALOGE("%s: not released id=%d state=%" PRIx32 ".", __FUNCTION__, id(),
+          current_buffer_state);
+    return -EBUSY;
+  }
+  // Change the buffer state to gained state.
+  uint32_t updated_buffer_state = client_state_mask();
+  while (!buffer_state_->compare_exchange_weak(
+      current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+      std::memory_order_acquire)) {
+    ALOGD(
+        "%s: Failed to gain the buffer. Current buffer state was changed to "
+        "%" PRIx32
+        " when trying to gain the buffer and modify the buffer state to "
+        "%" PRIx32
+        ". About to try again if the buffer is still not read by other "
+        "clients.",
+        __FUNCTION__, current_buffer_state, updated_buffer_state);
+
+    if (BufferHubDefs::AnyClientAcquired(current_buffer_state) ||
+        BufferHubDefs::AnyClientGained(current_buffer_state) ||
+        (BufferHubDefs::AnyClientPosted(
+             current_buffer_state &
+             active_clients_bit_mask_->load(std::memory_order_acquire)) &&
+         !gain_posted_buffer)) {
+      ALOGE(
+          "%s: Failed to gain the buffer. The buffer is no longer released. "
+          "id=%d state=%" PRIx32 ".",
+          __FUNCTION__, id(), current_buffer_state);
+      return -EBUSY;
+    }
+  }
+
+  // Canonical metadata is undefined on Gain. Except for user_metadata and
+  // release_fence_mask. Fill in the user_metadata_ptr in address space of the
+  // local process.
+  if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) {
+    out_meta->user_metadata_size =
+        metadata_header_->metadata.user_metadata_size;
+    out_meta->user_metadata_ptr =
+        reinterpret_cast<uint64_t>(user_metadata_ptr_);
+  } else {
+    out_meta->user_metadata_size = 0;
+    out_meta->user_metadata_ptr = 0;
+  }
+
+  uint32_t current_fence_state = fence_state_->load(std::memory_order_acquire);
+  uint32_t current_active_clients_bit_mask =
+      active_clients_bit_mask_->load(std::memory_order_acquire);
+  // If there are release fence(s) from consumer(s), we need to return it to the
+  // consumer(s).
+  // TODO(b/112007999) add an atomic variable in metadata header in shared
+  // memory to indicate which client is the last producer of the buffer.
+  // Currently, assume the first client is the only producer to the buffer.
+  if (current_fence_state & current_active_clients_bit_mask &
+      (~BufferHubDefs::kFirstClientBitMask)) {
+    *out_fence = shared_release_fence_.Duplicate();
+    out_meta->release_fence_mask = current_fence_state &
+                                   current_active_clients_bit_mask &
+                                   (~BufferHubDefs::kFirstClientBitMask);
+  }
+
+  return 0;
+}
+
+int ProducerBuffer::Gain(LocalHandle* release_fence, bool gain_posted_buffer) {
+  ATRACE_NAME("ProducerBuffer::Gain");
+
+  DvrNativeBufferMetadata meta;
+  if (const int error = LocalGain(&meta, release_fence, gain_posted_buffer))
+    return error;
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
+  if (!status)
+    return -status.error();
+  return 0;
+}
+
+int ProducerBuffer::GainAsync(DvrNativeBufferMetadata* out_meta,
+                              LocalHandle* release_fence,
+                              bool gain_posted_buffer) {
+  ATRACE_NAME("ProducerBuffer::GainAsync");
+
+  if (const int error = LocalGain(out_meta, release_fence, gain_posted_buffer))
+    return error;
+
+  return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
+}
+
+int ProducerBuffer::GainAsync() {
+  DvrNativeBufferMetadata meta;
+  LocalHandle fence;
+  return GainAsync(&meta, &fence);
+}
+
+std::unique_ptr<ProducerBuffer> ProducerBuffer::Import(
+    LocalChannelHandle channel) {
+  ALOGD_IF(TRACE, "ProducerBuffer::Import: channel=%d", channel.value());
+  return ProducerBuffer::Create(std::move(channel));
+}
+
+std::unique_ptr<ProducerBuffer> ProducerBuffer::Import(
+    Status<LocalChannelHandle> status) {
+  return Import(status ? status.take()
+                       : LocalChannelHandle{nullptr, -status.error()});
+}
+
+Status<LocalChannelHandle> ProducerBuffer::Detach() {
+  // TODO(b/112338294) remove after migrate producer buffer to binder
+  ALOGW("ProducerBuffer::Detach: not supported operation during migration");
+  return {};
+
+  // TODO(b/112338294) Keep here for reference. Remove it after new logic is
+  // written.
+  /* uint32_t buffer_state = buffer_state_->load(std::memory_order_acquire);
+  if (!BufferHubDefs::IsClientGained(
+      buffer_state, BufferHubDefs::kFirstClientStateMask)) {
+    // Can only detach a ProducerBuffer when it's in gained state.
+    ALOGW("ProducerBuffer::Detach: The buffer (id=%d, state=0x%" PRIx32
+          ") is not in gained state.",
+          id(), buffer_state);
+    return {};
+  }
+
+  Status<LocalChannelHandle> status =
+      InvokeRemoteMethod<BufferHubRPC::ProducerBufferDetach>();
+  ALOGE_IF(!status,
+           "ProducerBuffer::Detach: Failed to detach buffer (id=%d): %s.", id(),
+           status.GetErrorMessage().c_str());
+  return status; */
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
index 9f72c05..20894e3 100644
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -59,6 +59,9 @@
     static_libs: staticLibraries,
     shared_libs: sharedLibraries,
     header_libs: headerLibraries,
+
+    // TODO(b/117568153): Temporarily opt out using libcrt.
+    no_libcrt: true,
 }
 
 subdirs = ["benchmarks", "tests"]
diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
index 5089b87..ef1eed6 100644
--- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp
+++ b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
@@ -5,7 +5,7 @@
         "libbase",
         "libbinder",
         "libcutils",
-        "libdvr",
+        "libdvr.google",
         "libgui",
         "liblog",
         "libhardware",
diff --git a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
index 4ca8671..b6813eb 100644
--- a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
+++ b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
@@ -66,7 +66,7 @@
         reply->writeStrongBinder(
             IGraphicBufferProducer::asBinder(new_queue->producer));
         buffer_queues_.push_back(new_queue);
-        return NO_ERROR;
+        return OK;
       }
       default:
         return UNKNOWN_TRANSACTION;
@@ -89,7 +89,7 @@
                                                    /*waitForFence=*/false);
       }
 
-      if (ret != NO_ERROR) {
+      if (ret != OK) {
         LOG(ERROR) << "Failed to acquire next buffer.";
         return;
       }
@@ -99,7 +99,7 @@
         ret = buffer_item_consumer_->releaseBuffer(buffer);
       }
 
-      if (ret != NO_ERROR) {
+      if (ret != OK) {
         LOG(ERROR) << "Failed to release buffer.";
         return;
       }
@@ -171,14 +171,14 @@
     Parcel data;
     Parcel reply;
     int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply);
-    if (error != NO_ERROR) {
+    if (error != OK) {
       LOG(ERROR) << "Failed to get buffer queue over binder.";
       return nullptr;
     }
 
     sp<IBinder> binder;
     error = reply.readNullableStrongBinder(&binder);
-    if (error != NO_ERROR) {
+    if (error != OK) {
       LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
       return nullptr;
     }
@@ -206,7 +206,7 @@
 class DvrApi {
  public:
   DvrApi() {
-    handle_ = dlopen("libdvr.so", RTLD_NOW | RTLD_LOCAL);
+    handle_ = dlopen("libdvr.google.so", RTLD_NOW | RTLD_LOCAL);
     CHECK(handle_);
 
     auto dvr_get_api =
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index 8feb1cd..d7833f3 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -69,7 +69,7 @@
       .data = {.u64 = Stuff(-1, BufferHubQueue::kEpollQueueEventIndex)}};
   ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event);
   if (ret < 0) {
-    ALOGE("BufferHubQueue::Initialize: Failed to add event fd to epoll set: %s",
+    ALOGE("%s: Failed to add event fd to epoll set: %s", __FUNCTION__,
           strerror(-ret));
   }
 }
@@ -77,7 +77,7 @@
 Status<void> BufferHubQueue::ImportQueue() {
   auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>();
   if (!status) {
-    ALOGE("BufferHubQueue::ImportQueue: Failed to import queue: %s",
+    ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
           status.GetErrorMessage().c_str());
     return ErrorStatus(status.error());
   } else {
@@ -136,9 +136,7 @@
       consumer_queue->GetChannel()->TakeChannelParcelable());
 
   if (!queue_parcelable.IsValid()) {
-    ALOGE(
-        "BufferHubQueue::CreateConsumerQueueParcelable: Failed to create "
-        "consumer queue parcelable.");
+    ALOGE("%s: Failed to create consumer queue parcelable.", __FUNCTION__);
     return ErrorStatus(EINVAL);
   }
 
@@ -169,8 +167,7 @@
     }
 
     if (ret < 0 && ret != -EINTR) {
-      ALOGE("BufferHubQueue::WaitForBuffers: Failed to wait for buffers: %s",
-            strerror(-ret));
+      ALOGE("%s: Failed to wait for buffers: %s", __FUNCTION__, strerror(-ret));
       return false;
     }
 
@@ -264,27 +261,26 @@
     // wait will be tried again to acquire the newly imported buffer.
     auto buffer_status = OnBufferAllocated();
     if (!buffer_status) {
-      ALOGE("BufferHubQueue::HandleQueueEvent: Failed to import buffer: %s",
+      ALOGE("%s: Failed to import buffer: %s", __FUNCTION__,
             buffer_status.GetErrorMessage().c_str());
     }
   } else if (events & EPOLLHUP) {
-    ALOGD_IF(TRACE, "BufferHubQueue::HandleQueueEvent: hang up event!");
+    ALOGD_IF(TRACE, "%s: hang up event!", __FUNCTION__);
     hung_up_ = true;
   } else {
-    ALOGW("BufferHubQueue::HandleQueueEvent: Unknown epoll events=%x", events);
+    ALOGW("%s: Unknown epoll events=%x", __FUNCTION__, events);
   }
 
   return {};
 }
 
 Status<void> BufferHubQueue::AddBuffer(
-    const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) {
-  ALOGD_IF(TRACE, "BufferHubQueue::AddBuffer: buffer_id=%d slot=%zu",
-           buffer->id(), slot);
+    const std::shared_ptr<BufferHubBase>& buffer, size_t slot) {
+  ALOGD_IF(TRACE, "%s: buffer_id=%d slot=%zu", __FUNCTION__, buffer->id(),
+           slot);
 
   if (is_full()) {
-    ALOGE("BufferHubQueue::AddBuffer queue is at maximum capacity: %zu",
-          capacity_);
+    ALOGE("%s: queue is at maximum capacity: %zu", __FUNCTION__, capacity_);
     return ErrorStatus(E2BIG);
   }
 
@@ -303,7 +299,7 @@
     const int ret =
         epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event);
     if (ret < 0) {
-      ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s",
+      ALOGE("%s: Failed to add buffer to epoll set: %s", __FUNCTION__,
             strerror(-ret));
       return ErrorStatus(-ret);
     }
@@ -315,17 +311,15 @@
 }
 
 Status<void> BufferHubQueue::RemoveBuffer(size_t slot) {
-  ALOGD_IF(TRACE, "BufferHubQueue::RemoveBuffer: slot=%zu", slot);
+  ALOGD_IF(TRACE, "%s: slot=%zu", __FUNCTION__, slot);
 
   if (buffers_[slot]) {
     for (const auto& event_source : buffers_[slot]->GetEventSources()) {
       const int ret =
           epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr);
       if (ret < 0) {
-        ALOGE(
-            "BufferHubQueue::RemoveBuffer: Failed to remove buffer from epoll "
-            "set: %s",
-            strerror(-ret));
+        ALOGE("%s: Failed to remove buffer from epoll set: %s", __FUNCTION__,
+              strerror(-ret));
         return ErrorStatus(-ret);
       }
     }
@@ -343,6 +337,15 @@
 
 Status<void> BufferHubQueue::Enqueue(Entry entry) {
   if (!is_full()) {
+    // Find and remove the enqueued buffer from unavailable_buffers_slot if
+    // exist.
+    auto enqueued_buffer_iter = std::find_if(
+        unavailable_buffers_slot_.begin(), unavailable_buffers_slot_.end(),
+        [&entry](size_t slot) -> bool { return slot == entry.slot; });
+    if (enqueued_buffer_iter != unavailable_buffers_slot_.end()) {
+      unavailable_buffers_slot_.erase(enqueued_buffer_iter);
+    }
+
     available_buffers_.push(std::move(entry));
 
     // Trigger OnBufferAvailable callback if registered.
@@ -351,17 +354,16 @@
 
     return {};
   } else {
-    ALOGE("BufferHubQueue::Enqueue: Buffer queue is full!");
+    ALOGE("%s: Buffer queue is full!", __FUNCTION__);
     return ErrorStatus(E2BIG);
   }
 }
 
-Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(int timeout,
-                                                                 size_t* slot) {
-  ALOGD_IF(TRACE, "BufferHubQueue::Dequeue: count=%zu, timeout=%d", count(),
-           timeout);
+Status<std::shared_ptr<BufferHubBase>> BufferHubQueue::Dequeue(int timeout,
+                                                               size_t* slot) {
+  ALOGD_IF(TRACE, "%s: count=%zu, timeout=%d", __FUNCTION__, count(), timeout);
 
-  PDX_TRACE_FORMAT("BufferHubQueue::Dequeue|count=%zu|", count());
+  PDX_TRACE_FORMAT("%s|count=%zu|", __FUNCTION__, count());
 
   if (count() == 0) {
     if (!WaitForBuffers(timeout))
@@ -372,10 +374,11 @@
   PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(),
                    entry.slot);
 
-  std::shared_ptr<BufferHubBuffer> buffer = std::move(entry.buffer);
+  std::shared_ptr<BufferHubBase> buffer = std::move(entry.buffer);
   *slot = entry.slot;
 
   available_buffers_.pop();
+  unavailable_buffers_slot_.push_back(*slot);
 
   return {std::move(buffer)};
 }
@@ -440,6 +443,10 @@
 Status<std::vector<size_t>> ProducerQueue::AllocateBuffers(
     uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
     uint64_t usage, size_t buffer_count) {
+  if (buffer_count == 0) {
+    return {std::vector<size_t>()};
+  }
+
   if (capacity() + buffer_count > kMaxQueueCapacity) {
     ALOGE(
         "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot "
@@ -473,7 +480,7 @@
 
     // Note that import might (though very unlikely) fail. If so, buffer_handle
     // will be closed and included in returned buffer_slots.
-    if (AddBuffer(BufferProducer::Import(std::move(buffer_handle)),
+    if (AddBuffer(ProducerBuffer::Import(std::move(buffer_handle)),
                   buffer_slot)) {
       ALOGD_IF(TRACE, "ProducerQueue::AllocateBuffers: new buffer at slot: %zu",
                buffer_slot);
@@ -481,10 +488,13 @@
     }
   }
 
-  if (buffer_slots.size() == 0) {
-    // Error out if no buffer is allocated and improted.
-    ALOGE_IF(TRACE, "ProducerQueue::AllocateBuffers: no buffer allocated.");
-    ErrorStatus(ENOMEM);
+  if (buffer_slots.size() != buffer_count) {
+    // Error out if the count of imported buffer(s) is not correct.
+    ALOGE(
+        "ProducerQueue::AllocateBuffers: requested to import %zu "
+        "buffers, but actually imported %zu buffers.",
+        buffer_count, buffer_slots.size());
+    return ErrorStatus(ENOMEM);
   }
 
   return {std::move(buffer_slots)};
@@ -503,16 +513,11 @@
     return status.error_status();
   }
 
-  if (status.get().size() == 0) {
-    ALOGE_IF(TRACE, "ProducerQueue::AllocateBuffer: no buffer allocated.");
-    ErrorStatus(ENOMEM);
-  }
-
   return {status.get()[0]};
 }
 
 Status<void> ProducerQueue::AddBuffer(
-    const std::shared_ptr<BufferProducer>& buffer, size_t slot) {
+    const std::shared_ptr<ProducerBuffer>& buffer, size_t slot) {
   ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
            id(), buffer->id(), slot);
   // For producer buffer, we need to enqueue the newly added buffer
@@ -524,11 +529,46 @@
   return BufferHubQueue::Enqueue({buffer, slot, 0ULL});
 }
 
+Status<size_t> ProducerQueue::InsertBuffer(
+    const std::shared_ptr<ProducerBuffer>& buffer) {
+  if (buffer == nullptr ||
+      !BufferHubDefs::IsClientGained(buffer->buffer_state(),
+                                     buffer->client_state_mask())) {
+    ALOGE(
+        "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in "
+        "gained state.");
+    return ErrorStatus(EINVAL);
+  }
+
+  auto status_or_slot =
+      InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>(
+          buffer->cid());
+  if (!status_or_slot) {
+    ALOGE(
+        "ProducerQueue::InsertBuffer: Failed to insert producer buffer: "
+        "buffer_cid=%d, error: %s.",
+        buffer->cid(), status_or_slot.GetErrorMessage().c_str());
+    return status_or_slot.error_status();
+  }
+
+  size_t slot = status_or_slot.get();
+
+  // Note that we are calling AddBuffer() from the base class to explicitly
+  // avoid Enqueue() the ProducerBuffer.
+  auto status = BufferHubQueue::AddBuffer(buffer, slot);
+  if (!status) {
+    ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.",
+          status.GetErrorMessage().c_str());
+    return status.error_status();
+  }
+  return {slot};
+}
+
 Status<void> ProducerQueue::RemoveBuffer(size_t slot) {
   auto status =
       InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot);
   if (!status) {
-    ALOGE("ProducerQueue::RemoveBuffer: Failed to remove producer buffer: %s",
+    ALOGE("%s: Failed to remove producer buffer: %s", __FUNCTION__,
           status.GetErrorMessage().c_str());
     return status.error_status();
   }
@@ -536,39 +576,89 @@
   return BufferHubQueue::RemoveBuffer(slot);
 }
 
-Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue(
+Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
     int timeout, size_t* slot, LocalHandle* release_fence) {
   DvrNativeBufferMetadata canonical_meta;
   return Dequeue(timeout, slot, &canonical_meta, release_fence);
 }
 
-pdx::Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue(
+pdx::Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
     int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
-    pdx::LocalHandle* release_fence) {
+    pdx::LocalHandle* release_fence, bool gain_posted_buffer) {
   ATRACE_NAME("ProducerQueue::Dequeue");
   if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) {
-    ALOGE("ProducerQueue::Dequeue: Invalid parameter.");
+    ALOGE("%s: Invalid parameter.", __FUNCTION__);
     return ErrorStatus(EINVAL);
   }
 
-  auto status = BufferHubQueue::Dequeue(timeout, slot);
-  if (!status)
-    return status.error_status();
-
-  auto buffer = std::static_pointer_cast<BufferProducer>(status.take());
-  const int ret = buffer->GainAsync(out_meta, release_fence);
+  std::shared_ptr<ProducerBuffer> buffer;
+  Status<std::shared_ptr<BufferHubBase>> dequeue_status =
+      BufferHubQueue::Dequeue(timeout, slot);
+  if (dequeue_status.ok()) {
+    buffer = std::static_pointer_cast<ProducerBuffer>(dequeue_status.take());
+  } else {
+    if (gain_posted_buffer) {
+      Status<std::shared_ptr<ProducerBuffer>> dequeue_unacquired_status =
+          ProducerQueue::DequeueUnacquiredBuffer(slot);
+      if (!dequeue_unacquired_status.ok()) {
+        ALOGE("%s: DequeueUnacquiredBuffer returned error: %d", __FUNCTION__,
+              dequeue_unacquired_status.error());
+        return dequeue_unacquired_status.error_status();
+      }
+      buffer = dequeue_unacquired_status.take();
+    } else {
+      return dequeue_status.error_status();
+    }
+  }
+  const int ret =
+      buffer->GainAsync(out_meta, release_fence, gain_posted_buffer);
   if (ret < 0 && ret != -EALREADY)
     return ErrorStatus(-ret);
 
   return {std::move(buffer)};
 }
 
+Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::DequeueUnacquiredBuffer(
+    size_t* slot) {
+  if (unavailable_buffers_slot_.size() < 1) {
+    ALOGE(
+        "%s: Failed to dequeue un-acquired buffer. All buffer(s) are in "
+        "acquired state if exist.",
+        __FUNCTION__);
+    return ErrorStatus(ENOMEM);
+  }
+
+  // Find the first buffer that is not in acquired state from
+  // unavailable_buffers_slot_.
+  for (auto iter = unavailable_buffers_slot_.begin();
+       iter != unavailable_buffers_slot_.end(); iter++) {
+    std::shared_ptr<ProducerBuffer> buffer = ProducerQueue::GetBuffer(*iter);
+    if (buffer == nullptr) {
+      ALOGE("%s failed. Buffer slot %d is  null.", __FUNCTION__,
+            static_cast<int>(*slot));
+      return ErrorStatus(EIO);
+    }
+    if (!BufferHubDefs::AnyClientAcquired(buffer->buffer_state())) {
+      *slot = *iter;
+      unavailable_buffers_slot_.erase(iter);
+      unavailable_buffers_slot_.push_back(*slot);
+      ALOGD("%s: Producer queue dequeue unacquired buffer in slot %d",
+            __FUNCTION__, static_cast<int>(*slot));
+      return {std::move(buffer)};
+    }
+  }
+  ALOGE(
+      "%s: Failed to dequeue un-acquired buffer. No un-acquired buffer exist.",
+      __FUNCTION__);
+  return ErrorStatus(EBUSY);
+}
+
 pdx::Status<ProducerQueueParcelable> ProducerQueue::TakeAsParcelable() {
   if (capacity() != 0) {
     ALOGE(
-        "ProducerQueue::TakeAsParcelable: producer queue can only be taken out"
-        " as a parcelable when empty. Current queue capacity: %zu",
-        capacity());
+        "%s: producer queue can only be taken out as a parcelable when empty. "
+        "Current queue capacity: %zu",
+        __FUNCTION__, capacity());
     return ErrorStatus(EINVAL);
   }
 
@@ -592,17 +682,16 @@
     : BufferHubQueue(std::move(handle)) {
   auto status = ImportQueue();
   if (!status) {
-    ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s",
+    ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
           status.GetErrorMessage().c_str());
     Close(-status.error());
   }
 
   auto import_status = ImportBuffers();
   if (import_status) {
-    ALOGI("ConsumerQueue::ConsumerQueue: Imported %zu buffers.",
-          import_status.get());
+    ALOGI("%s: Imported %zu buffers.", __FUNCTION__, import_status.get());
   } else {
-    ALOGE("ConsumerQueue::ConsumerQueue: Failed to import buffers: %s",
+    ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
           import_status.GetErrorMessage().c_str());
   }
 }
@@ -611,14 +700,11 @@
   auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
   if (!status) {
     if (status.error() == EBADR) {
-      ALOGI(
-          "ConsumerQueue::ImportBuffers: Queue is silent, no buffers "
-          "imported.");
+      ALOGI("%s: Queue is silent, no buffers imported.", __FUNCTION__);
       return {0};
     } else {
-      ALOGE(
-          "ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s",
-          status.GetErrorMessage().c_str());
+      ALOGE("%s: Failed to import consumer buffer: %s", __FUNCTION__,
+            status.GetErrorMessage().c_str());
       return status.error_status();
     }
   }
@@ -629,22 +715,22 @@
 
   auto buffer_handle_slots = status.take();
   for (auto& buffer_handle_slot : buffer_handle_slots) {
-    ALOGD_IF(TRACE, "ConsumerQueue::ImportBuffers: buffer_handle=%d",
+    ALOGD_IF(TRACE, ": buffer_handle=%d", __FUNCTION__,
              buffer_handle_slot.first.value());
 
-    std::unique_ptr<BufferConsumer> buffer_consumer =
-        BufferConsumer::Import(std::move(buffer_handle_slot.first));
-    if (!buffer_consumer) {
-      ALOGE("ConsumerQueue::ImportBuffers: Failed to import buffer: slot=%zu",
+    std::unique_ptr<ConsumerBuffer> consumer_buffer =
+        ConsumerBuffer::Import(std::move(buffer_handle_slot.first));
+    if (!consumer_buffer) {
+      ALOGE("%s: Failed to import buffer: slot=%zu", __FUNCTION__,
             buffer_handle_slot.second);
       last_error = ErrorStatus(EPIPE);
       continue;
     }
 
     auto add_status =
-        AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second);
+        AddBuffer(std::move(consumer_buffer), buffer_handle_slot.second);
     if (!add_status) {
-      ALOGE("ConsumerQueue::ImportBuffers: Failed to add buffer: %s",
+      ALOGE("%s: Failed to add buffer: %s", __FUNCTION__,
             add_status.GetErrorMessage().c_str());
       last_error = add_status;
     } else {
@@ -659,20 +745,20 @@
 }
 
 Status<void> ConsumerQueue::AddBuffer(
-    const std::shared_ptr<BufferConsumer>& buffer, size_t slot) {
-  ALOGD_IF(TRACE, "ConsumerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
-           id(), buffer->id(), slot);
+    const std::shared_ptr<ConsumerBuffer>& buffer, size_t slot) {
+  ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu", __FUNCTION__, id(),
+           buffer->id(), slot);
   return BufferHubQueue::AddBuffer(buffer, slot);
 }
 
-Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue(
+Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
     int timeout, size_t* slot, void* meta, size_t user_metadata_size,
     LocalHandle* acquire_fence) {
   if (user_metadata_size != user_metadata_size_) {
     ALOGE(
-        "ConsumerQueue::Dequeue: Metadata size (%zu) for the dequeuing buffer "
-        "does not match metadata size (%zu) for the queue.",
-        user_metadata_size, user_metadata_size_);
+        "%s: Metadata size (%zu) for the dequeuing buffer does not match "
+        "metadata size (%zu) for the queue.",
+        __FUNCTION__, user_metadata_size, user_metadata_size_);
     return ErrorStatus(EINVAL);
   }
 
@@ -687,19 +773,19 @@
     if (metadata_src) {
       memcpy(meta, metadata_src, user_metadata_size);
     } else {
-      ALOGW("ConsumerQueue::Dequeue: no user-defined metadata.");
+      ALOGW("%s: no user-defined metadata.", __FUNCTION__);
     }
   }
 
   return status;
 }
 
-Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue(
+Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
     int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
     pdx::LocalHandle* acquire_fence) {
   ATRACE_NAME("ConsumerQueue::Dequeue");
   if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) {
-    ALOGE("ConsumerQueue::Dequeue: Invalid parameter.");
+    ALOGE("%s: Invalid parameter.", __FUNCTION__);
     return ErrorStatus(EINVAL);
   }
 
@@ -707,7 +793,7 @@
   if (!status)
     return status.error_status();
 
-  auto buffer = std::static_pointer_cast<BufferConsumer>(status.take());
+  auto buffer = std::static_pointer_cast<ConsumerBuffer>(status.take());
   const int ret = buffer->AcquireAsync(out_meta, acquire_fence);
   if (ret < 0)
     return ErrorStatus(-ret);
@@ -716,19 +802,18 @@
 }
 
 Status<void> ConsumerQueue::OnBufferAllocated() {
-  ALOGD_IF(TRACE, "ConsumerQueue::OnBufferAllocated: queue_id=%d", id());
+  ALOGD_IF(TRACE, "%s: queue_id=%d", __FUNCTION__, id());
 
   auto status = ImportBuffers();
   if (!status) {
-    ALOGE("ConsumerQueue::OnBufferAllocated: Failed to import buffers: %s",
+    ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
           status.GetErrorMessage().c_str());
     return ErrorStatus(status.error());
   } else if (status.get() == 0) {
-    ALOGW("ConsumerQueue::OnBufferAllocated: No new buffers allocated!");
+    ALOGW("%s: No new buffers allocated!", __FUNCTION__);
     return ErrorStatus(ENOBUFS);
   } else {
-    ALOGD_IF(TRACE,
-             "ConsumerQueue::OnBufferAllocated: Imported %zu consumer buffers.",
+    ALOGD_IF(TRACE, "%s: Imported %zu consumer buffers.", __FUNCTION__,
              status.get());
     return {};
   }
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
index 2cd7c45..f705749 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
@@ -35,7 +35,7 @@
   }
 
   status_t res = parcel->writeUint32(Magic);
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic.");
     return res;
   }
@@ -53,10 +53,10 @@
   }
 
   uint32_t out_magic = 0;
-  status_t res = NO_ERROR;
+  status_t res = OK;
 
   res = parcel->readUint32(&out_magic);
-  if (res != NO_ERROR)
+  if (res != OK)
     return res;
 
   if (out_magic != Magic) {
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 8f7b0a1..74b4b3d 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
@@ -13,10 +13,11 @@
 // in these headers and their dependencies.
 #include <pdx/client.h>
 #include <pdx/status.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_parcelable.h>
 #include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/consumer_buffer.h>
 #include <private/dvr/epoll_file_descriptor.h>
+#include <private/dvr/producer_buffer.h>
 
 #if defined(__clang__)
 #pragma clang diagnostic pop
@@ -31,13 +32,13 @@
 
 class ConsumerQueue;
 
-// |BufferHubQueue| manages a queue of |BufferHubBuffer|s. Buffers are
+// |BufferHubQueue| manages a queue of |BufferHubBase|s. Buffers are
 // automatically re-requeued when released by the remote side.
 class BufferHubQueue : public pdx::Client {
  public:
   using BufferAvailableCallback = std::function<void()>;
   using BufferRemovedCallback =
-      std::function<void(const std::shared_ptr<BufferHubBuffer>&)>;
+      std::function<void(const std::shared_ptr<BufferHubBase>&)>;
 
   virtual ~BufferHubQueue() {}
 
@@ -57,10 +58,10 @@
   uint32_t default_width() const { return default_width_; }
 
   // Returns the default buffer height of this buffer queue.
-  uint32_t default_height() const { return static_cast<uint32_t>(default_height_); }
+  uint32_t default_height() const { return default_height_; }
 
   // Returns the default buffer format of this buffer queue.
-  uint32_t default_format() const { return static_cast<uint32_t>(default_format_); }
+  uint32_t default_format() const { return default_format_; }
 
   // Creates a new consumer in handle form for immediate transport over RPC.
   pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle(
@@ -93,7 +94,7 @@
                                                       : -1;
   }
 
-  std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const {
+  std::shared_ptr<BufferHubBase> GetBuffer(size_t slot) const {
     return buffers_[slot];
   }
 
@@ -142,7 +143,7 @@
 
   // Register a buffer for management by the queue. Used by subclasses to add a
   // buffer to internal bookkeeping.
-  pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBuffer>& buffer,
+  pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBase>& buffer,
                               size_t slot);
 
   // Called by ProducerQueue::RemoveBuffer and ConsumerQueue::RemoveBuffer only
@@ -158,8 +159,8 @@
   // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely,
   // while specifying a timeout equal to zero cause Dequeue() to return
   // immediately, even if no buffers are available.
-  pdx::Status<std::shared_ptr<BufferHubBuffer>> Dequeue(int timeout,
-                                                        size_t* slot);
+  pdx::Status<std::shared_ptr<BufferHubBase>> Dequeue(int timeout,
+                                                      size_t* slot);
 
   // Waits for buffers to become available and adds them to the available queue.
   bool WaitForBuffers(int timeout);
@@ -172,10 +173,10 @@
   // per-buffer data.
   struct Entry {
     Entry() : slot(0) {}
-    Entry(const std::shared_ptr<BufferHubBuffer>& in_buffer, size_t in_slot,
+    Entry(const std::shared_ptr<BufferHubBase>& in_buffer, size_t in_slot,
           uint64_t in_index)
         : buffer(in_buffer), slot(in_slot), index(in_index) {}
-    Entry(const std::shared_ptr<BufferHubBuffer>& in_buffer,
+    Entry(const std::shared_ptr<BufferHubBase>& in_buffer,
           std::unique_ptr<uint8_t[]> in_metadata, pdx::LocalHandle in_fence,
           size_t in_slot)
         : buffer(in_buffer),
@@ -185,7 +186,7 @@
     Entry(Entry&&) = default;
     Entry& operator=(Entry&&) = default;
 
-    std::shared_ptr<BufferHubBuffer> buffer;
+    std::shared_ptr<BufferHubBase> buffer;
     std::unique_ptr<uint8_t[]> metadata;
     pdx::LocalHandle fence;
     size_t slot;
@@ -208,6 +209,14 @@
   // Size of the metadata that buffers in this queue cary.
   size_t user_metadata_size_{0};
 
+  // Buffers and related data that are available for dequeue.
+  std::priority_queue<Entry, std::vector<Entry>, EntryComparator>
+      available_buffers_;
+
+  // Slot of the buffers that are not available for normal dequeue. For example,
+  // the slot of posted or acquired buffers in the perspective of a producer.
+  std::vector<size_t> unavailable_buffers_slot_;
+
  private:
   void Initialize();
 
@@ -250,11 +259,7 @@
   // Tracks the buffers belonging to this queue. Buffers are stored according to
   // "slot" in this vector. Each slot is a logical id of the buffer within this
   // queue regardless of its queue position or presence in the ring buffer.
-  std::array<std::shared_ptr<BufferHubBuffer>, kMaxQueueCapacity> buffers_;
-
-  // Buffers and related data that are available for dequeue.
-  std::priority_queue<Entry, std::vector<Entry>, EntryComparator>
-      available_buffers_;
+  std::array<std::shared_ptr<BufferHubBase>, kMaxQueueCapacity> buffers_;
 
   // Keeps track with how many buffers have been added into the queue.
   size_t capacity_{0};
@@ -302,12 +307,12 @@
     return BASE::Create(std::move(handle));
   }
 
-  // Get a buffer producer. Note that the method doesn't check whether the
+  // Get a producer buffer. Note that the method doesn't check whether the
   // buffer slot has a valid buffer that has been allocated already. When no
   // buffer has been imported before it returns nullptr; otherwise it returns
-  // a shared pointer to a BufferProducer.
-  std::shared_ptr<BufferProducer> GetBuffer(size_t slot) const {
-    return std::static_pointer_cast<BufferProducer>(
+  // a shared pointer to a ProducerBuffer.
+  std::shared_ptr<ProducerBuffer> GetBuffer(size_t slot) const {
+    return std::static_pointer_cast<ProducerBuffer>(
         BufferHubQueue::GetBuffer(slot));
   }
 
@@ -328,9 +333,16 @@
 
   // Add a producer buffer to populate the queue. Once added, a producer buffer
   // is available to use (i.e. in GAINED state).
-  pdx::Status<void> AddBuffer(const std::shared_ptr<BufferProducer>& buffer,
+  pdx::Status<void> AddBuffer(const std::shared_ptr<ProducerBuffer>& buffer,
                               size_t slot);
 
+  // Inserts a ProducerBuffer into the queue. On success, the method returns the
+  // |slot| number where the new buffer gets inserted. Note that the buffer
+  // being inserted should be in Gain'ed state prior to the call and it's
+  // considered as already Dequeued when the function returns.
+  pdx::Status<size_t> InsertBuffer(
+      const std::shared_ptr<ProducerBuffer>& buffer);
+
   // Remove producer buffer from the queue.
   pdx::Status<void> RemoveBuffer(size_t slot) override;
 
@@ -342,14 +354,33 @@
   // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
   // and caller should call Post() once it's done writing to release the buffer
   // to the consumer side.
-  pdx::Status<std::shared_ptr<BufferProducer>> Dequeue(
+  // @return a buffer in gained state, which was originally in released state.
+  pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
       int timeout, size_t* slot, pdx::LocalHandle* release_fence);
-  pdx::Status<std::shared_ptr<BufferProducer>> Dequeue(
+
+  // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
+  // and caller should call Post() once it's done writing to release the buffer
+  // to the consumer side.
+  //
+  // @param timeout to dequeue a buffer.
+  // @param slot is the slot of the output ProducerBuffer.
+  // @param release_fence for gaining a buffer.
+  // @param out_meta metadata of the output buffer.
+  // @param gain_posted_buffer whether to gain posted buffer if no released
+  //     buffer is available to gain.
+  // @return a buffer in gained state, which was originally in released state if
+  //     gain_posted_buffer is false, or in posted/released state if
+  //     gain_posted_buffer is true.
+  // TODO(b/112007999): gain_posted_buffer true is only used to prevent
+  // libdvrtracking from starving when there are non-responding clients. This
+  // gain_posted_buffer param can be removed once libdvrtracking start to use
+  // the new AHardwareBuffer API.
+  pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
       int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
-      pdx::LocalHandle* release_fence);
+      pdx::LocalHandle* release_fence, bool gain_posted_buffer = false);
 
   // Enqueues a producer buffer in the queue.
-  pdx::Status<void> Enqueue(const std::shared_ptr<BufferProducer>& buffer,
+  pdx::Status<void> Enqueue(const std::shared_ptr<ProducerBuffer>& buffer,
                             size_t slot, uint64_t index) {
     return BufferHubQueue::Enqueue({buffer, slot, index});
   }
@@ -367,16 +398,26 @@
   // arguments as the constructors.
   explicit ProducerQueue(pdx::LocalChannelHandle handle);
   ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage);
+
+  // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
+  // and caller should call Post() once it's done writing to release the buffer
+  // to the consumer side.
+  //
+  // @param slot the slot of the returned buffer.
+  // @return a buffer in gained state, which was originally in posted state or
+  //     released state.
+  pdx::Status<std::shared_ptr<ProducerBuffer>> DequeueUnacquiredBuffer(
+      size_t* slot);
 };
 
 class ConsumerQueue : public BufferHubQueue {
  public:
-  // Get a buffer consumer. Note that the method doesn't check whether the
+  // Get a consumer buffer. Note that the method doesn't check whether the
   // buffer slot has a valid buffer that has been imported already. When no
   // buffer has been imported before it returns nullptr; otherwise returns a
-  // shared pointer to a BufferConsumer.
-  std::shared_ptr<BufferConsumer> GetBuffer(size_t slot) const {
-    return std::static_pointer_cast<BufferConsumer>(
+  // shared pointer to a ConsumerBuffer.
+  std::shared_ptr<ConsumerBuffer> GetBuffer(size_t slot) const {
+    return std::static_pointer_cast<ConsumerBuffer>(
         BufferHubQueue::GetBuffer(slot));
   }
 
@@ -394,23 +435,23 @@
   // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed
   // mode, and caller should call Releasse() once it's done writing to release
   // the buffer to the producer side. |meta| is passed along from BufferHub,
-  // The user of BufferProducer is responsible with making sure that the
+  // The user of ProducerBuffer is responsible with making sure that the
   // Dequeue() is done with the corect metadata type and size with those used
   // when the buffer is orignally created.
   template <typename Meta>
-  pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
       int timeout, size_t* slot, Meta* meta, pdx::LocalHandle* acquire_fence) {
     return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence);
   }
-  pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
       int timeout, size_t* slot, pdx::LocalHandle* acquire_fence) {
     return Dequeue(timeout, slot, nullptr, 0, acquire_fence);
   }
 
-  pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
       int timeout, size_t* slot, void* meta, size_t user_metadata_size,
       pdx::LocalHandle* acquire_fence);
-  pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+  pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
       int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
       pdx::LocalHandle* acquire_fence);
 
@@ -423,7 +464,7 @@
   // is NOT available to use until the producer side |Post| it. |WaitForBuffers|
   // will catch the |Post| and |Acquire| the buffer to make it available for
   // consumer.
-  pdx::Status<void> AddBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+  pdx::Status<void> AddBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
                               size_t slot);
 
   pdx::Status<void> OnBufferAllocated() override;
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
index 47a2734..6ae603b 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -1,7 +1,9 @@
 #include <base/logging.h>
 #include <binder/Parcel.h>
-#include <private/dvr/buffer_hub_client.h>
+#include <dvr/dvr_api.h>
 #include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/consumer_buffer.h>
+#include <private/dvr/producer_buffer.h>
 
 #include <gtest/gtest.h>
 #include <poll.h>
@@ -111,7 +113,7 @@
 
     // Consumer acquires a buffer.
     auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(c1_status.ok());
+    EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
     auto c1 = c1_status.take();
     ASSERT_NE(c1, nullptr);
     EXPECT_EQ(mi.index, i);
@@ -122,6 +124,147 @@
   }
 }
 
+TEST_F(BufferHubQueueTest,
+       TestDequeuePostedBufferIfNoAvailableReleasedBuffer_withConsumerBuffer) {
+  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
+
+  // Allocate 3 buffers to use.
+  const size_t test_queue_capacity = 3;
+  for (int64_t i = 0; i < test_queue_capacity; i++) {
+    AllocateBuffer();
+  }
+  EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
+
+  size_t producer_slot, consumer_slot;
+  LocalHandle fence;
+  DvrNativeBufferMetadata mi, mo;
+
+  // Producer posts 2 buffers and remember their posted sequence.
+  std::deque<size_t> posted_slots;
+  for (int64_t i = 0; i < 2; i++) {
+    auto p1_status =
+        producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
+    EXPECT_TRUE(p1_status.ok());
+    auto p1 = p1_status.take();
+    ASSERT_NE(p1, nullptr);
+
+    // Producer should not be gaining posted buffer when there are still
+    // available buffers to gain.
+    auto found_iter =
+        std::find(posted_slots.begin(), posted_slots.end(), producer_slot);
+    EXPECT_EQ(found_iter, posted_slots.end());
+    posted_slots.push_back(producer_slot);
+
+    // Producer posts the buffer.
+    mi.index = i;
+    EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
+  }
+
+  // Consumer acquires one buffer.
+  auto c1_status =
+      consumer_queue_->Dequeue(kTimeoutMs, &consumer_slot, &mo, &fence);
+  EXPECT_TRUE(c1_status.ok());
+  auto c1 = c1_status.take();
+  ASSERT_NE(c1, nullptr);
+  // Consumer should get the oldest posted buffer. No checks here.
+  // posted_slots[0] should be in acquired state now.
+  EXPECT_EQ(mo.index, 0);
+  // Consumer releases the buffer.
+  EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0);
+  // posted_slots[0] should be in released state now.
+
+  // Producer gain and post 2 buffers.
+  for (int64_t i = 0; i < 2; i++) {
+    auto p1_status =
+        producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
+    EXPECT_TRUE(p1_status.ok());
+    auto p1 = p1_status.take();
+    ASSERT_NE(p1, nullptr);
+
+    // The gained buffer should be the one in released state or the one haven't
+    // been use.
+    EXPECT_NE(posted_slots[1], producer_slot);
+
+    mi.index = i + 2;
+    EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
+  }
+
+  // Producer gains a buffer.
+  auto p1_status =
+      producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
+  EXPECT_TRUE(p1_status.ok());
+  auto p1 = p1_status.take();
+  ASSERT_NE(p1, nullptr);
+
+  // The gained buffer should be the oldest posted buffer.
+  EXPECT_EQ(posted_slots[1], producer_slot);
+
+  // Producer posts the buffer.
+  mi.index = 4;
+  EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
+}
+
+TEST_F(BufferHubQueueTest,
+       TestDequeuePostedBufferIfNoAvailableReleasedBuffer_noConsumerBuffer) {
+  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
+
+  // Allocate 4 buffers to use.
+  const size_t test_queue_capacity = 4;
+  for (int64_t i = 0; i < test_queue_capacity; i++) {
+    AllocateBuffer();
+  }
+  EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
+
+  // Post all allowed buffers and remember their posted sequence.
+  std::deque<size_t> posted_slots;
+  for (int64_t i = 0; i < test_queue_capacity; i++) {
+    size_t slot;
+    LocalHandle fence;
+    DvrNativeBufferMetadata mi, mo;
+
+    // Producer gains a buffer.
+    auto p1_status =
+        producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
+    EXPECT_TRUE(p1_status.ok());
+    auto p1 = p1_status.take();
+    ASSERT_NE(p1, nullptr);
+
+    // Producer should not be gaining posted buffer when there are still
+    // available buffers to gain.
+    auto found_iter = std::find(posted_slots.begin(), posted_slots.end(), slot);
+    EXPECT_EQ(found_iter, posted_slots.end());
+    posted_slots.push_back(slot);
+
+    // Producer posts the buffer.
+    mi.index = i;
+    EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
+  }
+
+  // Gain posted buffers in sequence.
+  const int64_t nb_dequeue_all_times = 2;
+  for (int j = 0; j < nb_dequeue_all_times; ++j) {
+    for (int i = 0; i < test_queue_capacity; ++i) {
+      size_t slot;
+      LocalHandle fence;
+      DvrNativeBufferMetadata mi, mo;
+
+      // Producer gains a buffer.
+      auto p1_status =
+          producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
+      EXPECT_TRUE(p1_status.ok());
+      auto p1 = p1_status.take();
+      ASSERT_NE(p1, nullptr);
+
+      // The gained buffer should be the oldest posted buffer.
+      EXPECT_EQ(posted_slots[i], slot);
+
+      // Producer posts the buffer.
+      mi.index = i + test_queue_capacity * (j + 1);
+      EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
+    }
+  }
+}
+
 TEST_F(BufferHubQueueTest, TestProducerConsumer) {
   const size_t kBufferCount = 16;
   size_t slot;
@@ -181,6 +324,42 @@
   }
 }
 
+TEST_F(BufferHubQueueTest, TestInsertBuffer) {
+  ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
+
+  consumer_queue_ = producer_queue_->CreateConsumerQueue();
+  ASSERT_TRUE(consumer_queue_ != nullptr);
+  EXPECT_EQ(producer_queue_->capacity(), 0);
+  EXPECT_EQ(consumer_queue_->capacity(), 0);
+
+  std::shared_ptr<ProducerBuffer> p1 = ProducerBuffer::Create(
+      kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0);
+  ASSERT_TRUE(p1 != nullptr);
+  ASSERT_EQ(p1->GainAsync(), 0);
+
+  // Inserting a posted buffer will fail.
+  DvrNativeBufferMetadata meta;
+  EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0);
+  auto status_or_slot = producer_queue_->InsertBuffer(p1);
+  EXPECT_FALSE(status_or_slot.ok());
+  EXPECT_EQ(status_or_slot.error(), EINVAL);
+
+  // Inserting a gained buffer will succeed.
+  std::shared_ptr<ProducerBuffer> p2 = ProducerBuffer::Create(
+      kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage);
+  ASSERT_EQ(p2->GainAsync(), 0);
+  ASSERT_TRUE(p2 != nullptr);
+  status_or_slot = producer_queue_->InsertBuffer(p2);
+  EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage();
+  // This is the first buffer inserted, should take slot 0.
+  size_t slot = status_or_slot.get();
+  EXPECT_EQ(slot, 0);
+
+  // Wait and expect the consumer to kick up the newly inserted buffer.
+  WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs);
+  EXPECT_EQ(consumer_queue_->capacity(), 1ULL);
+}
+
 TEST_F(BufferHubQueueTest, TestRemoveBuffer) {
   ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
   DvrNativeBufferMetadata mo;
@@ -203,7 +382,7 @@
   // Dequeue all the buffers and keep track of them in an array. This prevents
   // the producer queue ring buffer ref counts from interfering with the tests.
   struct Entry {
-    std::shared_ptr<BufferProducer> buffer;
+    std::shared_ptr<ProducerBuffer> buffer;
     LocalHandle fence;
     size_t slot;
   };
@@ -211,8 +390,8 @@
 
   for (size_t i = 0; i < kBufferCount; i++) {
     Entry* entry = &buffers[i];
-    auto producer_status = producer_queue_->Dequeue(
-        kTimeoutMs, &entry->slot, &mo, &entry->fence);
+    auto producer_status =
+        producer_queue_->Dequeue(kTimeoutMs, &entry->slot, &mo, &entry->fence);
     ASSERT_TRUE(producer_status.ok());
     entry->buffer = producer_status.take();
     ASSERT_NE(nullptr, entry->buffer);
@@ -409,7 +588,7 @@
     mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
     EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
     auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    EXPECT_TRUE(c1_status.ok());
+    EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
     auto c1 = c1_status.take();
     ASSERT_NE(c1, nullptr);
 
@@ -513,7 +692,7 @@
 
   size_t cs1, cs2;
   auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence);
-  ASSERT_TRUE(c1_status.ok());
+  ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
   auto c1 = c1_status.take();
   ASSERT_NE(c1, nullptr);
   ASSERT_EQ(consumer_queue_->count(), 0U);
@@ -528,6 +707,30 @@
   ASSERT_EQ(cs2, ps2);
 }
 
+TEST_F(BufferHubQueueTest, TestAllocateTwoBuffers) {
+  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
+  ASSERT_EQ(producer_queue_->capacity(), 0);
+  auto status = producer_queue_->AllocateBuffers(
+      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
+      kBufferUsage, /*buffer_count=*/2);
+  ASSERT_TRUE(status.ok());
+  std::vector<size_t> buffer_slots = status.take();
+  ASSERT_EQ(buffer_slots.size(), 2);
+  ASSERT_EQ(producer_queue_->capacity(), 2);
+}
+
+TEST_F(BufferHubQueueTest, TestAllocateZeroBuffers) {
+  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
+  ASSERT_EQ(producer_queue_->capacity(), 0);
+  auto status = producer_queue_->AllocateBuffers(
+      kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
+      kBufferUsage, /*buffer_count=*/0);
+  ASSERT_TRUE(status.ok());
+  std::vector<size_t> buffer_slots = status.take();
+  ASSERT_EQ(buffer_slots.size(), 0);
+  ASSERT_EQ(producer_queue_->capacity(), 0);
+}
+
 TEST_F(BufferHubQueueTest, TestUsageSetMask) {
   const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
   ASSERT_TRUE(
@@ -645,10 +848,10 @@
   size_t slot;
   LocalHandle fence;
   pdx::Status<void> status;
-  pdx::Status<std::shared_ptr<BufferConsumer>> consumer_status;
-  pdx::Status<std::shared_ptr<BufferProducer>> producer_status;
-  std::shared_ptr<BufferConsumer> consumer_buffer;
-  std::shared_ptr<BufferProducer> producer_buffer;
+  pdx::Status<std::shared_ptr<ConsumerBuffer>> consumer_status;
+  pdx::Status<std::shared_ptr<ProducerBuffer>> producer_status;
+  std::shared_ptr<ConsumerBuffer> consumer_buffer;
+  std::shared_ptr<ProducerBuffer> producer_buffer;
   DvrNativeBufferMetadata mi, mo;
 
   ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
@@ -705,7 +908,7 @@
     ASSERT_NE(producer_buffer, nullptr);
     ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
     consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
-    ASSERT_TRUE(consumer_status.ok());
+    ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage();
   }
 
   status = producer_queue_->FreeAllBuffers();
@@ -748,7 +951,7 @@
   Parcel parcel;
   status_t res;
   res = output_parcelable.writeToParcel(&parcel);
-  EXPECT_EQ(res, NO_ERROR);
+  EXPECT_EQ(res, OK);
 
   // After written into parcelable, the output_parcelable is still valid has
   // keeps the producer channel alive.
@@ -770,7 +973,7 @@
   EXPECT_FALSE(input_parcelable.IsValid());
 
   res = input_parcelable.readFromParcel(&parcel);
-  EXPECT_EQ(res, NO_ERROR);
+  EXPECT_EQ(res, OK);
   EXPECT_TRUE(input_parcelable.IsValid());
 
   EXPECT_EQ(producer_queue_, nullptr);
@@ -791,7 +994,7 @@
   auto s3 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
   EXPECT_TRUE(s3.ok());
 
-  std::shared_ptr<BufferProducer> p1 = s3.take();
+  std::shared_ptr<ProducerBuffer> p1 = s3.take();
   ASSERT_NE(p1, nullptr);
 
   producer_meta.timestamp = 42;
@@ -799,7 +1002,7 @@
 
   // Make sure the buffer can be dequeued from consumer side.
   auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
-  EXPECT_TRUE(s4.ok());
+  EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage();
   EXPECT_EQ(consumer_queue_->capacity(), 1U);
 
   auto consumer = s4.take();
@@ -840,7 +1043,7 @@
   EXPECT_FALSE(input_parcelable.IsValid());
 
   res = input_parcelable.readFromParcel(&parcel);
-  EXPECT_EQ(res, NO_ERROR);
+  EXPECT_EQ(res, OK);
   EXPECT_TRUE(input_parcelable.IsValid());
 
   consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle());
@@ -858,7 +1061,7 @@
   auto s2 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
   EXPECT_TRUE(s2.ok());
 
-  std::shared_ptr<BufferProducer> p1 = s2.take();
+  std::shared_ptr<ProducerBuffer> p1 = s2.take();
   ASSERT_NE(p1, nullptr);
 
   producer_meta.timestamp = 42;
@@ -866,7 +1069,7 @@
 
   // Make sure the buffer can be dequeued from consumer side.
   auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
-  EXPECT_TRUE(s3.ok());
+  EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage();
   EXPECT_EQ(consumer_queue_->capacity(), 1U);
 
   auto consumer = s3.take();
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
index 4f10f83..8cc7081 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -108,8 +108,8 @@
   void ConnectProducer() {
     IGraphicBufferProducer::QueueBufferOutput output;
     // Can connect the first time.
-    ASSERT_EQ(NO_ERROR, mProducer->connect(kDummyListener, kTestApi,
-                                           kTestControlledByApp, &output));
+    ASSERT_EQ(OK, mProducer->connect(kDummyListener, kTestApi,
+                                     kTestControlledByApp, &output));
   }
 
   // Dequeue a buffer in a 'correct' fashion.
@@ -170,7 +170,7 @@
 TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) {
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
-  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
 }
 
 TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) {
@@ -186,26 +186,24 @@
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
   int32_t value = -1;
-  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
   EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value));
 
-  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
   EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value));
 
-  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
   EXPECT_EQ(kDefaultFormat, value);
 
-  EXPECT_EQ(NO_ERROR,
-            mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
   EXPECT_LE(0, value);
   EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value);
 
-  EXPECT_EQ(NO_ERROR,
+  EXPECT_EQ(OK,
             mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value));
   EXPECT_FALSE(value);  // Can't run behind when we haven't touched the queue
 
-  EXPECT_EQ(NO_ERROR,
-            mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
   EXPECT_EQ(kDefaultConsumerUsageBits, value);
 }
 
@@ -243,14 +241,14 @@
 
   // Request the buffer (pre-requisite for queueing)
   sp<GraphicBuffer> buffer;
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   // A generic "valid" input
   IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
   IGraphicBufferProducer::QueueBufferOutput output;
 
   // Queue the buffer back into the BQ
-  ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+  ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
 
   EXPECT_EQ(kDefaultWidth, output.width);
   EXPECT_EQ(kDefaultHeight, output.height);
@@ -313,7 +311,7 @@
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
 
   sp<GraphicBuffer> buffer;
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   sp<Fence> nullFence = NULL;
 
@@ -332,7 +330,7 @@
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
 
   sp<GraphicBuffer> buffer;
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   IGraphicBufferProducer::QueueBufferInput input =
       QueueBufferInputBuilder().setScalingMode(-1).build();
@@ -353,7 +351,7 @@
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
 
   sp<GraphicBuffer> buffer;
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   IGraphicBufferProducer::QueueBufferInput input =
       QueueBufferInputBuilder()
@@ -372,7 +370,7 @@
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
 
   // Should be able to cancel buffer after a dequeue.
-  EXPECT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+  EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
 }
 
 TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
@@ -380,16 +378,15 @@
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
   int minUndequeuedBuffers;
-  ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                                       &minUndequeuedBuffers));
+  ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                                 &minUndequeuedBuffers));
 
   const int minBuffers = 1;
   const int maxBuffers =
       BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
 
-  ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
-      << "async mode: " << false;
-  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(minBuffers))
+  ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
+  ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(minBuffers))
       << "bufferCount: " << minBuffers;
 
   // Should now be able to dequeue up to minBuffers times
@@ -399,14 +396,14 @@
     ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
   }
 
-  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers));
+  ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers));
 
   // queue the first buffer to enable max dequeued buffer count checking
   IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
   IGraphicBufferProducer::QueueBufferOutput output;
   sp<GraphicBuffer> buffer;
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
-  ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
 
   sp<Fence> fence;
   for (int i = 0; i < maxBuffers; ++i) {
@@ -414,25 +411,24 @@
   }
 
   // Cancel a buffer, so we can decrease the buffer count
-  ASSERT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+  ASSERT_EQ(OK, mProducer->cancelBuffer(slot, fence));
 
   // Should now be able to decrease the max dequeued count by 1
-  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
+  ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
 }
 
 TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) {
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
   int minUndequeuedBuffers;
-  ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                                       &minUndequeuedBuffers));
+  ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                                 &minUndequeuedBuffers));
 
   const int minBuffers = 1;
   const int maxBuffers =
       BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
 
-  ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
-      << "async mode: " << false;
+  ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
   // Buffer count was out of range
   EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0))
       << "bufferCount: " << 0;
@@ -440,7 +436,7 @@
       << "bufferCount: " << maxBuffers + 1;
 
   // Set max dequeue count to 2
-  ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(2));
+  ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
   // Dequeue 2 buffers
   int slot = -1;
   sp<Fence> fence;
@@ -478,7 +474,7 @@
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
 
   // Shouldn't be able to request buffer after disconnect.
-  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
   ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer));
 }
 
@@ -489,14 +485,14 @@
 
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   // A generic "valid" input
   IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
   IGraphicBufferProducer::QueueBufferOutput output;
 
   // Shouldn't be able to queue buffer after disconnect.
-  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
   ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output));
 }
 
@@ -507,10 +503,10 @@
 
   ASSERT_NO_FATAL_FAILURE(ConnectProducer());
   ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-  ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+  ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
 
   // Shouldn't be able to cancel buffer after disconnect.
-  ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
   ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE));
 }
 
@@ -524,32 +520,32 @@
 
   constexpr int maxDequeuedBuffers = 1;
   int minUndequeuedBuffers;
-  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                                       &minUndequeuedBuffers));
-  EXPECT_EQ(NO_ERROR, mProducer->setAsyncMode(false));
-  EXPECT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                                 &minUndequeuedBuffers));
+  EXPECT_EQ(OK, mProducer->setAsyncMode(false));
+  EXPECT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers));
 
   int maxCapacity = maxDequeuedBuffers + minUndequeuedBuffers;
 
   // Dequeue, request, and queue all buffers.
   for (int i = 0; i < maxCapacity; i++) {
     EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-    EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
-    EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+    EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+    EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
   }
 
   // Disconnect then reconnect.
-  EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
   EXPECT_NO_FATAL_FAILURE(ConnectProducer());
 
   // Dequeue, request, and queue all buffers.
   for (int i = 0; i < maxCapacity; i++) {
     EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-    EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
-    EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+    EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+    EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
   }
 
-  EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+  EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
 }
 
 TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) {
@@ -568,21 +564,21 @@
   EXPECT_TRUE(dummy_producer_parcelable.IsValid());
 
   // Disconnect producer can be taken out, but only to an invalid parcelable.
-  ASSERT_EQ(mProducer->disconnect(kTestApi), NO_ERROR);
+  ASSERT_EQ(mProducer->disconnect(kTestApi), OK);
   EXPECT_EQ(mProducer->TakeAsParcelable(&dummy_producer_parcelable), BAD_VALUE);
   EXPECT_FALSE(producer_parcelable.IsValid());
-  EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), NO_ERROR);
+  EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK);
   EXPECT_TRUE(producer_parcelable.IsValid());
 
   // Should still be able to query buffer dimension after disconnect.
   int32_t value = -1;
-  EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
+  EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
   EXPECT_EQ(static_cast<uint32_t>(value), kDefaultWidth);
 
-  EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), NO_ERROR);
+  EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), OK);
   EXPECT_EQ(static_cast<uint32_t>(value), kDefaultHeight);
 
-  EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), NO_ERROR);
+  EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), OK);
   EXPECT_EQ(value, kDefaultFormat);
 
   // But connect to API will fail.
@@ -598,7 +594,7 @@
   ASSERT_TRUE(new_producer != nullptr);
   EXPECT_EQ(new_producer->connect(kDummyListener, kTestApi,
                                   kTestControlledByApp, &output),
-            NO_ERROR);
+            OK);
 }
 
 }  // namespace
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
index 9c67881..8c354fb 100644
--- a/libs/vr/libdisplay/Android.bp
+++ b/libs/vr/libdisplay/Android.bp
@@ -16,8 +16,8 @@
     "display_client.cpp",
     "display_manager_client.cpp",
     "display_protocol.cpp",
-    "vsync_client.cpp",
     "shared_buffer_helpers.cpp",
+    "vsync_service.cpp",
 ]
 
 localIncludeFiles = [
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
index 974c231..fdeeb70 100644
--- a/libs/vr/libdisplay/display_manager_client.cpp
+++ b/libs/vr/libdisplay/display_manager_client.cpp
@@ -1,7 +1,6 @@
 #include "include/private/dvr/display_manager_client.h"
 
 #include <pdx/default_transport/client_channel_factory.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_client.h>
 #include <private/dvr/display_protocol.h>
 #include <utils/Log.h>
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
index caf3182..f8f5b3d 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_client.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_client.h
@@ -5,7 +5,6 @@
 #include <hardware/hwcomposer.h>
 #include <pdx/client.h>
 #include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_client.h>
 #include <private/dvr/display_protocol.h>
 
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_client.h b/libs/vr/libdisplay/include/private/dvr/vsync_client.h
deleted file mode 100644
index 1eeb80e..0000000
--- a/libs/vr/libdisplay/include/private/dvr/vsync_client.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef ANDROID_DVR_VSYNC_CLIENT_H_
-#define ANDROID_DVR_VSYNC_CLIENT_H_
-
-#include <stdint.h>
-
-#include <pdx/client.h>
-
-struct dvr_vsync_client {};
-
-namespace android {
-namespace dvr {
-
-/*
- * VSyncClient is a remote interface to the vsync service in displayd.
- * This class is used to wait for and retrieve information about the
- * display vsync.
- */
-class VSyncClient : public pdx::ClientBase<VSyncClient>,
-                    public dvr_vsync_client {
- public:
-  /*
-   * Wait for the next vsync signal.
-   * The timestamp (in ns) is written into *ts when ts is non-NULL.
-   */
-  int Wait(int64_t* timestamp_ns);
-
-  /*
-   * Returns the file descriptor used to communicate with the vsync system
-   * service or -1 on error.
-   */
-  int GetFd();
-
-  /*
-   * Clears the select/poll/epoll event so that subsequent calls to
-   * these will not signal until the next vsync.
-   */
-  int Acknowledge();
-
-  /*
-   * Get the timestamp of the last vsync event in ns. This call has
-   * the same side effect on events as Acknowledge(), which saves
-   * an IPC message.
-   */
-  int GetLastTimestamp(int64_t* timestamp_ns);
-
-  /*
-   * Get vsync scheduling info.
-   * 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 GetSchedInfo(int64_t* vsync_period_ns, int64_t* next_timestamp_ns,
-                   uint32_t* next_vsync_count);
-
- private:
-  friend BASE;
-
-  VSyncClient();
-  explicit VSyncClient(long timeout_ms);
-
-  VSyncClient(const VSyncClient&) = delete;
-  void operator=(const VSyncClient&) = delete;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_VSYNC_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_service.h b/libs/vr/libdisplay/include/private/dvr/vsync_service.h
new file mode 100644
index 0000000..152464a
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/vsync_service.h
@@ -0,0 +1,65 @@
+#ifndef ANDROID_DVR_VSYNC_SERVICE_H_
+#define ANDROID_DVR_VSYNC_SERVICE_H_
+
+#include <binder/IInterface.h>
+
+namespace android {
+namespace dvr {
+
+class IVsyncCallback : public IInterface {
+ public:
+  DECLARE_META_INTERFACE(VsyncCallback)
+
+  enum {
+    ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION
+  };
+
+  virtual status_t onVsync(int64_t vsync_timestamp) = 0;
+};
+
+class BnVsyncCallback : public BnInterface<IVsyncCallback> {
+ public:
+  virtual status_t onTransact(uint32_t code, const Parcel& data,
+                              Parcel* reply, uint32_t flags = 0);
+};
+
+// Register a callback with IVsyncService to be notified of vsync events and
+// timestamps. There's also a shared memory vsync buffer defined in
+// dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared
+// memory buffer that make it preferable in certain situations:
+//
+// 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService
+// is always available as long as surface flinger is running.
+//
+// 2. IVsyncService will make a binder callback when a vsync event occurs. This
+// allows the client to not write code to implement periodic "get the latest
+// vsync" calls, which is necessary with the vsync shared memory buffer.
+//
+// 3. The IVsyncService provides the real vsync timestamp reported by hardware
+// composer, whereas the vsync shared memory buffer only has predicted vsync
+// times.
+class IVsyncService : public IInterface {
+public:
+  DECLARE_META_INTERFACE(VsyncService)
+
+  static const char* GetServiceName() { return "vrflinger_vsync"; }
+
+  enum {
+    REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
+    UNREGISTER_CALLBACK
+  };
+
+  virtual status_t registerCallback(const sp<IVsyncCallback> callback) = 0;
+  virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) = 0;
+};
+
+class BnVsyncService : public BnInterface<IVsyncService> {
+ public:
+  virtual status_t onTransact(uint32_t code, const Parcel& data,
+                              Parcel* reply, uint32_t flags = 0);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VSYNC_SERVICE_H_
diff --git a/libs/vr/libdisplay/vsync_client.cpp b/libs/vr/libdisplay/vsync_client.cpp
deleted file mode 100644
index bc6cf6c..0000000
--- a/libs/vr/libdisplay/vsync_client.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "include/private/dvr/vsync_client.h"
-
-#include <log/log.h>
-
-#include <pdx/default_transport/client_channel_factory.h>
-#include <private/dvr/display_protocol.h>
-
-using android::dvr::display::VSyncProtocol;
-using android::pdx::Transaction;
-
-namespace android {
-namespace dvr {
-
-VSyncClient::VSyncClient(long timeout_ms)
-    : BASE(pdx::default_transport::ClientChannelFactory::Create(
-               VSyncProtocol::kClientPath),
-           timeout_ms) {}
-
-VSyncClient::VSyncClient()
-    : BASE(pdx::default_transport::ClientChannelFactory::Create(
-          VSyncProtocol::kClientPath)) {}
-
-int VSyncClient::Wait(int64_t* timestamp_ns) {
-  auto status = InvokeRemoteMethod<VSyncProtocol::Wait>();
-  if (!status) {
-    ALOGE("VSyncClient::Wait: Failed to wait for vsync: %s",
-          status.GetErrorMessage().c_str());
-    return -status.error();
-  }
-
-  if (timestamp_ns != nullptr) {
-    *timestamp_ns = status.get();
-  }
-  return 0;
-}
-
-int VSyncClient::GetFd() { return event_fd(); }
-
-int VSyncClient::GetLastTimestamp(int64_t* timestamp_ns) {
-  auto status = InvokeRemoteMethod<VSyncProtocol::GetLastTimestamp>();
-  if (!status) {
-    ALOGE("VSyncClient::GetLastTimestamp: Failed to get vsync timestamp: %s",
-          status.GetErrorMessage().c_str());
-    return -status.error();
-  }
-  *timestamp_ns = status.get();
-  return 0;
-}
-
-int VSyncClient::GetSchedInfo(int64_t* vsync_period_ns, int64_t* timestamp_ns,
-                              uint32_t* next_vsync_count) {
-  if (!vsync_period_ns || !timestamp_ns || !next_vsync_count)
-    return -EINVAL;
-
-  auto status = InvokeRemoteMethod<VSyncProtocol::GetSchedInfo>();
-  if (!status) {
-    ALOGE("VSyncClient::GetSchedInfo:: Failed to get warp timestamp: %s",
-          status.GetErrorMessage().c_str());
-    return -status.error();
-  }
-
-  *vsync_period_ns = status.get().vsync_period_ns;
-  *timestamp_ns = status.get().timestamp_ns;
-  *next_vsync_count = status.get().next_vsync_count;
-  return 0;
-}
-
-int VSyncClient::Acknowledge() {
-  auto status = InvokeRemoteMethod<VSyncProtocol::Acknowledge>();
-  ALOGE_IF(!status, "VSuncClient::Acknowledge: Failed to ack vsync because: %s",
-           status.GetErrorMessage().c_str());
-  return ReturnStatusOrError(status);
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp
new file mode 100644
index 0000000..4668b98
--- /dev/null
+++ b/libs/vr/libdisplay/vsync_service.cpp
@@ -0,0 +1,146 @@
+#include "include/private/dvr/vsync_service.h"
+
+#include <binder/Parcel.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+status_t BnVsyncCallback::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+  switch (code) {
+    case ON_VSYNC: {
+      CHECK_INTERFACE(IVsyncCallback, data, reply);
+      int64_t vsync_timestamp = 0;
+      status_t result = data.readInt64(&vsync_timestamp);
+      if (result != OK) {
+        ALOGE("onVsync failed to readInt64: %d", result);
+        return result;
+      }
+      onVsync(vsync_timestamp);
+      return OK;
+    }
+    default: {
+      return BBinder::onTransact(code, data, reply, flags);
+    }
+  }
+}
+
+class BpVsyncCallback : public BpInterface<IVsyncCallback> {
+public:
+  explicit BpVsyncCallback(const sp<IBinder>& impl)
+      : BpInterface<IVsyncCallback>(impl) {}
+  virtual ~BpVsyncCallback() {}
+
+  virtual status_t onVsync(int64_t vsync_timestamp) {
+    Parcel data, reply;
+    status_t result = data.writeInterfaceToken(
+        IVsyncCallback::getInterfaceDescriptor());
+    if (result != OK) {
+      ALOGE("onVsync failed to writeInterfaceToken: %d", result);
+      return result;
+    }
+    result = data.writeInt64(vsync_timestamp);
+    if (result != OK) {
+      ALOGE("onVsync failed to writeInt64: %d", result);
+      return result;
+    }
+    result = remote()->transact(
+        BnVsyncCallback::ON_VSYNC, data, &reply, TF_ONE_WAY);
+    if (result != OK) {
+      ALOGE("onVsync failed to transact: %d", result);
+      return result;
+    }
+    return result;
+  }
+};
+
+IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback");
+
+
+status_t BnVsyncService::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+  switch (code) {
+    case REGISTER_CALLBACK: {
+      CHECK_INTERFACE(IVsyncService, data, reply);
+      sp<IBinder> callback;
+      status_t result = data.readStrongBinder(&callback);
+      if (result != OK) {
+        ALOGE("registerCallback failed to readStrongBinder: %d", result);
+        return result;
+      }
+      registerCallback(interface_cast<IVsyncCallback>(callback));
+      return OK;
+    }
+    case UNREGISTER_CALLBACK: {
+      CHECK_INTERFACE(IVsyncService, data, reply);
+      sp<IBinder> callback;
+      status_t result = data.readStrongBinder(&callback);
+      if (result != OK) {
+        ALOGE("unregisterCallback failed to readStrongBinder: %d", result);
+        return result;
+      }
+      unregisterCallback(interface_cast<IVsyncCallback>(callback));
+      return OK;
+    }
+    default: {
+      return BBinder::onTransact(code, data, reply, flags);
+    }
+  }
+}
+
+class BpVsyncService : public BpInterface<IVsyncService> {
+public:
+  explicit BpVsyncService(const sp<IBinder>& impl)
+      : BpInterface<IVsyncService>(impl) {}
+  virtual ~BpVsyncService() {}
+
+  virtual status_t registerCallback(const sp<IVsyncCallback> callback) {
+    Parcel data, reply;
+    status_t result = data.writeInterfaceToken(
+        IVsyncService::getInterfaceDescriptor());
+    if (result != OK) {
+      ALOGE("registerCallback failed to writeInterfaceToken: %d", result);
+      return result;
+    }
+    result = data.writeStrongBinder(IInterface::asBinder(callback));
+    if (result != OK) {
+      ALOGE("registerCallback failed to writeStrongBinder: %d", result);
+      return result;
+    }
+    result = remote()->transact(
+        BnVsyncService::REGISTER_CALLBACK, data, &reply);
+    if (result != OK) {
+      ALOGE("registerCallback failed to transact: %d", result);
+      return result;
+    }
+    return result;
+  }
+
+  virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) {
+    Parcel data, reply;
+    status_t result = data.writeInterfaceToken(
+        IVsyncService::getInterfaceDescriptor());
+    if (result != OK) {
+      ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result);
+      return result;
+    }
+    result = data.writeStrongBinder(IInterface::asBinder(callback));
+    if (result != OK) {
+      ALOGE("unregisterCallback failed to writeStrongBinder: %d", result);
+      return result;
+    }
+    result = remote()->transact(
+        BnVsyncService::UNREGISTER_CALLBACK, data, &reply);
+    if (result != OK) {
+      ALOGE("unregisterCallback failed to transact: %d", result);
+      return result;
+    }
+    return result;
+  }
+};
+
+IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService");
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 16906f5..81a9b2d 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -19,7 +19,14 @@
     vendor_available: true,
 }
 
+cc_library_headers {
+    name: "libdvr_private_headers",
+    export_include_dirs: ["."],
+    vendor_available: false,
+}
+
 cflags = [
+    "-DDVR_TRACKING_IMPLEMENTED=0",
     "-DLOG_TAG=\"libdvr\"",
     "-DTRACE=0",
     "-Wall",
@@ -36,7 +43,7 @@
     "dvr_performance.cpp",
     "dvr_pose.cpp",
     "dvr_surface.cpp",
-    "dvr_vsync.cpp",
+    "dvr_tracking.cpp",
 ]
 
 static_libs = [
@@ -66,7 +73,7 @@
 ]
 
 cc_library_shared {
-    name: "libdvr",
+    name: "libdvr.google",
     owner: "google",
     cflags: cflags,
     header_libs: ["libdvr_headers"],
@@ -81,7 +88,7 @@
 // restricting function access in the shared lib makes it inconvenient to use in
 // test code.
 cc_library_static {
-    name: "libdvr_static",
+    name: "libdvr_static.google",
     owner: "google",
     cflags: cflags,
     header_libs: ["libdvr_headers"],
diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp
index d14f040..e099f6a 100644
--- a/libs/vr/libdvr/dvr_api.cpp
+++ b/libs/vr/libdvr/dvr_api.cpp
@@ -12,6 +12,7 @@
 #include <dvr/dvr_display_manager.h>
 #include <dvr/dvr_performance.h>
 #include <dvr/dvr_surface.h>
+#include <dvr/dvr_tracking.h>
 #include <dvr/dvr_vsync.h>
 
 // Headers not yet moved into libdvr.
diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp
index baf1f2f..c11706f 100644
--- a/libs/vr/libdvr/dvr_buffer.cpp
+++ b/libs/vr/libdvr/dvr_buffer.cpp
@@ -2,7 +2,8 @@
 
 #include <android/hardware_buffer.h>
 #include <dvr/dvr_shared_buffers.h>
-#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/consumer_buffer.h>
+#include <private/dvr/producer_buffer.h>
 #include <ui/GraphicBuffer.h>
 
 #include "dvr_internal.h"
diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp
index 571558a..1ca653c 100644
--- a/libs/vr/libdvr/dvr_buffer_queue.cpp
+++ b/libs/vr/libdvr/dvr_buffer_queue.cpp
@@ -8,10 +8,10 @@
 #include "dvr_buffer_queue_internal.h"
 
 using namespace android;
-using android::dvr::BufferConsumer;
-using android::dvr::BufferHubBuffer;
-using android::dvr::BufferProducer;
+using android::dvr::BufferHubBase;
+using android::dvr::ConsumerBuffer;
 using android::dvr::ConsumerQueue;
+using android::dvr::ProducerBuffer;
 using android::dvr::ProducerQueue;
 using android::dvr::ProducerQueueConfigBuilder;
 using android::dvr::UsagePolicy;
@@ -103,13 +103,13 @@
         "DvrWriteBufferQueue::GainBuffer: Buffer slot is not empty: %zu", slot);
     write_buffers_[slot]->write_buffer = std::move(buffer_status.take());
 
-    const auto& buffer_producer = write_buffers_[slot]->write_buffer;
-    if (!buffer_producer)
+    const auto& producer_buffer = write_buffers_[slot]->write_buffer;
+    if (!producer_buffer)
       return -ENOMEM;
 
-    if (width_ == buffer_producer->width() &&
-        height_ == buffer_producer->height() &&
-        format_ == buffer_producer->format()) {
+    if (width_ == producer_buffer->width() &&
+        height_ == producer_buffer->height() &&
+        format_ == producer_buffer->format()) {
       // Producer queue returns a buffer matches the current request.
       break;
     }
@@ -122,14 +122,14 @@
              "DvrWriteBufferQueue::Dequeue: requested buffer at slot: %zu "
              "(w=%u, h=%u, fmt=%u) is different from the buffer returned "
              "(w=%u, h=%u, fmt=%u). Need re-allocation.",
-             slot, width_, height_, format_, buffer_producer->width(),
-             buffer_producer->height(), buffer_producer->format());
+             slot, width_, height_, format_, producer_buffer->width(),
+             producer_buffer->height(), producer_buffer->format());
 
     // Currently, we are not storing |layer_count| and |usage| in queue
     // configuration. Copy those setup from the last buffer dequeued before we
     // remove it.
-    uint32_t old_layer_count = buffer_producer->layer_count();
-    uint64_t old_usage = buffer_producer->usage();
+    uint32_t old_layer_count = producer_buffer->layer_count();
+    uint64_t old_usage = producer_buffer->usage();
 
     // Allocate a new producer buffer with new buffer configs. Note that if
     // there are already multiple available buffers in the queue, the next one
@@ -439,11 +439,11 @@
     consumer_queue_->SetBufferRemovedCallback(nullptr);
   } else {
     consumer_queue_->SetBufferRemovedCallback(
-        [callback, context](const std::shared_ptr<BufferHubBuffer>& buffer) {
+        [callback, context](const std::shared_ptr<BufferHubBase>& buffer) {
           // When buffer is removed from the queue, the slot is already invalid.
           auto read_buffer = std::make_unique<DvrReadBuffer>();
           read_buffer->read_buffer =
-              std::static_pointer_cast<BufferConsumer>(buffer);
+              std::static_pointer_cast<ConsumerBuffer>(buffer);
           callback(read_buffer.release(), context);
         });
   }
diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp
index 852f9a4..7f631e3 100644
--- a/libs/vr/libdvr/dvr_display_manager.cpp
+++ b/libs/vr/libdvr/dvr_display_manager.cpp
@@ -2,18 +2,18 @@
 
 #include <dvr/dvr_buffer.h>
 #include <pdx/rpc/variant.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/consumer_buffer.h>
 #include <private/dvr/display_client.h>
 #include <private/dvr/display_manager_client.h>
 
 #include "dvr_internal.h"
 #include "dvr_buffer_queue_internal.h"
 
-using android::dvr::BufferConsumer;
+using android::dvr::ConsumerBuffer;
 using android::dvr::display::DisplayManagerClient;
-using android::dvr::display::SurfaceAttributes;
 using android::dvr::display::SurfaceAttribute;
+using android::dvr::display::SurfaceAttributes;
 using android::dvr::display::SurfaceState;
 using android::pdx::rpc::EmptyVariant;
 
diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h
index de8bb96..f845cd8 100644
--- a/libs/vr/libdvr/dvr_internal.h
+++ b/libs/vr/libdvr/dvr_internal.h
@@ -16,18 +16,11 @@
 namespace android {
 namespace dvr {
 
-class BufferProducer;
-class BufferConsumer;
 class IonBuffer;
 
 DvrBuffer* CreateDvrBufferFromIonBuffer(
     const std::shared_ptr<IonBuffer>& ion_buffer);
 
-DvrReadBuffer* CreateDvrReadBufferFromBufferConsumer(
-    const std::shared_ptr<BufferConsumer>& buffer_consumer);
-DvrWriteBuffer* CreateDvrWriteBufferFromBufferProducer(
-    const std::shared_ptr<BufferProducer>& buffer_producer);
-
 }  // namespace dvr
 }  // namespace android
 
@@ -39,7 +32,7 @@
   // DvrWriteBuffer acquired from a DvrWriteBufferQueue.
   int32_t slot = -1;
 
-  std::shared_ptr<android::dvr::BufferProducer> write_buffer;
+  std::shared_ptr<android::dvr::ProducerBuffer> write_buffer;
 };
 
 struct DvrReadBuffer {
@@ -48,7 +41,7 @@
   // DvrReadBuffer acquired from a DvrReadBufferQueue.
   int32_t slot = -1;
 
-  std::shared_ptr<android::dvr::BufferConsumer> read_buffer;
+  std::shared_ptr<android::dvr::ConsumerBuffer> read_buffer;
 };
 
 struct DvrBuffer {
diff --git a/libs/vr/libdvr/dvr_surface.cpp b/libs/vr/libdvr/dvr_surface.cpp
index a3a47f1..0c7ec01 100644
--- a/libs/vr/libdvr/dvr_surface.cpp
+++ b/libs/vr/libdvr/dvr_surface.cpp
@@ -14,7 +14,6 @@
 using android::dvr::display::Surface;
 using android::dvr::display::SurfaceAttributes;
 using android::dvr::display::SurfaceAttributeValue;
-using android::dvr::CreateDvrReadBufferFromBufferConsumer;
 using android::pdx::rpc::EmptyVariant;
 
 namespace {
diff --git a/libs/vr/libdvr/dvr_tracking.cpp b/libs/vr/libdvr/dvr_tracking.cpp
new file mode 100644
index 0000000..73addc9
--- /dev/null
+++ b/libs/vr/libdvr/dvr_tracking.cpp
@@ -0,0 +1,82 @@
+#include "include/dvr/dvr_tracking.h"
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#if !DVR_TRACKING_IMPLEMENTED
+
+extern "C" {
+
+// This file provides the stub implementation of dvrTrackingXXX APIs. On
+// platforms that implement these APIs, set -DDVR_TRACKING_IMPLEMENTED=1 in the
+// build file.
+int dvrTrackingCameraCreate(DvrTrackingCamera**) {
+  ALOGE("dvrTrackingCameraCreate is not implemented.");
+  return -ENOSYS;
+}
+
+void dvrTrackingCameraDestroy(DvrTrackingCamera*) {
+  ALOGE("dvrTrackingCameraDestroy is not implemented.");
+}
+
+int dvrTrackingCameraStart(DvrTrackingCamera*, DvrWriteBufferQueue*) {
+  ALOGE("dvrTrackingCameraCreate is not implemented.");
+  return -ENOSYS;
+}
+
+int dvrTrackingCameraStop(DvrTrackingCamera*) {
+  ALOGE("dvrTrackingCameraCreate is not implemented.");
+  return -ENOSYS;
+}
+
+int dvrTrackingFeatureExtractorCreate(DvrTrackingFeatureExtractor**) {
+  ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
+  return -ENOSYS;
+}
+
+void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor*) {
+  ALOGE("dvrTrackingFeatureExtractorDestroy is not implemented.");
+}
+
+int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor*,
+                                     DvrTrackingFeatureCallback, void*) {
+  ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
+  return -ENOSYS;
+}
+
+int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor*) {
+  ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
+  return -ENOSYS;
+}
+
+int dvrTrackingFeatureExtractorProcessBuffer(DvrTrackingFeatureExtractor*,
+                                             DvrReadBuffer*,
+                                             const DvrTrackingBufferMetadata*,
+                                             bool*) {
+  ALOGE("dvrTrackingFeatureExtractorProcessBuffer is not implemented.");
+  return -ENOSYS;
+}
+
+int dvrTrackingSensorsCreate(DvrTrackingSensors**, const char*) {
+  ALOGE("dvrTrackingSensorsCreate is not implemented.");
+  return -ENOSYS;
+}
+
+void dvrTrackingSensorsDestroy(DvrTrackingSensors*) {
+  ALOGE("dvrTrackingSensorsDestroy is not implemented.");
+}
+
+int dvrTrackingSensorsStart(DvrTrackingSensors*, DvrTrackingSensorEventCallback,
+                            void*) {
+  ALOGE("dvrTrackingStart is not implemented.");
+  return -ENOSYS;
+}
+
+int dvrTrackingSensorsStop(DvrTrackingSensors*) {
+  ALOGE("dvrTrackingStop is not implemented.");
+  return -ENOSYS;
+}
+
+}  // extern "C"
+
+#endif  // DVR_TRACKING_IMPLEMENTED
diff --git a/libs/vr/libdvr/dvr_vsync.cpp b/libs/vr/libdvr/dvr_vsync.cpp
deleted file mode 100644
index 099240e..0000000
--- a/libs/vr/libdvr/dvr_vsync.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "include/dvr/dvr_vsync.h"
-
-#include <utils/Log.h>
-
-#include <private/dvr/vsync_client.h>
-
-extern "C" {
-
-struct DvrVSyncClient {
-  std::unique_ptr<android::dvr::VSyncClient> client;
-};
-
-int dvrVSyncClientCreate(DvrVSyncClient** client_out) {
-  auto client = android::dvr::VSyncClient::Create();
-  if (!client) {
-    ALOGE("dvrVSyncClientCreate: Failed to create vsync client!");
-    return -EIO;
-  }
-
-  *client_out = new DvrVSyncClient{std::move(client)};
-  return 0;
-}
-
-void dvrVSyncClientDestroy(DvrVSyncClient* client) { delete client; }
-
-int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns,
-                               int64_t* next_timestamp_ns,
-                               uint32_t* next_vsync_count) {
-  return client->client->GetSchedInfo(vsync_period_ns, next_timestamp_ns,
-                                      next_vsync_count);
-}
-
-}  // extern "C"
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index 80ffc82..e383bb2 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -10,6 +10,7 @@
 #include <dvr/dvr_display_types.h>
 #include <dvr/dvr_hardware_composer_types.h>
 #include <dvr/dvr_pose.h>
+#include <dvr/dvr_tracking_types.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -50,6 +51,12 @@
 typedef struct DvrSurfaceAttributeValue DvrSurfaceAttributeValue;
 typedef struct DvrSurfaceAttribute DvrSurfaceAttribute;
 
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrTrackingCamera DvrTrackingCamera;
+typedef struct DvrTrackingFeatureExtractor DvrTrackingFeatureExtractor;
+typedef struct DvrTrackingSensors DvrTrackingSensors;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+
 // Note: To avoid breaking others during active development, only modify this
 // struct by appending elements to the end.
 // If you do feel we should to re-arrange or remove elements, please make a
@@ -367,12 +374,45 @@
 typedef int (*DvrPerformanceSetSchedulerPolicyPtr)(
     pid_t task_id, const char* scheduler_policy);
 
+// dvr_tracking.h
+typedef int (*DvrTrackingCameraCreatePtr)(DvrTrackingCamera** out_camera);
+typedef void (*DvrTrackingCameraDestroyPtr)(DvrTrackingCamera* camera);
+typedef int (*DvrTrackingCameraStartPtr)(DvrTrackingCamera* camera,
+                                         DvrWriteBufferQueue* write_queue);
+typedef int (*DvrTrackingCameraStopPtr)(DvrTrackingCamera* camera);
+
+typedef int (*DvrTrackingFeatureExtractorCreatePtr)(
+    DvrTrackingFeatureExtractor** out_extractor);
+typedef void (*DvrTrackingFeatureExtractorDestroyPtr)(
+    DvrTrackingFeatureExtractor* extractor);
+typedef void (*DvrTrackingFeatureCallback)(void* context,
+                                           const DvrTrackingFeatures* event);
+typedef int (*DvrTrackingFeatureExtractorStartPtr)(
+    DvrTrackingFeatureExtractor* extractor,
+    DvrTrackingFeatureCallback callback, void* context);
+typedef int (*DvrTrackingFeatureExtractorStopPtr)(
+    DvrTrackingFeatureExtractor* extractor);
+typedef int (*DvrTrackingFeatureExtractorProcessBufferPtr)(
+    DvrTrackingFeatureExtractor* extractor, DvrReadBuffer* buffer,
+    const DvrTrackingBufferMetadata* metadata, bool* out_skipped);
+
+typedef void (*DvrTrackingSensorEventCallback)(void* context,
+                                               DvrTrackingSensorEvent* event);
+typedef int (*DvrTrackingSensorsCreatePtr)(DvrTrackingSensors** out_sensors,
+                                           const char* mode);
+typedef void (*DvrTrackingSensorsDestroyPtr)(DvrTrackingSensors* sensors);
+typedef int (*DvrTrackingSensorsStartPtr)(
+    DvrTrackingSensors* sensors, DvrTrackingSensorEventCallback callback,
+    void* context);
+typedef int (*DvrTrackingSensorsStopPtr)(DvrTrackingSensors* sensors);
+
 // The buffer metadata that an Android Surface (a.k.a. ANativeWindow)
 // will populate. A DvrWriteBufferQueue must be created with this metadata iff
 // ANativeWindow access is needed. Please do not remove, modify, or reorder
 // existing data members. If new fields need to be added, please take extra care
 // to make sure that new data field is padded properly the size of the struct
 // stays same.
+// TODO(b/118893702): move the definition to libnativewindow or libui
 struct ALIGNED_DVR_STRUCT(8) DvrNativeBufferMetadata {
 #ifdef __cplusplus
   DvrNativeBufferMetadata()
@@ -426,11 +466,11 @@
 
   // Only applicable for metadata retrieved from GainAsync. This indicates which
   // consumer has pending fence that producer should epoll on.
-  uint64_t release_fence_mask;
+  uint32_t release_fence_mask;
 
   // Reserved bytes for so that the struct is forward compatible and padding to
   // 104 bytes so the size is a multiple of 8.
-  int32_t reserved[8];
+  int32_t reserved[9];
 };
 
 #ifdef __cplusplus
diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
index f0d8ec6..3006b61 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -85,9 +85,9 @@
 DVR_V1_API_ENTRY(ReadBufferQueueHandleEvents);
 
 // V-Sync client
-DVR_V1_API_ENTRY(VSyncClientCreate);
-DVR_V1_API_ENTRY(VSyncClientDestroy);
-DVR_V1_API_ENTRY(VSyncClientGetSchedInfo);
+DVR_V1_API_ENTRY_DEPRECATED(VSyncClientCreate);
+DVR_V1_API_ENTRY_DEPRECATED(VSyncClientDestroy);
+DVR_V1_API_ENTRY_DEPRECATED(VSyncClientGetSchedInfo);
 
 // Display surface
 DVR_V1_API_ENTRY(SurfaceCreate);
@@ -181,3 +181,20 @@
 DVR_V1_API_ENTRY(PoseClientGetDataReader);
 DVR_V1_API_ENTRY(PoseClientDataCapture);
 DVR_V1_API_ENTRY(PoseClientDataReaderDestroy);
+
+// Tracking
+DVR_V1_API_ENTRY(TrackingCameraCreate);
+DVR_V1_API_ENTRY(TrackingCameraDestroy);
+DVR_V1_API_ENTRY(TrackingCameraStart);
+DVR_V1_API_ENTRY(TrackingCameraStop);
+
+DVR_V1_API_ENTRY(TrackingFeatureExtractorCreate);
+DVR_V1_API_ENTRY(TrackingFeatureExtractorDestroy);
+DVR_V1_API_ENTRY(TrackingFeatureExtractorStart);
+DVR_V1_API_ENTRY(TrackingFeatureExtractorStop);
+DVR_V1_API_ENTRY(TrackingFeatureExtractorProcessBuffer);
+
+DVR_V1_API_ENTRY(TrackingSensorsCreate);
+DVR_V1_API_ENTRY(TrackingSensorsDestroy);
+DVR_V1_API_ENTRY(TrackingSensorsStart);
+DVR_V1_API_ENTRY(TrackingSensorsStop);
diff --git a/libs/vr/libdvr/include/dvr/dvr_deleter.h b/libs/vr/libdvr/include/dvr/dvr_deleter.h
index 943384f..fe59d1f 100644
--- a/libs/vr/libdvr/include/dvr/dvr_deleter.h
+++ b/libs/vr/libdvr/include/dvr/dvr_deleter.h
@@ -20,7 +20,6 @@
 typedef struct DvrSurface DvrSurface;
 typedef struct DvrHwcClient DvrHwcClient;
 typedef struct DvrHwcFrame DvrHwcFrame;
-typedef struct DvrVSyncClient DvrVSyncClient;
 
 void dvrBufferDestroy(DvrBuffer* buffer);
 void dvrReadBufferDestroy(DvrReadBuffer* read_buffer);
@@ -32,7 +31,6 @@
 void dvrSurfaceDestroy(DvrSurface* surface);
 void dvrHwcClientDestroy(DvrHwcClient* client);
 void dvrHwcFrameDestroy(DvrHwcFrame* frame);
-void dvrVSyncClientDestroy(DvrVSyncClient* client);
 
 __END_DECLS
 
@@ -55,7 +53,6 @@
   void operator()(DvrSurface* p) { dvrSurfaceDestroy(p); }
   void operator()(DvrHwcClient* p) { dvrHwcClientDestroy(p); }
   void operator()(DvrHwcFrame* p) { dvrHwcFrameDestroy(p); }
-  void operator()(DvrVSyncClient* p) { dvrVSyncClientDestroy(p); }
 };
 
 // Helper to define unique pointers for DVR object types.
@@ -73,7 +70,6 @@
 using UniqueDvrSurface = MakeUniqueDvrPointer<DvrSurface>;
 using UniqueDvrHwcClient = MakeUniqueDvrPointer<DvrHwcClient>;
 using UniqueDvrHwcFrame = MakeUniqueDvrPointer<DvrHwcFrame>;
-using UniqueDvrVSyncClient = MakeUniqueDvrPointer<DvrVSyncClient>;
 
 // TODO(eieio): Add an adapter for std::shared_ptr that injects the deleter into
 // the relevant constructors.
diff --git a/libs/vr/libdvr/include/dvr/dvr_tracking.h b/libs/vr/libdvr/include/dvr/dvr_tracking.h
new file mode 100644
index 0000000..5e388f3
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_tracking.h
@@ -0,0 +1,185 @@
+#ifndef ANDROID_DVR_TRACKING_H_
+#define ANDROID_DVR_TRACKING_H_
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#include <dvr/dvr_tracking_types.h>
+
+__BEGIN_DECLS
+
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrTrackingCamera DvrTrackingCamera;
+typedef struct DvrTrackingFeatureExtractor DvrTrackingFeatureExtractor;
+typedef struct DvrTrackingSensors DvrTrackingSensors;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+
+// The callback for DvrTrackingFeatureExtractor that will deliver the feature
+// events. This callback is passed to dvrTrackingFeatureExtractorStart.
+typedef void (*DvrTrackingFeatureCallback)(void* context,
+                                           const DvrTrackingFeatures* event);
+
+// The callback for DvrTrackingSensors session that will deliver the events.
+// This callback is passed to dvrTrackingSensorsStart.
+typedef void (*DvrTrackingSensorEventCallback)(void* context,
+                                               DvrTrackingSensorEvent* event);
+
+// Creates a DvrTrackingCamera session.
+//
+// On creation, the session is not in operating mode. Client code must call
+// dvrTrackingCameraStart to bootstrap the underlying camera stack.
+//
+// There is no plan to expose camera configuration through this API. All camera
+// parameters are determined by the system optimized for better tracking
+// results. See b/78662281 for detailed deprecation plan of this API and the
+// Stage 2 of VR tracking data source refactoring.
+//
+// @param out_camera The pointer of a DvrTrackingCamera will be filled here if
+//     the method call succeeds.
+// @return Zero on success, or negative error code.
+int dvrTrackingCameraCreate(DvrTrackingCamera** out_camera);
+
+// Destroys a DvrTrackingCamera handle.
+//
+// @param camera The DvrTrackingCamera of interest.
+void dvrTrackingCameraDestroy(DvrTrackingCamera* camera);
+
+// Starts the DvrTrackingCamera.
+//
+// On successful return, all DvrReadBufferQueue's associated with the given
+// write_queue will start to receive buffers from the camera stack. Note that
+// clients of this API should not assume the buffer dimension, format, and/or
+// usage of the outcoming buffers, as they are governed by the underlying camera
+// logic. Also note that it's the client's responsibility to consume buffers
+// from DvrReadBufferQueue on time and return them back to the producer;
+// otherwise the camera stack might be blocked.
+//
+// @param camera The DvrTrackingCamera of interest.
+// @param write_queue A DvrWriteBufferQueue that the camera stack can use to
+//     populate the buffer into. The queue must be empty and the camera stack
+//     will request buffer allocation with proper buffer dimension, format, and
+//     usage. Note that the write queue must be created with user_metadata_size
+//     set to sizeof(DvrTrackingBufferMetadata). On success, the write_queue
+//     handle will become invalid and the ownership of the queue handle will be
+//     transferred into the camera; otherwise, the write_queue handle will keep
+//     untouched and the caller still has the ownership.
+// @return Zero on success, or negative error code.
+int dvrTrackingCameraStart(DvrTrackingCamera* camera,
+                           DvrWriteBufferQueue* write_queue);
+
+// Stops the DvrTrackingCamera.
+//
+// On successful return, the DvrWriteBufferQueue set during
+// dvrTrackingCameraStart will stop getting new buffers from the camera stack.
+//
+// @param camera The DvrTrackingCamera of interest.
+// @return Zero on success, or negative error code.
+int dvrTrackingCameraStop(DvrTrackingCamera* camera);
+
+// Creates a DvrTrackingSensors session.
+//
+// This will initialize but not start device sensors (gyro / accel). Upon
+// successfull creation, the clients can call dvrTrackingSensorsStart to start
+// receiving sensor events.
+//
+// @param out_sensors The pointer of a DvrTrackingSensors will be filled here if
+//     the method call succeeds.
+// @param mode The sensor mode.
+//        mode="ndk": Use the Android NDK.
+//        mode="direct": Use direct mode sensors (lower latency).
+// @return Zero on success, or negative error code.
+int dvrTrackingSensorsCreate(DvrTrackingSensors** out_sensors,
+                             const char* mode);
+
+// Destroys a DvrTrackingSensors session.
+//
+// @param sensors The DvrTrackingSensors struct to destroy.
+void dvrTrackingSensorsDestroy(DvrTrackingSensors* sensors);
+
+// Starts the tracking sensor session.
+//
+// This will start the device sensors and start pumping the feature and sensor
+// events as they arrive.
+//
+// @param client A tracking client created by dvrTrackingSensorsCreate.
+// @param context A client supplied pointer that will be passed to the callback.
+// @param callback A callback that will receive the sensor events on an
+// arbitrary thread.
+// @return Zero on success, or negative error code.
+int dvrTrackingSensorsStart(DvrTrackingSensors* sensors,
+                            DvrTrackingSensorEventCallback callback,
+                            void* context);
+
+// Stops a DvrTrackingSensors session.
+//
+// This will stop the device sensors. dvrTrackingSensorsStart can be called to
+// restart them again.
+//
+// @param client A tracking client created by dvrTrackingClientCreate.
+// @return Zero on success, or negative error code.
+int dvrTrackingSensorsStop(DvrTrackingSensors* sensors);
+
+// Creates a tracking feature extractor.
+//
+// This will initialize but not start the feature extraction session. Upon
+// successful creation, the client can call dvrTrackingFeatureExtractorStart to
+// start receiving features.
+//
+// @param out_extractor The pointer of a DvrTrackingFeatureExtractor will be
+//     filled here if the method call succeeds.
+int dvrTrackingFeatureExtractorCreate(
+    DvrTrackingFeatureExtractor** out_extractor);
+
+// Destroys a tracking feature extractor.
+//
+// @param extractor The DvrTrackingFeatureExtractor to destroy.
+void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor* extractor);
+
+// Starts the tracking feature extractor.
+//
+// This will start the extractor and start pumping the output feature events to
+// the registered callback. Note that this method will create one or more
+// threads to handle feature processing.
+//
+// @param extractor The DvrTrackingFeatureExtractor to destroy.
+int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor* extractor,
+                                     DvrTrackingFeatureCallback callback,
+                                     void* context);
+
+// Stops the tracking feature extractor.
+//
+// This will stop the extractor session and clean up all internal resourcse
+// related to this extractor. On succssful return, all internal therad started
+// by dvrTrackingFeatureExtractorStart should be stopped.
+//
+// @param extractor The DvrTrackingFeatureExtractor to destroy.
+int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor* extractor);
+
+// Processes one buffer to extract features from.
+//
+// The buffer will be sent over to DSP for feature extraction. Once the process
+// is done, the processing thread will invoke DvrTrackingFeatureCallback with
+// newly extracted features. Note that not all buffers will be processed, as the
+// underlying DSP can only process buffers at a certain framerate. If a buffer
+// needs to be skipped, out_skipped filed will be set to true. Also note that
+// for successfully processed stereo buffer, two callbacks (one for each eye)
+// will be fired.
+//
+// @param extractor The DvrTrackingFeatureExtractor to destroy.
+// @param buffer The buffer to extract features from. Note that the buffer must
+//     be in acquired state for the buffer to be processed. Also note that the
+//     buffer will be released back to its producer on successful return of the
+//     method.
+// @param metadata The metadata associated with the buffer. Should be populated
+//     by DvrTrackingCamera session as user defined metadata.
+// @param out_skipped On successful return, the field will be set to true iff
+//     the buffer was skipped; and false iff the buffer was processed. This
+//     field is optional and nullptr can be passed here to ignore the field.
+// @return Zero on success, or negative error code.
+int dvrTrackingFeatureExtractorProcessBuffer(
+    DvrTrackingFeatureExtractor* extractor, DvrReadBuffer* buffer,
+    const DvrTrackingBufferMetadata* metadata, bool* out_skipped);
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_TRACKING_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_tracking_types.h b/libs/vr/libdvr/include/dvr/dvr_tracking_types.h
new file mode 100644
index 0000000..81310d2
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_tracking_types.h
@@ -0,0 +1,104 @@
+#ifndef ANDROID_DVR_TRACKING_TYPES_H_
+#define ANDROID_DVR_TRACKING_TYPES_H_
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+typedef struct DvrTrackingBufferMetadata {
+  // Specifies the source of this image.
+  uint32_t camera_mask;
+  // Specifies the memory format of this image.
+  uint32_t format;
+  /// The width of the image data.
+  uint32_t width;
+  /// The height of the image data.
+  uint32_t height;
+  /// The number of bytes per scanline of image data.
+  uint32_t stride;
+  /// The frame number of this image.
+  int32_t frame_number;
+  /// The timestamp of this image in nanoseconds. Taken in the middle of the
+  /// exposure interval.
+  int64_t timestamp_ns;
+  // This is the timestamp for recording when the system using the HAL
+  // received the callback.  It will not be populated by the HAL.
+  int64_t callback_timestamp_ns;
+  /// The exposure duration of this image in nanoseconds.
+  int64_t exposure_duration_ns;
+} DvrTrackingBufferMetadata;
+
+// Represents a set of features extracted from a camera frame. Note that this
+// should be in sync with TangoHalCallbacks defined in tango-hal.h.
+typedef struct DvrTrackingFeatures {
+  // Specifies the source of the features.
+  uint32_t camera_mask;
+
+  // This is unused.
+  uint32_t unused;
+
+  // The timestamp in nanoseconds from the image that generated the features.
+  // Taken in the middle of the exposure interval.
+  int64_t timestamp_ns;
+
+  // This is the timestamp for recording when the system using the HAL
+  // received the callback.  It will not be populated by the HAL.
+  int64_t callback_timestamp_ns;
+
+  // The frame number from the image that generated the features.
+  int64_t frame_number;
+
+  // The number of features.
+  int count;
+
+  // An array of 2D image points for each feature in the current image.
+  // This is sub-pixel refined extremum location at the fine resolution.
+  float (*positions)[2];
+
+  // The id of these measurements.
+  int32_t* ids;
+
+  // The feature descriptors.
+  uint64_t (*descriptors)[8];
+
+  // Laplacian scores for each feature.
+  float* scores;
+
+  // Is this feature a minimum or maximum in the Laplacian image.
+  // 0 if the feature is a maximum, 1 if it is a minimum.
+  int32_t* is_minimum;
+
+  // This corresponds to the sub-pixel index of the laplacian image
+  // that the extremum was found.
+  float* scales;
+
+  // Computed orientation of keypoint as part of FREAK extraction, except
+  // it's represented in radians and measured anti-clockwise.
+  float* angles;
+
+  // Edge scores for each feature.
+  float* edge_scores;
+} DvrTrackingFeatures;
+
+// Represents a sensor event.
+typedef struct DvrTrackingSensorEvent {
+  // The sensor type.
+  int32_t sensor;
+
+  // Event type.
+  int32_t type;
+
+  // This is the timestamp recorded from the device. Taken in the middle
+  // of the integration interval and adjusted for any low pass filtering.
+  int64_t timestamp_ns;
+
+  // The event data.
+  float x;
+  float y;
+  float z;
+} DvrTrackingSensorEvent;
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_TRACKING_TYPES_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_vsync.h b/libs/vr/libdvr/include/dvr/dvr_vsync.h
index 87fdf31..498bb5c 100644
--- a/libs/vr/libdvr/include/dvr/dvr_vsync.h
+++ b/libs/vr/libdvr/include/dvr/dvr_vsync.h
@@ -6,8 +6,6 @@
 
 __BEGIN_DECLS
 
-typedef struct DvrVSyncClient DvrVSyncClient;
-
 // Represents a vsync sample. The size of this struct is 32 bytes.
 typedef struct __attribute__((packed, aligned(16))) DvrVsync {
   // The timestamp for the last vsync in nanoseconds.
@@ -29,19 +27,6 @@
   uint8_t padding[8];
 } DvrVsync;
 
-// Creates a new client to the system vsync service.
-int dvrVSyncClientCreate(DvrVSyncClient** client_out);
-
-// Destroys the vsync client.
-void dvrVSyncClientDestroy(DvrVSyncClient* client);
-
-// 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.
-int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns,
-                               int64_t* next_timestamp_ns,
-                               uint32_t* next_vsync_count);
-
 __END_DECLS
 
 #endif  // ANDROID_DVR_VSYNC_H_
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
index 1ae75fb..357dffe 100644
--- a/libs/vr/libdvr/tests/Android.bp
+++ b/libs/vr/libdvr/tests/Android.bp
@@ -12,38 +12,36 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-shared_libraries = [
-    "libbase",
-    "libbinder",
-    "libbufferhubqueue",
-    "libcutils",
-    "libgui",
-    "liblog",
-    "libhardware",
-    "libui",
-    "libutils",
-    "libnativewindow",
-    "libpdx_default_transport",
-]
-
-static_libraries = [
-    "libdvr_static",
-    "libchrome",
-    "libdvrcommon",
-    "libdisplay",
-    "libbroadcastring",
-]
-
 cc_test {
     srcs: [
         "dvr_display_manager-test.cpp",
         "dvr_named_buffer-test.cpp",
+        "dvr_tracking-test.cpp",
     ],
 
     header_libs: ["libdvr_headers"],
-    static_libs: static_libraries,
-    shared_libs: shared_libraries,
+    static_libs: [
+        "libdvr_static.google",
+        "libchrome",
+        "libdvrcommon",
+        "libdisplay",
+        "libbroadcastring",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libbufferhubqueue",
+        "libcutils",
+        "libgui",
+        "liblog",
+        "libhardware",
+        "libui",
+        "libutils",
+        "libnativewindow",
+        "libpdx_default_transport",
+    ],
     cflags: [
+        "-DDVR_TRACKING_IMPLEMENTED=0",
         "-DLOG_TAG=\"dvr_api-test\"",
         "-DTRACE=0",
         "-Wno-missing-field-initializers",
@@ -51,4 +49,59 @@
         "-g",
     ],
     name: "dvr_api-test",
+
+    // TODO(b/117568153): Temporarily opt out using libcrt.
+    no_libcrt: true,
+}
+
+cc_test {
+    name: "dvr_buffer_queue-test",
+
+    // Includes the dvr_api.h header. Tests should only include "dvr_api.h",
+    // and shall only get access to |dvrGetApi|, as other symbols are hidden
+    // from the library.
+    include_dirs: ["frameworks/native/libs/vr/libdvr/include"],
+
+    srcs: ["dvr_buffer_queue-test.cpp"],
+
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+
+    cflags: [
+        "-DTRACE=0",
+        "-O2",
+        "-g",
+    ],
+
+    // DTS Should only link to NDK libraries.
+    sdk_version: "26",
+    stl: "c++_static",
+}
+
+cc_test {
+    name: "dvr_display-test",
+
+    include_dirs: [
+        "frameworks/native/libs/vr/libdvr/include",
+        "frameworks/native/libs/nativewindow/include",
+    ],
+
+    srcs: ["dvr_display-test.cpp"],
+
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+
+    cflags: [
+        "-DTRACE=0",
+        "-O2",
+        "-g",
+    ],
+
+    // DTS Should only link to NDK libraries.
+    sdk_version: "26",
+    stl: "c++_static",
 }
diff --git a/libs/vr/libdvr/tests/Android.mk b/libs/vr/libdvr/tests/Android.mk
deleted file mode 100644
index 0f3840d..0000000
--- a/libs/vr/libdvr/tests/Android.mk
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-# TODO(b/73133405): Currently, building cc_test against NDK using Android.bp
-# doesn't work well. Migrate to use Android.bp once b/73133405 gets fixed.
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:= dvr_buffer_queue-test
-
-# Includes the dvr_api.h header. Tests should only include "dvr_api.h",
-# and shall only get access to |dvrGetApi|, as other symbols are hidden from the
-# library.
-LOCAL_C_INCLUDES := \
-    frameworks/native/libs/vr/libdvr/include \
-
-LOCAL_SANITIZE := thread
-
-LOCAL_SRC_FILES := dvr_buffer_queue-test.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    libandroid \
-    liblog \
-
-LOCAL_CFLAGS := \
-    -DTRACE=0 \
-    -O2 \
-    -g \
-
-# DTS Should only link to NDK libraries.
-LOCAL_SDK_VERSION := 26
-LOCAL_NDK_STL_VARIANT := c++_static
-
-include $(BUILD_NATIVE_TEST)
-
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:= dvr_display-test
-
-LOCAL_C_INCLUDES := \
-    frameworks/native/libs/vr/libdvr/include \
-    frameworks/native/libs/nativewindow/include
-
-LOCAL_SANITIZE := thread
-
-LOCAL_SRC_FILES := dvr_display-test.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    libandroid \
-    liblog
-
-LOCAL_CFLAGS := \
-    -DTRACE=0 \
-    -O2 \
-    -g
-
-# DTS Should only link to NDK libraries.
-LOCAL_SDK_VERSION := 26
-LOCAL_NDK_STL_VARIANT := c++_static
-
-include $(BUILD_NATIVE_TEST)
\ No newline at end of file
diff --git a/libs/vr/libdvr/tests/dvr_api_test.h b/libs/vr/libdvr/tests/dvr_api_test.h
index d8359e7..5d2ec28 100644
--- a/libs/vr/libdvr/tests/dvr_api_test.h
+++ b/libs/vr/libdvr/tests/dvr_api_test.h
@@ -14,7 +14,7 @@
     // workaround for an Android NDK bug. See more detail:
     // https://github.com/android-ndk/ndk/issues/360
     flags |= RTLD_NODELETE;
-    platform_handle_ = dlopen("libdvr.so", flags);
+    platform_handle_ = dlopen("libdvr.google.so", flags);
     ASSERT_NE(nullptr, platform_handle_) << "Dvr shared library missing.";
 
     auto dvr_get_api = reinterpret_cast<decltype(&dvrGetApi)>(
diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
index 2d5f004..df06097 100644
--- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
@@ -62,7 +62,7 @@
              buffer_removed_count_);
   }
 
-  DvrWriteBufferQueue* write_queue_{nullptr};
+  DvrWriteBufferQueue* write_queue_ = nullptr;
   int buffer_available_count_{0};
   int buffer_removed_count_{0};
 };
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
index c9a5c09..ed72577 100644
--- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
@@ -1,5 +1,6 @@
 #include <android-base/properties.h>
 #include <base/logging.h>
+#include <cutils/properties.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
 #include <poll.h>
@@ -479,6 +480,11 @@
 #endif
 
 TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) {
+  // This test doesn't apply to standalone vr devices.
+  if (property_get_bool("ro.boot.vr", false)) {
+    return;
+  }
+
   // Get surface state and verify there are no surfaces.
   ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
   ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
@@ -518,6 +524,11 @@
 }
 
 TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) {
+  // This test doesn't apply to standalone vr devices.
+  if (property_get_bool("ro.boot.vr", false)) {
+    return;
+  }
+
   // Get surface state and verify there are no surfaces.
   ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
   ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
@@ -757,6 +768,11 @@
 }
 
 TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) {
+  // This test doesn't apply to standalone vr devices.
+  if (property_get_bool("ro.boot.vr", false)) {
+    return;
+  }
+
   // Create an application surface.
   auto surface_status = CreateApplicationSurface();
   ASSERT_STATUS_OK(surface_status);
@@ -825,6 +841,11 @@
 }
 
 TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) {
+  // This test doesn't apply to standalone vr devices.
+  if (property_get_bool("ro.boot.vr", false)) {
+    return;
+  }
+
   // Create an application surface.
   auto surface_status = CreateApplicationSurface();
   ASSERT_STATUS_OK(surface_status);
diff --git a/libs/vr/libdvr/tests/dvr_tracking-test.cpp b/libs/vr/libdvr/tests/dvr_tracking-test.cpp
new file mode 100644
index 0000000..3b6d6e1
--- /dev/null
+++ b/libs/vr/libdvr/tests/dvr_tracking-test.cpp
@@ -0,0 +1,103 @@
+#include <android/log.h>
+#include <gtest/gtest.h>
+
+#include "dvr_api_test.h"
+
+namespace {
+
+class DvrTrackingTest : public DvrApiTest {};
+
+#if DVR_TRACKING_IMPLEMENTED
+
+TEST_F(DvrTrackingTest, Implemented) {
+  ASSERT_TRUE(api_.TrackingCameraCreate != nullptr);
+  ASSERT_TRUE(api_.TrackingCameraStart != nullptr);
+  ASSERT_TRUE(api_.TrackingCameraStop != nullptr);
+
+  ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr);
+  ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr);
+  ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr);
+  ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr);
+  ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr);
+}
+
+TEST_F(DvrTrackingTest, CameraCreateFailsForInvalidInput) {
+  int ret;
+  ret = api_.TrackingCameraCreate(nullptr);
+  EXPECT_EQ(ret, -EINVAL);
+
+  DvrTrackingCamera* camera = reinterpret_cast<DvrTrackingCamera*>(42);
+  ret = api_.TrackingCameraCreate(&camera);
+  EXPECT_EQ(ret, -EINVAL);
+}
+
+TEST_F(DvrTrackingTest, CameraCreateDestroy) {
+  DvrTrackingCamera* camera = nullptr;
+  int ret = api_.TrackingCameraCreate(&camera);
+
+  EXPECT_EQ(ret, 0);
+  ASSERT_TRUE(camera != nullptr);
+
+  api_.TrackingCameraDestroy(camera);
+}
+
+TEST_F(DvrTrackingTest, FeatureExtractorCreateFailsForInvalidInput) {
+  int ret;
+  ret = api_.TrackingFeatureExtractorCreate(nullptr);
+  EXPECT_EQ(ret, -EINVAL);
+
+  DvrTrackingFeatureExtractor* camera =
+      reinterpret_cast<DvrTrackingFeatureExtractor*>(42);
+  ret = api_.TrackingFeatureExtractorCreate(&camera);
+  EXPECT_EQ(ret, -EINVAL);
+}
+
+TEST_F(DvrTrackingTest, FeatureExtractorCreateDestroy) {
+  DvrTrackingFeatureExtractor* camera = nullptr;
+  int ret = api_.TrackingFeatureExtractorCreate(&camera);
+
+  EXPECT_EQ(ret, 0);
+  ASSERT_TRUE(camera != nullptr);
+
+  api_.TrackingFeatureExtractorDestroy(camera);
+}
+
+#else  // !DVR_TRACKING_IMPLEMENTED
+
+TEST_F(DvrTrackingTest, NotImplemented) {
+  ASSERT_TRUE(api_.TrackingCameraCreate != nullptr);
+  ASSERT_TRUE(api_.TrackingCameraDestroy != nullptr);
+  ASSERT_TRUE(api_.TrackingCameraStart != nullptr);
+  ASSERT_TRUE(api_.TrackingCameraStop != nullptr);
+
+  EXPECT_EQ(api_.TrackingCameraCreate(nullptr), -ENOSYS);
+  EXPECT_EQ(api_.TrackingCameraStart(nullptr, nullptr), -ENOSYS);
+  EXPECT_EQ(api_.TrackingCameraStop(nullptr), -ENOSYS);
+
+  ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr);
+  ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr);
+  ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr);
+  ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr);
+  ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr);
+
+  EXPECT_EQ(api_.TrackingFeatureExtractorCreate(nullptr), -ENOSYS);
+  EXPECT_EQ(api_.TrackingFeatureExtractorStart(nullptr, nullptr, nullptr),
+            -ENOSYS);
+  EXPECT_EQ(api_.TrackingFeatureExtractorStop(nullptr), -ENOSYS);
+  EXPECT_EQ(api_.TrackingFeatureExtractorProcessBuffer(nullptr, nullptr,
+                                                       nullptr, nullptr),
+            -ENOSYS);
+
+  ASSERT_TRUE(api_.TrackingSensorsCreate != nullptr);
+  ASSERT_TRUE(api_.TrackingSensorsDestroy != nullptr);
+  ASSERT_TRUE(api_.TrackingSensorsStart != nullptr);
+  ASSERT_TRUE(api_.TrackingSensorsStop != nullptr);
+
+  EXPECT_EQ(api_.TrackingSensorsCreate(nullptr, nullptr), -ENOSYS);
+  EXPECT_EQ(api_.TrackingSensorsStart(nullptr, nullptr, nullptr), -ENOSYS);
+  EXPECT_EQ(api_.TrackingSensorsStop(nullptr), -ENOSYS);
+}
+
+#endif  // DVR_TRACKING_IMPLEMENTED
+
+}  // namespace
diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h
index 853d53c..f5a2d5e 100644
--- a/libs/vr/libpdx/private/pdx/service.h
+++ b/libs/vr/libpdx/private/pdx/service.h
@@ -59,9 +59,18 @@
   virtual ~Channel() {}
 
   /*
+   * Accessors to the pid of the last active client.
+   */
+  pid_t GetActiveProcessId() const { return client_pid_; }
+  void SetActiveProcessId(pid_t pid) { client_pid_ = pid; }
+
+  /*
    * Utility to get a shared_ptr reference from the channel context pointer.
    */
   static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info);
+
+ private:
+  pid_t client_pid_ = 0;
 };
 
 /*
diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp
index 68b8dd7..3769162 100644
--- a/libs/vr/libpdx/service.cpp
+++ b/libs/vr/libpdx/service.cpp
@@ -318,13 +318,7 @@
   PDX_TRACE_NAME("Message::ReplyFileHandle");
   auto svc = service_.lock();
   if (!replied_ && svc) {
-    Status<void> ret;
-
-    if (handle)
-      ret = svc->endpoint()->MessageReply(this, handle.Get());
-    else
-      ret = svc->endpoint()->MessageReply(this, handle.Get());
-
+    Status<void> ret = svc->endpoint()->MessageReply(this, handle.Get());
     replied_ = ret.ok();
     return ret;
   } else {
diff --git a/libs/vr/libpdx_uds/channel_parcelable.cpp b/libs/vr/libpdx_uds/channel_parcelable.cpp
index e7bce27..5156846 100644
--- a/libs/vr/libpdx_uds/channel_parcelable.cpp
+++ b/libs/vr/libpdx_uds/channel_parcelable.cpp
@@ -36,7 +36,7 @@
 }
 
 status_t ChannelParcelable::writeToParcel(Parcel* parcel) const {
-  status_t res = NO_ERROR;
+  status_t res = OK;
 
   if (!IsValid()) {
     ALOGE("ChannelParcelable::writeToParcel: Invalid channel parcel.");
@@ -44,20 +44,20 @@
   }
 
   res = parcel->writeUint32(kUdsMagicParcelHeader);
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE("ChannelParcelable::writeToParcel: Cannot write magic: res=%d.", res);
     return res;
   }
 
   res = parcel->writeFileDescriptor(data_fd_.Get());
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE("ChannelParcelable::writeToParcel: Cannot write data fd: res=%d.",
           res);
     return res;
   }
 
   res = parcel->writeFileDescriptor(pollin_event_fd_.Get());
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE(
         "ChannelParcelable::writeToParcel: Cannot write pollin event fd: "
         "res=%d.",
@@ -66,7 +66,7 @@
   }
 
   res = parcel->writeFileDescriptor(pollhup_event_fd_.Get());
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE(
         "ChannelParcelable::writeToParcel: Cannot write pollhup event fd: "
         "res=%d.",
@@ -79,7 +79,7 @@
 
 status_t ChannelParcelable::readFromParcel(const Parcel* parcel) {
   uint32_t magic = 0;
-  status_t res = NO_ERROR;
+  status_t res = OK;
 
   if (IsValid()) {
     ALOGE(
@@ -89,7 +89,7 @@
   }
 
   res = parcel->readUint32(&magic);
-  if (res != NO_ERROR) {
+  if (res != OK) {
     ALOGE("ChannelParcelable::readFromParcel: Failed to read magic: res=%d.",
           res);
     return res;
diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp
index 32d40e8..ecbfdba 100644
--- a/libs/vr/libpdx_uds/service_endpoint.cpp
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -521,6 +521,9 @@
   info.flags = 0;
   info.service = service_;
   info.channel = GetChannelState(channel_id);
+  if (info.channel != nullptr) {
+    info.channel->SetActiveProcessId(request.cred.pid);
+  }
   info.send_len = request.send_len;
   info.recv_len = request.max_recv_len;
   info.fd_count = request.file_descriptors.size();
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 26e8201..2829353 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -20,7 +20,6 @@
     "display_surface.cpp",
     "hardware_composer.cpp",
     "vr_flinger.cpp",
-    "vsync_service.cpp",
 ]
 
 includeFiles = [ "include" ]
@@ -40,6 +39,7 @@
     "android.hardware.graphics.allocator@2.0",
     "android.hardware.graphics.composer@2.1",
     "android.hardware.graphics.composer@2.2",
+    "android.hardware.graphics.composer@2.3",
     "libbinder",
     "libbase",
     "libbufferhubqueue",
@@ -65,6 +65,7 @@
 headerLibraries = [
     "android.hardware.graphics.composer@2.1-command-buffer",
     "android.hardware.graphics.composer@2.2-command-buffer",
+    "android.hardware.graphics.composer@2.3-command-buffer",
     "libdvr_headers",
     "libsurfaceflinger_headers",
 ]
@@ -90,3 +91,7 @@
     header_libs: headerLibraries,
     name: "libvrflinger",
 }
+
+subdirs = [
+    "tests",
+]
diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp
index 5d873d1..c360dee 100644
--- a/libs/vr/libvrflinger/acquired_buffer.cpp
+++ b/libs/vr/libvrflinger/acquired_buffer.cpp
@@ -8,11 +8,11 @@
 namespace android {
 namespace dvr {
 
-AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
                                LocalHandle acquire_fence, std::size_t slot)
     : buffer_(buffer), acquire_fence_(std::move(acquire_fence)), slot_(slot) {}
 
-AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
                                int* error) {
   LocalHandle fence;
   const int ret = buffer->Acquire(&fence);
@@ -75,7 +75,7 @@
   return std::move(acquire_fence_);
 }
 
-std::shared_ptr<BufferConsumer> AcquiredBuffer::ClaimBuffer() {
+std::shared_ptr<ConsumerBuffer> AcquiredBuffer::ClaimBuffer() {
   return std::move(buffer_);
 }
 
diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h
index 1a200aa..7643e75 100644
--- a/libs/vr/libvrflinger/acquired_buffer.h
+++ b/libs/vr/libvrflinger/acquired_buffer.h
@@ -2,49 +2,49 @@
 #define ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
 
 #include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/consumer_buffer.h>
 
 #include <memory>
 
 namespace android {
 namespace dvr {
 
-// Manages the ACQUIRE/RELEASE ownership cycle of a BufferConsumer.
+// Manages the ACQUIRE/RELEASE ownership cycle of a ConsumerBuffer.
 class AcquiredBuffer {
  public:
   static constexpr int kEmptyFence = pdx::LocalHandle::kEmptyFileHandle;
 
   AcquiredBuffer() : buffer_(nullptr), acquire_fence_(kEmptyFence) {}
 
-  // Constructs an AcquiredBuffer from a BufferConsumer pointer and an acquire
-  // fence. The BufferConsumer MUST be in the ACQUIRED state prior to calling
+  // Constructs an AcquiredBuffer from a ConsumerBuffer pointer and an acquire
+  // fence. The ConsumerBuffer MUST be in the ACQUIRED state prior to calling
   // this constructor; the constructor does not attempt to ACQUIRE the buffer
   // itself.
-  AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+  AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
                  pdx::LocalHandle acquire_fence, std::size_t slot = 0);
 
-  // Constructs an AcquiredBuffer from a BufferConsumer. The BufferConsumer MUST
+  // Constructs an AcquiredBuffer from a ConsumerBuffer. The ConsumerBuffer MUST
   // be in the POSTED state prior to calling this constructor, as this
   // constructor attempts to ACQUIRE the buffer. If ACQUIRING the buffer fails
   // this instance is left in the empty state. An optional error code is
   // returned in |error|, which may be nullptr if not needed.
-  AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, int* error);
+  AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer, int* error);
 
   // Move constructor. Behaves similarly to the move assignment operator below.
   AcquiredBuffer(AcquiredBuffer&& other) noexcept;
 
   ~AcquiredBuffer();
 
-  // Move assignment operator. Moves the BufferConsumer and acquire fence from
-  // |other| into this instance after RELEASING the current BufferConsumer and
+  // Move assignment operator. Moves the ConsumerBuffer and acquire fence from
+  // |other| into this instance after RELEASING the current ConsumerBuffer and
   // closing the acquire fence. After the move |other| is left in the empty
   // state.
   AcquiredBuffer& operator=(AcquiredBuffer&& other) noexcept;
 
-  // Accessors for the underlying BufferConsumer, the acquire fence, and the
+  // Accessors for the underlying ConsumerBuffer, the acquire fence, and the
   // use-case specific sequence value from the acquisition (see
-  // private/dvr/buffer_hub_client.h).
-  std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+  // private/dvr/consumer_buffer.h).
+  std::shared_ptr<ConsumerBuffer> buffer() const { return buffer_; }
   int acquire_fence() const { return acquire_fence_.Get(); }
 
   // When non-empty, returns true if the acquired fence was signaled (or if the
@@ -58,11 +58,11 @@
 
   // Returns the buffer, passing ownership to the caller. Caller is responsible
   // for calling Release on the returned buffer.
-  std::shared_ptr<BufferConsumer> ClaimBuffer();
+  std::shared_ptr<ConsumerBuffer> ClaimBuffer();
 
-  // Releases the BufferConsumer, passing the release fence in |release_fence|
-  // to the producer. On success, the BufferConsumer and acquire fence are set
-  // to empty state; if release fails, the BufferConsumer and acquire fence are
+  // Releases the ConsumerBuffer, passing the release fence in |release_fence|
+  // to the producer. On success, the ConsumerBuffer and acquire fence are set
+  // to empty state; if release fails, the ConsumerBuffer and acquire fence are
   // left in place and a negative error code is returned.
   int Release(pdx::LocalHandle release_fence = {});
 
@@ -71,7 +71,7 @@
   std::size_t slot() const { return slot_; }
 
  private:
-  std::shared_ptr<BufferConsumer> buffer_;
+  std::shared_ptr<ConsumerBuffer> buffer_;
   // Mutable so that the fence can be closed when it is determined to be
   // signaled during IsAvailable().
   mutable pdx::LocalHandle acquire_fence_;
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index 3090bd1..e0f2edd 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -4,7 +4,6 @@
 #include <dvr/dvr_api.h>
 #include <pdx/service.h>
 #include <pdx/status.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/bufferhub_rpc.h>
 #include <private/dvr/display_protocol.h>
 
@@ -60,11 +59,6 @@
   void SetDisplayConfigurationUpdateNotifier(
       DisplayConfigurationUpdateNotifier notifier);
 
-  using VSyncCallback = HardwareComposer::VSyncCallback;
-  void SetVSyncCallback(VSyncCallback callback) {
-    hardware_composer_.SetVSyncCallback(callback);
-  }
-
   void GrantDisplayOwnership() { hardware_composer_.Enable(); }
   void SeizeDisplayOwnership() { hardware_composer_.Disable(); }
   void OnBootFinished() { hardware_composer_.OnBootFinished(); }
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 64079e1..e1240d6 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -1,5 +1,6 @@
 #include "hardware_composer.h"
 
+#include <binder/IServiceManager.h>
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
 #include <fcntl.h>
@@ -52,6 +53,10 @@
 
 const char kUseExternalDisplayProperty[] = "persist.vr.use_external_display";
 
+// Surface flinger uses "VSYNC-sf" and "VSYNC-app" for its version of these
+// events. Name ours similarly.
+const char kVsyncTraceEventName[] = "VSYNC-vrflinger";
+
 // How long to wait after boot finishes before we turn the display off.
 constexpr int kBootFinishedDisplayOffTimeoutSec = 10;
 
@@ -131,6 +136,7 @@
   UpdatePostThreadState(PostThreadState::Quit, true);
   if (post_thread_.joinable())
     post_thread_.join();
+  composer_callback_->SetVsyncService(nullptr);
 }
 
 bool HardwareComposer::Initialize(
@@ -147,6 +153,13 @@
 
   primary_display_ = GetDisplayParams(composer, primary_display_id, true);
 
+  vsync_service_ = new VsyncService;
+  sp<IServiceManager> sm(defaultServiceManager());
+  auto result = sm->addService(String16(VsyncService::GetServiceName()),
+      vsync_service_, false);
+  LOG_ALWAYS_FATAL_IF(result != android::OK,
+      "addService(%s) failed", VsyncService::GetServiceName());
+
   post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
   LOG_ALWAYS_FATAL_IF(
       !post_thread_event_fd_,
@@ -223,6 +236,7 @@
   LOG_ALWAYS_FATAL_IF(!composer_callback_->GotFirstHotplug(),
       "Registered composer callback but didn't get hotplug for primary"
       " display");
+  composer_callback_->SetVsyncService(vsync_service_);
 }
 
 void HardwareComposer::OnPostThreadResumed() {
@@ -242,7 +256,10 @@
   // Standalones only create the composer client once and then use SetPowerMode
   // to control the screen on pause/resume.
   if (!is_standalone_device_) {
-    composer_callback_ = nullptr;
+    if (composer_callback_ != nullptr) {
+      composer_callback_->SetVsyncService(nullptr);
+      composer_callback_ = nullptr;
+    }
     composer_.reset(nullptr);
   } else {
     EnableDisplay(*target_display_, false);
@@ -336,7 +353,6 @@
   // According to the documentation, this fence is signaled at the time of
   // vsync/DMA for physical displays.
   if (error == HWC::Error::None) {
-    ATRACE_INT("HardwareComposer: VsyncFence", present_fence);
     retire_fence_fds_.emplace_back(present_fence);
   } else {
     ATRACE_INT("HardwareComposer: PresentResult", error);
@@ -775,6 +791,11 @@
       std::unique_lock<std::mutex> lock(post_thread_mutex_);
       ALOGI("HardwareComposer::PostThread: Entering quiescent state.");
 
+      if (was_running) {
+        vsync_trace_parity_ = false;
+        ATRACE_INT(kVsyncTraceEventName, 0);
+      }
+
       // Tear down resources.
       OnPostThreadPaused();
       was_running = false;
@@ -848,6 +869,9 @@
       vsync_timestamp = status.get();
     }
 
+    vsync_trace_parity_ = !vsync_trace_parity_;
+    ATRACE_INT(kVsyncTraceEventName, vsync_trace_parity_ ? 1 : 0);
+
     // Advance the vsync counter only if the system is keeping up with hardware
     // vsync to give clients an indication of the delays.
     if (vsync_prediction_interval_ == 1)
@@ -867,11 +891,6 @@
       vsync_ring_->Publish(vsync);
     }
 
-    // Signal all of the vsync clients. Because absolute time is used for the
-    // wakeup time below, this can take a little time if necessary.
-    if (vsync_callback_)
-      vsync_callback_(vsync_timestamp, /*frame_time_estimate*/ 0, vsync_count_);
-
     {
       // Sleep until shortly before vsync.
       ATRACE_NAME("sleep");
@@ -1063,8 +1082,45 @@
            layers_.size());
 }
 
-void HardwareComposer::SetVSyncCallback(VSyncCallback callback) {
-  vsync_callback_ = callback;
+std::vector<sp<IVsyncCallback>>::const_iterator
+HardwareComposer::VsyncService::FindCallback(
+    const sp<IVsyncCallback>& callback) const {
+  sp<IBinder> binder = IInterface::asBinder(callback);
+  return std::find_if(callbacks_.cbegin(), callbacks_.cend(),
+                      [&](const sp<IVsyncCallback>& callback) {
+                        return IInterface::asBinder(callback) == binder;
+                      });
+}
+
+status_t HardwareComposer::VsyncService::registerCallback(
+    const sp<IVsyncCallback> callback) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  if (FindCallback(callback) == callbacks_.cend()) {
+    callbacks_.push_back(callback);
+  }
+  return OK;
+}
+
+status_t HardwareComposer::VsyncService::unregisterCallback(
+    const sp<IVsyncCallback> callback) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  auto iter = FindCallback(callback);
+  if (iter != callbacks_.cend()) {
+    callbacks_.erase(iter);
+  }
+  return OK;
+}
+
+void HardwareComposer::VsyncService::OnVsync(int64_t vsync_timestamp) {
+  ATRACE_NAME("VsyncService::OnVsync");
+  std::lock_guard<std::mutex> autolock(mutex_);
+  for (auto iter = callbacks_.begin(); iter != callbacks_.end();) {
+    if ((*iter)->onVsync(vsync_timestamp) == android::DEAD_OBJECT) {
+      iter = callbacks_.erase(iter);
+    } else {
+      ++iter;
+    }
+  }
 }
 
 Return<void> HardwareComposer::ComposerCallback::onHotplug(
@@ -1123,16 +1179,26 @@
 
 Return<void> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display,
                                                          int64_t timestamp) {
+  TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
+               display, timestamp);
+  std::lock_guard<std::mutex> lock(mutex_);
   DisplayInfo* display_info = GetDisplayInfo(display);
   if (display_info) {
-    TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
-                 display, timestamp);
     display_info->callback_vsync_timestamp = timestamp;
   }
+  if (primary_display_.id == display && vsync_service_ != nullptr) {
+    vsync_service_->OnVsync(timestamp);
+  }
 
   return Void();
 }
 
+void HardwareComposer::ComposerCallback::SetVsyncService(
+    const sp<VsyncService>& vsync_service) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  vsync_service_ = vsync_service;
+}
+
 HardwareComposer::ComposerCallback::Displays
 HardwareComposer::ComposerCallback::GetDisplays() {
   std::lock_guard<std::mutex> lock(mutex_);
@@ -1149,6 +1215,7 @@
 
 Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime(
     hwc2_display_t display) {
+  std::lock_guard<std::mutex> autolock(mutex_);
   DisplayInfo* display_info = GetDisplayInfo(display);
   if (!display_info) {
     ALOGW("Attempt to get vsync time for unknown display %" PRIu64, display);
@@ -1160,7 +1227,6 @@
   if (!event_fd) {
     // Fall back to returning the last timestamp returned by the vsync
     // callback.
-    std::lock_guard<std::mutex> autolock(mutex_);
     return display_info->callback_vsync_timestamp;
   }
 
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index f1a755b..db0d6a7 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -22,8 +22,8 @@
 #include <dvr/dvr_vsync.h>
 #include <pdx/file_handle.h>
 #include <pdx/rpc/variant.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/shared_buffer_helpers.h>
+#include <private/dvr/vsync_service.h>
 
 #include "acquired_buffer.h"
 #include "display_surface.h"
@@ -300,8 +300,6 @@
 // will access the state and whether it needs to be synchronized.
 class HardwareComposer {
  public:
-  // Type for vsync callback.
-  using VSyncCallback = std::function<void(int64_t, int64_t, uint32_t)>;
   using RequestDisplayCallback = std::function<void(bool)>;
 
   HardwareComposer();
@@ -325,8 +323,6 @@
 
   std::string Dump();
 
-  void SetVSyncCallback(VSyncCallback callback);
-
   const DisplayParams& GetPrimaryDisplayParams() const {
     return primary_display_;
   }
@@ -350,6 +346,18 @@
   // on/off. Returns true on success, false on failure.
   bool EnableDisplay(const DisplayParams& display, bool enabled);
 
+  class VsyncService : public BnVsyncService {
+   public:
+    status_t registerCallback(const sp<IVsyncCallback> callback) override;
+    status_t unregisterCallback(const sp<IVsyncCallback> callback) override;
+    void OnVsync(int64_t vsync_timestamp);
+   private:
+    std::vector<sp<IVsyncCallback>>::const_iterator FindCallback(
+        const sp<IVsyncCallback>& callback) const;
+    std::mutex mutex_;
+    std::vector<sp<IVsyncCallback>> callbacks_;
+  };
+
   class ComposerCallback : public Hwc2::IComposerCallback {
    public:
     ComposerCallback() = default;
@@ -360,6 +368,7 @@
                                    int64_t timestamp) override;
 
     bool GotFirstHotplug() { return got_first_hotplug_; }
+    void SetVsyncService(const sp<VsyncService>& vsync_service);
 
     struct Displays {
       hwc2_display_t primary_display = 0;
@@ -385,6 +394,7 @@
     DisplayInfo primary_display_;
     std::optional<DisplayInfo> external_display_;
     bool external_display_was_hotplugged_ = false;
+    sp<VsyncService> vsync_service_;
   };
 
   HWC::Error Validate(hwc2_display_t display);
@@ -484,9 +494,6 @@
   // vector must be sorted by surface_id in ascending order.
   std::vector<Layer> layers_;
 
-  // Handler to hook vsync events outside of this class.
-  VSyncCallback vsync_callback_;
-
   // The layer posting thread. This thread wakes up a short time before vsync to
   // hand buffers to hardware composer.
   std::thread post_thread_;
@@ -534,6 +541,9 @@
   DvrConfig post_thread_config_;
   std::mutex shared_config_mutex_;
 
+  bool vsync_trace_parity_ = false;
+  sp<VsyncService> vsync_service_;
+
   static constexpr int kPostThreadInterrupted = 1;
 
   HardwareComposer(const HardwareComposer&) = delete;
diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp
new file mode 100644
index 0000000..c884cb3
--- /dev/null
+++ b/libs/vr/libvrflinger/tests/Android.bp
@@ -0,0 +1,39 @@
+shared_libs = [
+    "android.hardware.configstore-utils",
+    "android.hardware.configstore@1.0",
+    "libbinder",
+    "libbufferhubqueue",
+    "libcutils",
+    "libgui",
+    "libhidlbase",
+    "liblog",
+    "libui",
+    "libutils",
+    "libnativewindow",
+    "libpdx_default_transport",
+]
+
+static_libs = [
+    "libdisplay",
+]
+
+cc_test {
+    srcs: ["vrflinger_test.cpp"],
+    // See go/apct-presubmit for documentation on how this .filter file is used
+    // by Android's automated testing infrastructure for test filtering.
+    data: ["vrflinger_test.filter"],
+    static_libs: static_libs,
+    shared_libs: shared_libs,
+    cflags: [
+        "-DLOG_TAG=\"VrFlingerTest\"",
+        "-DTRACE=0",
+        "-O0",
+        "-g",
+        "-Wall",
+        "-Werror",
+    ],
+    name: "vrflinger_test",
+
+    // TODO(b/117568153): Temporarily opt out using libcrt.
+    no_libcrt: true,
+}
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
new file mode 100644
index 0000000..0eb7fec
--- /dev/null
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
@@ -0,0 +1,261 @@
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/types.h>
+#include <android/hardware_buffer.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <configstore/Utils.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <gui/ISurfaceComposer.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <thread>
+
+#include <private/dvr/display_client.h>
+
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+using android::dvr::display::DisplayClient;
+using android::dvr::display::Surface;
+using android::dvr::display::SurfaceAttribute;
+using android::dvr::display::SurfaceAttributeValue;
+
+namespace android {
+namespace dvr {
+
+// The transaction code for asking surface flinger if vr flinger is active. This
+// is done as a hidden api since it's only used for tests. See the "case 1028"
+// block in SurfaceFlinger::onTransact() in SurfaceFlinger.cpp.
+constexpr uint32_t kIsVrFlingerActiveTransactionCode = 1028;
+
+// The maximum amount of time to give vr flinger to activate/deactivate. If the
+// switch hasn't completed in this amount of time, the test will fail.
+constexpr auto kVrFlingerSwitchMaxTime = std::chrono::seconds(1);
+
+// How long to wait between each check to see if the vr flinger switch
+// completed.
+constexpr auto kVrFlingerSwitchPollInterval = std::chrono::milliseconds(50);
+
+// How long to wait for a device that boots to VR to have vr flinger ready.
+constexpr auto kBootVrFlingerWaitTimeout = std::chrono::seconds(30);
+
+// A Binder connection to surface flinger.
+class SurfaceFlingerConnection {
+ public:
+  static std::unique_ptr<SurfaceFlingerConnection> Create() {
+    sp<ISurfaceComposer> surface_flinger = interface_cast<ISurfaceComposer>(
+        defaultServiceManager()->getService(String16("SurfaceFlinger")));
+    if (surface_flinger == nullptr) {
+      return nullptr;
+    }
+
+    return std::unique_ptr<SurfaceFlingerConnection>(
+        new SurfaceFlingerConnection(surface_flinger));
+  }
+
+  // Returns true if the surface flinger process is still running. We use this
+  // to detect if surface flinger has crashed.
+  bool IsAlive() {
+    IInterface::asBinder(surface_flinger_)->pingBinder();
+    return IInterface::asBinder(surface_flinger_)->isBinderAlive();
+  }
+
+  // Return true if vr flinger is currently active, false otherwise. If there's
+  // an error communicating with surface flinger, std::nullopt is returned.
+  std::optional<bool> IsVrFlingerActive() {
+    Parcel data, reply;
+    status_t result =
+        data.writeInterfaceToken(surface_flinger_->getInterfaceDescriptor());
+    if (result != OK) {
+      return std::nullopt;
+    }
+    result = IInterface::asBinder(surface_flinger_)
+                 ->transact(kIsVrFlingerActiveTransactionCode, data, &reply);
+    if (result != OK) {
+      return std::nullopt;
+    }
+    bool vr_flinger_active;
+    result = reply.readBool(&vr_flinger_active);
+    if (result != OK) {
+      return std::nullopt;
+    }
+    return vr_flinger_active;
+  }
+
+  enum class VrFlingerSwitchResult : int8_t {
+    kSuccess,
+    kTimedOut,
+    kCommunicationError,
+    kSurfaceFlingerDied
+  };
+
+  // Wait for vr flinger to become active or inactive.
+  VrFlingerSwitchResult WaitForVrFlinger(bool wait_active) {
+    return WaitForVrFlingerTimed(wait_active, kVrFlingerSwitchPollInterval,
+        kVrFlingerSwitchMaxTime);
+  }
+
+  // Wait for vr flinger to become active or inactive, specifying custom timeouts.
+  VrFlingerSwitchResult WaitForVrFlingerTimed(bool wait_active,
+      std::chrono::milliseconds pollInterval, std::chrono::seconds timeout) {
+    auto start_time = std::chrono::steady_clock::now();
+    while (1) {
+      std::this_thread::sleep_for(pollInterval);
+      if (!IsAlive()) {
+        return VrFlingerSwitchResult::kSurfaceFlingerDied;
+      }
+      std::optional<bool> vr_flinger_active = IsVrFlingerActive();
+      if (!vr_flinger_active.has_value()) {
+        return VrFlingerSwitchResult::kCommunicationError;
+      }
+      if (vr_flinger_active.value() == wait_active) {
+        return VrFlingerSwitchResult::kSuccess;
+      } else if (std::chrono::steady_clock::now() - start_time > timeout) {
+        return VrFlingerSwitchResult::kTimedOut;
+      }
+    }
+  }
+
+ private:
+  SurfaceFlingerConnection(sp<ISurfaceComposer> surface_flinger)
+      : surface_flinger_(surface_flinger) {}
+
+  sp<ISurfaceComposer> surface_flinger_ = nullptr;
+};
+
+// This test activates vr flinger by creating a vr flinger surface, then
+// deactivates vr flinger by destroying the surface. We verify that vr flinger
+// is activated and deactivated as expected, and that surface flinger doesn't
+// crash.
+//
+// If the device doesn't support vr flinger (as repoted by ConfigStore), the
+// test does nothing.
+//
+// If the device is a standalone vr device, the test also does nothing, since
+// this test verifies the behavior of display handoff from surface flinger to vr
+// flinger and back, and standalone devices never hand control of the display
+// back to surface flinger.
+TEST(VrFlingerTest, ActivateDeactivate) {
+  android::ProcessState::self()->startThreadPool();
+
+  // Exit immediately if the device doesn't support vr flinger. This ConfigStore
+  // check is the same mechanism used by surface flinger to decide if it should
+  // initialize vr flinger.
+  bool vr_flinger_enabled =
+      getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useVrFlinger>(
+          false);
+  if (!vr_flinger_enabled) {
+    return;
+  }
+
+  // This test doesn't apply to standalone vr devices.
+  if (property_get_bool("ro.boot.vr", false)) {
+    return;
+  }
+
+  auto surface_flinger_connection = SurfaceFlingerConnection::Create();
+  ASSERT_NE(surface_flinger_connection, nullptr);
+
+  // Verify we start off with vr flinger disabled.
+  ASSERT_TRUE(surface_flinger_connection->IsAlive());
+  auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive();
+  ASSERT_TRUE(vr_flinger_active.has_value());
+  ASSERT_FALSE(vr_flinger_active.value());
+
+  // Create a vr flinger surface, and verify vr flinger becomes active.
+  // Introduce a scope so that, at the end of the scope, the vr flinger surface
+  // is destroyed, and vr flinger deactivates.
+  {
+    auto display_client = DisplayClient::Create();
+    ASSERT_NE(display_client, nullptr);
+    auto metrics = display_client->GetDisplayMetrics();
+    ASSERT_TRUE(metrics.ok());
+
+    auto surface = Surface::CreateSurface({
+        {SurfaceAttribute::Direct, SurfaceAttributeValue(true)},
+        {SurfaceAttribute::Visible, SurfaceAttributeValue(true)},
+    });
+    ASSERT_TRUE(surface.ok());
+    ASSERT_TRUE(surface.get() != nullptr);
+
+    auto queue = surface.get()->CreateQueue(
+        metrics.get().display_width, metrics.get().display_height,
+        /*layer_count=*/1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
+        AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+            AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
+            AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+        /*capacity=*/1,
+        /*metadata_size=*/0);
+    ASSERT_TRUE(queue.ok());
+    ASSERT_TRUE(queue.get() != nullptr);
+
+    size_t slot;
+    pdx::LocalHandle release_fence;
+    auto buffer = queue.get()->Dequeue(/*timeout=*/0, &slot, &release_fence);
+    ASSERT_TRUE(buffer.ok());
+    ASSERT_TRUE(buffer.get() != nullptr);
+
+    ASSERT_EQ(buffer.get()->width(), metrics.get().display_width);
+    ASSERT_EQ(buffer.get()->height(), metrics.get().display_height);
+
+    void* raw_buf = nullptr;
+    ASSERT_GE(buffer.get()->Lock(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+                                 /*x=*/0, /*y=*/0, buffer.get()->width(),
+                                 buffer.get()->height(), &raw_buf),
+              0);
+    ASSERT_NE(raw_buf, nullptr);
+    uint32_t* pixels = static_cast<uint32_t*>(raw_buf);
+
+    for (int i = 0; i < buffer.get()->stride() * buffer.get()->height(); ++i) {
+      pixels[i] = 0x0000ff00;
+    }
+
+    ASSERT_GE(buffer.get()->Unlock(), 0);
+
+    ASSERT_GE(buffer.get()->Post(/*ready_fence=*/pdx::LocalHandle()), 0);
+
+    ASSERT_EQ(
+        surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/true),
+        SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
+  }
+
+  // Now that the vr flinger surface is destroyed, vr flinger should deactivate.
+  ASSERT_EQ(
+      surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/false),
+      SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
+}
+
+// This test runs only on devices that boot to vr. Such a device should boot to
+// a state where vr flinger is running, and the test verifies this after a
+// delay.
+TEST(BootVrFlingerTest, BootsToVrFlinger) {
+  // Exit if we are not running on a device that boots to vr.
+  if (!property_get_bool("ro.boot.vr", false)) {
+    return;
+  }
+
+  auto surface_flinger_connection = SurfaceFlingerConnection::Create();
+  ASSERT_NE(surface_flinger_connection, nullptr);
+
+  // Verify that vr flinger is enabled.
+  ASSERT_TRUE(surface_flinger_connection->IsAlive());
+  auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive();
+  ASSERT_TRUE(vr_flinger_active.has_value());
+
+  bool active_value = vr_flinger_active.value();
+  if (!active_value) {
+    // Try again, but delay up to 30 seconds.
+    ASSERT_EQ(surface_flinger_connection->WaitForVrFlingerTimed(true,
+        kVrFlingerSwitchPollInterval, kBootVrFlingerWaitTimeout),
+        SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.filter b/libs/vr/libvrflinger/tests/vrflinger_test.filter
new file mode 100644
index 0000000..030bb7b
--- /dev/null
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.filter
@@ -0,0 +1,5 @@
+{
+        "presubmit": {
+            "filter": "BootVrFlingerTest.*"
+        }
+}
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
index a27d58d..a8a8476 100644
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -23,7 +23,6 @@
 #include "DisplayHardware/ComposerHal.h"
 #include "display_manager_service.h"
 #include "display_service.h"
-#include "vsync_service.h"
 
 namespace android {
 namespace dvr {
@@ -85,16 +84,6 @@
   CHECK_ERROR(!service, error, "Failed to create display manager service.");
   dispatcher_->AddService(service);
 
-  service = android::dvr::VSyncService::Create();
-  CHECK_ERROR(!service, error, "Failed to create vsync service.");
-  dispatcher_->AddService(service);
-
-  display_service_->SetVSyncCallback(
-      std::bind(&android::dvr::VSyncService::VSyncEvent,
-                std::static_pointer_cast<android::dvr::VSyncService>(service),
-                std::placeholders::_1, std::placeholders::_2,
-                std::placeholders::_3));
-
   dispatcher_thread_ = std::thread([this]() {
     prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0);
     ALOGI("Entering message loop.");
diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp
deleted file mode 100644
index b8d8b08..0000000
--- a/libs/vr/libvrflinger/vsync_service.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-#include "vsync_service.h"
-
-#include <hardware/hwcomposer.h>
-#include <log/log.h>
-#include <poll.h>
-#include <sys/prctl.h>
-#include <time.h>
-#include <utils/Trace.h>
-
-#include <dvr/dvr_display_types.h>
-#include <pdx/default_transport/service_endpoint.h>
-#include <private/dvr/clock_ns.h>
-#include <private/dvr/display_protocol.h>
-
-using android::dvr::display::VSyncProtocol;
-using android::dvr::display::VSyncSchedInfo;
-using android::pdx::Channel;
-using android::pdx::Message;
-using android::pdx::MessageInfo;
-using android::pdx::default_transport::Endpoint;
-using android::pdx::rpc::DispatchRemoteMethod;
-
-namespace android {
-namespace dvr {
-
-VSyncService::VSyncService()
-    : BASE("VSyncService", Endpoint::Create(VSyncProtocol::kClientPath)),
-      last_vsync_(0),
-      current_vsync_(0),
-      compositor_time_ns_(0),
-      current_vsync_count_(0) {}
-
-VSyncService::~VSyncService() {}
-
-void VSyncService::VSyncEvent(int64_t timestamp_ns,
-                              int64_t compositor_time_ns,
-                              uint32_t vsync_count) {
-  ATRACE_NAME("VSyncService::VSyncEvent");
-  std::lock_guard<std::mutex> autolock(mutex_);
-
-  last_vsync_ = current_vsync_;
-  current_vsync_ = timestamp_ns;
-  compositor_time_ns_ = compositor_time_ns;
-  current_vsync_count_ = vsync_count;
-
-  NotifyWaiters();
-  UpdateClients();
-}
-
-std::shared_ptr<Channel> VSyncService::OnChannelOpen(pdx::Message& message) {
-  const MessageInfo& info = message.GetInfo();
-
-  auto client = std::make_shared<VSyncChannel>(*this, info.pid, info.cid);
-  AddClient(client);
-
-  return client;
-}
-
-void VSyncService::OnChannelClose(pdx::Message& /*message*/,
-                                  const std::shared_ptr<Channel>& channel) {
-  auto client = std::static_pointer_cast<VSyncChannel>(channel);
-  if (!client) {
-    ALOGW("WARNING: VSyncChannel was NULL!!!\n");
-    return;
-  }
-
-  RemoveClient(client);
-}
-
-void VSyncService::AddWaiter(pdx::Message& message) {
-  std::lock_guard<std::mutex> autolock(mutex_);
-  std::unique_ptr<VSyncWaiter> waiter(new VSyncWaiter(message));
-  waiters_.push_back(std::move(waiter));
-}
-
-void VSyncService::AddClient(const std::shared_ptr<VSyncChannel>& client) {
-  std::lock_guard<std::mutex> autolock(mutex_);
-  clients_.push_back(client);
-}
-
-void VSyncService::RemoveClient(const std::shared_ptr<VSyncChannel>& client) {
-  std::lock_guard<std::mutex> autolock(mutex_);
-  clients_.remove(client);
-}
-
-// Private. Assumes mutex is held.
-void VSyncService::NotifyWaiters() {
-  ATRACE_NAME("VSyncService::NotifyWaiters");
-  auto first = waiters_.begin();
-  auto last = waiters_.end();
-
-  while (first != last) {
-    (*first)->Notify(current_vsync_);
-    waiters_.erase(first++);
-  }
-}
-
-// Private. Assumes mutex is held.
-void VSyncService::UpdateClients() {
-  ATRACE_NAME("VSyncService::UpdateClients");
-  auto first = clients_.begin();
-  auto last = clients_.end();
-
-  while (first != last) {
-    (*first)->Signal();
-    first++;
-  }
-}
-
-pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) {
-  ATRACE_NAME("VSyncService::HandleMessage");
-  switch (message.GetOp()) {
-    case VSyncProtocol::Wait::Opcode:
-      AddWaiter(message);
-      return {};
-
-    case VSyncProtocol::GetLastTimestamp::Opcode:
-      DispatchRemoteMethod<VSyncProtocol::GetLastTimestamp>(
-          *this, &VSyncService::OnGetLastTimestamp, message);
-      return {};
-
-    case VSyncProtocol::GetSchedInfo::Opcode:
-      DispatchRemoteMethod<VSyncProtocol::GetSchedInfo>(
-          *this, &VSyncService::OnGetSchedInfo, message);
-      return {};
-
-    case VSyncProtocol::Acknowledge::Opcode:
-      DispatchRemoteMethod<VSyncProtocol::Acknowledge>(
-          *this, &VSyncService::OnAcknowledge, message);
-      return {};
-
-    default:
-      return Service::HandleMessage(message);
-  }
-}
-
-pdx::Status<int64_t> VSyncService::OnGetLastTimestamp(pdx::Message& message) {
-  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
-  std::lock_guard<std::mutex> autolock(mutex_);
-
-  // Getting the timestamp has the side effect of ACKing.
-  client->Ack();
-  return {current_vsync_};
-}
-
-pdx::Status<VSyncSchedInfo> VSyncService::OnGetSchedInfo(
-    pdx::Message& message) {
-  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
-  std::lock_guard<std::mutex> autolock(mutex_);
-
-  // Getting the timestamp has the side effect of ACKing.
-  client->Ack();
-
-  uint32_t next_vsync_count = current_vsync_count_ + 1;
-  int64_t current_time = GetSystemClockNs();
-  int64_t vsync_period_ns = 0;
-  int64_t next_warp;
-  if (current_vsync_ == 0 || last_vsync_ == 0) {
-    // Handle startup when current_vsync_ or last_vsync_ are 0.
-    // Normally should not happen because vsync_service is running before
-    // applications, but in case it does a sane time prevents applications
-    // from malfunctioning.
-    vsync_period_ns = 20000000;
-    next_warp = current_time;
-  } else {
-    // TODO(jbates) When we have an accurate reading of the true vsync
-    // period, use that instead of this estimated value.
-    vsync_period_ns = current_vsync_ - last_vsync_;
-    // Clamp the period, because when there are no surfaces the last_vsync_
-    // value will get stale. Note this is temporary and goes away as soon
-    // as we have an accurate vsync period reported by the system.
-    vsync_period_ns = std::min(vsync_period_ns, INT64_C(20000000));
-    next_warp = current_vsync_ + vsync_period_ns - compositor_time_ns_;
-    // If the request missed the present window, move up to the next vsync.
-    if (current_time > next_warp) {
-      next_warp += vsync_period_ns;
-      ++next_vsync_count;
-    }
-  }
-
-  return {{vsync_period_ns, next_warp, next_vsync_count}};
-}
-
-pdx::Status<void> VSyncService::OnAcknowledge(pdx::Message& message) {
-  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
-  std::lock_guard<std::mutex> autolock(mutex_);
-  client->Ack();
-  return {};
-}
-
-void VSyncWaiter::Notify(int64_t timestamp) {
-  timestamp_ = timestamp;
-  DispatchRemoteMethod<VSyncProtocol::Wait>(*this, &VSyncWaiter::OnWait,
-                                            message_);
-}
-
-pdx::Status<int64_t> VSyncWaiter::OnWait(pdx::Message& /*message*/) {
-  return {timestamp_};
-}
-
-void VSyncChannel::Ack() {
-  ALOGD_IF(TRACE > 1, "VSyncChannel::Ack: pid=%d cid=%d\n", pid_, cid_);
-  service_.ModifyChannelEvents(cid_, POLLPRI, 0);
-}
-
-void VSyncChannel::Signal() {
-  ALOGD_IF(TRACE > 1, "VSyncChannel::Signal: pid=%d cid=%d\n", pid_, cid_);
-  service_.ModifyChannelEvents(cid_, 0, POLLPRI);
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrflinger/vsync_service.h b/libs/vr/libvrflinger/vsync_service.h
deleted file mode 100644
index 822f02b..0000000
--- a/libs/vr/libvrflinger/vsync_service.h
+++ /dev/null
@@ -1,107 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
-
-#include <pdx/service.h>
-
-#include <list>
-#include <memory>
-#include <mutex>
-#include <thread>
-
-#include "display_service.h"
-
-namespace android {
-namespace dvr {
-
-// VSyncWaiter encapsulates a client blocked waiting for the next vsync.
-// It is used to enqueue the Message to reply to when the next vsync event
-// occurs.
-class VSyncWaiter {
- public:
-  explicit VSyncWaiter(pdx::Message& message) : message_(std::move(message)) {}
-
-  void Notify(int64_t timestamp);
-
- private:
-  pdx::Status<int64_t> OnWait(pdx::Message& message);
-
-  pdx::Message message_;
-  int64_t timestamp_ = 0;
-
-  VSyncWaiter(const VSyncWaiter&) = delete;
-  void operator=(const VSyncWaiter&) = delete;
-};
-
-// VSyncChannel manages the service-side per-client context for each client
-// using the service.
-class VSyncChannel : public pdx::Channel {
- public:
-  VSyncChannel(pdx::Service& service, int pid, int cid)
-      : service_(service), pid_(pid), cid_(cid) {}
-
-  void Ack();
-  void Signal();
-
- private:
-  pdx::Service& service_;
-  pid_t pid_;
-  int cid_;
-
-  VSyncChannel(const VSyncChannel&) = delete;
-  void operator=(const VSyncChannel&) = delete;
-};
-
-// VSyncService implements the displayd vsync service over ServiceFS.
-class VSyncService : public pdx::ServiceBase<VSyncService> {
- public:
-  ~VSyncService() override;
-
-  pdx::Status<void> HandleMessage(pdx::Message& message) override;
-
-  std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
-  void OnChannelClose(pdx::Message& message,
-                      const std::shared_ptr<pdx::Channel>& channel) override;
-
-  // Called by the hardware composer HAL, or similar, whenever a vsync event
-  // occurs on the primary display. |compositor_time_ns| is the number of ns
-  // before the next vsync when the compositor will preempt the GPU to do EDS
-  // and lens warp.
-  void VSyncEvent(int64_t timestamp_ns, int64_t compositor_time_ns,
-                  uint32_t vsync_count);
-
- private:
-  friend BASE;
-
-  VSyncService();
-
-  pdx::Status<int64_t> OnGetLastTimestamp(pdx::Message& message);
-  pdx::Status<display::VSyncSchedInfo> OnGetSchedInfo(pdx::Message& message);
-  pdx::Status<void> OnAcknowledge(pdx::Message& message);
-
-  void NotifierThreadFunction();
-
-  void AddWaiter(pdx::Message& message);
-  void NotifyWaiters();
-  void UpdateClients();
-
-  void AddClient(const std::shared_ptr<VSyncChannel>& client);
-  void RemoveClient(const std::shared_ptr<VSyncChannel>& client);
-
-  int64_t last_vsync_;
-  int64_t current_vsync_;
-  int64_t compositor_time_ns_;
-  uint32_t current_vsync_count_;
-
-  std::mutex mutex_;
-
-  std::list<std::unique_ptr<VSyncWaiter>> waiters_;
-  std::list<std::shared_ptr<VSyncChannel>> clients_;
-
-  VSyncService(const VSyncService&) = delete;
-  void operator=(VSyncService&) = delete;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
index 4acc085..4ff6a09 100644
--- a/libs/vr/libvrsensor/pose_client.cpp
+++ b/libs/vr/libvrsensor/pose_client.cpp
@@ -8,8 +8,8 @@
 #include <pdx/client.h>
 #include <pdx/default_transport/client_channel_factory.h>
 #include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/consumer_buffer.h>
 #include <private/dvr/display_client.h>
 #include <private/dvr/pose-ipc.h>
 #include <private/dvr/shared_buffer_helpers.h>
@@ -221,14 +221,14 @@
       return -status.error();
     }
 
-    auto buffer = BufferConsumer::Import(status.take());
+    auto buffer = ConsumerBuffer::Import(status.take());
     if (!buffer) {
       ALOGE("Pose failed to import ring buffer");
       return -EIO;
     }
     constexpr size_t size = DvrVsyncPoseBuffer::kSize * sizeof(DvrPoseAsync);
     void* addr = nullptr;
-    int ret = buffer->GetBlobReadOnlyPointer(size, &addr);
+    int ret = buffer->GetBlobReadWritePointer(size, &addr);
     if (ret < 0 || !addr) {
       ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
       return -EIO;
@@ -290,7 +290,7 @@
   const DvrVsyncPoseBuffer* mapped_vsync_pose_buffer_ = nullptr;
 
   struct ControllerClientState {
-    std::unique_ptr<BufferConsumer> pose_buffer;
+    std::unique_ptr<ConsumerBuffer> pose_buffer;
     const DvrPoseAsync* mapped_pose_buffer = nullptr;
   };
   ControllerClientState controllers_[MAX_CONTROLLERS];
diff --git a/libs/vr/public.libraries-google.txt b/libs/vr/public.libraries-google.txt
new file mode 100644
index 0000000..8271b94
--- /dev/null
+++ b/libs/vr/public.libraries-google.txt
@@ -0,0 +1 @@
+libdvr.google.so
\ No newline at end of file
diff --git a/opengl/include/EGL/Platform.h b/opengl/include/EGL/Platform.h
new file mode 100644
index 0000000..f2b501c
--- /dev/null
+++ b/opengl/include/EGL/Platform.h
@@ -0,0 +1,338 @@
+//
+// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Platform.h: The public interface ANGLE exposes to the API layer, for
+//   doing platform-specific tasks like gathering data, or for tracing.
+
+#ifndef ANGLE_PLATFORM_H
+#define ANGLE_PLATFORM_H
+
+#include <stdint.h>
+#include <array>
+
+#define EGL_PLATFORM_ANGLE_PLATFORM_METHODS_ANGLEX 0x3482
+
+#if defined(_WIN32)
+#   if !defined(LIBANGLE_IMPLEMENTATION)
+#       define ANGLE_PLATFORM_EXPORT __declspec(dllimport)
+#   else
+#       define ANGLE_PLATFORM_EXPORT __declspec(dllexport)
+#   endif
+#elif defined(__GNUC__) || defined(__clang__)
+#   define ANGLE_PLATFORM_EXPORT __attribute__((visibility ("default")))
+#endif
+#if !defined(ANGLE_PLATFORM_EXPORT)
+#   define ANGLE_PLATFORM_EXPORT
+#endif
+
+#if defined(_WIN32)
+#   define ANGLE_APIENTRY __stdcall
+#else
+#   define ANGLE_APIENTRY
+#endif
+
+namespace angle
+{
+struct WorkaroundsD3D;
+struct FeaturesVk;
+using TraceEventHandle = uint64_t;
+using EGLDisplayType   = void *;
+struct PlatformMethods;
+
+// Use a C-like API to not trigger undefined calling behaviour.
+// Avoid using decltype here to work around sanitizer limitations.
+// TODO(jmadill): Use decltype here if/when UBSAN is fixed.
+
+// System --------------------------------------------------------------
+
+// Wall clock time in seconds since the epoch.
+// TODO(jmadill): investigate using an ANGLE internal time library
+using CurrentTimeFunc = double (*)(PlatformMethods *platform);
+inline double DefaultCurrentTime(PlatformMethods *platform)
+{
+    return 0.0;
+}
+
+// Monotonically increasing time in seconds from an arbitrary fixed point in the past.
+// This function is expected to return at least millisecond-precision values. For this reason,
+// it is recommended that the fixed point be no further in the past than the epoch.
+using MonotonicallyIncreasingTimeFunc = double (*)(PlatformMethods *platform);
+inline double DefaultMonotonicallyIncreasingTime(PlatformMethods *platform)
+{
+    return 0.0;
+}
+
+// Logging ------------------------------------------------------------
+
+// Log an error message within the platform implementation.
+using LogErrorFunc = void (*)(PlatformMethods *platform, const char *errorMessage);
+inline void DefaultLogError(PlatformMethods *platform, const char *errorMessage)
+{
+}
+
+// Log a warning message within the platform implementation.
+using LogWarningFunc = void (*)(PlatformMethods *platform, const char *warningMessage);
+inline void DefaultLogWarning(PlatformMethods *platform, const char *warningMessage)
+{
+}
+
+// Log an info message within the platform implementation.
+using LogInfoFunc = void (*)(PlatformMethods *platform, const char *infoMessage);
+inline void DefaultLogInfo(PlatformMethods *platform, const char *infoMessage)
+{
+}
+
+// Tracing --------
+
+// Get a pointer to the enabled state of the given trace category. The
+// embedder can dynamically change the enabled state as trace event
+// recording is started and stopped by the application. Only long-lived
+// literal strings should be given as the category name. The implementation
+// expects the returned pointer to be held permanently in a local static. If
+// the unsigned char is non-zero, tracing is enabled. If tracing is enabled,
+// addTraceEvent is expected to be called by the trace event macros.
+using GetTraceCategoryEnabledFlagFunc = const unsigned char *(*)(PlatformMethods *platform,
+                                                                 const char *categoryName);
+inline const unsigned char *DefaultGetTraceCategoryEnabledFlag(PlatformMethods *platform,
+                                                               const char *categoryName)
+{
+    return nullptr;
+}
+
+//
+// Add a trace event to the platform tracing system. Depending on the actual
+// enabled state, this event may be recorded or dropped.
+// - phase specifies the type of event:
+//   - BEGIN ('B'): Marks the beginning of a scoped event.
+//   - END ('E'): Marks the end of a scoped event.
+//   - COMPLETE ('X'): Marks the beginning of a scoped event, but doesn't
+//     need a matching END event. Instead, at the end of the scope,
+//     updateTraceEventDuration() must be called with the TraceEventHandle
+//     returned from addTraceEvent().
+//   - INSTANT ('I'): Standalone, instantaneous event.
+//   - START ('S'): Marks the beginning of an asynchronous event (the end
+//     event can occur in a different scope or thread). The id parameter is
+//     used to match START/FINISH pairs.
+//   - FINISH ('F'): Marks the end of an asynchronous event.
+//   - COUNTER ('C'): Used to trace integer quantities that change over
+//     time. The argument values are expected to be of type int.
+//   - METADATA ('M'): Reserved for internal use.
+// - categoryEnabled is the pointer returned by getTraceCategoryEnabledFlag.
+// - name is the name of the event. Also used to match BEGIN/END and
+//   START/FINISH pairs.
+// - id optionally allows events of the same name to be distinguished from
+//   each other. For example, to trace the construction and destruction of
+//   objects, specify the pointer as the id parameter.
+// - timestamp should be a time value returned from monotonicallyIncreasingTime.
+// - numArgs specifies the number of elements in argNames, argTypes, and
+//   argValues.
+// - argNames is the array of argument names. Use long-lived literal strings
+//   or specify the COPY flag.
+// - argTypes is the array of argument types:
+//   - BOOL (1): bool
+//   - UINT (2): unsigned long long
+//   - INT (3): long long
+//   - DOUBLE (4): double
+//   - POINTER (5): void*
+//   - STRING (6): char* (long-lived null-terminated char* string)
+//   - COPY_STRING (7): char* (temporary null-terminated char* string)
+//   - CONVERTABLE (8): WebConvertableToTraceFormat
+// - argValues is the array of argument values. Each value is the unsigned
+//   long long member of a union of all supported types.
+// - flags can be 0 or one or more of the following, ORed together:
+//   - COPY (0x1): treat all strings (name, argNames and argValues of type
+//     string) as temporary so that they will be copied by addTraceEvent.
+//   - HAS_ID (0x2): use the id argument to uniquely identify the event for
+//     matching with other events of the same name.
+//   - MANGLE_ID (0x4): specify this flag if the id parameter is the value
+//     of a pointer.
+using AddTraceEventFunc = angle::TraceEventHandle (*)(PlatformMethods *platform,
+                                                      char phase,
+                                                      const unsigned char *categoryEnabledFlag,
+                                                      const char *name,
+                                                      unsigned long long id,
+                                                      double timestamp,
+                                                      int numArgs,
+                                                      const char **argNames,
+                                                      const unsigned char *argTypes,
+                                                      const unsigned long long *argValues,
+                                                      unsigned char flags);
+inline angle::TraceEventHandle DefaultAddTraceEvent(PlatformMethods *platform,
+                                                    char phase,
+                                                    const unsigned char *categoryEnabledFlag,
+                                                    const char *name,
+                                                    unsigned long long id,
+                                                    double timestamp,
+                                                    int numArgs,
+                                                    const char **argNames,
+                                                    const unsigned char *argTypes,
+                                                    const unsigned long long *argValues,
+                                                    unsigned char flags)
+{
+    return 0;
+}
+
+// Set the duration field of a COMPLETE trace event.
+using UpdateTraceEventDurationFunc = void (*)(PlatformMethods *platform,
+                                              const unsigned char *categoryEnabledFlag,
+                                              const char *name,
+                                              angle::TraceEventHandle eventHandle);
+inline void DefaultUpdateTraceEventDuration(PlatformMethods *platform,
+                                            const unsigned char *categoryEnabledFlag,
+                                            const char *name,
+                                            angle::TraceEventHandle eventHandle)
+{
+}
+
+// Callbacks for reporting histogram data.
+// CustomCounts histogram has exponential bucket sizes, so that min=1, max=1000000, bucketCount=50
+// would do.
+using HistogramCustomCountsFunc = void (*)(PlatformMethods *platform,
+                                           const char *name,
+                                           int sample,
+                                           int min,
+                                           int max,
+                                           int bucketCount);
+inline void DefaultHistogramCustomCounts(PlatformMethods *platform,
+                                         const char *name,
+                                         int sample,
+                                         int min,
+                                         int max,
+                                         int bucketCount)
+{
+}
+// Enumeration histogram buckets are linear, boundaryValue should be larger than any possible sample
+// value.
+using HistogramEnumerationFunc = void (*)(PlatformMethods *platform,
+                                          const char *name,
+                                          int sample,
+                                          int boundaryValue);
+inline void DefaultHistogramEnumeration(PlatformMethods *platform,
+                                        const char *name,
+                                        int sample,
+                                        int boundaryValue)
+{
+}
+// Unlike enumeration histograms, sparse histograms only allocate memory for non-empty buckets.
+using HistogramSparseFunc = void (*)(PlatformMethods *platform, const char *name, int sample);
+inline void DefaultHistogramSparse(PlatformMethods *platform, const char *name, int sample)
+{
+}
+// Boolean histograms track two-state variables.
+using HistogramBooleanFunc = void (*)(PlatformMethods *platform, const char *name, bool sample);
+inline void DefaultHistogramBoolean(PlatformMethods *platform, const char *name, bool sample)
+{
+}
+
+// Allows us to programatically override ANGLE's default workarounds for testing purposes.
+using OverrideWorkaroundsD3DFunc = void (*)(PlatformMethods *platform,
+                                            angle::WorkaroundsD3D *workaroundsD3D);
+inline void DefaultOverrideWorkaroundsD3D(PlatformMethods *platform,
+                                          angle::WorkaroundsD3D *workaroundsD3D)
+{
+}
+
+using OverrideFeaturesVkFunc = void (*)(PlatformMethods *platform,
+                                        angle::FeaturesVk *workaroundsVulkan);
+inline void DefaultOverrideFeaturesVk(PlatformMethods *platform,
+                                      angle::FeaturesVk *workaroundsVulkan)
+{
+}
+
+// Callback on a successful program link with the program binary. Can be used to store
+// shaders to disk. Keys are a 160-bit SHA-1 hash.
+using ProgramKeyType   = std::array<uint8_t, 20>;
+using CacheProgramFunc = void (*)(PlatformMethods *platform,
+                                  const ProgramKeyType &key,
+                                  size_t programSize,
+                                  const uint8_t *programBytes);
+inline void DefaultCacheProgram(PlatformMethods *platform,
+                                const ProgramKeyType &key,
+                                size_t programSize,
+                                const uint8_t *programBytes)
+{
+}
+
+// Platform methods are enumerated here once.
+#define ANGLE_PLATFORM_OP(OP)                                    \
+    OP(currentTime, CurrentTime)                                 \
+    OP(monotonicallyIncreasingTime, MonotonicallyIncreasingTime) \
+    OP(logError, LogError)                                       \
+    OP(logWarning, LogWarning)                                   \
+    OP(logInfo, LogInfo)                                         \
+    OP(getTraceCategoryEnabledFlag, GetTraceCategoryEnabledFlag) \
+    OP(addTraceEvent, AddTraceEvent)                             \
+    OP(updateTraceEventDuration, UpdateTraceEventDuration)       \
+    OP(histogramCustomCounts, HistogramCustomCounts)             \
+    OP(histogramEnumeration, HistogramEnumeration)               \
+    OP(histogramSparse, HistogramSparse)                         \
+    OP(histogramBoolean, HistogramBoolean)                       \
+    OP(overrideWorkaroundsD3D, OverrideWorkaroundsD3D)           \
+    OP(overrideFeaturesVk, OverrideFeaturesVk)                   \
+    OP(cacheProgram, CacheProgram)
+
+#define ANGLE_PLATFORM_METHOD_DEF(Name, CapsName) CapsName##Func Name = Default##CapsName;
+
+struct ANGLE_PLATFORM_EXPORT PlatformMethods
+{
+    PlatformMethods() {}
+
+    // User data pointer for any implementation specific members. Put it at the start of the
+    // platform structure so it doesn't become overwritten if one version of the platform
+    // adds or removes new members.
+    void *context = 0;
+
+    ANGLE_PLATFORM_OP(ANGLE_PLATFORM_METHOD_DEF);
+};
+
+#undef ANGLE_PLATFORM_METHOD_DEF
+
+// Subtract one to account for the context pointer.
+constexpr unsigned int g_NumPlatformMethods = (sizeof(PlatformMethods) / sizeof(uintptr_t)) - 1;
+
+#define ANGLE_PLATFORM_METHOD_STRING(Name) #Name
+#define ANGLE_PLATFORM_METHOD_STRING2(Name, CapsName) ANGLE_PLATFORM_METHOD_STRING(Name),
+
+constexpr const char *const g_PlatformMethodNames[g_NumPlatformMethods] = {
+    ANGLE_PLATFORM_OP(ANGLE_PLATFORM_METHOD_STRING2)};
+
+#undef ANGLE_PLATFORM_METHOD_STRING2
+#undef ANGLE_PLATFORM_METHOD_STRING
+
+}  // namespace angle
+
+extern "C" {
+
+// Gets the platform methods on the passed-in EGL display. If the method name signature does not
+// match the compiled signature for this ANGLE, false is returned. On success true is returned.
+// The application should set any platform methods it cares about on the returned pointer.
+// If display is not valid, behaviour is undefined.
+
+ANGLE_PLATFORM_EXPORT bool ANGLE_APIENTRY ANGLEGetDisplayPlatform(angle::EGLDisplayType display,
+                                                                  const char *const methodNames[],
+                                                                  unsigned int methodNameCount,
+                                                                  void *context,
+                                                                  void *platformMethodsOut);
+
+// Sets the platform methods back to their defaults.
+// If display is not valid, behaviour is undefined.
+ANGLE_PLATFORM_EXPORT void ANGLE_APIENTRY ANGLEResetDisplayPlatform(angle::EGLDisplayType display);
+
+}  // extern "C"
+
+namespace angle
+{
+typedef bool(ANGLE_APIENTRY *GetDisplayPlatformFunc)(angle::EGLDisplayType,
+                                                     const char *const *,
+                                                     unsigned int,
+                                                     void *,
+                                                     void *);
+typedef void(ANGLE_APIENTRY *ResetDisplayPlatformFunc)(angle::EGLDisplayType);
+}  // namespace angle
+
+// This function is not exported
+angle::PlatformMethods *ANGLEPlatformCurrent();
+
+#endif // ANGLE_PLATFORM_H
diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h
index 93a3965..c9e8b7c 100644
--- a/opengl/include/EGL/egl.h
+++ b/opengl/include/EGL/egl.h
@@ -33,12 +33,12 @@
 ** used to make the header, and the header can be found at
 **   http://www.khronos.org/registry/egl
 **
-** Khronos $Git commit SHA1: a732b061e7 $ on $Git commit date: 2017-06-17 23:27:53 +0100 $
+** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $
 */
 
 #include <EGL/eglplatform.h>
 
-/* Generated on date 20170627 */
+/* Generated on date 20180517 */
 
 /* Generated C header for:
  * API: egl
@@ -235,10 +235,66 @@
 EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext (void);
 #endif /* EGL_VERSION_1_4 */
 
-/* This version of Android does not yet support EGL 1.5, but the following
- * portion of EGL 1.5 is included in order to support portions of "eglext.h".
- */
+#ifndef EGL_VERSION_1_5
+#define EGL_VERSION_1_5 1
+typedef void *EGLSync;
 typedef intptr_t EGLAttrib;
+typedef khronos_utime_nanoseconds_t EGLTime;
+typedef void *EGLImage;
+#define EGL_CONTEXT_MAJOR_VERSION         0x3098
+#define EGL_CONTEXT_MINOR_VERSION         0x30FB
+#define EGL_CONTEXT_OPENGL_PROFILE_MASK   0x30FD
+#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY 0x31BD
+#define EGL_NO_RESET_NOTIFICATION         0x31BE
+#define EGL_LOSE_CONTEXT_ON_RESET         0x31BF
+#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001
+#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT 0x00000002
+#define EGL_CONTEXT_OPENGL_DEBUG          0x31B0
+#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE 0x31B1
+#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS  0x31B2
+#define EGL_OPENGL_ES3_BIT                0x00000040
+#define EGL_CL_EVENT_HANDLE               0x309C
+#define EGL_SYNC_CL_EVENT                 0x30FE
+#define EGL_SYNC_CL_EVENT_COMPLETE        0x30FF
+#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE  0x30F0
+#define EGL_SYNC_TYPE                     0x30F7
+#define EGL_SYNC_STATUS                   0x30F1
+#define EGL_SYNC_CONDITION                0x30F8
+#define EGL_SIGNALED                      0x30F2
+#define EGL_UNSIGNALED                    0x30F3
+#define EGL_SYNC_FLUSH_COMMANDS_BIT       0x0001
+#define EGL_FOREVER                       0xFFFFFFFFFFFFFFFFull
+#define EGL_TIMEOUT_EXPIRED               0x30F5
+#define EGL_CONDITION_SATISFIED           0x30F6
+#define EGL_NO_SYNC                       EGL_CAST(EGLSync,0)
+#define EGL_SYNC_FENCE                    0x30F9
+#define EGL_GL_COLORSPACE                 0x309D
+#define EGL_GL_COLORSPACE_SRGB            0x3089
+#define EGL_GL_COLORSPACE_LINEAR          0x308A
+#define EGL_GL_RENDERBUFFER               0x30B9
+#define EGL_GL_TEXTURE_2D                 0x30B1
+#define EGL_GL_TEXTURE_LEVEL              0x30BC
+#define EGL_GL_TEXTURE_3D                 0x30B2
+#define EGL_GL_TEXTURE_ZOFFSET            0x30BD
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x30B3
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x30B4
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x30B5
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x30B6
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x30B7
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8
+#define EGL_IMAGE_PRESERVED               0x30D2
+#define EGL_NO_IMAGE                      EGL_CAST(EGLImage,0)
+EGLAPI EGLSync EGLAPIENTRY eglCreateSync (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroySync (EGLDisplay dpy, EGLSync sync);
+EGLAPI EGLint EGLAPIENTRY eglClientWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
+EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttrib (EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value);
+EGLAPI EGLImage EGLAPIENTRY eglCreateImage (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImage (EGLDisplay dpy, EGLImage image);
+EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplay (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurface (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags);
+#endif /* EGL_VERSION_1_5 */
 
 #ifdef __cplusplus
 }
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 44f4dbc..501bf58 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -28,17 +28,17 @@
 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */
 /*
-** This header is generated from the Khronos OpenGL / OpenGL ES XML
-** API Registry. The current version of the Registry, generator scripts
+** This header is generated from the Khronos EGL XML API Registry.
+** The current version of the Registry, generator scripts
 ** used to make the header, and the header can be found at
 **   http://www.khronos.org/registry/egl
 **
-** Khronos $Git commit SHA1: feaaeb19e1 $ on $Git commit date: 2018-02-26 20:49:02 -0800 $
+** Khronos $Git commit SHA1: 726475c203 $ on $Git commit date: 2018-10-03 23:51:49 -0700 $
 */
 
 #include <EGL/eglplatform.h>
 
-#define EGL_EGLEXT_VERSION 20180228
+#define EGL_EGLEXT_VERSION 20181204
 
 /* Generated C header for:
  * API: egl
@@ -618,6 +618,16 @@
 #define EGL_EXT_client_extensions 1
 #endif /* EGL_EXT_client_extensions */
 
+#ifndef EGL_EXT_client_sync
+#define EGL_EXT_client_sync 1
+#define EGL_SYNC_CLIENT_EXT               0x3364
+#define EGL_SYNC_CLIENT_SIGNAL_EXT        0x3365
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLCLIENTSIGNALSYNCEXTPROC) (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglClientSignalSyncEXT (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#endif
+#endif /* EGL_EXT_client_sync */
+
 #ifndef EGL_EXT_compositor
 #define EGL_EXT_compositor 1
 #define EGL_PRIMARY_COMPOSITOR_CONTEXT_EXT 0x3460
@@ -671,6 +681,7 @@
 #ifndef EGL_EXT_device_drm
 #define EGL_EXT_device_drm 1
 #define EGL_DRM_DEVICE_FILE_EXT           0x3233
+#define EGL_DRM_MASTER_FD_EXT             0x333C
 #endif /* EGL_EXT_device_drm */
 
 #ifndef EGL_EXT_device_enumeration
@@ -706,6 +717,11 @@
 #define EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT 0x3362
 #endif /* EGL_EXT_gl_colorspace_display_p3_linear */
 
+#ifndef EGL_EXT_gl_colorspace_display_p3_passthrough
+#define EGL_EXT_gl_colorspace_display_p3_passthrough 1
+#define EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT 0x3490
+#endif /* EGL_EXT_gl_colorspace_display_p3_passthrough */
+
 #ifndef EGL_EXT_gl_colorspace_scrgb
 #define EGL_EXT_gl_colorspace_scrgb 1
 #define EGL_GL_COLORSPACE_SCRGB_EXT       0x3351
@@ -903,6 +919,14 @@
 #endif
 #endif /* EGL_EXT_swap_buffers_with_damage */
 
+#ifndef EGL_EXT_sync_reuse
+#define EGL_EXT_sync_reuse 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNSIGNALSYNCEXTPROC) (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglUnsignalSyncEXT (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#endif
+#endif /* EGL_EXT_sync_reuse */
+
 #ifndef EGL_EXT_yuv_surface
 #define EGL_EXT_yuv_surface 1
 #define EGL_YUV_ORDER_EXT                 0x3301
@@ -1147,6 +1171,14 @@
 #define EGL_STREAM_FIFO_SYNCHRONOUS_NV    0x3336
 #endif /* EGL_NV_stream_fifo_synchronous */
 
+#ifndef EGL_NV_stream_flush
+#define EGL_NV_stream_flush 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMFLUSHNVPROC) (EGLDisplay dpy, EGLStreamKHR stream);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamFlushNV (EGLDisplay dpy, EGLStreamKHR stream);
+#endif
+#endif /* EGL_NV_stream_flush */
+
 #ifndef EGL_NV_stream_frame_limits
 #define EGL_NV_stream_frame_limits 1
 #define EGL_PRODUCER_MAX_FRAME_HINT_NV    0x3337
@@ -1285,17 +1317,6 @@
 #define EGL_NATIVE_SURFACE_TIZEN          0x32A1
 #endif /* EGL_TIZEN_image_native_surface */
 
-/* This is a private Android extension that does not exist in the EGL registry,
- * formerly used to work around a hardware issue on Nexus 4. It is deprecated
- * and unimplemented. It has been added to this header manually. */
-#ifndef EGL_ANDROID_image_crop
-#define EGL_ANDROID_image_crop 1
-#define EGL_IMAGE_CROP_LEFT_ANDROID       0x3148
-#define EGL_IMAGE_CROP_TOP_ANDROID        0x3149
-#define EGL_IMAGE_CROP_RIGHT_ANDROID      0x314A
-#define EGL_IMAGE_CROP_BOTTOM_ANDROID     0x314B
-#endif
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h
new file mode 100644
index 0000000..0556ea1
--- /dev/null
+++ b/opengl/include/EGL/eglext_angle.h
@@ -0,0 +1,191 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// eglext_angle.h: ANGLE modifications to the eglext.h header file.
+//   Currently we don't include this file directly, we patch eglext.h
+//   to include it implicitly so it is visible throughout our code.
+
+#ifndef INCLUDE_EGL_EGLEXT_ANGLE_
+#define INCLUDE_EGL_EGLEXT_ANGLE_
+
+// clang-format off
+
+#ifndef EGL_ANGLE_robust_resource_initialization
+#define EGL_ANGLE_robust_resource_initialization 1
+#define EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE 0x3453
+#endif /* EGL_ANGLE_robust_resource_initialization */
+
+#ifndef EGL_ANGLE_keyed_mutex
+#define EGL_ANGLE_keyed_mutex 1
+#define EGL_DXGI_KEYED_MUTEX_ANGLE        0x33A2
+#endif /* EGL_ANGLE_keyed_mutex */
+
+#ifndef EGL_ANGLE_d3d_texture_client_buffer
+#define EGL_ANGLE_d3d_texture_client_buffer 1
+#define EGL_D3D_TEXTURE_ANGLE             0x33A3
+#endif /* EGL_ANGLE_d3d_texture_client_buffer */
+
+#ifndef EGL_ANGLE_software_display
+#define EGL_ANGLE_software_display 1
+#define EGL_SOFTWARE_DISPLAY_ANGLE ((EGLNativeDisplayType)-1)
+#endif /* EGL_ANGLE_software_display */
+
+#ifndef EGL_ANGLE_direct3d_display
+#define EGL_ANGLE_direct3d_display 1
+#define EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ((EGLNativeDisplayType)-2)
+#define EGL_D3D11_ONLY_DISPLAY_ANGLE ((EGLNativeDisplayType)-3)
+#endif /* EGL_ANGLE_direct3d_display */
+
+#ifndef EGL_ANGLE_direct_composition
+#define EGL_ANGLE_direct_composition 1
+#define EGL_DIRECT_COMPOSITION_ANGLE 0x33A5
+#endif /* EGL_ANGLE_direct_composition */
+
+#ifndef EGL_ANGLE_platform_angle
+#define EGL_ANGLE_platform_angle 1
+#define EGL_PLATFORM_ANGLE_ANGLE          0x3202
+#define EGL_PLATFORM_ANGLE_TYPE_ANGLE     0x3203
+#define EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE 0x3204
+#define EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE 0x3205
+#define EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE 0x3206
+#define EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE 0x3451
+#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE 0x3209
+#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE 0x320A
+#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE 0x345E
+#endif /* EGL_ANGLE_platform_angle */
+
+#ifndef EGL_ANGLE_platform_angle_d3d
+#define EGL_ANGLE_platform_angle_d3d 1
+#define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207
+#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208
+#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE 0x320B
+#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE 0x320C
+#define EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE 0x320F
+#endif /* EGL_ANGLE_platform_angle_d3d */
+
+#ifndef EGL_ANGLE_platform_angle_opengl
+#define EGL_ANGLE_platform_angle_opengl 1
+#define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320D
+#define EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE 0x320E
+#define EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE 0x3480
+#endif /* EGL_ANGLE_platform_angle_opengl */
+
+#ifndef EGL_ANGLE_platform_angle_null
+#define EGL_ANGLE_platform_angle_null 1
+#define EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE 0x33AE
+#endif /* EGL_ANGLE_platform_angle_null */
+
+#ifndef EGL_ANGLE_platform_angle_vulkan
+#define EGL_ANGLE_platform_angle_vulkan 1
+#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450
+#endif /* EGL_ANGLE_platform_angle_vulkan */
+
+#ifndef EGL_ANGLE_platform_angle_context_virtualization
+#define EGL_ANGLE_platform_angle_context_virtualization 1
+#define EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE 0x3481
+#endif /* EGL_ANGLE_platform_angle_context_virtualization */
+
+#ifndef EGL_ANGLE_x11_visual
+#define EGL_ANGLE_x11_visual
+#define EGL_X11_VISUAL_ID_ANGLE 0x33A3
+#endif /* EGL_ANGLE_x11_visual */
+
+#ifndef EGL_ANGLE_flexible_surface_compatibility
+#define EGL_ANGLE_flexible_surface_compatibility 1
+#define EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE 0x33A6
+#endif /* EGL_ANGLE_flexible_surface_compatibility */
+
+#ifndef EGL_ANGLE_surface_orientation
+#define EGL_ANGLE_surface_orientation
+#define EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7
+#define EGL_SURFACE_ORIENTATION_ANGLE 0x33A8
+#define EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE 0x0001
+#define EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002
+#endif /* EGL_ANGLE_surface_orientation */
+
+#ifndef EGL_ANGLE_experimental_present_path
+#define EGL_ANGLE_experimental_present_path
+#define EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE 0x33A4
+#define EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE 0x33A9
+#define EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE 0x33AA
+#endif /* EGL_ANGLE_experimental_present_path */
+
+#ifndef EGL_ANGLE_stream_producer_d3d_texture
+#define EGL_ANGLE_stream_producer_d3d_texture
+#define EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE 0x33AB
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLCREATESTREAMPRODUCERD3DTEXTUREANGLEPROC)(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list);
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLSTREAMPOSTD3DTEXTUREANGLEPROC)(EGLDisplay dpy, EGLStreamKHR stream, void *texture, const EGLAttrib *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglCreateStreamProducerD3DTextureANGLE(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamPostD3DTextureANGLE(EGLDisplay dpy, EGLStreamKHR stream, void *texture, const EGLAttrib *attrib_list);
+#endif
+#endif /* EGL_ANGLE_stream_producer_d3d_texture */
+
+#ifndef EGL_ANGLE_create_context_webgl_compatibility
+#define EGL_ANGLE_create_context_webgl_compatibility 1
+#define EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE 0x33AC
+#endif /* EGL_ANGLE_create_context_webgl_compatibility */
+
+#ifndef EGL_ANGLE_display_texture_share_group
+#define EGL_ANGLE_display_texture_share_group 1
+#define EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE 0x33AF
+#endif /* EGL_ANGLE_display_texture_share_group */
+
+#ifndef EGL_CHROMIUM_create_context_bind_generates_resource
+#define EGL_CHROMIUM_create_context_bind_generates_resource 1
+#define EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM 0x33AD
+#endif /* EGL_CHROMIUM_create_context_bind_generates_resource */
+
+#ifndef EGL_ANGLE_create_context_client_arrays
+#define EGL_ANGLE_create_context_client_arrays 1
+#define EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE 0x3452
+#endif /* EGL_ANGLE_create_context_client_arrays */
+
+#ifndef EGL_ANGLE_device_creation
+#define EGL_ANGLE_device_creation 1
+typedef EGLDeviceEXT(EGLAPIENTRYP PFNEGLCREATEDEVICEANGLEPROC) (EGLint device_type, void *native_device, const EGLAttrib *attrib_list);
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLRELEASEDEVICEANGLEPROC) (EGLDeviceEXT device);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLDeviceEXT EGLAPIENTRY eglCreateDeviceANGLE(EGLint device_type, void *native_device, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglReleaseDeviceANGLE(EGLDeviceEXT device);
+#endif
+#endif /* EGL_ANGLE_device_creation */
+
+#ifndef EGL_ANGLE_program_cache_control
+#define EGL_ANGLE_program_cache_control 1
+#define EGL_PROGRAM_CACHE_SIZE_ANGLE 0x3455
+#define EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE 0x3456
+#define EGL_PROGRAM_CACHE_RESIZE_ANGLE 0x3457
+#define EGL_PROGRAM_CACHE_TRIM_ANGLE 0x3458
+#define EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE 0x3459
+typedef EGLint (EGLAPIENTRYP PFNEGLPROGRAMCACHEGETATTRIBANGLEPROC) (EGLDisplay dpy, EGLenum attrib);
+typedef void (EGLAPIENTRYP PFNEGLPROGRAMCACHEQUERYANGLEPROC) (EGLDisplay dpy, EGLint index, void *key, EGLint *keysize, void *binary, EGLint *binarysize);
+typedef void (EGLAPIENTRYP PFNEGPROGRAMCACHELPOPULATEANGLEPROC) (EGLDisplay dpy, const void *key, EGLint keysize, const void *binary, EGLint binarysize);
+typedef EGLint (EGLAPIENTRYP PFNEGLPROGRAMCACHERESIZEANGLEPROC) (EGLDisplay dpy, EGLint limit, EGLenum mode);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLint EGLAPIENTRY eglProgramCacheGetAttribANGLE(EGLDisplay dpy, EGLenum attrib);
+EGLAPI void EGLAPIENTRY eglProgramCacheQueryANGLE(EGLDisplay dpy, EGLint index, void *key, EGLint *keysize, void *binary, EGLint *binarysize);
+EGLAPI void EGLAPIENTRY eglProgramCachePopulateANGLE(EGLDisplay dpy, const void *key, EGLint keysize, const void *binary, EGLint binarysize);
+EGLAPI EGLint EGLAPIENTRY eglProgramCacheResizeANGLE(EGLDisplay dpy, EGLint limit, EGLenum mode);
+#endif
+#endif /* EGL_ANGLE_program_cache_control */
+
+#ifndef EGL_ANGLE_iosurface_client_buffer
+#define EGL_ANGLE_iosurface_client_buffer 1
+#define EGL_IOSURFACE_ANGLE 0x3454
+#define EGL_IOSURFACE_PLANE_ANGLE 0x345A
+#define EGL_TEXTURE_RECTANGLE_ANGLE 0x345B
+#define EGL_TEXTURE_TYPE_ANGLE 0x345C
+#define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D
+#endif /* EGL_ANGLE_iosurface_client_buffer */
+
+#ifndef EGL_ANGLE_create_context_extensions_enabled
+#define EGL_ANGLE_create_context_extensions_enabled 1
+#define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F
+#endif /* EGL_ANGLE_create_context_extensions_enabled */
+
+// clang-format on
+
+#endif // INCLUDE_EGL_EGLEXT_ANGLE_
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index c77c333..0bc2cb9 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -77,12 +77,24 @@
 typedef HBITMAP EGLNativePixmapType;
 typedef HWND    EGLNativeWindowType;
 
-#elif defined(__APPLE__) || defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian */
+#elif defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian */
 
 typedef int   EGLNativeDisplayType;
 typedef void *EGLNativeWindowType;
 typedef void *EGLNativePixmapType;
 
+#elif defined(WL_EGL_PLATFORM)
+
+typedef struct wl_display     *EGLNativeDisplayType;
+typedef struct wl_egl_pixmap  *EGLNativePixmapType;
+typedef struct wl_egl_window  *EGLNativeWindowType;
+
+#elif defined(__GBM__)
+
+typedef struct gbm_device  *EGLNativeDisplayType;
+typedef struct gbm_bo      *EGLNativePixmapType;
+typedef void               *EGLNativeWindowType;
+
 #elif defined(__ANDROID__) || defined(ANDROID)
 
 struct ANativeWindow;
@@ -92,7 +104,13 @@
 typedef struct egl_native_pixmap_t*     EGLNativePixmapType;
 typedef void*                           EGLNativeDisplayType;
 
-#elif defined(__unix__)
+#elif defined(USE_OZONE)
+
+typedef intptr_t EGLNativeDisplayType;
+typedef intptr_t EGLNativeWindowType;
+typedef intptr_t EGLNativePixmapType;
+
+#elif defined(__unix__) || defined(USE_X11)
 
 /* X11 (tentative)  */
 #include <X11/Xlib.h>
@@ -102,6 +120,20 @@
 typedef Pixmap   EGLNativePixmapType;
 typedef Window   EGLNativeWindowType;
 
+#elif defined(__APPLE__)
+
+typedef int   EGLNativeDisplayType;
+typedef void *EGLNativeWindowType;
+typedef void *EGLNativePixmapType;
+
+#elif defined(__HAIKU__)
+
+#include <kernel/image.h>
+
+typedef void              *EGLNativeDisplayType;
+typedef khronos_uintptr_t  EGLNativePixmapType;
+typedef khronos_uintptr_t  EGLNativeWindowType;
+
 #else
 #error "Platform not recognized"
 #endif
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index d43c164..c80b79a 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -140,16 +140,22 @@
         "EGL/egl_cache.cpp",
         "EGL/egl_display.cpp",
         "EGL/egl_object.cpp",
+        "EGL/egl_layers.cpp",
         "EGL/egl.cpp",
         "EGL/eglApi.cpp",
+        "EGL/egl_platform_entries.cpp",
         "EGL/Loader.cpp",
+        "EGL/egl_angle_platform.cpp",
     ],
     shared_libs: [
         "libvndksupport",
         "android.hardware.configstore@1.0",
         "android.hardware.configstore-utils",
+        "libbase",
         "libhidlbase",
         "libhidltransport",
+        "libnativebridge",
+        "libnativeloader",
         "libutils",
     ],
     static_libs: [
@@ -191,6 +197,7 @@
     defaults: ["gles_libs_defaults"],
     srcs: ["GLES_CM/gl.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv1\""],
+    version_script: "libGLESv1_CM.map.txt",
 }
 
 //##############################################################################
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index b3752f5..74c4d7d 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -79,7 +79,7 @@
     }
 
     std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
-    CacheEntry dummyEntry(dummyKey, NULL);
+    CacheEntry dummyEntry(dummyKey, nullptr);
 
     while (true) {
         auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry);
@@ -139,7 +139,7 @@
         return 0;
     }
     std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
-    CacheEntry dummyEntry(dummyKey, NULL);
+    CacheEntry dummyEntry(dummyKey, nullptr);
     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);
@@ -308,7 +308,7 @@
         mData(copyData ? malloc(size) : data),
         mSize(size),
         mOwnsData(copyData) {
-    if (data != NULL && copyData) {
+    if (data != nullptr && copyData) {
         memcpy(const_cast<void*>(mData), data, size);
     }
 }
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
index 1f5d535..e5c5e5b 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -97,6 +97,10 @@
     //
     int unflatten(void const* buffer, size_t size);
 
+    // clear flushes out all contents of the cache then the BlobCache, leaving
+    // it in an empty state.
+    void clear() { mCacheEntries.clear(); }
+
 protected:
     // 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
diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp
index edbaaf0..cf67cf4 100644
--- a/opengl/libs/EGL/BlobCache_test.cpp
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -97,7 +97,7 @@
 
 TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
     mBC->set("abcd", 4, "efgh", 4);
-    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, nullptr, 0));
 }
 
 TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
@@ -169,7 +169,7 @@
     }
 
     mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
-    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, nullptr, 0));
 }
 
 TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
@@ -219,7 +219,7 @@
     }
 
     mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
-    ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+    ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, nullptr, 0));
 }
 
 TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
@@ -237,7 +237,7 @@
     int numCached = 0;
     for (int i = 0; i < 256; i++) {
         uint8_t k = i;
-        if (mBC->get(&k, 1, NULL, 0) == 1) {
+        if (mBC->get(&k, 1, nullptr, 0) == 1) {
             numCached++;
         }
     }
@@ -260,7 +260,7 @@
     int numCached = 0;
     for (int i = 0; i < maxEntries+1; i++) {
         uint8_t k = i;
-        if (mBC->get(&k, 1, NULL, 0) == 1) {
+        if (mBC->get(&k, 1, nullptr, 0) == 1) {
             numCached++;
         }
     }
diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp
index 7923715..cc42ac7 100644
--- a/opengl/libs/EGL/FileBlobCache.cpp
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -77,7 +77,7 @@
             return;
         }
 
-        uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
+        uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(nullptr, fileSize,
                 PROT_READ, MAP_PRIVATE, fd, 0));
         if (buf == MAP_FAILED) {
             ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 91a3455..8a409ae 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -17,7 +17,7 @@
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include "Loader.h"
+#include <EGL/Loader.h>
 
 #include <string>
 
@@ -33,12 +33,10 @@
 #endif
 #include <vndksupport/linker.h>
 
+#include "egl_platform_entries.h"
 #include "egl_trace.h"
 #include "egldefs.h"
-
-extern "C" {
-  android_namespace_t* android_get_exported_namespace(const char*);
-}
+#include <EGL/eglext_angle.h>
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -129,7 +127,7 @@
 {
     dso[0] = gles;
     for (size_t i=1 ; i<NELEM(dso) ; i++)
-        dso[i] = 0;
+        dso[i] = nullptr;
 }
 
 Loader::driver_t::~driver_t()
@@ -137,7 +135,7 @@
     for (size_t i=0 ; i<NELEM(dso) ; i++) {
         if (dso[i]) {
             dlclose(dso[i]);
-            dso[i] = 0;
+            dso[i] = nullptr;
         }
     }
 }
@@ -163,7 +161,7 @@
 // ----------------------------------------------------------------------------
 
 Loader::Loader()
-    : getProcAddress(NULL)
+    : getProcAddress(nullptr)
 {
 }
 
@@ -221,10 +219,17 @@
     ATRACE_CALL();
 
     void* dso;
-    driver_t* hnd = 0;
+    driver_t* hnd = nullptr;
 
     setEmulatorGlesValue();
 
+    // Check if we should use ANGLE early, so loading each driver doesn't require repeated queries.
+    if (android::GraphicsEnv::getInstance().shouldUseAngle()) {
+        cnx->shouldUseAngle = true;
+    } else {
+        cnx->shouldUseAngle = false;
+    }
+
     dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2);
     if (dso) {
         hnd = new driver_t(dso);
@@ -253,14 +258,25 @@
     return (void*)hnd;
 }
 
-void Loader::close(void* driver)
+void Loader::close(egl_connection_t* cnx)
 {
-    driver_t* hnd = (driver_t*)driver;
+    driver_t* hnd = (driver_t*) cnx->dso;
     delete hnd;
+    cnx->dso = nullptr;
+
+    cnx->shouldUseAngle = false;
+    cnx->angleDecided = false;
+    cnx->useAngle = false;
+
+    if (cnx->vendorEGL) {
+        dlclose(cnx->vendorEGL);
+        cnx->vendorEGL = nullptr;
+    }
 }
 
 void Loader::init_api(void* dso,
         char const * const * api,
+        char const * const * ref_api,
         __eglMustCastToProperFunctionPointerType* curr,
         getProcAddressType getProcAddress)
 {
@@ -270,13 +286,22 @@
     char scrap[SIZE];
     while (*api) {
         char const * name = *api;
+        if (ref_api) {
+            char const * ref_name = *ref_api;
+            if (std::strcmp(name, ref_name) != 0) {
+                *curr++ = nullptr;
+                ref_api++;
+                continue;
+            }
+        }
+
         __eglMustCastToProperFunctionPointerType f =
             (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
-        if (f == NULL) {
+        if (f == nullptr) {
             // couldn't find the entry-point, use eglGetProcAddress()
             f = getProcAddress(name);
         }
-        if (f == NULL) {
+        if (f == nullptr) {
             // Try without the OES postfix
             ssize_t index = ssize_t(strlen(name)) - 3;
             if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) {
@@ -286,7 +311,7 @@
                 //ALOGD_IF(f, "found <%s> instead", scrap);
             }
         }
-        if (f == NULL) {
+        if (f == nullptr) {
             // Try with the OES postfix
             ssize_t index = ssize_t(strlen(name)) - 3;
             if (index>0 && strcmp(name+index, "OES")) {
@@ -295,7 +320,7 @@
                 //ALOGD_IF(f, "found <%s> instead", scrap);
             }
         }
-        if (f == NULL) {
+        if (f == nullptr) {
             //ALOGD("%s", name);
             f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;
 
@@ -314,6 +339,7 @@
         }
         *curr++ = f;
         api++;
+        if (ref_api) ref_api++;
     }
 }
 
@@ -406,9 +432,9 @@
             }
 
             DIR* d = opendir(search);
-            if (d != NULL) {
+            if (d != nullptr) {
                 struct dirent* e;
-                while ((e = readdir(d)) != NULL) {
+                while ((e = readdir(d)) != nullptr) {
                     if (e->d_type == DT_DIR) {
                         continue;
                     }
@@ -434,7 +460,7 @@
     std::string absolutePath = MatchFile::find(kind);
     if (absolutePath.empty()) {
         // this happens often, we don't want to log an error
-        return 0;
+        return nullptr;
     }
     const char* const driver_absolute_path = absolutePath.c_str();
 
@@ -444,10 +470,10 @@
     // sphal namespace.
     void* dso = do_android_load_sphal_library(driver_absolute_path,
                                               RTLD_NOW | RTLD_LOCAL);
-    if (dso == 0) {
+    if (dso == nullptr) {
         const char* err = dlerror();
         ALOGE("load_driver(%s): %s", driver_absolute_path, err ? err : "unknown");
-        return 0;
+        return nullptr;
     }
 
     ALOGD("loaded %s", driver_absolute_path);
@@ -455,6 +481,81 @@
     return dso;
 }
 
+static void* load_angle_from_namespace(const char* kind, android_namespace_t* ns) {
+    const android_dlextinfo dlextinfo = {
+            .flags = ANDROID_DLEXT_USE_NAMESPACE,
+            .library_namespace = ns,
+    };
+
+    std::string name = std::string("lib") + kind + "_angle.so";
+
+    void* so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+
+    if (so) {
+        ALOGD("dlopen_ext from APK (%s) success at %p", name.c_str(), so);
+        return so;
+    } else {
+        ALOGE("dlopen_ext(\"%s\") failed: %s", name.c_str(), dlerror());
+    }
+
+    return nullptr;
+}
+
+static void* load_angle(const char* kind, android_namespace_t* ns, egl_connection_t* cnx) {
+    // Only attempt to load ANGLE libs
+    if (strcmp(kind, "EGL") != 0 && strcmp(kind, "GLESv2") != 0 && strcmp(kind, "GLESv1_CM") != 0) {
+        return nullptr;
+    }
+
+    void* so = nullptr;
+
+    if ((cnx->shouldUseAngle) || android::GraphicsEnv::getInstance().shouldUseAngle()) {
+        so = load_angle_from_namespace(kind, ns);
+        cnx->shouldUseAngle = true;
+    } else {
+        cnx->shouldUseAngle = false;
+    }
+
+    if (so) {
+        ALOGV("Loaded ANGLE %s library for '%s' (instead of native)", kind,
+            android::GraphicsEnv::getInstance().getAngleAppName().c_str());
+        cnx->useAngle = true;
+
+        char prop[PROPERTY_VALUE_MAX];
+
+        property_get("debug.hwui.renderer", prop, "UNSET");
+        ALOGV("Skia's renderer set to %s", prop);
+
+        EGLint angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
+        property_get("debug.angle.backend", prop, "0");
+        switch (atoi(prop)) {
+            case 1:
+                ALOGV("%s: Requesting OpenGLES back-end", __FUNCTION__);
+                angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
+                break;
+            case 2:
+                ALOGV("%s: Requesting Vulkan back-end", __FUNCTION__);
+                angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
+                break;
+            default:
+                break;
+        }
+
+        cnx->angleBackend = angleBackendDefault;
+        if (!cnx->vendorEGL && (cnx->angleBackend == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)) {
+            // Find and load vendor libEGL for ANGLE's GL back-end to use.
+            cnx->vendorEGL = load_system_driver("EGL");
+        }
+    } else {
+        ALOGV("Loaded native %s library for '%s' (instead of ANGLE)", kind,
+            android::GraphicsEnv::getInstance().getAngleAppName().c_str());
+        cnx->useAngle = false;
+    }
+    cnx->angleDecided = true;
+
+    return so;
+}
+
 static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = {
     "ro.hardware.egl",
     "ro.board.platform",
@@ -486,16 +587,22 @@
     ATRACE_CALL();
 
     void* dso = nullptr;
-#ifndef __ANDROID_VNDK__
-    android_namespace_t* ns = android_getDriverNamespace();
+    android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
     if (ns) {
-        dso = load_updated_driver(kind, ns);
+        dso = load_angle(kind, ns, cnx);
+    }
+#ifndef __ANDROID_VNDK__
+    if (!dso) {
+        android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace();
+        if (ns) {
+            dso = load_updated_driver(kind, ns);
+        }
     }
 #endif
     if (!dso) {
         dso = load_system_driver(kind);
         if (!dso)
-            return NULL;
+            return nullptr;
     }
 
     if (mask & EGL) {
@@ -512,11 +619,11 @@
             char const * name = *api;
             __eglMustCastToProperFunctionPointerType f =
                 (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
-            if (f == NULL) {
+            if (f == nullptr) {
                 // couldn't find the entry-point, use eglGetProcAddress()
                 f = getProcAddress(name);
-                if (f == NULL) {
-                    f = (__eglMustCastToProperFunctionPointerType)0;
+                if (f == nullptr) {
+                    f = (__eglMustCastToProperFunctionPointerType)nullptr;
                 }
             }
             *curr++ = f;
@@ -525,14 +632,14 @@
     }
 
     if (mask & GLESv1_CM) {
-        init_api(dso, gl_names,
+        init_api(dso, gl_names_1, gl_names,
             (__eglMustCastToProperFunctionPointerType*)
                 &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl,
             getProcAddress);
     }
 
     if (mask & GLESv2) {
-      init_api(dso, gl_names,
+        init_api(dso, gl_names, nullptr,
             (__eglMustCastToProperFunctionPointerType*)
                 &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,
             getProcAddress);
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 6a32bb3..392887d 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -29,11 +29,12 @@
 
 class Loader {
     typedef __eglMustCastToProperFunctionPointerType (* getProcAddressType)(const char*);
-   
+
     enum {
         EGL         = 0x01,
         GLESv1_CM   = 0x02,
-        GLESv2      = 0x04
+        GLESv2      = 0x04,
+        PLATFORM    = 0x08
     };
     struct driver_t {
         explicit driver_t(void* gles);
@@ -42,25 +43,26 @@
         int set(void* hnd, int32_t api);
         void* dso[3];
     };
-    
+
     getProcAddressType getProcAddress;
 
 public:
     static Loader& getInstance();
     ~Loader();
-    
+
     void* open(egl_connection_t* cnx);
-    void close(void* driver);
-    
+    void close(egl_connection_t* cnx);
+
 private:
     Loader();
     void *load_driver(const char* kind, egl_connection_t* cnx, uint32_t mask);
 
     static __attribute__((noinline))
-    void init_api(void* dso, 
-            char const * const * api, 
-            __eglMustCastToProperFunctionPointerType* curr, 
-            getProcAddressType getProcAddress); 
+    void init_api(void* dso,
+            char const * const * api,
+            char const * const * ref_api,
+            __eglMustCastToProperFunctionPointerType* curr,
+            getProcAddressType getProcAddress);
 };
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index f53cf3f..8870d5f 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -30,11 +30,10 @@
 #include "egl_tls.h"
 #include "egl_display.h"
 #include "egl_object.h"
+#include "egl_layers.h"
 #include "CallStack.h"
 #include "Loader.h"
 
-typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
-
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
@@ -89,22 +88,22 @@
 egl_display_ptr validate_display(EGLDisplay dpy) {
     egl_display_ptr dp = get_display(dpy);
     if (!dp)
-        return setError(EGL_BAD_DISPLAY, egl_display_ptr(NULL));
+        return setError(EGL_BAD_DISPLAY, egl_display_ptr(nullptr));
     if (!dp->isReady())
-        return setError(EGL_NOT_INITIALIZED, egl_display_ptr(NULL));
+        return setError(EGL_NOT_INITIALIZED, egl_display_ptr(nullptr));
 
     return dp;
 }
 
 egl_display_ptr validate_display_connection(EGLDisplay dpy,
         egl_connection_t*& cnx) {
-    cnx = NULL;
+    cnx = nullptr;
     egl_display_ptr dp = validate_display(dpy);
     if (!dp)
         return dp;
     cnx = &gEGLImpl;
-    if (cnx->dso == 0) {
-        return setError(EGL_BAD_CONFIG, egl_display_ptr(NULL));
+    if (cnx->dso == nullptr) {
+        return setError(EGL_BAD_CONFIG, egl_display_ptr(nullptr));
     }
     return dp;
 }
@@ -117,14 +116,14 @@
 
     EGLContext context = egl_tls_t::getContext();
     if (context == EGL_NO_CONTEXT)
-        return NULL;
+        return nullptr;
 
     egl_context_t const * const c = get_context(context);
-    if (c == NULL) // this should never happen, by construction
-        return NULL;
+    if (c == nullptr) // this should never happen, by construction
+        return nullptr;
 
     if (name != GL_EXTENSIONS)
-        return NULL;
+        return nullptr;
 
     return (const GLubyte *)c->gl_extensions.c_str();
 }
@@ -135,19 +134,19 @@
 
     EGLContext context = egl_tls_t::getContext();
     if (context == EGL_NO_CONTEXT)
-        return NULL;
+        return nullptr;
 
     egl_context_t const * const c = get_context(context);
-    if (c == NULL) // this should never happen, by construction
-        return NULL;
+    if (c == nullptr) // this should never happen, by construction
+        return nullptr;
 
     if (name != GL_EXTENSIONS)
-        return NULL;
+        return nullptr;
 
     // if index is out of bounds, assume it will be in the default
     // implementation too, so we don't have to generate a GL error here
     if (index >= c->tokenized_gl_extensions.size())
-        return NULL;
+        return nullptr;
 
     return (const GLubyte *)c->tokenized_gl_extensions[index].c_str();
 }
@@ -161,12 +160,16 @@
         return -1;
 
     egl_context_t const * const c = get_context(context);
-    if (c == NULL) // this should never happen, by construction
+    if (c == nullptr) // this should never happen, by construction
         return -1;
 
     return (GLint)c->tokenized_gl_extensions.size();
 }
 
+egl_connection_t* egl_get_connection() {
+    return &gEGLImpl;
+}
+
 // ----------------------------------------------------------------------------
 
 // this mutex protects:
@@ -184,7 +187,7 @@
 
     // dynamically load our EGL implementation
     egl_connection_t* cnx = &gEGLImpl;
-    if (cnx->dso == 0) {
+    if (cnx->dso == nullptr) {
         cnx->hooks[egl_connection_t::GLESv1_INDEX] =
                 &gHooks[egl_connection_t::GLESv1_INDEX];
         cnx->hooks[egl_connection_t::GLESv2_INDEX] =
@@ -192,6 +195,14 @@
         cnx->dso = loader.open(cnx);
     }
 
+    // Check to see if any layers are enabled and route functions through them
+    if (cnx->dso) {
+        // Layers can be enabled long after the drivers have been loaded.
+        // They will only be initialized once.
+        LayerLoader& layer_loader(LayerLoader::getInstance());
+        layer_loader.InitLayers(cnx);
+    }
+
     return cnx->dso ? EGL_TRUE : EGL_FALSE;
 }
 
@@ -249,12 +260,22 @@
 
 char const * const gl_names[] = {
     #include "../entries.in"
-    NULL
+    nullptr
+};
+
+char const * const gl_names_1[] = {
+    #include "../entries_gles1.in"
+    nullptr
 };
 
 char const * const egl_names[] = {
     #include "egl_entries.in"
-    NULL
+    nullptr
+};
+
+char const * const platform_names[] = {
+    #include "platform_entries.in"
+    nullptr
 };
 
 #undef GL_ENTRY
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 36deedc..29a966d 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1,5 +1,5 @@
 /*
- ** Copyright 2007, The Android Open Source Project
+ ** Copyright 2018, The Android Open Source Project
  **
  ** Licensed under the Apache License, Version 2.0 (the "License");
  ** you may not use this file except in compliance with the License.
@@ -16,1170 +16,225 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <ctype.h>
-#include <dlfcn.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <hardware/gralloc1.h>
-
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <android/hardware_buffer.h>
-#include <private/android/AHardwareBufferHelpers.h>
-
-#include <cutils/compiler.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-
-#include <condition_variable>
-#include <deque>
-#include <mutex>
-#include <unordered_map>
-#include <string>
-#include <thread>
-
 #include "../egl_impl.h"
 
-#include "egl_display.h"
-#include "egl_object.h"
+#include "egl_layers.h"
+#include "egl_platform_entries.h"
 #include "egl_tls.h"
 #include "egl_trace.h"
 
 using namespace android;
 
-// ----------------------------------------------------------------------------
-
 namespace android {
 
-using nsecs_t = int64_t;
+extern EGLBoolean egl_init_drivers();
 
-struct extention_map_t {
-    const char* name;
-    __eglMustCastToProperFunctionPointerType address;
-};
+} // namespace android
 
-/*
- * This is the list of EGL extensions exposed to applications.
- *
- * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL
- * wrapper and are always available.
- *
- * The rest (gExtensionString) depend on support in the EGL driver, and are
- * only available if the driver supports them. However, some of these must be
- * supported because they are used by the Android system itself; these are
- * listed as mandatory below and are required by the CDD. The system *assumes*
- * the mandatory extensions are present and may not function properly if some
- * are missing.
- *
- * NOTE: Both strings MUST have a single space as the last character.
- */
-
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
-
-// clang-format off
-// Extensions implemented by the EGL wrapper.
-char const * const gBuiltinExtensionString =
-        "EGL_KHR_get_all_proc_addresses "
-        "EGL_ANDROID_presentation_time "
-        "EGL_KHR_swap_buffers_with_damage "
-        "EGL_ANDROID_get_native_client_buffer "
-        "EGL_ANDROID_front_buffer_auto_refresh "
-        "EGL_ANDROID_get_frame_timestamps "
-        "EGL_EXT_surface_SMPTE2086_metadata "
-        "EGL_EXT_surface_CTA861_3_metadata "
-        ;
-
-// Whitelist of extensions exposed to applications if implemented in the vendor driver.
-char const * const gExtensionString  =
-        "EGL_KHR_image "                        // mandatory
-        "EGL_KHR_image_base "                   // mandatory
-        "EGL_EXT_image_gl_colorspace "
-        "EGL_KHR_image_pixmap "
-        "EGL_KHR_lock_surface "
-        "EGL_KHR_gl_colorspace "
-        "EGL_KHR_gl_texture_2D_image "
-        "EGL_KHR_gl_texture_3D_image "
-        "EGL_KHR_gl_texture_cubemap_image "
-        "EGL_KHR_gl_renderbuffer_image "
-        "EGL_KHR_reusable_sync "
-        "EGL_KHR_fence_sync "
-        "EGL_KHR_create_context "
-        "EGL_KHR_config_attribs "
-        "EGL_KHR_surfaceless_context "
-        "EGL_KHR_stream "
-        "EGL_KHR_stream_fifo "
-        "EGL_KHR_stream_producer_eglsurface "
-        "EGL_KHR_stream_consumer_gltexture "
-        "EGL_KHR_stream_cross_process_fd "
-        "EGL_EXT_create_context_robustness "
-        "EGL_NV_system_time "
-        "EGL_ANDROID_image_native_buffer "      // mandatory
-        "EGL_KHR_wait_sync "                    // strongly recommended
-        "EGL_ANDROID_recordable "               // mandatory
-        "EGL_KHR_partial_update "               // strongly recommended
-        "EGL_EXT_pixel_format_float "
-        "EGL_EXT_buffer_age "                   // strongly recommended with partial_update
-        "EGL_KHR_create_context_no_error "
-        "EGL_KHR_mutable_render_buffer "
-        "EGL_EXT_yuv_surface "
-        "EGL_EXT_protected_content "
-        "EGL_IMG_context_priority "
-        "EGL_KHR_no_config_context "
-        ;
-// clang-format on
-
-// extensions not exposed to applications but used by the ANDROID system
-//      "EGL_ANDROID_blob_cache "               // strongly recommended
-//      "EGL_IMG_hibernate_process "            // optional
-//      "EGL_ANDROID_native_fence_sync "        // strongly recommended
-//      "EGL_ANDROID_framebuffer_target "       // mandatory for HWC 1.1
-//      "EGL_ANDROID_image_crop "               // optional
-
-/*
- * EGL Extensions entry-points exposed to 3rd party applications
- * (keep in sync with gExtensionString above)
- *
- */
-static const extention_map_t sExtensionMap[] = {
-    // EGL_KHR_lock_surface
-    { "eglLockSurfaceKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
-    { "eglUnlockSurfaceKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
-
-    // EGL_KHR_image, EGL_KHR_image_base
-    { "eglCreateImageKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
-    { "eglDestroyImageKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
-
-    // EGL_KHR_reusable_sync, EGL_KHR_fence_sync
-    { "eglCreateSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
-    { "eglDestroySyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
-    { "eglClientWaitSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
-    { "eglSignalSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
-    { "eglGetSyncAttribKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
-
-    // EGL_NV_system_time
-    { "eglGetSystemTimeFrequencyNV",
-            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
-    { "eglGetSystemTimeNV",
-            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
-
-    // EGL_KHR_wait_sync
-    { "eglWaitSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
-
-    // EGL_ANDROID_presentation_time
-    { "eglPresentationTimeANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
-
-    // EGL_KHR_swap_buffers_with_damage
-    { "eglSwapBuffersWithDamageKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
-
-    // EGL_ANDROID_get_native_client_buffer
-    { "eglGetNativeClientBufferANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
-
-    // EGL_KHR_partial_update
-    { "eglSetDamageRegionKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
-
-    { "eglCreateStreamKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
-    { "eglDestroyStreamKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
-    { "eglStreamAttribKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
-    { "eglQueryStreamKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
-    { "eglQueryStreamu64KHR",
-            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
-    { "eglQueryStreamTimeKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
-    { "eglCreateStreamProducerSurfaceKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
-    { "eglStreamConsumerGLTextureExternalKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
-    { "eglStreamConsumerAcquireKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
-    { "eglStreamConsumerReleaseKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
-    { "eglGetStreamFileDescriptorKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
-    { "eglCreateStreamFromFileDescriptorKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
-
-    // EGL_ANDROID_get_frame_timestamps
-    { "eglGetNextFrameIdANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
-    { "eglGetCompositorTimingANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
-    { "eglGetCompositorTimingSupportedANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
-    { "eglGetFrameTimestampsANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
-    { "eglGetFrameTimestampSupportedANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
-
-    // EGL_ANDROID_native_fence_sync
-    { "eglDupNativeFenceFDANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
-};
-
-/*
- * These extensions entry-points should not be exposed to applications.
- * They're used internally by the Android EGL layer.
- */
-#define FILTER_EXTENSIONS(procname) \
-        (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") ||    \
-         !strcmp((procname), "eglHibernateProcessIMG")      ||    \
-         !strcmp((procname), "eglAwakenProcessIMG"))
-
-// accesses protected by sExtensionMapMutex
-static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
-
-static int sGLExtentionSlot = 0;
-static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
-
-static void(*findProcAddress(const char* name,
-        const extention_map_t* map, size_t n))() {
-    for (uint32_t i=0 ; i<n ; i++) {
-        if (!strcmp(name, map[i].name)) {
-            return map[i].address;
-        }
-    }
-    return NULL;
+static inline void clearError() {
+    egl_tls_t::clearError();
 }
 
-// ----------------------------------------------------------------------------
-
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
-extern EGLBoolean egl_init_drivers();
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
-extern gl_hooks_t gHooksTrace;
-
-} // namespace android;
-
-
-// ----------------------------------------------------------------------------
-
-static inline void clearError() { egl_tls_t::clearError(); }
-static inline EGLContext getContext() { return egl_tls_t::getContext(); }
-
-// ----------------------------------------------------------------------------
-
-EGLDisplay eglGetDisplay(EGLNativeDisplayType display)
-{
+EGLDisplay eglGetDisplay(EGLNativeDisplayType display) {
     ATRACE_CALL();
     clearError();
 
-    uintptr_t index = reinterpret_cast<uintptr_t>(display);
-    if (index >= NUM_DISPLAYS) {
-        return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
-    }
-
     if (egl_init_drivers() == EGL_FALSE) {
         return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
     }
 
-    EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display);
-    return dpy;
+    // Call down the chain, which usually points directly to the impl
+    // but may also be routed through layers
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetDisplay(display);
 }
 
-// ----------------------------------------------------------------------------
-// Initialization
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
-{
+EGLDisplay eglGetPlatformDisplay(EGLenum platform, EGLNativeDisplayType display,
+                                 const EGLAttrib* attrib_list) {
+    ATRACE_CALL();
     clearError();
 
-    egl_display_ptr dp = get_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-
-    EGLBoolean res = dp->initialize(major, minor);
-
-    return res;
-}
-
-EGLBoolean eglTerminate(EGLDisplay dpy)
-{
-    // NOTE: don't unload the drivers b/c some APIs can be called
-    // after eglTerminate() has been called. eglTerminate() only
-    // terminates an EGLDisplay, not a EGL itself.
-
-    clearError();
-
-    egl_display_ptr dp = get_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-
-    EGLBoolean res = dp->terminate();
-
-    return res;
-}
-
-// ----------------------------------------------------------------------------
-// configuration
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglGetConfigs(   EGLDisplay dpy,
-                            EGLConfig *configs,
-                            EGLint config_size, EGLint *num_config)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    if (num_config==0) {
-        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+    if (egl_init_drivers() == EGL_FALSE) {
+        return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
     }
 
-    EGLBoolean res = EGL_FALSE;
-    *num_config = 0;
+    // Call down the chain, which usually points directly to the impl
+    // but may also be routed through layers
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetPlatformDisplay(platform, display, attrib_list);
+}
+
+EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) {
+    clearError();
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso) {
-        res = cnx->egl.eglGetConfigs(
-                dp->disp.dpy, configs, config_size, num_config);
-    }
-
-    return res;
+    return cnx->platform.eglInitialize(dpy, major, minor);
 }
 
-EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
-                            EGLConfig *configs, EGLint config_size,
-                            EGLint *num_config)
-{
+EGLBoolean eglTerminate(EGLDisplay dpy) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    if (num_config==0) {
-        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
-    }
-
-    EGLBoolean res = EGL_FALSE;
-    *num_config = 0;
-
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso) {
-        if (attrib_list) {
-            char value[PROPERTY_VALUE_MAX];
-            property_get("debug.egl.force_msaa", value, "false");
-
-            if (!strcmp(value, "true")) {
-                size_t attribCount = 0;
-                EGLint attrib = attrib_list[0];
-
-                // Only enable MSAA if the context is OpenGL ES 2.0 and
-                // if no caveat is requested
-                const EGLint *attribRendererable = NULL;
-                const EGLint *attribCaveat = NULL;
-
-                // Count the number of attributes and look for
-                // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
-                while (attrib != EGL_NONE) {
-                    attrib = attrib_list[attribCount];
-                    switch (attrib) {
-                        case EGL_RENDERABLE_TYPE:
-                            attribRendererable = &attrib_list[attribCount];
-                            break;
-                        case EGL_CONFIG_CAVEAT:
-                            attribCaveat = &attrib_list[attribCount];
-                            break;
-                        default:
-                            break;
-                    }
-                    attribCount++;
-                }
-
-                if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
-                        (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
-
-                    // Insert 2 extra attributes to force-enable MSAA 4x
-                    EGLint aaAttribs[attribCount + 4];
-                    aaAttribs[0] = EGL_SAMPLE_BUFFERS;
-                    aaAttribs[1] = 1;
-                    aaAttribs[2] = EGL_SAMPLES;
-                    aaAttribs[3] = 4;
-
-                    memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
-
-                    EGLint numConfigAA;
-                    EGLBoolean resAA = cnx->egl.eglChooseConfig(
-                            dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
-
-                    if (resAA == EGL_TRUE && numConfigAA > 0) {
-                        ALOGD("Enabling MSAA 4x");
-                        *num_config = numConfigAA;
-                        return resAA;
-                    }
-                }
-            }
-        }
-
-        res = cnx->egl.eglChooseConfig(
-                dp->disp.dpy, attrib_list, configs, config_size, num_config);
-    }
-    return res;
+    return cnx->platform.eglTerminate(dpy);
 }
 
-EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
-        EGLint attribute, EGLint *value)
-{
+EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size,
+                         EGLint* num_config) {
     clearError();
 
-    egl_connection_t* cnx = NULL;
-    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (!dp) return EGL_FALSE;
-
-    return cnx->egl.eglGetConfigAttrib(
-            dp->disp.dpy, config, attribute, value);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetConfigs(dpy, configs, config_size, num_config);
 }
 
-// ----------------------------------------------------------------------------
-// surfaces
-// ----------------------------------------------------------------------------
-
-// Translates EGL color spaces to Android data spaces.
-static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) {
-    if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
-        return HAL_DATASPACE_UNKNOWN;
-    } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
-        return HAL_DATASPACE_SRGB;
-    } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
-        return HAL_DATASPACE_DISPLAY_P3;
-    } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) {
-        return HAL_DATASPACE_DISPLAY_P3_LINEAR;
-    } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) {
-        return HAL_DATASPACE_V0_SCRGB;
-    } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
-        return HAL_DATASPACE_V0_SCRGB_LINEAR;
-    } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
-        return HAL_DATASPACE_BT2020_LINEAR;
-    } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
-        return HAL_DATASPACE_BT2020_PQ;
-    }
-    return HAL_DATASPACE_UNKNOWN;
-}
-
-// Get the colorspace value that should be reported from queries. When the colorspace
-// is unknown (no attribute passed), default to reporting LINEAR.
-static EGLint getReportedColorSpace(EGLint colorspace) {
-    return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace;
-}
-
-// Returns a list of color spaces understood by the vendor EGL driver.
-static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) {
-    std::vector<EGLint> colorSpaces;
-
-    // sRGB and linear are always supported when color space support is present.
-    colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
-    colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
-
-    if (findExtension(dp->disp.queryString.extensions,
-                      "EGL_EXT_gl_colorspace_display_p3")) {
-        colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
-    }
-    if (findExtension(dp->disp.queryString.extensions,
-                      "EGL_EXT_gl_colorspace_scrgb")) {
-        colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT);
-    }
-    if (findExtension(dp->disp.queryString.extensions,
-                      "EGL_EXT_gl_colorspace_scrgb_linear")) {
-        colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
-    }
-    if (findExtension(dp->disp.queryString.extensions,
-                      "EGL_EXT_gl_colorspace_bt2020_linear")) {
-        colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
-    }
-    if (findExtension(dp->disp.queryString.extensions,
-                      "EGL_EXT_gl_colorspace_bt2020_pq")) {
-        colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
-    }
-    if (findExtension(dp->disp.queryString.extensions,
-                      "EGL_EXT_gl_colorspace_display_p3_linear")) {
-        colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
-    }
-    return colorSpaces;
-}
-
-// Cleans up color space related parameters that the driver does not understand.
-// If there is no color space attribute in attrib_list, colorSpace is left
-// unmodified.
-static EGLBoolean processAttributes(egl_display_ptr dp, NativeWindowType window,
-                                    const EGLint* attrib_list, EGLint* colorSpace,
-                                    std::vector<EGLint>* strippedAttribList) {
-    for (const EGLint* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
-        bool copyAttribute = true;
-        if (attr[0] == EGL_GL_COLORSPACE_KHR) {
-            switch (attr[1]) {
-                case EGL_GL_COLORSPACE_LINEAR_KHR:
-                case EGL_GL_COLORSPACE_SRGB_KHR:
-                case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
-                case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT:
-                case EGL_GL_COLORSPACE_SCRGB_EXT:
-                case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT:
-                case EGL_GL_COLORSPACE_BT2020_PQ_EXT:
-                case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT:
-                    // Fail immediately if the driver doesn't have color space support at all.
-                    if (!dp->hasColorSpaceSupport) return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
-                    break;
-                default:
-                    // BAD_ATTRIBUTE if attr is not any of the EGL_GL_COLORSPACE_*
-                    return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
-            }
-            *colorSpace = attr[1];
-
-            // Strip the attribute if the driver doesn't understand it.
-            copyAttribute = false;
-            std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp);
-            for (auto driverColorSpace : driverColorSpaces) {
-                if (attr[1] == driverColorSpace) {
-                    copyAttribute = true;
-                    break;
-                }
-            }
-
-            // If the driver doesn't understand it, we should map sRGB-encoded P3 to
-            // sRGB rather than just dropping the colorspace on the floor.
-            // For this format, the driver is expected to apply the sRGB
-            // transfer function during framebuffer operations.
-            if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
-                strippedAttribList->push_back(attr[0]);
-                strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR);
-            }
-        }
-        if (copyAttribute) {
-            strippedAttribList->push_back(attr[0]);
-            strippedAttribList->push_back(attr[1]);
-        }
-    }
-    // Terminate the attribute list.
-    strippedAttribList->push_back(EGL_NONE);
-
-    // If the passed color space has wide color gamut, check whether the target native window
-    // supports wide color.
-    const bool colorSpaceIsNarrow =
-        *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR ||
-        *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR ||
-        *colorSpace == EGL_UNKNOWN;
-    if (window && !colorSpaceIsNarrow) {
-        bool windowSupportsWideColor = true;
-        // Ordinarily we'd put a call to native_window_get_wide_color_support
-        // at the beginning of the function so that we'll have the
-        // result when needed elsewhere in the function.
-        // However, because eglCreateWindowSurface is called by SurfaceFlinger and
-        // SurfaceFlinger is required to answer the call below we would
-        // end up in a deadlock situation. By moving the call to only happen
-        // if the application has specifically asked for wide-color we avoid
-        // the deadlock with SurfaceFlinger since it will not ask for a
-        // wide-color surface.
-        int err = native_window_get_wide_color_support(window, &windowSupportsWideColor);
-
-        if (err) {
-            ALOGE("processAttributes: invalid window (win=%p) "
-                  "failed (%#x) (already connected to another API?)",
-                  window, err);
-            return setError(EGL_BAD_NATIVE_WINDOW, EGL_FALSE);
-        }
-        if (!windowSupportsWideColor) {
-            // Application has asked for a wide-color colorspace but
-            // wide-color support isn't available on the display the window is on.
-            return setError(EGL_BAD_MATCH, EGL_FALSE);
-        }
-    }
-    return true;
-}
-
-// Gets the native pixel format corrsponding to the passed EGLConfig.
-void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
-                          android_pixel_format* format) {
-    // Set the native window's buffers format to match what this config requests.
-    // Whether to use sRGB gamma is not part of the EGLconfig, but is part
-    // of our native format. So if sRGB gamma is requested, we have to
-    // modify the EGLconfig's format before setting the native window's
-    // format.
-
-    EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
-    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType);
-
-    EGLint a = 0;
-    EGLint r, g, b;
-    r = g = b = 0;
-    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
-    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
-    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
-    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
-    EGLint colorDepth = r + g + b;
-
-    // Today, the driver only understands sRGB and linear on 888X
-    // formats. Strip other colorspaces from the attribute list and
-    // only use them to set the dataspace via
-    // native_window_set_buffers_dataspace
-    // if pixel format is RGBX 8888
-    //    TBD: Can test for future extensions that indicate that driver
-    //    handles requested color space and we can let it through.
-    //    allow SRGB and LINEAR. All others need to be stripped.
-    // else if 565, 4444
-    //    TBD: Can we assume these are supported if 8888 is?
-    // else if FP16 or 1010102
-    //    strip colorspace from attribs.
-    // endif
-    if (a == 0) {
-        if (colorDepth <= 16) {
-            *format = HAL_PIXEL_FORMAT_RGB_565;
-        } else {
-            if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
-                if (colorDepth > 24) {
-                    *format = HAL_PIXEL_FORMAT_RGBA_1010102;
-                } else {
-                    *format = HAL_PIXEL_FORMAT_RGBX_8888;
-                }
-            } else {
-                *format = HAL_PIXEL_FORMAT_RGBA_FP16;
-            }
-        }
-    } else {
-        if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
-            if (colorDepth > 24) {
-                *format = HAL_PIXEL_FORMAT_RGBA_1010102;
-            } else {
-                *format = HAL_PIXEL_FORMAT_RGBA_8888;
-            }
-        } else {
-            *format = HAL_PIXEL_FORMAT_RGBA_FP16;
-        }
-    }
-}
-
-EGLBoolean sendSurfaceMetadata(egl_surface_t* s) {
-    android_smpte2086_metadata smpteMetadata;
-    if (s->getSmpte2086Metadata(smpteMetadata)) {
-        int err =
-                native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata);
-        s->resetSmpte2086Metadata();
-        if (err != 0) {
-            ALOGE("error setting native window smpte2086 metadata: %s (%d)",
-                  strerror(-err), err);
-            return EGL_FALSE;
-        }
-    }
-    android_cta861_3_metadata cta8613Metadata;
-    if (s->getCta8613Metadata(cta8613Metadata)) {
-        int err =
-                native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata);
-        s->resetCta8613Metadata();
-        if (err != 0) {
-            ALOGE("error setting native window CTS 861.3 metadata: %s (%d)",
-                  strerror(-err), err);
-            return EGL_FALSE;
-        }
-    }
-    return EGL_TRUE;
-}
-
-EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
-                                    NativeWindowType window,
-                                    const EGLint *attrib_list)
-{
-    const EGLint *origAttribList = attrib_list;
+EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs,
+                           EGLint config_size, EGLint* num_config) {
     clearError();
 
-    egl_connection_t* cnx = NULL;
-    egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (dp) {
-        if (!window) {
-            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
-        }
-
-        int value = 0;
-        window->query(window, NATIVE_WINDOW_IS_VALID, &value);
-        if (!value) {
-            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
-        }
-
-        int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
-        if (result < 0) {
-            ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
-                    "failed (%#x) (already connected to another API?)",
-                    window, result);
-            return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
-        }
-
-        EGLDisplay iDpy = dp->disp.dpy;
-        android_pixel_format format;
-        getNativePixelFormat(iDpy, cnx, config, &format);
-
-        // now select correct colorspace and dataspace based on user's attribute list
-        EGLint colorSpace = EGL_UNKNOWN;
-        std::vector<EGLint> strippedAttribList;
-        if (!processAttributes(dp, window, attrib_list, &colorSpace,
-                               &strippedAttribList)) {
-            ALOGE("error invalid colorspace: %d", colorSpace);
-            native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
-            return EGL_NO_SURFACE;
-        }
-        attrib_list = strippedAttribList.data();
-
-        {
-            int err = native_window_set_buffers_format(window, format);
-            if (err != 0) {
-                ALOGE("error setting native window pixel format: %s (%d)",
-                      strerror(-err), err);
-                native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
-                return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
-            }
-        }
-
-        android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace);
-        // Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN. HAL_DATASPACE_UNKNOWN
-        // is the default value, but it may have changed at this point.
-        int err = native_window_set_buffers_data_space(window, dataSpace);
-        if (err != 0) {
-            ALOGE("error setting native window pixel dataSpace: %s (%d)",
-                  strerror(-err), err);
-            native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
-            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
-        }
-
-        // the EGL spec requires that a new EGLSurface default to swap interval
-        // 1, so explicitly set that on the window here.
-        ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
-        anw->setSwapInterval(anw, 1);
-
-        EGLSurface surface = cnx->egl.eglCreateWindowSurface(
-                iDpy, config, window, attrib_list);
-        if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s =
-                    new egl_surface_t(dp.get(), config, window, surface,
-                                      getReportedColorSpace(colorSpace), cnx);
-            return s;
-        }
-
-        // EGLSurface creation failed
-        native_window_set_buffers_format(window, 0);
-        native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
-    }
-    return EGL_NO_SURFACE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglChooseConfig(dpy, attrib_list, configs, config_size, num_config);
 }
 
-EGLSurface eglCreatePixmapSurface(  EGLDisplay dpy, EGLConfig config,
-                                    NativePixmapType pixmap,
-                                    const EGLint *attrib_list)
-{
+EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value) {
     clearError();
 
-    egl_connection_t* cnx = NULL;
-    egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (dp) {
-        EGLDisplay iDpy = dp->disp.dpy;
-        android_pixel_format format;
-        getNativePixelFormat(iDpy, cnx, config, &format);
-
-        // now select a corresponding sRGB format if needed
-        EGLint colorSpace = EGL_UNKNOWN;
-        std::vector<EGLint> strippedAttribList;
-        if (!processAttributes(dp, nullptr, attrib_list, &colorSpace,
-                               &strippedAttribList)) {
-            ALOGE("error invalid colorspace: %d", colorSpace);
-            return EGL_NO_SURFACE;
-        }
-        attrib_list = strippedAttribList.data();
-
-        EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
-                dp->disp.dpy, config, pixmap, attrib_list);
-        if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s =
-                    new egl_surface_t(dp.get(), config, NULL, surface,
-                                      getReportedColorSpace(colorSpace), cnx);
-            return s;
-        }
-    }
-    return EGL_NO_SURFACE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetConfigAttrib(dpy, config, attribute, value);
 }
 
-EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
-                                    const EGLint *attrib_list)
-{
+EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
+                                  const EGLint* attrib_list) {
     clearError();
 
-    egl_connection_t* cnx = NULL;
-    egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (dp) {
-        EGLDisplay iDpy = dp->disp.dpy;
-        android_pixel_format format;
-        getNativePixelFormat(iDpy, cnx, config, &format);
-
-        // Select correct colorspace based on user's attribute list
-        EGLint colorSpace = EGL_UNKNOWN;
-        std::vector<EGLint> strippedAttribList;
-        if (!processAttributes(dp, nullptr, attrib_list, &colorSpace,
-                               &strippedAttribList)) {
-            ALOGE("error invalid colorspace: %d", colorSpace);
-            return EGL_NO_SURFACE;
-        }
-        attrib_list = strippedAttribList.data();
-
-        EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
-                dp->disp.dpy, config, attrib_list);
-        if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s =
-                    new egl_surface_t(dp.get(), config, NULL, surface,
-                                      getReportedColorSpace(colorSpace), cnx);
-            return s;
-        }
-    }
-    return EGL_NO_SURFACE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreateWindowSurface(dpy, config, window, attrib_list);
 }
 
-EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface)
-{
+EGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void* native_window,
+                                          const EGLAttrib* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t * const s = get_surface(surface);
-    EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
-    if (result == EGL_TRUE) {
-        _s.terminate();
-    }
-    return result;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreatePlatformWindowSurface(dpy, config, native_window, attrib_list);
 }
 
-EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface,
-                            EGLint attribute, EGLint *value)
-{
+EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap,
+                                  const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreatePixmapSurface(dpy, config, pixmap, attrib_list);
+}
 
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+EGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap,
+                                          const EGLAttrib* attrib_list) {
+    clearError();
 
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->getColorSpaceAttribute(attribute, value)) {
-        return EGL_TRUE;
-    } else if (s->getSmpte2086Attribute(attribute, value)) {
-        return EGL_TRUE;
-    } else if (s->getCta8613Attribute(attribute, value)) {
-        return EGL_TRUE;
-    }
-    return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreatePlatformPixmapSurface(dpy, config, native_pixmap, attrib_list);
+}
+
+EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreatePbufferSurface(dpy, config, attrib_list);
+}
+
+EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglDestroySurface(dpy, surface);
+}
+
+EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglQuerySurface(dpy, surface, attribute, value);
 }
 
 void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {
     ATRACE_CALL();
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return;
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-    }
+    egl_connection_t* const cnx = &gEGLImpl;
+    cnx->platform.eglBeginFrame(dpy, surface);
 }
 
-// ----------------------------------------------------------------------------
-// Contexts
-// ----------------------------------------------------------------------------
-
-EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
-                            EGLContext share_list, const EGLint *attrib_list)
-{
-    clearError();
-
-    egl_connection_t* cnx = NULL;
-    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (dp) {
-        if (share_list != EGL_NO_CONTEXT) {
-            if (!ContextRef(dp.get(), share_list).get()) {
-                return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
-            }
-            egl_context_t* const c = get_context(share_list);
-            share_list = c->context;
-        }
-        EGLContext context = cnx->egl.eglCreateContext(
-                dp->disp.dpy, config, share_list, attrib_list);
-        if (context != EGL_NO_CONTEXT) {
-            // figure out if it's a GLESv1 or GLESv2
-            int version = 0;
-            if (attrib_list) {
-                while (*attrib_list != EGL_NONE) {
-                    GLint attr = *attrib_list++;
-                    GLint value = *attrib_list++;
-                    if (attr == EGL_CONTEXT_CLIENT_VERSION) {
-                        if (value == 1) {
-                            version = egl_connection_t::GLESv1_INDEX;
-                        } else if (value == 2 || value == 3) {
-                            version = egl_connection_t::GLESv2_INDEX;
-                        }
-                    }
-                };
-            }
-            egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
-                    version);
-            return c;
-        }
-    }
-    return EGL_NO_CONTEXT;
-}
-
-EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp)
-        return EGL_FALSE;
-
-    ContextRef _c(dp.get(), ctx);
-    if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    egl_context_t * const c = get_context(ctx);
-    EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
-    if (result == EGL_TRUE) {
-        _c.terminate();
-    }
-    return result;
-}
-
-EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
-                            EGLSurface read, EGLContext ctx)
-{
-    clearError();
-
-    egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-
-    // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
-    // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
-    // a valid but uninitialized display.
-    if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
-         (draw != EGL_NO_SURFACE) ) {
-        if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
-    }
-
-    // get a reference to the object passed in
-    ContextRef _c(dp.get(), ctx);
-    SurfaceRef _d(dp.get(), draw);
-    SurfaceRef _r(dp.get(), read);
-
-    // validate the context (if not EGL_NO_CONTEXT)
-    if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
-        // EGL_NO_CONTEXT is valid
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-    }
-
-    // these are the underlying implementation's object
-    EGLContext impl_ctx  = EGL_NO_CONTEXT;
-    EGLSurface impl_draw = EGL_NO_SURFACE;
-    EGLSurface impl_read = EGL_NO_SURFACE;
-
-    // these are our objects structs passed in
-    egl_context_t       * c = NULL;
-    egl_surface_t const * d = NULL;
-    egl_surface_t const * r = NULL;
-
-    // these are the current objects structs
-    egl_context_t * cur_c = get_context(getContext());
-
-    if (ctx != EGL_NO_CONTEXT) {
-        c = get_context(ctx);
-        impl_ctx = c->context;
-    } else {
-        // no context given, use the implementation of the current context
-        if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
-            // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
-            return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE);
-        }
-        if (cur_c == NULL) {
-            // no current context
-            // not an error, there is just no current context.
-            return EGL_TRUE;
-        }
-    }
-
-    // retrieve the underlying implementation's draw EGLSurface
-    if (draw != EGL_NO_SURFACE) {
-        if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-        d = get_surface(draw);
-        impl_draw = d->surface;
-    }
-
-    // retrieve the underlying implementation's read EGLSurface
-    if (read != EGL_NO_SURFACE) {
-        if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-        r = get_surface(read);
-        impl_read = r->surface;
-    }
-
-
-    EGLBoolean result = dp->makeCurrent(c, cur_c,
-            draw, read, ctx,
-            impl_draw, impl_read, impl_ctx);
-
-    if (result == EGL_TRUE) {
-        if (c) {
-            setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
-            egl_tls_t::setContext(ctx);
-            _c.acquire();
-            _r.acquire();
-            _d.acquire();
-        } else {
-            setGLHooksThreadSpecific(&gHooksNoContext);
-            egl_tls_t::setContext(EGL_NO_CONTEXT);
-        }
-    } else {
-        // this will ALOGE the error
-        egl_connection_t* const cnx = &gEGLImpl;
-        result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE);
-    }
-    return result;
-}
-
-
-EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
-                            EGLint attribute, EGLint *value)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    ContextRef _c(dp.get(), ctx);
-    if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    egl_context_t * const c = get_context(ctx);
-    return c->cnx->egl.eglQueryContext(
-            dp->disp.dpy, c->context, attribute, value);
-
-}
-
-EGLContext eglGetCurrentContext(void)
-{
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would correctly return EGL_NO_CONTEXT.
-
-    clearError();
-
-    EGLContext ctx = getContext();
-    return ctx;
-}
-
-EGLSurface eglGetCurrentSurface(EGLint readdraw)
-{
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would correctly return EGL_NO_SURFACE.
-
-    clearError();
-
-    EGLContext ctx = getContext();
-    if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
-        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
-        switch (readdraw) {
-            case EGL_READ: return c->read;
-            case EGL_DRAW: return c->draw;
-            default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
-        }
-    }
-    return EGL_NO_SURFACE;
-}
-
-EGLDisplay eglGetCurrentDisplay(void)
-{
-    // could be called before eglInitialize(), but we wouldn't have a context
-    // then, and this function would correctly return EGL_NO_DISPLAY.
-
-    clearError();
-
-    EGLContext ctx = getContext();
-    if (ctx) {
-        egl_context_t const * const c = get_context(ctx);
-        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
-        return c->dpy;
-    }
-    return EGL_NO_DISPLAY;
-}
-
-EGLBoolean eglWaitGL(void)
-{
+EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_list,
+                            const EGLint* attrib_list) {
     clearError();
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    return cnx->egl.eglWaitGL();
+    return cnx->platform.eglCreateContext(dpy, config, share_list, attrib_list);
 }
 
-EGLBoolean eglWaitNative(EGLint engine)
-{
+EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) {
     clearError();
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    return cnx->egl.eglWaitNative(engine);
+    return cnx->platform.eglDestroyContext(dpy, ctx);
 }
 
-EGLint eglGetError(void)
-{
-    EGLint err = EGL_SUCCESS;
+EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
+    clearError();
+
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso) {
-        err = cnx->egl.eglGetError();
-    }
-    if (err == EGL_SUCCESS) {
-        err = egl_tls_t::getError();
-    }
-    return err;
+    return cnx->platform.eglMakeCurrent(dpy, draw, read, ctx);
 }
 
-static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
-        const char* procname) {
-    const egl_connection_t* cnx = &gEGLImpl;
-    void* proc = NULL;
+EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) {
+    clearError();
 
-    proc = dlsym(cnx->libEgl, procname);
-    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
-    proc = dlsym(cnx->libGles2, procname);
-    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
-    proc = dlsym(cnx->libGles1, procname);
-    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
-    return NULL;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglQueryContext(dpy, ctx, attribute, value);
 }
 
-__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
-{
+EGLContext eglGetCurrentContext(void) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCurrentContext();
+}
+
+EGLSurface eglGetCurrentSurface(EGLint readdraw) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCurrentSurface(readdraw);
+}
+
+EGLDisplay eglGetCurrentDisplay(void) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCurrentDisplay();
+}
+
+EGLBoolean eglWaitGL(void) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglWaitGL();
+}
+
+EGLBoolean eglWaitNative(EGLint engine) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglWaitNative(engine);
+}
+
+EGLint eglGetError(void) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetError();
+}
+
+__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char* procname) {
     // eglGetProcAddress() could be the very first function called
     // in which case we must make sure we've initialized ourselves, this
     // happens the first time egl_get_display() is called.
@@ -1188,431 +243,87 @@
 
     if (egl_init_drivers() == EGL_FALSE) {
         setError(EGL_BAD_PARAMETER, NULL);
-        return  NULL;
+        return nullptr;
     }
 
-    if (FILTER_EXTENSIONS(procname)) {
-        return NULL;
-    }
-
-    __eglMustCastToProperFunctionPointerType addr;
-    addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap));
-    if (addr) return addr;
-
-    addr = findBuiltinWrapper(procname);
-    if (addr) return addr;
-
-    // this protects accesses to sGLExtentionMap and sGLExtentionSlot
-    pthread_mutex_lock(&sExtensionMapMutex);
-
-        /*
-         * Since eglGetProcAddress() is not associated to anything, it needs
-         * to return a function pointer that "works" regardless of what
-         * the current context is.
-         *
-         * For this reason, we return a "forwarder", a small stub that takes
-         * care of calling the function associated with the context
-         * currently bound.
-         *
-         * We first look for extensions we've already resolved, if we're seeing
-         * this extension for the first time, we go through all our
-         * implementations and call eglGetProcAddress() and record the
-         * result in the appropriate implementation hooks and return the
-         * address of the forwarder corresponding to that hook set.
-         *
-         */
-
-        const std::string name(procname);
-
-    auto& extentionMap = sGLExtentionMap;
-    auto pos = extentionMap.find(name);
-        addr = (pos != extentionMap.end()) ? pos->second : nullptr;
-        const int slot = sGLExtentionSlot;
-
-        ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
-                "no more slots for eglGetProcAddress(\"%s\")",
-                procname);
-
-        if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
-            bool found = false;
-
-            egl_connection_t* const cnx = &gEGLImpl;
-            if (cnx->dso && cnx->egl.eglGetProcAddress) {
-                // Extensions are independent of the bound context
-                addr =
-                cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
-                cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] =
-                        cnx->egl.eglGetProcAddress(procname);
-                if (addr) found = true;
-            }
-
-            if (found) {
-                addr = gExtensionForwarders[slot];
-                extentionMap[name] = addr;
-                sGLExtentionSlot++;
-            }
-        }
-
-    pthread_mutex_unlock(&sExtensionMapMutex);
-    return addr;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetProcAddress(procname);
 }
 
-class FrameCompletionThread {
-public:
-
-    static void queueSync(EGLSyncKHR sync) {
-        static FrameCompletionThread thread;
-
-        char name[64];
-
-        std::lock_guard<std::mutex> lock(thread.mMutex);
-        snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued);
-        ATRACE_NAME(name);
-
-        thread.mQueue.push_back(sync);
-        thread.mCondition.notify_one();
-        thread.mFramesQueued++;
-        ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size()));
-    }
-
-private:
-
-    FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
-        std::thread thread(&FrameCompletionThread::loop, this);
-        thread.detach();
-    }
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wmissing-noreturn"
-    void loop() {
-        while (true) {
-            threadLoop();
-        }
-    }
-#pragma clang diagnostic pop
-
-    void threadLoop() {
-        EGLSyncKHR sync;
-        uint32_t frameNum;
-        {
-            std::unique_lock<std::mutex> lock(mMutex);
-            while (mQueue.empty()) {
-                mCondition.wait(lock);
-            }
-            sync = mQueue[0];
-            frameNum = mFramesCompleted;
-        }
-        EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-        {
-            char name[64];
-            snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum);
-            ATRACE_NAME(name);
-
-            EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
-            if (result == EGL_FALSE) {
-                ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
-            } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
-                ALOGE("FrameCompletion: timeout waiting for fence");
-            }
-            eglDestroySyncKHR(dpy, sync);
-        }
-        {
-            std::lock_guard<std::mutex> lock(mMutex);
-            mQueue.pop_front();
-            mFramesCompleted++;
-            ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size()));
-        }
-    }
-
-    uint32_t mFramesQueued;
-    uint32_t mFramesCompleted;
-    std::deque<EGLSyncKHR> mQueue;
-    std::condition_variable mCondition;
-    std::mutex mMutex;
-};
-
-EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw,
-        EGLint *rects, EGLint n_rects)
-{
+EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+                                       EGLint n_rects) {
     ATRACE_CALL();
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), draw);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t* const s = get_surface(draw);
-
-    if (CC_UNLIKELY(dp->traceGpuCompletion)) {
-        EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
-        if (sync != EGL_NO_SYNC_KHR) {
-            FrameCompletionThread::queueSync(sync);
-        }
-    }
-
-    if (CC_UNLIKELY(dp->finishOnSwap)) {
-        uint32_t pixel;
-        egl_context_t * const c = get_context( egl_tls_t::getContext() );
-        if (c) {
-            // glReadPixels() ensures that the frame is complete
-            s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
-                    GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
-        }
-    }
-
-    if (!sendSurfaceMetadata(s)) {
-        native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL);
-        return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE);
-    }
-
-    if (n_rects == 0) {
-        return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
-    }
-
-    std::vector<android_native_rect_t> androidRects((size_t)n_rects);
-    for (int r = 0; r < n_rects; ++r) {
-        int offset = r * 4;
-        int x = rects[offset];
-        int y = rects[offset + 1];
-        int width = rects[offset + 2];
-        int height = rects[offset + 3];
-        android_native_rect_t androidRect;
-        androidRect.left = x;
-        androidRect.top = y + height;
-        androidRect.right = x + width;
-        androidRect.bottom = y;
-        androidRects.push_back(androidRect);
-    }
-    native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(), androidRects.size());
-
-    if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
-        return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
-                rects, n_rects);
-    } else {
-        return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
-    }
-}
-
-EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
-{
-    return eglSwapBuffersWithDamageKHR(dpy, surface, NULL, 0);
-}
-
-EGLBoolean eglCopyBuffers(  EGLDisplay dpy, EGLSurface surface,
-                            NativePixmapType target)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t const * const s = get_surface(surface);
-    return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
-}
-
-const char* eglQueryString(EGLDisplay dpy, EGLint name)
-{
-    clearError();
-
-    // Generate an error quietly when client extensions (as defined by
-    // EGL_EXT_client_extensions) are queried.  We do not want to rely on
-    // validate_display to generate the error as validate_display would log
-    // the error, which can be misleading.
-    //
-    // If we want to support EGL_EXT_client_extensions later, we can return
-    // the client extension string here instead.
-    if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS)
-        return setErrorQuiet(EGL_BAD_DISPLAY, (const char*)0);
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return (const char *) NULL;
-
-    switch (name) {
-        case EGL_VENDOR:
-            return dp->getVendorString();
-        case EGL_VERSION:
-            return dp->getVersionString();
-        case EGL_EXTENSIONS:
-            return dp->getExtensionString();
-        case EGL_CLIENT_APIS:
-            return dp->getClientApiString();
-        default:
-            break;
-    }
-    return setError(EGL_BAD_PARAMETER, (const char *)0);
-}
-
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return (const char *) NULL;
-
-    switch (name) {
-        case EGL_VENDOR:
-            return dp->disp.queryString.vendor;
-        case EGL_VERSION:
-            return dp->disp.queryString.version;
-        case EGL_EXTENSIONS:
-            return dp->disp.queryString.extensions;
-        case EGL_CLIENT_APIS:
-            return dp->disp.queryString.clientApi;
-        default:
-            break;
-    }
-    return setError(EGL_BAD_PARAMETER, (const char *)0);
-}
-
-// ----------------------------------------------------------------------------
-// EGL 1.1
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglSurfaceAttrib(
-        EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t * const s = get_surface(surface);
-
-    if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
-        if (!s->getNativeWindow()) {
-            setError(EGL_BAD_SURFACE, EGL_FALSE);
-        }
-        int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
-        return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    if (attribute == EGL_TIMESTAMPS_ANDROID) {
-        if (!s->getNativeWindow()) {
-            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-        }
-        int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
-        return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    if (s->setSmpte2086Attribute(attribute, value)) {
-        return EGL_TRUE;
-    } else if (s->setCta8613Attribute(attribute, value)) {
-        return EGL_TRUE;
-    } else if (s->cnx->egl.eglSurfaceAttrib) {
-        return s->cnx->egl.eglSurfaceAttrib(
-                dp->disp.dpy, s->surface, attribute, value);
-    }
-    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglBindTexImage(
-        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->cnx->egl.eglBindTexImage) {
-        return s->cnx->egl.eglBindTexImage(
-                dp->disp.dpy, s->surface, buffer);
-    }
-    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglReleaseTexImage(
-        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->cnx->egl.eglReleaseTexImage) {
-        return s->cnx->egl.eglReleaseTexImage(
-                dp->disp.dpy, s->surface, buffer);
-    }
-    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
-{
-    clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean res = EGL_TRUE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglSwapInterval) {
-        res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval);
-    }
-
-    return res;
+    return cnx->platform.eglSwapBuffersWithDamageKHR(dpy, draw, rects, n_rects);
 }
 
-
-// ----------------------------------------------------------------------------
-// EGL 1.2
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglWaitClient(void)
-{
+EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
+    ATRACE_CALL();
     clearError();
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
-    EGLBoolean res;
-    if (cnx->egl.eglWaitClient) {
-        res = cnx->egl.eglWaitClient();
-    } else {
-        res = cnx->egl.eglWaitGL();
-    }
-    return res;
+    return cnx->platform.eglSwapBuffers(dpy, surface);
 }
 
-EGLBoolean eglBindAPI(EGLenum api)
-{
+EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) {
     clearError();
 
-    if (egl_init_drivers() == EGL_FALSE) {
-        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
-    }
-
-    // bind this API on all EGLs
-    EGLBoolean res = EGL_TRUE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglBindAPI) {
-        res = cnx->egl.eglBindAPI(api);
-    }
-    return res;
+    return cnx->platform.eglCopyBuffers(dpy, surface, target);
 }
 
-EGLenum eglQueryAPI(void)
-{
+const char* eglQueryString(EGLDisplay dpy, EGLint name) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglQueryString(dpy, name);
+}
+
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglQueryStringImplementationANDROID(dpy, name);
+}
+
+EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglSurfaceAttrib(dpy, surface, attribute, value);
+}
+
+EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglBindTexImage(dpy, surface, buffer);
+}
+
+EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglReleaseTexImage(dpy, surface, buffer);
+}
+
+EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglSwapInterval(dpy, interval);
+}
+
+EGLBoolean eglWaitClient(void) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglWaitClient();
+}
+
+EGLBoolean eglBindAPI(EGLenum api) {
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
@@ -1620,824 +331,335 @@
     }
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglQueryAPI) {
-        return cnx->egl.eglQueryAPI();
-    }
-
-    // or, it can only be OpenGL ES
-    return EGL_OPENGL_ES_API;
+    return cnx->platform.eglBindAPI(api);
 }
 
-EGLBoolean eglReleaseThread(void)
-{
+EGLenum eglQueryAPI(void) {
+    clearError();
+
+    if (egl_init_drivers() == EGL_FALSE) {
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglQueryAPI();
+}
+
+EGLBoolean eglReleaseThread(void) {
     clearError();
 
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglReleaseThread) {
-        cnx->egl.eglReleaseThread();
-    }
-
-    // If there is context bound to the thread, release it
-    egl_display_t::loseCurrent(get_context(getContext()));
-
-    egl_tls_t::clearTLS();
-    return EGL_TRUE;
+    return cnx->platform.eglReleaseThread();
 }
 
-EGLSurface eglCreatePbufferFromClientBuffer(
-          EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
-          EGLConfig config, const EGLint *attrib_list)
-{
+EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
+                                            EGLConfig config, const EGLint* attrib_list) {
     clearError();
 
-    egl_connection_t* cnx = NULL;
-    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    if (!dp) return EGL_FALSE;
-    if (cnx->egl.eglCreatePbufferFromClientBuffer) {
-        return cnx->egl.eglCreatePbufferFromClientBuffer(
-                dp->disp.dpy, buftype, buffer, config, attrib_list);
-    }
-    return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreatePbufferFromClientBuffer(dpy, buftype, buffer, config,
+                                                          attrib_list);
 }
 
-// ----------------------------------------------------------------------------
-// EGL_EGLEXT_VERSION 3
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface,
-        const EGLint *attrib_list)
-{
+EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->cnx->egl.eglLockSurfaceKHR) {
-        return s->cnx->egl.eglLockSurfaceKHR(
-                dp->disp.dpy, s->surface, attrib_list);
-    }
-    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglLockSurfaceKHR(dpy, surface, attrib_list);
 }
 
-EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface)
-{
+EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get())
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->cnx->egl.eglUnlockSurfaceKHR) {
-        return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
-    }
-    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglUnlockSurfaceKHR(dpy, surface);
 }
 
 EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
-        EGLClientBuffer buffer, const EGLint *attrib_list)
-{
+                              EGLClientBuffer buffer, const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_IMAGE_KHR;
-
-    ContextRef _c(dp.get(), ctx);
-    egl_context_t * const c = _c.get();
-
-    // Temporary hack: eglImageCreateKHR should accept EGL_GL_COLORSPACE_LINEAR_KHR,
-    // EGL_GL_COLORSPACE_SRGB_KHR and EGL_GL_COLORSPACE_DEFAULT_EXT if
-    // EGL_EXT_image_gl_colorspace is supported, but some drivers don't like
-    // the DEFAULT value and generate an error.
-    std::vector<EGLint> strippedAttribList;
-    for (const EGLint *attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
-        if (attr[0] == EGL_GL_COLORSPACE_KHR &&
-            dp->haveExtension("EGL_EXT_image_gl_colorspace")) {
-            if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR &&
-                attr[1] != EGL_GL_COLORSPACE_SRGB_KHR) {
-                continue;
-            }
-        }
-        strippedAttribList.push_back(attr[0]);
-        strippedAttribList.push_back(attr[1]);
-    }
-    strippedAttribList.push_back(EGL_NONE);
-
-    EGLImageKHR result = EGL_NO_IMAGE_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglCreateImageKHR) {
-        result = cnx->egl.eglCreateImageKHR(
-                dp->disp.dpy,
-                c ? c->context : EGL_NO_CONTEXT,
-                target, buffer, strippedAttribList.data());
-    }
-    return result;
+    return cnx->platform.eglCreateImageKHR(dpy, ctx, target, buffer, attrib_list);
 }
 
-EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
-{
+EGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
+                        const EGLAttrib* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglDestroyImageKHR) {
-        result = cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img);
-    }
-    return result;
+    return cnx->platform.eglCreateImage(dpy, ctx, target, buffer, attrib_list);
+}
+
+EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglDestroyImageKHR(dpy, img);
+}
+
+EGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImageKHR img) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglDestroyImage(dpy, img);
 }
 
 // ----------------------------------------------------------------------------
 // EGL_EGLEXT_VERSION 5
 // ----------------------------------------------------------------------------
 
-
-EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
-{
+EGLSyncKHR eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_SYNC_KHR;
-
-    EGLSyncKHR result = EGL_NO_SYNC_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglCreateSyncKHR) {
-        result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list);
-    }
-    return result;
+    return cnx->platform.eglCreateSync(dpy, type, attrib_list);
 }
 
-EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
-{
+EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglDestroySyncKHR) {
-        result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync);
-    }
-    return result;
+    return cnx->platform.eglCreateSyncKHR(dpy, type, attrib_list);
+}
+
+EGLBoolean eglDestroySync(EGLDisplay dpy, EGLSyncKHR sync) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglDestroySync(dpy, sync);
+}
+
+EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglDestroySyncKHR(dpy, sync);
 }
 
 EGLBoolean eglSignalSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglSignalSyncKHR) {
-        result = cnx->egl.eglSignalSyncKHR(
-                dp->disp.dpy, sync, mode);
-    }
-    return result;
+    return cnx->platform.eglSignalSyncKHR(dpy, sync, mode);
 }
 
-EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync,
-        EGLint flags, EGLTimeKHR timeout)
-{
+EGLint eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLint result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) {
-        result = cnx->egl.eglClientWaitSyncKHR(
-                dp->disp.dpy, sync, flags, timeout);
-    }
-    return result;
+    return cnx->platform.eglClientWaitSyncKHR(dpy, sync, flags, timeout);
 }
 
-EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync,
-        EGLint attribute, EGLint *value)
-{
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) {
-        result = cnx->egl.eglGetSyncAttribKHR(
-                dp->disp.dpy, sync, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglClientWaitSyncKHR(dpy, sync, flags, timeout);
 }
 
-EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint *attrib_list)
-{
+EGLBoolean eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_STREAM_KHR;
-
-    EGLStreamKHR result = EGL_NO_STREAM_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
-        result = cnx->egl.eglCreateStreamKHR(
-                dp->disp.dpy, attrib_list);
-    }
-    return result;
+    return cnx->platform.eglGetSyncAttrib(dpy, sync, attribute, value);
 }
 
-EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream)
-{
+EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint* value) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
-        result = cnx->egl.eglDestroyStreamKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglGetSyncAttribKHR(dpy, sync, attribute, value);
 }
 
-EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLint value)
-{
+EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
-        result = cnx->egl.eglStreamAttribKHR(
-                dp->disp.dpy, stream, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglCreateStreamKHR(dpy, attrib_list);
 }
 
-EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLint *value)
-{
+EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
-        result = cnx->egl.eglQueryStreamKHR(
-                dp->disp.dpy, stream, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglDestroyStreamKHR(dpy, stream);
 }
 
-EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLuint64KHR *value)
-{
+EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                              EGLint value) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
-        result = cnx->egl.eglQueryStreamu64KHR(
-                dp->disp.dpy, stream, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglStreamAttribKHR(dpy, stream, attribute, value);
 }
 
-EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream,
-        EGLenum attribute, EGLTimeKHR *value)
-{
+EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                             EGLint* value) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
-        result = cnx->egl.eglQueryStreamTimeKHR(
-                dp->disp.dpy, stream, attribute, value);
-    }
-    return result;
+    return cnx->platform.eglQueryStreamKHR(dpy, stream, attribute, value);
 }
 
-EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config,
-        EGLStreamKHR stream, const EGLint *attrib_list)
-{
+EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                                EGLuint64KHR* value) {
     clearError();
 
-    egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_SURFACE;
-
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
-        EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
-                dp->disp.dpy, config, stream, attrib_list);
-        if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL, surface,
-                                                 EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
-            return s;
-        }
-    }
-    return EGL_NO_SURFACE;
+    return cnx->platform.eglQueryStreamu64KHR(dpy, stream, attribute, value);
 }
 
-EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy,
-        EGLStreamKHR stream)
-{
+EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+                                 EGLTimeKHR* value) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
-        result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglQueryStreamTimeKHR(dpy, stream, attribute, value);
 }
 
-EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy,
-        EGLStreamKHR stream)
-{
+EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream,
+                                             const EGLint* attrib_list) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
-        result = cnx->egl.eglStreamConsumerAcquireKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglCreateStreamProducerSurfaceKHR(dpy, config, stream, attrib_list);
 }
 
-EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy,
-        EGLStreamKHR stream)
-{
+EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy, EGLStreamKHR stream) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-
-    EGLBoolean result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
-        result = cnx->egl.eglStreamConsumerReleaseKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglStreamConsumerGLTextureExternalKHR(dpy, stream);
 }
 
-EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(
-        EGLDisplay dpy, EGLStreamKHR stream)
-{
+EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy, EGLStreamKHR stream) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
-
-    EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
-        result = cnx->egl.eglGetStreamFileDescriptorKHR(
-                dp->disp.dpy, stream);
-    }
-    return result;
+    return cnx->platform.eglStreamConsumerAcquireKHR(dpy, stream);
 }
 
-EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(
-        EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
-{
+EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy, EGLStreamKHR stream) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_STREAM_KHR;
-
-    EGLStreamKHR result = EGL_NO_STREAM_KHR;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
-        result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
-                dp->disp.dpy, file_descriptor);
-    }
-    return result;
+    return cnx->platform.eglStreamConsumerReleaseKHR(dpy, stream);
 }
 
-// ----------------------------------------------------------------------------
-// EGL_EGLEXT_VERSION 15
-// ----------------------------------------------------------------------------
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(EGLDisplay dpy, EGLStreamKHR stream) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetStreamFileDescriptorKHR(dpy, stream);
+}
+
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(EGLDisplay dpy,
+                                                  EGLNativeFileDescriptorKHR file_descriptor) {
+    clearError();
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglCreateStreamFromFileDescriptorKHR(dpy, file_descriptor);
+}
 
 EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) {
     clearError();
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_FALSE;
-    EGLint result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglWaitSyncKHR) {
-        result = cnx->egl.eglWaitSyncKHR(dp->disp.dpy, sync, flags);
-    }
-    return result;
+    return cnx->platform.eglWaitSyncKHR(dpy, sync, flags);
 }
 
-// ----------------------------------------------------------------------------
-// ANDROID extensions
-// ----------------------------------------------------------------------------
-
-EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync)
-{
+EGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags) {
     clearError();
-
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
-
-    EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
     egl_connection_t* const cnx = &gEGLImpl;
-    if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) {
-        result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync);
-    }
-    return result;
+    return cnx->platform.eglWaitSync(dpy, sync, flags);
 }
 
-EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface,
-        EGLnsecsANDROID time)
-{
+EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return EGL_FALSE;
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-    native_window_set_buffers_timestamp(s->getNativeWindow(), time);
-
-    return EGL_TRUE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglDupNativeFenceFDANDROID(dpy, sync);
 }
 
-EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) {
+EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface, EGLnsecsANDROID time) {
     clearError();
-    // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
-    // this function cannot be implemented when this libEGL is built for
-    // vendors.
-#ifndef __ANDROID_VNDK__
-    if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
-    return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
-#else
-    return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
-#endif
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglPresentationTimeANDROID(dpy, surface, time);
 }
 
-// ----------------------------------------------------------------------------
-// NVIDIA extensions
-// ----------------------------------------------------------------------------
-EGLuint64NV eglGetSystemTimeFrequencyNV()
-{
+EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer* buffer) {
+    clearError();
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetNativeClientBufferANDROID(buffer);
+}
+
+EGLuint64NV eglGetSystemTimeFrequencyNV() {
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
         return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
     }
 
-    EGLuint64NV ret = 0;
     egl_connection_t* const cnx = &gEGLImpl;
-
-    if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) {
-        return cnx->egl.eglGetSystemTimeFrequencyNV();
-    }
-
-    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+    return cnx->platform.eglGetSystemTimeFrequencyNV();
 }
 
-EGLuint64NV eglGetSystemTimeNV()
-{
+EGLuint64NV eglGetSystemTimeNV() {
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
         return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
     }
 
-    EGLuint64NV ret = 0;
     egl_connection_t* const cnx = &gEGLImpl;
-
-    if (cnx->dso && cnx->egl.eglGetSystemTimeNV) {
-        return cnx->egl.eglGetSystemTimeNV();
-    }
-
-    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+    return cnx->platform.eglGetSystemTimeNV();
 }
 
-// ----------------------------------------------------------------------------
-// Partial update extension
-// ----------------------------------------------------------------------------
-EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface,
-        EGLint *rects, EGLint n_rects)
-{
+EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, EGLint* rects,
+                                 EGLint n_rects) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        return EGL_FALSE;
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-    if (s->cnx->egl.eglSetDamageRegionKHR) {
-        return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
-                rects, n_rects);
-    }
-
-    return EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglSetDamageRegionKHR(dpy, surface, rects, n_rects);
 }
 
-EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
-            EGLuint64KHR *frameId) {
+EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR* frameId) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-
-    if (!s->getNativeWindow()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    uint64_t nextFrameId = 0;
-    int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId);
-
-    if (ret != 0) {
-        // This should not happen. Return an error that is not in the spec
-        // so it's obvious something is very wrong.
-        ALOGE("eglGetNextFrameId: Unexpected error.");
-        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
-    }
-
-    *frameId = nextFrameId;
-    return EGL_TRUE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetNextFrameIdANDROID(dpy, surface, frameId);
 }
 
-EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface,
-        EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
-{
+EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps,
+                                         const EGLint* names, EGLnsecsANDROID* values) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-
-    if (!s->getNativeWindow()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    nsecs_t* compositeDeadline = nullptr;
-    nsecs_t* compositeInterval = nullptr;
-    nsecs_t* compositeToPresentLatency = nullptr;
-
-    for (int i = 0; i < numTimestamps; i++) {
-        switch (names[i]) {
-            case EGL_COMPOSITE_DEADLINE_ANDROID:
-                compositeDeadline = &values[i];
-                break;
-            case EGL_COMPOSITE_INTERVAL_ANDROID:
-                compositeInterval = &values[i];
-                break;
-            case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
-                compositeToPresentLatency = &values[i];
-                break;
-            default:
-                return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
-        }
-    }
-
-    int ret = native_window_get_compositor_timing(s->getNativeWindow(),
-            compositeDeadline, compositeInterval, compositeToPresentLatency);
-
-    switch (ret) {
-      case 0:
-        return EGL_TRUE;
-      case -ENOSYS:
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-      default:
-        // This should not happen. Return an error that is not in the spec
-        // so it's obvious something is very wrong.
-        ALOGE("eglGetCompositorTiming: Unexpected error.");
-        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
-    }
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCompositorTimingANDROID(dpy, surface, numTimestamps, names, values);
 }
 
-EGLBoolean eglGetCompositorTimingSupportedANDROID(
-        EGLDisplay dpy, EGLSurface surface, EGLint name)
-{
+EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint name) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-
-    ANativeWindow* window = s->getNativeWindow();
-    if (!window) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    switch (name) {
-        case EGL_COMPOSITE_DEADLINE_ANDROID:
-        case EGL_COMPOSITE_INTERVAL_ANDROID:
-        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
-            return EGL_TRUE;
-        default:
-            return EGL_FALSE;
-    }
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetCompositorTimingSupportedANDROID(dpy, surface, name);
 }
 
-EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
-        EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
-        EGLnsecsANDROID *values)
-{
+EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId,
+                                        EGLint numTimestamps, const EGLint* timestamps,
+                                        EGLnsecsANDROID* values) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-
-    if (!s->getNativeWindow()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    nsecs_t* requestedPresentTime = nullptr;
-    nsecs_t* acquireTime = nullptr;
-    nsecs_t* latchTime = nullptr;
-    nsecs_t* firstRefreshStartTime = nullptr;
-    nsecs_t* gpuCompositionDoneTime = nullptr;
-    nsecs_t* lastRefreshStartTime = nullptr;
-    nsecs_t* displayPresentTime = nullptr;
-    nsecs_t* dequeueReadyTime = nullptr;
-    nsecs_t* releaseTime = nullptr;
-
-    for (int i = 0; i < numTimestamps; i++) {
-        switch (timestamps[i]) {
-            case EGL_REQUESTED_PRESENT_TIME_ANDROID:
-                requestedPresentTime = &values[i];
-                break;
-            case EGL_RENDERING_COMPLETE_TIME_ANDROID:
-                acquireTime = &values[i];
-                break;
-            case EGL_COMPOSITION_LATCH_TIME_ANDROID:
-                latchTime = &values[i];
-                break;
-            case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
-                firstRefreshStartTime = &values[i];
-                break;
-            case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
-                lastRefreshStartTime = &values[i];
-                break;
-            case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
-                gpuCompositionDoneTime = &values[i];
-                break;
-            case EGL_DISPLAY_PRESENT_TIME_ANDROID:
-                displayPresentTime = &values[i];
-                break;
-            case EGL_DEQUEUE_READY_TIME_ANDROID:
-                dequeueReadyTime = &values[i];
-                break;
-            case EGL_READS_DONE_TIME_ANDROID:
-                releaseTime = &values[i];
-                break;
-            default:
-                return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
-        }
-    }
-
-    int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
-            requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
-            lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
-            dequeueReadyTime, releaseTime);
-
-    switch (ret) {
-        case 0:
-            return EGL_TRUE;
-        case -ENOENT:
-            return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
-        case -ENOSYS:
-            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-        case -EINVAL:
-            return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
-        default:
-            // This should not happen. Return an error that is not in the spec
-            // so it's obvious something is very wrong.
-            ALOGE("eglGetFrameTimestamps: Unexpected error.");
-            return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
-    }
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetFrameTimestampsANDROID(dpy, surface, frameId, numTimestamps,
+                                                      timestamps, values);
 }
 
-EGLBoolean eglGetFrameTimestampSupportedANDROID(
-        EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
-{
+EGLBoolean eglGetFrameTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface,
+                                                EGLint timestamp) {
     clearError();
 
-    const egl_display_ptr dp = validate_display(dpy);
-    if (!dp) {
-        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-    }
-
-    SurfaceRef _s(dp.get(), surface);
-    if (!_s.get()) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    egl_surface_t const * const s = get_surface(surface);
-
-    ANativeWindow* window = s->getNativeWindow();
-    if (!window) {
-        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-    }
-
-    switch (timestamp) {
-        case EGL_COMPOSITE_DEADLINE_ANDROID:
-        case EGL_COMPOSITE_INTERVAL_ANDROID:
-        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
-        case EGL_REQUESTED_PRESENT_TIME_ANDROID:
-        case EGL_RENDERING_COMPLETE_TIME_ANDROID:
-        case EGL_COMPOSITION_LATCH_TIME_ANDROID:
-        case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
-        case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
-        case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
-        case EGL_DEQUEUE_READY_TIME_ANDROID:
-        case EGL_READS_DONE_TIME_ANDROID:
-            return EGL_TRUE;
-        case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
-            int value = 0;
-            window->query(window,
-                    NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
-            return value == 0 ? EGL_FALSE : EGL_TRUE;
-        }
-        default:
-            return EGL_FALSE;
-    }
+    egl_connection_t* const cnx = &gEGLImpl;
+    return cnx->platform.eglGetFrameTimestampSupportedANDROID(dpy, surface, timestamp);
 }
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
new file mode 100644
index 0000000..00caff2
--- /dev/null
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(__ANDROID__)
+
+#include <cutils/properties.h>
+#include "Loader.h"
+#include "egl_angle_platform.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#include <EGL/Platform.h>
+#pragma GCC diagnostic pop
+
+#include <android/dlext.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <time.h>
+#include <log/log.h>
+
+namespace angle {
+
+static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr;
+static ResetDisplayPlatformFunc angleResetDisplayPlatform = nullptr;
+
+static time_t startTime = time(nullptr);
+
+static const unsigned char* getTraceCategoryEnabledFlag(PlatformMethods* /*platform*/,
+                                                        const char* /*categoryName*/) {
+    // Returning ptr to 'g' (non-zero) to ALWAYS enable tracing initially.
+    // This ptr is what will be passed into "category_group_enabled" of addTraceEvent
+    static const unsigned char traceEnabled = 'g';
+    return &traceEnabled;
+}
+
+static double monotonicallyIncreasingTime(PlatformMethods* /*platform*/) {
+    return difftime(time(nullptr), startTime);
+}
+
+static void logError(PlatformMethods* /*platform*/, const char* errorMessage) {
+    ALOGE("ANGLE Error:%s", errorMessage);
+}
+
+static void logWarning(PlatformMethods* /*platform*/, const char* warningMessage) {
+    ALOGW("ANGLE Warn:%s", warningMessage);
+}
+
+static void logInfo(PlatformMethods* /*platform*/, const char* infoMessage) {
+    ALOGD("ANGLE Info:%s", infoMessage);
+}
+
+static TraceEventHandle addTraceEvent(
+        PlatformMethods* /**platform*/, char phase, const unsigned char* /*category_group_enabled*/,
+        const char* name, unsigned long long /*id*/, double /*timestamp*/, int /*num_args*/,
+        const char** /*arg_names*/, const unsigned char* /*arg_types*/,
+        const unsigned long long* /*arg_values*/, unsigned char /*flags*/) {
+    switch (phase) {
+        case 'B': {
+            ATRACE_BEGIN(name);
+            break;
+        }
+        case 'E': {
+            ATRACE_END();
+            break;
+        }
+        case 'I': {
+            ATRACE_NAME(name);
+            break;
+        }
+        default:
+            // Could handle other event types here
+            break;
+    }
+    // Return any non-zero handle to avoid assert in ANGLE
+    TraceEventHandle result = 1.0;
+    return result;
+}
+
+static void assignAnglePlatformMethods(PlatformMethods* platformMethods) {
+    platformMethods->addTraceEvent = addTraceEvent;
+    platformMethods->getTraceCategoryEnabledFlag = getTraceCategoryEnabledFlag;
+    platformMethods->monotonicallyIncreasingTime = monotonicallyIncreasingTime;
+    platformMethods->logError = logError;
+    platformMethods->logWarning = logWarning;
+    platformMethods->logInfo = logInfo;
+}
+
+// Initialize function ptrs for ANGLE PlatformMethods struct, used for systrace
+bool initializeAnglePlatform(EGLDisplay dpy) {
+    // Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform
+    android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
+    const android_dlextinfo dlextinfo = {
+            .flags = ANDROID_DLEXT_USE_NAMESPACE,
+            .library_namespace = ns,
+    };
+    void* so = android_dlopen_ext("libGLESv2_angle.so", RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+    angleGetDisplayPlatform =
+            reinterpret_cast<GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform"));
+
+    if (!angleGetDisplayPlatform) {
+        ALOGE("dlsym lookup of ANGLEGetDisplayPlatform in libEGL_angle failed!");
+        return false;
+    }
+
+    angleResetDisplayPlatform =
+            reinterpret_cast<ResetDisplayPlatformFunc>(
+                    eglGetProcAddress("ANGLEResetDisplayPlatform"));
+
+    PlatformMethods* platformMethods = nullptr;
+    if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames,
+                                                              g_NumPlatformMethods, nullptr,
+                                                              &platformMethods))) {
+        ALOGE("ANGLEGetDisplayPlatform call failed!");
+        return false;
+    }
+    if (platformMethods) {
+        assignAnglePlatformMethods(platformMethods);
+    } else {
+        ALOGE("In initializeAnglePlatform() platformMethods struct ptr is NULL. Not assigning "
+              "tracing function ptrs!");
+    }
+    return true;
+}
+
+void resetAnglePlatform(EGLDisplay dpy) {
+    if (angleResetDisplayPlatform) {
+        angleResetDisplayPlatform(dpy);
+    }
+}
+
+}; // namespace angle
+
+#endif // __ANDROID__
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/opengl/libs/EGL/egl_angle_platform.h
similarity index 68%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to opengl/libs/EGL/egl_angle_platform.h
index e6ac6bf..6c24aa5 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.h
@@ -14,14 +14,20 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
 
-namespace android {
-namespace mock {
+#if defined(__ANDROID__)
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+#include "egldefs.h"
 
-} // namespace mock
-} // namespace android
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include "egl_trace.h"
+
+namespace angle {
+
+bool initializeAnglePlatform(EGLDisplay dpy);
+void resetAnglePlatform(EGLDisplay dpy);
+
+}; // namespace angle
+
+#endif // __ANDROID__
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index ec548f3..bcf4961 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -95,7 +95,7 @@
                     reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
                             cnx->egl.eglGetProcAddress(
                                     "eglSetBlobCacheFuncsANDROID"));
-            if (eglSetBlobCacheFuncsANDROID == NULL) {
+            if (eglSetBlobCacheFuncsANDROID == nullptr) {
                 ALOGE("EGL_ANDROID_blob_cache advertised, "
                         "but unable to get eglSetBlobCacheFuncsANDROID");
                 return;
@@ -119,7 +119,7 @@
     if (mBlobCache) {
         mBlobCache->writeToFile();
     }
-    mBlobCache = NULL;
+    mBlobCache = nullptr;
 }
 
 void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 2aec249..c100db7 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -21,14 +21,19 @@
 
 #include "../egl_impl.h"
 
+#include <EGL/eglext_angle.h>
 #include <private/EGL/display.h>
 
+#include <cutils/properties.h>
+#include "Loader.h"
+#include "egl_angle_platform.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 <android/dlext.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
 
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
@@ -41,7 +46,8 @@
 // ----------------------------------------------------------------------------
 
 static char const * const sVendorString     = "Android";
-static char const * const sVersionString    = "1.4 Android META-EGL";
+static char const* const sVersionString14 = "1.4 Android META-EGL";
+static char const* const sVersionString15 = "1.5 Android META-EGL";
 static char const * const sClientApiString  = "OpenGL_ES";
 
 extern char const * const gBuiltinExtensionString;
@@ -114,15 +120,91 @@
     return false;
 }
 
-EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp) {
+EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp,
+                                               const EGLAttrib* attrib_list) {
     if (uintptr_t(disp) >= NUM_DISPLAYS)
-        return NULL;
+        return nullptr;
 
-    return sDisplay[uintptr_t(disp)].getDisplay(disp);
+    return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list);
 }
 
-EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) {
+static bool addAnglePlatformAttributes(egl_connection_t* const cnx,
+                                       std::vector<EGLAttrib>& attrs) {
+    intptr_t vendorEGL = (intptr_t)cnx->vendorEGL;
 
+    attrs.reserve(4 * 2);
+
+    attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
+    attrs.push_back(cnx->angleBackend);
+
+    switch (cnx->angleBackend) {
+        case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
+            ALOGV("%s: Requesting Vulkan ANGLE back-end", __FUNCTION__);
+            char prop[PROPERTY_VALUE_MAX];
+            property_get("debug.angle.validation", prop, "0");
+            attrs.push_back(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE);
+            attrs.push_back(atoi(prop));
+            break;
+        case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
+            ALOGV("%s: Requesting Default ANGLE back-end", __FUNCTION__);
+            break;
+        case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
+            ALOGV("%s: Requesting OpenGL ES ANGLE back-end", __FUNCTION__);
+            // NOTE: This is only valid if the backend is OpenGL
+            attrs.push_back(EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE);
+            attrs.push_back(vendorEGL);
+            break;
+        default:
+            ALOGV("%s: Requesting Unknown (%d) ANGLE back-end", __FUNCTION__, cnx->angleBackend);
+            break;
+    }
+    attrs.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE);
+    attrs.push_back(EGL_FALSE);
+
+    return true;
+}
+
+static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx,
+                                          const EGLAttrib* attrib_list, EGLint* error) {
+    EGLDisplay dpy = EGL_NO_DISPLAY;
+    *error = EGL_NONE;
+
+    if (cnx->egl.eglGetPlatformDisplay) {
+        std::vector<EGLAttrib> attrs;
+        if (attrib_list) {
+            for (const EGLAttrib* attr = attrib_list; *attr != EGL_NONE; attr += 2) {
+                attrs.push_back(attr[0]);
+                attrs.push_back(attr[1]);
+            }
+        }
+
+        if (!addAnglePlatformAttributes(cnx, attrs)) {
+            ALOGE("eglGetDisplay(%p) failed: Mismatch display request", display);
+            *error = EGL_BAD_PARAMETER;
+            return EGL_NO_DISPLAY;
+        }
+        attrs.push_back(EGL_NONE);
+
+        dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
+                                             reinterpret_cast<void*>(EGL_DEFAULT_DISPLAY),
+                                             attrs.data());
+        if (dpy == EGL_NO_DISPLAY) {
+            ALOGE("eglGetPlatformDisplay failed!");
+        } else {
+            if (!angle::initializeAnglePlatform(dpy)) {
+                ALOGE("initializeAnglePlatform failed!");
+            }
+        }
+    } else {
+        ALOGE("eglGetDisplay(%p) failed: Unable to look up eglGetPlatformDisplay from ANGLE",
+              display);
+    }
+
+    return dpy;
+}
+
+EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display,
+                                             const EGLAttrib* attrib_list) {
     std::lock_guard<std::mutex> _l(lock);
     ATRACE_CALL();
 
@@ -131,11 +213,37 @@
 
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && disp.dpy == EGL_NO_DISPLAY) {
-        EGLDisplay dpy = cnx->egl.eglGetDisplay(display);
+        EGLDisplay dpy = EGL_NO_DISPLAY;
+
+        if (cnx->useAngle) {
+            EGLint error;
+            dpy = getPlatformDisplayAngle(display, cnx, attrib_list, &error);
+            if (error != EGL_NONE) {
+                return setError(error, dpy);
+            }
+        }
+        if (dpy == EGL_NO_DISPLAY) {
+            // NOTE: eglGetPlatformDisplay with a empty attribute list
+            // behaves the same as eglGetDisplay
+            if (cnx->egl.eglGetPlatformDisplay) {
+                dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANDROID_KHR, display,
+                                                     attrib_list);
+            }
+
+            // It is possible that eglGetPlatformDisplay does not have a
+            // working implementation for Android platform; in that case,
+            // one last fallback to eglGetDisplay
+            if(dpy == EGL_NO_DISPLAY) {
+                if (attrib_list) {
+                    ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored");
+                }
+                dpy = cnx->egl.eglGetDisplay(display);
+            }
+        }
+
         disp.dpy = dpy;
         if (dpy == EGL_NO_DISPLAY) {
-            loader.close(cnx->dso);
-            cnx->dso = NULL;
+            loader.close(cnx);
         }
     }
 
@@ -148,13 +256,20 @@
         std::unique_lock<std::mutex> _l(refLock);
         refs++;
         if (refs > 1) {
-            if (major != NULL)
-                *major = VERSION_MAJOR;
-            if (minor != NULL)
-                *minor = VERSION_MINOR;
+            // We don't know what to report until we know what the
+            // driver supports. Make sure we are initialized before
+            // returning the version info.
             while(!eglIsInitialized) {
                 refCond.wait(_l);
             }
+            egl_connection_t* const cnx = &gEGLImpl;
+
+            // TODO: If device doesn't provide 1.4 or 1.5 then we'll be
+            // changing the behavior from the past where we always advertise
+            // version 1.4. May need to check that revision is valid
+            // before using cnx->major & cnx->minor
+            if (major != nullptr) *major = cnx->major;
+            if (minor != nullptr) *minor = cnx->minor;
             return EGL_TRUE;
         }
         while(eglIsInitialized) {
@@ -201,7 +316,52 @@
 
         // the query strings are per-display
         mVendorString = sVendorString;
-        mVersionString = sVersionString;
+        mVersionString.clear();
+        cnx->driverVersion = EGL_MAKE_VERSION(1, 4, 0);
+        if ((cnx->major == 1) && (cnx->minor == 5)) {
+            mVersionString = sVersionString15;
+            cnx->driverVersion = EGL_MAKE_VERSION(1, 5, 0);
+        } else if ((cnx->major == 1) && (cnx->minor == 4)) {
+            mVersionString = sVersionString14;
+            // Extensions needed for an EGL 1.4 implementation to be
+            // able to support EGL 1.5 functionality
+            std::vector<const char*> egl15extensions = {
+                    "EGL_EXT_client_extensions",
+                    // "EGL_EXT_platform_base",  // implemented by EGL runtime
+                    "EGL_KHR_image_base",
+                    "EGL_KHR_fence_sync",
+                    "EGL_KHR_wait_sync",
+                    "EGL_KHR_create_context",
+                    "EGL_EXT_create_context_robustness",
+                    "EGL_KHR_gl_colorspace",
+                    "EGL_ANDROID_native_fence_sync",
+            };
+            bool extensionsFound = true;
+            for (const auto& name : egl15extensions) {
+                extensionsFound &= findExtension(disp.queryString.extensions, name);
+                ALOGV("Extension %s: %s", name,
+                      findExtension(disp.queryString.extensions, name) ? "Found" : "Missing");
+            }
+            // NOTE: From the spec:
+            // Creation of fence sync objects requires support from the bound
+            // client API, and will not succeed unless the client API satisfies:
+            // client API is OpenGL ES, and either the OpenGL ES version is 3.0
+            // or greater, or the GL_OES_EGL_sync extension is supported.
+            // We don't have a way to check the GL_EXTENSIONS string at this
+            // point in the code, assume that GL_OES_EGL_sync is supported
+            // because EGL_KHR_fence_sync is supported (as verified above).
+            if (extensionsFound) {
+                // Have everything needed to emulate EGL 1.5 so report EGL 1.5
+                // to the application.
+                mVersionString = sVersionString15;
+                cnx->major = 1;
+                cnx->minor = 5;
+            }
+        }
+        if (mVersionString.empty()) {
+            ALOGW("Unexpected driver version: %d.%d, want 1.4 or 1.5", cnx->major, cnx->minor);
+            mVersionString = sVersionString14;
+        }
         mClientApiString = sClientApiString;
 
         mExtensionString = gBuiltinExtensionString;
@@ -218,7 +378,8 @@
         if (wideColorBoardConfig && hasColorSpaceSupport) {
             mExtensionString.append(
                     "EGL_EXT_gl_colorspace_scrgb EGL_EXT_gl_colorspace_scrgb_linear "
-                    "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 ");
+                    "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 "
+                    "EGL_EXT_gl_colorspace_display_p3_passthrough ");
         }
 
         bool hasHdrBoardConfig =
@@ -240,12 +401,6 @@
             if (len) {
                 // NOTE: we could avoid the copy if we had strnstr.
                 const std::string ext(start, len);
-                // Temporary hack: Adreno 530 driver exposes this extension under the draft
-                // KHR name, but during Khronos review it was decided to demote it to EXT.
-                if (ext == "EGL_EXT_image_gl_colorspace" &&
-                    findExtension(disp.queryString.extensions, "EGL_KHR_image_gl_colorspace")) {
-                    mExtensionString.append("EGL_EXT_image_gl_colorspace ");
-                }
                 if (findExtension(disp.queryString.extensions, ext.c_str(), len)) {
                     mExtensionString.append(ext + " ");
                 }
@@ -268,10 +423,12 @@
             traceGpuCompletion = true;
         }
 
-        if (major != NULL)
-            *major = VERSION_MAJOR;
-        if (minor != NULL)
-            *minor = VERSION_MINOR;
+        // TODO: If device doesn't provide 1.4 or 1.5 then we'll be
+        // changing the behavior from the past where we always advertise
+        // version 1.4. May need to check that revision is valid
+        // before using cnx->major & cnx->minor
+        if (major != nullptr) *major = cnx->major;
+        if (minor != nullptr) *minor = cnx->minor;
     }
 
     { // scope for refLock
@@ -311,6 +468,10 @@
 
         egl_connection_t* const cnx = &gEGLImpl;
         if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
+            // If we're using ANGLE reset any custom DisplayPlatform
+            if (cnx->useAngle) {
+                angle::resetAnglePlatform(disp.dpy);
+            }
             if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
                 ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
                         egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
@@ -361,8 +522,8 @@
     // by construction, these are either 0 or valid (possibly terminated)
     // it should be impossible for these to be invalid
     ContextRef _cur_c(cur_c);
-    SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL);
-    SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
+    SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : nullptr);
+    SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : nullptr);
 
     { // scope for the lock
         std::lock_guard<std::mutex> _l(lock);
@@ -387,8 +548,8 @@
     // by construction, these are either 0 or valid (possibly terminated)
     // it should be impossible for these to be invalid
     ContextRef _cur_c(cur_c);
-    SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL);
-    SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
+    SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : nullptr);
+    SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : nullptr);
 
     { // scope for the lock
         std::lock_guard<std::mutex> _l(lock);
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 79a9f08..36856b7 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -49,6 +49,7 @@
 class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
     static egl_display_t sDisplay[NUM_DISPLAYS];
     EGLDisplay getDisplay(EGLNativeDisplayType display);
+    EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list);
     void loseCurrentImpl(egl_context_t * cur_c);
 
 public:
@@ -72,7 +73,7 @@
     bool getObject(egl_object_t* object) const;
 
     static egl_display_t* get(EGLDisplay dpy);
-    static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp);
+    static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list);
 
     EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c,
             EGLSurface draw, EGLSurface read, EGLContext ctx,
@@ -160,7 +161,7 @@
     const egl_display_t* get() const { return mDpy; }
           egl_display_t* get()       { return mDpy; }
 
-    operator bool() const { return mDpy != NULL; }
+    operator bool() const { return mDpy != nullptr; }
 
 private:
     egl_display_t* mDpy;
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index b587a16..2921d51 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -44,6 +44,18 @@
 
 /* EGL 1.4 */
 
+/* EGL 1.5 */
+EGL_ENTRY(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage)
+EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSyncKHR, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync)
+EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSync, EGLint, EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglWaitSync, EGLDisplay, EGLSync, EGLint)
+
 /* EGL_EGLEXT_VERSION 3 */
 
 EGL_ENTRY(EGLBoolean,  eglLockSurfaceKHR,   EGLDisplay, EGLSurface, const EGLint *)
@@ -75,6 +87,10 @@
 EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR,   EGLDisplay, EGLNativeFileDescriptorKHR)
 EGL_ENTRY(EGLint,       eglWaitSyncKHR,         EGLDisplay, EGLSyncKHR, EGLint)
 
+/* EGL_EGLEXT_VERSION 20170627 */
+EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *)
+
 /* ANDROID extensions */
 
 EGL_ENTRY(EGLBoolean, eglSetSwapRectangleANDROID, EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint)
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
new file mode 100644
index 0000000..f936ac0
--- /dev/null
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -0,0 +1,436 @@
+/*
+ ** Copyright 2018, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "egl_layers.h"
+
+#include <EGL/egl.h>
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android/dlext.h>
+#include <cutils/properties.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <log/log.h>
+#include <nativebridge/native_bridge.h>
+#include <nativeloader/native_loader.h>
+#include <sys/prctl.h>
+
+namespace android {
+
+// GLES Layers
+//
+// - Layer discovery -
+// 1. Check for debug layer list from GraphicsEnv
+// 2. If none enabled, check system properties
+//
+// - Layer initializing -
+// TODO: ADD DETAIL ABOUT NEW INTERFACES
+// - InitializeLayer (provided by layer, called by loader)
+// - GetLayerProcAddress (provided by layer, called by loader)
+// - getNextLayerProcAddress (provided by loader, called by layer)
+//
+// 1. Walk through defs for egl and each gl version
+// 2. Call GetLayerProcAddress passing the name and the target hook entry point
+//   - This tells the layer the next point in the chain it should call
+// 3. Replace the hook with the layer's entry point
+//    - All entryoints will be present, anything unsupported by the driver will
+//      have gl_unimplemented
+//
+// - Extension layering -
+//  Not all functions are known to Android, so libEGL handles extensions.
+//  They are looked up by applications using eglGetProcAddress
+//  Layers can look them up with getNextLayerProcAddress
+
+const int kFuncCount = sizeof(platform_impl_t) / sizeof(char*) + sizeof(egl_t) / sizeof(char*) +
+        sizeof(gl_hooks_t) / sizeof(char*);
+
+typedef struct FunctionTable {
+    EGLFuncPointer x[kFuncCount];
+    EGLFuncPointer& operator[](int i) { return x[i]; }
+} FunctionTable;
+
+// TODO: Move these to class
+std::unordered_map<std::string, int> func_indices;
+// func_indices.reserve(kFuncCount);
+
+std::unordered_map<int, std::string> func_names;
+// func_names.reserve(kFuncCount);
+
+std::vector<FunctionTable> layer_functions;
+
+const void* getNextLayerProcAddress(void* layer_id, const char* name) {
+    // Use layer_id to find funcs for layer below current
+    // This is the same key provided in InitializeLayer
+    auto next_layer_funcs = reinterpret_cast<FunctionTable*>(layer_id);
+    EGLFuncPointer val;
+
+    if (func_indices.find(name) == func_indices.end()) {
+        // No entry for this function - it is an extension
+        // call down the GPA chain directly to the impl
+        ALOGV("getNextLayerProcAddress servicing %s", name);
+
+        // Look up which GPA we should use
+        int gpaIndex = func_indices["eglGetProcAddress"];
+        EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
+
+        ALOGV("Calling down the GPA chain (%llu) for %s", (unsigned long long)gpaNext, name);
+
+        // Call it for the requested function
+        typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
+        PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);
+
+        val = reinterpret_cast<EGLFuncPointer>(next(name));
+        ALOGV("Got back %llu for %s", (unsigned long long)val, name);
+
+        // We should store it now, but to do that, we need to move func_idx to the class so we can
+        // increment it separately
+        // TODO: Move func_idx to class and store the result of GPA
+        return reinterpret_cast<void*>(val);
+    }
+
+    // int index = func_indices[name];
+    // val = (*next_layer_funcs)[index];
+    // return reinterpret_cast<void*>(val);
+    return reinterpret_cast<void*>((*next_layer_funcs)[func_indices[name]]);
+}
+
+void SetupFuncMaps(FunctionTable& functions, char const* const* entries, EGLFuncPointer* curr,
+                   int& func_idx) {
+    while (*entries) {
+        const char* name = *entries;
+
+        // Some names overlap, only fill with initial entry
+        // This does mean that some indices will not be used
+        if (func_indices.find(name) == func_indices.end()) {
+            func_names[func_idx] = name;
+            func_indices[name] = func_idx;
+        }
+
+        // Populate layer_functions once with initial value
+        // These values will arrive in priority order, starting with platform entries
+        if (functions[func_idx] == nullptr) {
+            functions[func_idx] = *curr;
+        }
+
+        entries++;
+        curr++;
+        func_idx++;
+    }
+}
+
+LayerLoader& LayerLoader::getInstance() {
+    // This function is mutex protected in egl_init_drivers_locked and eglGetProcAddressImpl
+    static LayerLoader layer_loader;
+
+    if (!layer_loader.layers_loaded_) layer_loader.LoadLayers();
+
+    return layer_loader;
+}
+
+const char kSystemLayerLibraryDir[] = "/data/local/debug/gles";
+
+std::string LayerLoader::GetDebugLayers() {
+    // Layers can be specified at the Java level in GraphicsEnvironemnt
+    // gpu_debug_layers_gles = layer1:layer2:layerN
+    std::string debug_layers = android::GraphicsEnv::getInstance().getDebugLayersGLES();
+
+    if (debug_layers.empty()) {
+        // Only check system properties if Java settings are empty
+        char prop[PROPERTY_VALUE_MAX];
+        property_get("debug.gles.layers", prop, "");
+        debug_layers = prop;
+    }
+
+    return debug_layers;
+}
+
+EGLFuncPointer LayerLoader::ApplyLayer(layer_setup_func layer_setup, const char* name,
+                                       EGLFuncPointer next) {
+    // Walk through our list of LayerSetup functions (they will already be in reverse order) to
+    // build up a call chain from the driver
+
+    EGLFuncPointer layer_entry = next;
+
+    layer_entry = layer_setup(name, layer_entry);
+
+    if (next != layer_entry) {
+        ALOGV("We succeeded, replacing hook (%llu) with layer entry (%llu), for %s",
+              (unsigned long long)next, (unsigned long long)layer_entry, name);
+    }
+
+    return layer_entry;
+}
+
+EGLFuncPointer LayerLoader::ApplyLayers(const char* name, EGLFuncPointer next) {
+    if (!layers_loaded_ || layer_setup_.empty()) return next;
+
+    ALOGV("ApplyLayers called for %s with next (%llu), current_layer_ (%i)", name,
+          (unsigned long long)next, current_layer_);
+
+    EGLFuncPointer val = next;
+
+    // Only ApplyLayers for layers that have been setup, not all layers yet
+    for (unsigned i = 0; i < current_layer_; i++) {
+        ALOGV("ApplyLayers: Calling ApplyLayer with i = %i for %s with next (%llu)", i, name,
+              (unsigned long long)next);
+        val = ApplyLayer(layer_setup_[i], name, val);
+    }
+
+    ALOGV("ApplyLayers returning %llu for %s", (unsigned long long)val, name);
+
+    return val;
+}
+
+void LayerLoader::LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer* curr,
+                                       char const* const* entries) {
+    while (*entries) {
+        char const* name = *entries;
+
+        EGLFuncPointer prev = *curr;
+
+        // Pass the existing entry point into the layer, replace the call with return value
+        *curr = ApplyLayer(layer_setup, name, *curr);
+
+        if (prev != *curr) {
+            ALOGV("LayerPlatformEntries: Replaced (%llu) with platform entry (%llu), for %s",
+                  (unsigned long long)prev, (unsigned long long)*curr, name);
+        } else {
+            ALOGV("LayerPlatformEntries: No change(%llu) for %s, which means layer did not "
+                  "intercept",
+                  (unsigned long long)prev, name);
+        }
+
+        curr++;
+        entries++;
+    }
+}
+
+void LayerLoader::LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer* curr,
+                                     char const* const* entries) {
+    while (*entries) {
+        char const* name = *entries;
+        EGLFuncPointer prev = *curr;
+
+        // Only apply layers to driver entries if not handled by the platform
+        if (FindPlatformImplAddr(name) == nullptr) {
+            // Pass the existing entry point into the layer, replace the call with return value
+            *curr = ApplyLayer(layer_setup, name, *prev);
+
+            if (prev != *curr) {
+                ALOGV("LayerDriverEntries: Replaced (%llu) with platform entry (%llu), for %s",
+                      (unsigned long long)prev, (unsigned long long)*curr, name);
+            }
+
+        } else {
+            ALOGV("LayerDriverEntries: Skipped (%llu) for %s", (unsigned long long)prev, name);
+        }
+
+        curr++;
+        entries++;
+    }
+}
+
+bool LayerLoader::Initialized() {
+    return initialized_;
+}
+
+void LayerLoader::InitLayers(egl_connection_t* cnx) {
+    if (!layers_loaded_) return;
+
+    if (initialized_) return;
+
+    if (layer_setup_.empty()) {
+        initialized_ = true;
+        return;
+    }
+
+    // Include the driver in layer_functions
+    layer_functions.resize(layer_setup_.size() + 1);
+
+    // Walk through the initial lists and create layer_functions[0]
+    int func_idx = 0;
+    char const* const* entries;
+    EGLFuncPointer* curr;
+
+    entries = platform_names;
+    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
+    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+    ALOGV("InitLayers: func_idx after platform_names: %i", func_idx);
+
+    entries = egl_names;
+    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
+    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+    ALOGV("InitLayers: func_idx after egl_names: %i", func_idx);
+
+    entries = gl_names;
+    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
+    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+    ALOGV("InitLayers: func_idx after gl_names: %i", func_idx);
+
+    // Walk through each layer's entry points per API, starting just above the driver
+    for (current_layer_ = 0; current_layer_ < layer_setup_.size(); current_layer_++) {
+        // Init the layer with a key that points to layer just below it
+        layer_init_[current_layer_](reinterpret_cast<void*>(&layer_functions[current_layer_]),
+                                    reinterpret_cast<PFNEGLGETNEXTLAYERPROCADDRESSPROC>(
+                                            getNextLayerProcAddress));
+
+        // Check functions implemented by the platform
+        func_idx = 0;
+        entries = platform_names;
+        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
+        LayerPlatformEntries(layer_setup_[current_layer_], curr, entries);
+
+        // Populate next function table after layers have been applied
+        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+
+        // EGL
+        entries = egl_names;
+        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
+        LayerDriverEntries(layer_setup_[current_layer_], curr, entries);
+
+        // Populate next function table after layers have been applied
+        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+
+        // GLES 2+
+        // NOTE: We route calls to GLESv2 hooks, not GLESv1, so layering does not support GLES 1.x
+        // If it were added in the future, a different layer initialization model would be needed,
+        // that defers loading GLES entrypoints until after eglMakeCurrent, so two phase
+        // initialization.
+        entries = gl_names;
+        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
+        LayerDriverEntries(layer_setup_[current_layer_], curr, entries);
+
+        // Populate next function table after layers have been applied
+        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+    }
+
+    // We only want to apply layers once
+    initialized_ = true;
+}
+
+void LayerLoader::LoadLayers() {
+    std::string debug_layers = GetDebugLayers();
+
+    // If no layers are specified, we're done
+    if (debug_layers.empty()) return;
+
+    // Only enable the system search path for non-user builds
+    std::string system_path;
+    if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+        system_path = kSystemLayerLibraryDir;
+    }
+
+    ALOGI("Debug layer list: %s", debug_layers.c_str());
+    std::vector<std::string> layers = android::base::Split(debug_layers, ":");
+
+    // Load the layers in reverse order so we start with the driver's entrypoint and work our way up
+    for (int32_t i = layers.size() - 1; i >= 0; i--) {
+        // Check each layer path for the layer
+        std::vector<std::string> paths =
+                android::base::Split(android::GraphicsEnv::getInstance().getLayerPaths().c_str(),
+                                     ":");
+
+        if (!system_path.empty()) {
+            // Prepend the system paths so they override other layers
+            auto it = paths.begin();
+            paths.insert(it, system_path);
+        }
+
+        bool layer_found = false;
+        for (uint32_t j = 0; j < paths.size() && !layer_found; j++) {
+            std::string layer;
+
+            ALOGI("Searching %s for GLES layers", paths[j].c_str());
+
+            // Realpath will return null for non-existent files
+            android::base::Realpath(paths[j] + "/" + layers[i], &layer);
+
+            if (!layer.empty()) {
+                layer_found = true;
+                ALOGI("GLES layer found: %s", layer.c_str());
+
+                // Load the layer
+                //
+                // TODO: This code is common with Vulkan loader, refactor
+                //
+                // Libraries in the system layer library dir can't be loaded into
+                // the application namespace. That causes compatibility problems, since
+                // any symbol dependencies will be resolved by system libraries. They
+                // can't safely use libc++_shared, for example. Which is one reason
+                // (among several) we only allow them in non-user builds.
+                void* handle = nullptr;
+                auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
+                if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
+                    bool native_bridge = false;
+                    char* error_message = nullptr;
+                    handle = OpenNativeLibraryInNamespace(
+                        app_namespace, layer.c_str(), &native_bridge, &error_message);
+                    if (!handle) {
+                        ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
+                              error_message);
+                        android::NativeLoaderFreeErrorMessage(error_message);
+                        return;
+                    }
+
+                } else {
+                    handle = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL);
+                }
+
+                if (handle) {
+                    ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)handle,
+                          layers[i].c_str());
+                } else {
+                    // If the layer is found but can't be loaded, try setenforce 0
+                    const char* dlsym_error = dlerror();
+                    ALOGE("Failed to load layer %s with error: %s", layer.c_str(), dlsym_error);
+                    return;
+                }
+
+                // Find the layer's Initialize function
+                std::string init_func = "InitializeLayer";
+                ALOGV("Looking for entrypoint %s", init_func.c_str());
+
+                layer_init_func LayerInit =
+                        reinterpret_cast<layer_init_func>(dlsym(handle, init_func.c_str()));
+                if (LayerInit) {
+                    ALOGV("Found %s for layer %s", init_func.c_str(), layer.c_str());
+                    layer_init_.push_back(LayerInit);
+                } else {
+                    ALOGE("Failed to dlsym %s for layer %s", init_func.c_str(), layer.c_str());
+                    return;
+                }
+
+                // Find the layer's setup function
+                std::string setup_func = "GetLayerProcAddress";
+                ALOGV("Looking for entrypoint %s", setup_func.c_str());
+
+                layer_setup_func LayerSetup =
+                        reinterpret_cast<layer_setup_func>(dlsym(handle, setup_func.c_str()));
+                if (LayerSetup) {
+                    ALOGV("Found %s for layer %s", setup_func.c_str(), layer.c_str());
+                    layer_setup_.push_back(LayerSetup);
+                } else {
+                    ALOGE("Failed to dlsym %s for layer %s", setup_func.c_str(), layer.c_str());
+                    return;
+                }
+            }
+        }
+    }
+    // Track this so we only attempt to load these once
+    layers_loaded_ = true;
+}
+
+} // namespace android
diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h
new file mode 100644
index 0000000..e401b44
--- /dev/null
+++ b/opengl/libs/EGL/egl_layers.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EGL_LAYERS_H
+#define ANDROID_EGL_LAYERS_H
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <EGL/egldefs.h>
+
+#include "egl_platform_entries.h"
+
+typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
+
+namespace android {
+
+class LayerLoader {
+public:
+    static LayerLoader& getInstance();
+    ~LayerLoader(){};
+
+    typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*);
+    typedef EGLFuncPointer (*layer_init_func)(
+            const void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address);
+    typedef EGLFuncPointer (*layer_setup_func)(const char* name, EGLFuncPointer next);
+
+    void LoadLayers();
+    void InitLayers(egl_connection_t*);
+    void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+    void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+    bool Initialized();
+    std::string GetDebugLayers();
+
+    EGLFuncPointer GetGpaNext(unsigned i);
+    EGLFuncPointer ApplyLayer(layer_setup_func layer_setup, const char* name, EGLFuncPointer next);
+    EGLFuncPointer ApplyLayers(const char* name, EGLFuncPointer next);
+
+    std::vector<layer_init_func> layer_init_;
+    std::vector<layer_setup_func> layer_setup_;
+
+private:
+    LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0){};
+    bool layers_loaded_;
+    bool initialized_;
+    unsigned current_layer_;
+};
+
+}; // namespace android
+
+#endif // ANDROID_EGL_LAYERS_H
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index f879254..ff4fe2d 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -81,14 +81,14 @@
 }
 
 egl_surface_t::~egl_surface_t() {
-    if (win != NULL) {
+    if (win != nullptr) {
         disconnect();
         win->decStrong(this);
     }
 }
 
 void egl_surface_t::disconnect() {
-    if (win != NULL && connected) {
+    if (win != nullptr && connected) {
         native_window_set_buffers_format(win, 0);
         if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) {
             ALOGW("EGLNativeWindowType %p disconnect failed", win);
@@ -281,12 +281,12 @@
 egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
         egl_connection_t const* cnx, int version) :
     egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
-            config(config), read(0), draw(0), cnx(cnx), version(version) {
+            config(config), read(nullptr), draw(nullptr), cnx(cnx), version(version) {
 }
 
 void egl_context_t::onLooseCurrent() {
-    read = NULL;
-    draw = NULL;
+    read = nullptr;
+    draw = nullptr;
 }
 
 void egl_context_t::onMakeCurrent(EGLSurface draw, EGLSurface read) {
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 4e1de5c..fb2bdf4 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -67,7 +67,7 @@
     public:
         ~LocalRef();
         explicit LocalRef(egl_object_t* rhs);
-        explicit LocalRef(egl_display_t const* display, T o) : ref(0) {
+        explicit LocalRef(egl_display_t const* display, T o) : ref(nullptr) {
             egl_object_t* native = reinterpret_cast<N*>(o);
             if (o && egl_object_t::get(display, native)) {
                 ref = native;
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
new file mode 100644
index 0000000..79166a7
--- /dev/null
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -0,0 +1,2699 @@
+/*
+ ** Copyright 2007, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "egl_platform_entries.h"
+
+#include <ctype.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hardware/gralloc1.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <EGL/eglext_angle.h>
+
+#include <android/hardware_buffer.h>
+#include <android-base/strings.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <private/android/AHardwareBufferHelpers.h>
+
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <log/log.h>
+
+#include <condition_variable>
+#include <deque>
+#include <mutex>
+#include <unordered_map>
+#include <string>
+#include <thread>
+
+#include "../egl_impl.h"
+
+#include "egl_display.h"
+#include "egl_object.h"
+#include "egl_layers.h"
+#include "egl_tls.h"
+#include "egl_trace.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+using nsecs_t = int64_t;
+
+struct extention_map_t {
+    const char* name;
+    __eglMustCastToProperFunctionPointerType address;
+};
+
+/*
+ * This is the list of EGL extensions exposed to applications.
+ *
+ * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL
+ * wrapper and are always available.
+ *
+ * The rest (gExtensionString) depend on support in the EGL driver, and are
+ * only available if the driver supports them. However, some of these must be
+ * supported because they are used by the Android system itself; these are
+ * listed as mandatory below and are required by the CDD. The system *assumes*
+ * the mandatory extensions are present and may not function properly if some
+ * are missing.
+ *
+ * NOTE: Both strings MUST have a single space as the last character.
+ */
+
+extern char const * const gBuiltinExtensionString;
+extern char const * const gExtensionString;
+
+// clang-format off
+// Extensions implemented by the EGL wrapper.
+char const * const gBuiltinExtensionString =
+        "EGL_KHR_get_all_proc_addresses "
+        "EGL_ANDROID_presentation_time "
+        "EGL_KHR_swap_buffers_with_damage "
+        "EGL_ANDROID_get_native_client_buffer "
+        "EGL_ANDROID_front_buffer_auto_refresh "
+        "EGL_ANDROID_get_frame_timestamps "
+        "EGL_EXT_surface_SMPTE2086_metadata "
+        "EGL_EXT_surface_CTA861_3_metadata "
+        ;
+
+// Whitelist of extensions exposed to applications if implemented in the vendor driver.
+char const * const gExtensionString  =
+        "EGL_KHR_image "                        // mandatory
+        "EGL_KHR_image_base "                   // mandatory
+        "EGL_EXT_image_gl_colorspace "
+        "EGL_KHR_image_pixmap "
+        "EGL_KHR_lock_surface "
+        "EGL_KHR_gl_colorspace "
+        "EGL_KHR_gl_texture_2D_image "
+        "EGL_KHR_gl_texture_3D_image "
+        "EGL_KHR_gl_texture_cubemap_image "
+        "EGL_KHR_gl_renderbuffer_image "
+        "EGL_KHR_reusable_sync "
+        "EGL_KHR_fence_sync "
+        "EGL_KHR_create_context "
+        "EGL_KHR_config_attribs "
+        "EGL_KHR_surfaceless_context "
+        "EGL_KHR_stream "
+        "EGL_KHR_stream_fifo "
+        "EGL_KHR_stream_producer_eglsurface "
+        "EGL_KHR_stream_consumer_gltexture "
+        "EGL_KHR_stream_cross_process_fd "
+        "EGL_EXT_create_context_robustness "
+        "EGL_NV_system_time "
+        "EGL_ANDROID_image_native_buffer "      // mandatory
+        "EGL_KHR_wait_sync "                    // strongly recommended
+        "EGL_ANDROID_recordable "               // mandatory
+        "EGL_KHR_partial_update "               // strongly recommended
+        "EGL_EXT_pixel_format_float "
+        "EGL_EXT_buffer_age "                   // strongly recommended with partial_update
+        "EGL_KHR_create_context_no_error "
+        "EGL_KHR_mutable_render_buffer "
+        "EGL_EXT_yuv_surface "
+        "EGL_EXT_protected_content "
+        "EGL_IMG_context_priority "
+        "EGL_KHR_no_config_context "
+        ;
+
+char const * const gClientExtensionString =
+        "EGL_EXT_client_extensions "
+        "EGL_KHR_platform_android "
+        "EGL_ANGLE_platform_angle";
+// clang-format on
+
+// extensions not exposed to applications but used by the ANDROID system
+//      "EGL_ANDROID_blob_cache "               // strongly recommended
+//      "EGL_IMG_hibernate_process "            // optional
+//      "EGL_ANDROID_native_fence_sync "        // strongly recommended
+//      "EGL_ANDROID_framebuffer_target "       // mandatory for HWC 1.1
+
+/*
+ * EGL Extensions entry-points exposed to 3rd party applications
+ * (keep in sync with gExtensionString above)
+ *
+ */
+static const extention_map_t sExtensionMap[] = {
+    // EGL_KHR_lock_surface
+    { "eglLockSurfaceKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
+    { "eglUnlockSurfaceKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
+
+    // EGL_KHR_image, EGL_KHR_image_base
+    { "eglCreateImageKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
+    { "eglDestroyImageKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
+
+    // EGL_KHR_reusable_sync, EGL_KHR_fence_sync
+    { "eglCreateSyncKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
+    { "eglDestroySyncKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
+    { "eglClientWaitSyncKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
+    { "eglSignalSyncKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
+    { "eglGetSyncAttribKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
+
+    // EGL_NV_system_time
+    { "eglGetSystemTimeFrequencyNV",
+            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
+    { "eglGetSystemTimeNV",
+            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
+
+    // EGL_KHR_wait_sync
+    { "eglWaitSyncKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
+
+    // EGL_ANDROID_presentation_time
+    { "eglPresentationTimeANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
+
+    // EGL_KHR_swap_buffers_with_damage
+    { "eglSwapBuffersWithDamageKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
+
+    // EGL_ANDROID_get_native_client_buffer
+    { "eglGetNativeClientBufferANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
+
+    // EGL_KHR_partial_update
+    { "eglSetDamageRegionKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
+
+    { "eglCreateStreamKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
+    { "eglDestroyStreamKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
+    { "eglStreamAttribKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
+    { "eglQueryStreamKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
+    { "eglQueryStreamu64KHR",
+            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
+    { "eglQueryStreamTimeKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
+    { "eglCreateStreamProducerSurfaceKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
+    { "eglStreamConsumerGLTextureExternalKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
+    { "eglStreamConsumerAcquireKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
+    { "eglStreamConsumerReleaseKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
+    { "eglGetStreamFileDescriptorKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
+    { "eglCreateStreamFromFileDescriptorKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+
+    // EGL_ANDROID_get_frame_timestamps
+    { "eglGetNextFrameIdANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
+    { "eglGetCompositorTimingANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
+    { "eglGetCompositorTimingSupportedANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
+    { "eglGetFrameTimestampsANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+    { "eglGetFrameTimestampSupportedANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
+
+    // EGL_ANDROID_native_fence_sync
+    { "eglDupNativeFenceFDANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
+};
+
+/*
+ * These extensions entry-points should not be exposed to applications.
+ * They're used internally by the Android EGL layer.
+ */
+#define FILTER_EXTENSIONS(procname) \
+        (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") ||    \
+         !strcmp((procname), "eglHibernateProcessIMG")      ||    \
+         !strcmp((procname), "eglAwakenProcessIMG"))
+
+// accesses protected by sExtensionMapMutex
+static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
+
+static int sGLExtentionSlot = 0;
+static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void(*findProcAddress(const char* name,
+        const extention_map_t* map, size_t n))() {
+    for (uint32_t i=0 ; i<n ; i++) {
+        if (!strcmp(name, map[i].name)) {
+            return map[i].address;
+        }
+    }
+    return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
+extern EGLBoolean egl_init_drivers();
+extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
+extern gl_hooks_t gHooksTrace;
+
+// ----------------------------------------------------------------------------
+
+static inline EGLContext getContext() { return egl_tls_t::getContext(); }
+
+// ----------------------------------------------------------------------------
+
+static EGLDisplay eglGetPlatformDisplayTmpl(EGLenum platform, EGLNativeDisplayType display,
+                                            const EGLAttrib* attrib_list) {
+    if (platform != EGL_PLATFORM_ANDROID_KHR) {
+        return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
+    }
+
+    uintptr_t index = reinterpret_cast<uintptr_t>(display);
+    if (index >= NUM_DISPLAYS) {
+        return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
+    }
+
+    EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display, attrib_list);
+    return dpy;
+}
+
+EGLDisplay eglGetDisplayImpl(EGLNativeDisplayType display) {
+    return eglGetPlatformDisplayTmpl(EGL_PLATFORM_ANDROID_KHR, display, nullptr);
+}
+
+EGLDisplay eglGetPlatformDisplayImpl(EGLenum platform, void* native_display,
+                                     const EGLAttrib* attrib_list) {
+    return eglGetPlatformDisplayTmpl(platform, static_cast<EGLNativeDisplayType>(native_display),
+                                     attrib_list);
+}
+
+// ----------------------------------------------------------------------------
+// Initialization
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor)
+{
+    egl_display_ptr dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+    EGLBoolean res = dp->initialize(major, minor);
+
+    return res;
+}
+
+EGLBoolean eglTerminateImpl(EGLDisplay dpy)
+{
+    // NOTE: don't unload the drivers b/c some APIs can be called
+    // after eglTerminate() has been called. eglTerminate() only
+    // terminates an EGLDisplay, not a EGL itself.
+
+    egl_display_ptr dp = get_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+    EGLBoolean res = dp->terminate();
+
+    return res;
+}
+
+// ----------------------------------------------------------------------------
+// configuration
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglGetConfigsImpl(EGLDisplay dpy,
+                             EGLConfig *configs,
+                             EGLint config_size, EGLint *num_config)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    if (num_config==nullptr) {
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+    }
+
+    EGLBoolean res = EGL_FALSE;
+    *num_config = 0;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        res = cnx->egl.eglGetConfigs(
+                dp->disp.dpy, configs, config_size, num_config);
+    }
+
+    return res;
+}
+
+EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list,
+                                EGLConfig *configs, EGLint config_size,
+                                EGLint *num_config)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    if (num_config==nullptr) {
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+    }
+
+    EGLBoolean res = EGL_FALSE;
+    *num_config = 0;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        if (attrib_list) {
+            char value[PROPERTY_VALUE_MAX];
+            property_get("debug.egl.force_msaa", value, "false");
+
+            if (!strcmp(value, "true")) {
+                size_t attribCount = 0;
+                EGLint attrib = attrib_list[0];
+
+                // Only enable MSAA if the context is OpenGL ES 2.0 and
+                // if no caveat is requested
+                const EGLint *attribRendererable = nullptr;
+                const EGLint *attribCaveat = nullptr;
+
+                // Count the number of attributes and look for
+                // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
+                while (attrib != EGL_NONE) {
+                    attrib = attrib_list[attribCount];
+                    switch (attrib) {
+                        case EGL_RENDERABLE_TYPE:
+                            attribRendererable = &attrib_list[attribCount];
+                            break;
+                        case EGL_CONFIG_CAVEAT:
+                            attribCaveat = &attrib_list[attribCount];
+                            break;
+                        default:
+                            break;
+                    }
+                    attribCount++;
+                }
+
+                if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
+                        (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+
+                    // Insert 2 extra attributes to force-enable MSAA 4x
+                    EGLint aaAttribs[attribCount + 4];
+                    aaAttribs[0] = EGL_SAMPLE_BUFFERS;
+                    aaAttribs[1] = 1;
+                    aaAttribs[2] = EGL_SAMPLES;
+                    aaAttribs[3] = 4;
+
+                    memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
+
+                    EGLint numConfigAA;
+                    EGLBoolean resAA = cnx->egl.eglChooseConfig(
+                            dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
+
+                    if (resAA == EGL_TRUE && numConfigAA > 0) {
+                        ALOGD("Enabling MSAA 4x");
+                        *num_config = numConfigAA;
+                        return resAA;
+                    }
+                }
+            }
+        }
+
+        res = cnx->egl.eglChooseConfig(
+                dp->disp.dpy, attrib_list, configs, config_size, num_config);
+    }
+    return res;
+}
+
+EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config,
+        EGLint attribute, EGLint *value)
+{
+    egl_connection_t* cnx = nullptr;
+    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (!dp) return EGL_FALSE;
+
+    return cnx->egl.eglGetConfigAttrib(
+            dp->disp.dpy, config, attribute, value);
+}
+
+// ----------------------------------------------------------------------------
+// surfaces
+// ----------------------------------------------------------------------------
+
+// Translates EGL color spaces to Android data spaces.
+static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) {
+    if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
+        return HAL_DATASPACE_UNKNOWN;
+    } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
+        return HAL_DATASPACE_V0_SRGB;
+    } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
+        return HAL_DATASPACE_DISPLAY_P3;
+    } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) {
+        return HAL_DATASPACE_DISPLAY_P3_LINEAR;
+    } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT) {
+        return HAL_DATASPACE_DISPLAY_P3;
+    } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) {
+        return HAL_DATASPACE_V0_SCRGB;
+    } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
+        return HAL_DATASPACE_V0_SCRGB_LINEAR;
+    } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
+        return HAL_DATASPACE_BT2020_LINEAR;
+    } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
+        return HAL_DATASPACE_BT2020_PQ;
+    }
+    return HAL_DATASPACE_UNKNOWN;
+}
+
+// Get the colorspace value that should be reported from queries. When the colorspace
+// is unknown (no attribute passed), default to reporting LINEAR.
+static EGLint getReportedColorSpace(EGLint colorspace) {
+    return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace;
+}
+
+// Returns a list of color spaces understood by the vendor EGL driver.
+static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) {
+    std::vector<EGLint> colorSpaces;
+
+    // sRGB and linear are always supported when color space support is present.
+    colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+    colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
+
+    if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
+    }
+    if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT);
+    }
+    if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb_linear")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
+    }
+    if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_linear")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
+    }
+    if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_pq")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
+    }
+    if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
+    }
+    if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_passthrough")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT);
+    }
+    return colorSpaces;
+}
+
+// Cleans up color space related parameters that the driver does not understand.
+// If there is no color space attribute in attrib_list, colorSpace is left
+// unmodified.
+template <typename AttrType>
+static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window,
+                                    const AttrType* attrib_list, EGLint* colorSpace,
+                                    std::vector<AttrType>* strippedAttribList) {
+    for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
+        bool copyAttribute = true;
+        if (attr[0] == EGL_GL_COLORSPACE_KHR) {
+            switch (attr[1]) {
+                case EGL_GL_COLORSPACE_LINEAR_KHR:
+                case EGL_GL_COLORSPACE_SRGB_KHR:
+                case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
+                case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
+                case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT:
+                case EGL_GL_COLORSPACE_SCRGB_EXT:
+                case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT:
+                case EGL_GL_COLORSPACE_BT2020_PQ_EXT:
+                case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT:
+                    // Fail immediately if the driver doesn't have color space support at all.
+                    if (!dp->hasColorSpaceSupport) return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
+                    break;
+                default:
+                    // BAD_ATTRIBUTE if attr is not any of the EGL_GL_COLORSPACE_*
+                    return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
+            }
+            *colorSpace = static_cast<EGLint>(attr[1]);
+
+            // Strip the attribute if the driver doesn't understand it.
+            copyAttribute = false;
+            std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp);
+            for (auto driverColorSpace : driverColorSpaces) {
+                if (static_cast<EGLint>(attr[1]) == driverColorSpace) {
+                    copyAttribute = true;
+                    break;
+                }
+            }
+
+            // If the driver doesn't understand it, we should map sRGB-encoded P3 to
+            // sRGB rather than just dropping the colorspace on the floor.
+            // For this format, the driver is expected to apply the sRGB
+            // transfer function during framebuffer operations.
+            if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
+                strippedAttribList->push_back(attr[0]);
+                strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+            }
+        }
+        if (copyAttribute) {
+            strippedAttribList->push_back(attr[0]);
+            strippedAttribList->push_back(attr[1]);
+        }
+    }
+    // Terminate the attribute list.
+    strippedAttribList->push_back(EGL_NONE);
+
+    // If the passed color space has wide color gamut, check whether the target native window
+    // supports wide color.
+    const bool colorSpaceIsNarrow = *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR ||
+            *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR || *colorSpace == EGL_UNKNOWN;
+    if (window && !colorSpaceIsNarrow) {
+        bool windowSupportsWideColor = true;
+        // Ordinarily we'd put a call to native_window_get_wide_color_support
+        // at the beginning of the function so that we'll have the
+        // result when needed elsewhere in the function.
+        // However, because eglCreateWindowSurface is called by SurfaceFlinger and
+        // SurfaceFlinger is required to answer the call below we would
+        // end up in a deadlock situation. By moving the call to only happen
+        // if the application has specifically asked for wide-color we avoid
+        // the deadlock with SurfaceFlinger since it will not ask for a
+        // wide-color surface.
+        int err = native_window_get_wide_color_support(window, &windowSupportsWideColor);
+
+        if (err) {
+            ALOGE("processAttributes: invalid window (win=%p) "
+                  "failed (%#x) (already connected to another API?)",
+                  window, err);
+            return setError(EGL_BAD_NATIVE_WINDOW, EGL_FALSE);
+        }
+        if (!windowSupportsWideColor) {
+            // Application has asked for a wide-color colorspace but
+            // wide-color support isn't available on the display the window is on.
+            return setError(EGL_BAD_MATCH, EGL_FALSE);
+        }
+    }
+    return true;
+}
+
+// Note: This only works for existing GLenum's that are all 32bits.
+// If you have 64bit attributes (e.g. pointers) you shouldn't be calling this.
+void convertAttribs(const EGLAttrib* attribList, std::vector<EGLint>& newList) {
+    for (const EGLAttrib* attr = attribList; attr && attr[0] != EGL_NONE; attr += 2) {
+        newList.push_back(static_cast<EGLint>(attr[0]));
+        newList.push_back(static_cast<EGLint>(attr[1]));
+    }
+    newList.push_back(EGL_NONE);
+}
+
+// Gets the native pixel format corrsponding to the passed EGLConfig.
+void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
+                          android_pixel_format* format) {
+    // Set the native window's buffers format to match what this config requests.
+    // Whether to use sRGB gamma is not part of the EGLconfig, but is part
+    // of our native format. So if sRGB gamma is requested, we have to
+    // modify the EGLconfig's format before setting the native window's
+    // format.
+
+    EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType);
+
+    EGLint a = 0;
+    EGLint r, g, b;
+    r = g = b = 0;
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
+    EGLint colorDepth = r + g + b;
+
+    // Today, the driver only understands sRGB and linear on 888X
+    // formats. Strip other colorspaces from the attribute list and
+    // only use them to set the dataspace via
+    // native_window_set_buffers_dataspace
+    // if pixel format is RGBX 8888
+    //    TBD: Can test for future extensions that indicate that driver
+    //    handles requested color space and we can let it through.
+    //    allow SRGB and LINEAR. All others need to be stripped.
+    // else if 565, 4444
+    //    TBD: Can we assume these are supported if 8888 is?
+    // else if FP16 or 1010102
+    //    strip colorspace from attribs.
+    // endif
+    if (a == 0) {
+        if (colorDepth <= 16) {
+            *format = HAL_PIXEL_FORMAT_RGB_565;
+        } else {
+            if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+                if (colorDepth > 24) {
+                    *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+                } else {
+                    *format = HAL_PIXEL_FORMAT_RGBX_8888;
+                }
+            } else {
+                *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+            }
+        }
+    } else {
+        if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+            if (colorDepth > 24) {
+                *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+            } else {
+                *format = HAL_PIXEL_FORMAT_RGBA_8888;
+            }
+        } else {
+            *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+        }
+    }
+}
+
+EGLBoolean sendSurfaceMetadata(egl_surface_t* s) {
+    android_smpte2086_metadata smpteMetadata;
+    if (s->getSmpte2086Metadata(smpteMetadata)) {
+        int err =
+                native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata);
+        s->resetSmpte2086Metadata();
+        if (err != 0) {
+            ALOGE("error setting native window smpte2086 metadata: %s (%d)", strerror(-err), err);
+            return EGL_FALSE;
+        }
+    }
+    android_cta861_3_metadata cta8613Metadata;
+    if (s->getCta8613Metadata(cta8613Metadata)) {
+        int err =
+                native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata);
+        s->resetCta8613Metadata();
+        if (err != 0) {
+            ALOGE("error setting native window CTS 861.3 metadata: %s (%d)", strerror(-err), err);
+            return EGL_FALSE;
+        }
+    }
+    return EGL_TRUE;
+}
+
+template <typename AttrType, typename CreateFuncType>
+EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config,
+                                      ANativeWindow* window, const AttrType* attrib_list,
+                                      CreateFuncType createWindowSurfaceFunc) {
+    const AttrType* origAttribList = attrib_list;
+
+    if (!window) {
+        return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+    }
+
+    int value = 0;
+    window->query(window, NATIVE_WINDOW_IS_VALID, &value);
+    if (!value) {
+        return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+    }
+
+    // NOTE: When using Vulkan backend, the Vulkan runtime makes all the
+    // native_window_* calls, so don't do them here.
+    if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+        int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+        if (result < 0) {
+            ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
+                  "failed (%#x) (already connected to another API?)",
+                  window, result);
+            return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
+        }
+    }
+
+    EGLDisplay iDpy = dp->disp.dpy;
+    android_pixel_format format;
+    getNativePixelFormat(iDpy, cnx, config, &format);
+
+    // now select correct colorspace and dataspace based on user's attribute list
+    EGLint colorSpace = EGL_UNKNOWN;
+    std::vector<AttrType> strippedAttribList;
+    if (!processAttributes<AttrType>(dp, window, attrib_list, &colorSpace, &strippedAttribList)) {
+        ALOGE("error invalid colorspace: %d", colorSpace);
+        if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+            native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+        }
+        return EGL_NO_SURFACE;
+    }
+    attrib_list = strippedAttribList.data();
+
+    if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+        int err = native_window_set_buffers_format(window, format);
+        if (err != 0) {
+            ALOGE("error setting native window pixel format: %s (%d)", strerror(-err), err);
+            native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+        }
+
+        android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace);
+        // Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN.
+        // HAL_DATASPACE_UNKNOWN is the default value, but it may have changed
+        // at this point.
+        err = native_window_set_buffers_data_space(window, dataSpace);
+        if (err != 0) {
+            ALOGE("error setting native window pixel dataSpace: %s (%d)", strerror(-err), err);
+            native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+        }
+    }
+
+    // the EGL spec requires that a new EGLSurface default to swap interval
+    // 1, so explicitly set that on the window here.
+    window->setSwapInterval(window, 1);
+
+    EGLSurface surface = createWindowSurfaceFunc(iDpy, config, window, attrib_list);
+    if (surface != EGL_NO_SURFACE) {
+        egl_surface_t* s = new egl_surface_t(dp.get(), config, window, surface,
+                                             getReportedColorSpace(colorSpace), cnx);
+        return s;
+    }
+
+    // EGLSurface creation failed
+    if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+        native_window_set_buffers_format(window, 0);
+        native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+    }
+    return EGL_NO_SURFACE;
+}
+
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay dpy, EGLConfig config,
+                                                               NativeWindowType window,
+                                                               const EGLint* attrib_list);
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC)(
+        EGLDisplay dpy, EGLConfig config, void* native_window, const EGLAttrib* attrib_list);
+
+EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
+                                      const EGLint* attrib_list) {
+    egl_connection_t* cnx = NULL;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        return eglCreateWindowSurfaceTmpl<
+                EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list,
+                                                       cnx->egl.eglCreateWindowSurface);
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window,
+                                              const EGLAttrib* attrib_list) {
+    egl_connection_t* cnx = NULL;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+            if (cnx->egl.eglCreatePlatformWindowSurface) {
+                return eglCreateWindowSurfaceTmpl<EGLAttrib, PFNEGLCREATEPLATFORMWINDOWSURFACEPROC>(
+                        dp, cnx, config, static_cast<ANativeWindow*>(native_window), attrib_list,
+                        cnx->egl.eglCreatePlatformWindowSurface);
+            }
+            // driver doesn't support native function, return EGL_BAD_DISPLAY
+            ALOGE("Driver indicates EGL 1.5 support, but does not have "
+                  "eglCreatePlatformWindowSurface");
+            return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
+        }
+
+        std::vector<EGLint> convertedAttribs;
+        convertAttribs(attrib_list, convertedAttribs);
+        if (cnx->egl.eglCreatePlatformWindowSurfaceEXT) {
+            return eglCreateWindowSurfaceTmpl<EGLint, PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(
+                    dp, cnx, config, static_cast<ANativeWindow*>(native_window),
+                    convertedAttribs.data(), cnx->egl.eglCreatePlatformWindowSurfaceEXT);
+        } else {
+            return eglCreateWindowSurfaceTmpl<
+                    EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config,
+                                                           static_cast<ANativeWindow*>(
+                                                                   native_window),
+                                                           convertedAttribs.data(),
+                                                           cnx->egl.eglCreateWindowSurface);
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePlatformPixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/,
+                                              void* /*native_pixmap*/,
+                                              const EGLAttrib* /*attrib_list*/) {
+    // Per EGL_KHR_platform_android:
+    // It is not valid to call eglCreatePlatformPixmapSurface with a <dpy> that
+    // belongs to the Android platform. Any such call fails and generates
+    // an EGL_BAD_PARAMETER error.
+
+    egl_connection_t* cnx = NULL;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/,
+                                      NativePixmapType /*pixmap*/, const EGLint* /*attrib_list*/) {
+    egl_connection_t* cnx = nullptr;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePbufferSurfaceImpl(EGLDisplay dpy, EGLConfig config,
+                                       const EGLint* attrib_list) {
+    egl_connection_t* cnx = nullptr;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        EGLDisplay iDpy = dp->disp.dpy;
+        android_pixel_format format;
+        getNativePixelFormat(iDpy, cnx, config, &format);
+
+        // Select correct colorspace based on user's attribute list
+        EGLint colorSpace = EGL_UNKNOWN;
+        std::vector<EGLint> strippedAttribList;
+        if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) {
+            ALOGE("error invalid colorspace: %d", colorSpace);
+            return EGL_NO_SURFACE;
+        }
+        attrib_list = strippedAttribList.data();
+
+        EGLSurface surface = cnx->egl.eglCreatePbufferSurface(dp->disp.dpy, config, attrib_list);
+        if (surface != EGL_NO_SURFACE) {
+            egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
+                                                 getReportedColorSpace(colorSpace), cnx);
+            return s;
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t* const s = get_surface(surface);
+    EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
+    if (result == EGL_TRUE) {
+        _s.terminate();
+    }
+    return result;
+}
+
+EGLBoolean eglQuerySurfaceImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute,
+                               EGLint* value) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const* const s = get_surface(surface);
+    if (s->getColorSpaceAttribute(attribute, value)) {
+        return EGL_TRUE;
+    } else if (s->getSmpte2086Attribute(attribute, value)) {
+        return EGL_TRUE;
+    } else if (s->getCta8613Attribute(attribute, value)) {
+        return EGL_TRUE;
+    }
+    return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value);
+}
+
+void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return;
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        setError(EGL_BAD_SURFACE, EGL_FALSE);
+    }
+}
+
+// ----------------------------------------------------------------------------
+// Contexts
+// ----------------------------------------------------------------------------
+
+EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config,
+                                EGLContext share_list, const EGLint *attrib_list)
+{
+    egl_connection_t* cnx = nullptr;
+    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
+        if (share_list != EGL_NO_CONTEXT) {
+            if (!ContextRef(dp.get(), share_list).get()) {
+                return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
+            }
+            egl_context_t* const c = get_context(share_list);
+            share_list = c->context;
+        }
+        EGLContext context = cnx->egl.eglCreateContext(
+                dp->disp.dpy, config, share_list, attrib_list);
+        if (context != EGL_NO_CONTEXT) {
+            // figure out if it's a GLESv1 or GLESv2
+            int version = 0;
+            if (attrib_list) {
+                while (*attrib_list != EGL_NONE) {
+                    GLint attr = *attrib_list++;
+                    GLint value = *attrib_list++;
+                    if (attr == EGL_CONTEXT_CLIENT_VERSION) {
+                        if (value == 1) {
+                            version = egl_connection_t::GLESv1_INDEX;
+                        } else if (value == 2 || value == 3) {
+                            version = egl_connection_t::GLESv2_INDEX;
+                        }
+                    }
+                };
+            }
+            egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
+                    version);
+            return c;
+        }
+    }
+    return EGL_NO_CONTEXT;
+}
+
+EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp)
+        return EGL_FALSE;
+
+    ContextRef _c(dp.get(), ctx);
+    if (!_c.get())
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+    egl_context_t * const c = get_context(ctx);
+    EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
+    if (result == EGL_TRUE) {
+        _c.terminate();
+    }
+    return result;
+}
+
+EGLBoolean eglMakeCurrentImpl(  EGLDisplay dpy, EGLSurface draw,
+                                EGLSurface read, EGLContext ctx)
+{
+    egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+    // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
+    // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
+    // a valid but uninitialized display.
+    if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
+         (draw != EGL_NO_SURFACE) ) {
+        if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+
+    // get a reference to the object passed in
+    ContextRef _c(dp.get(), ctx);
+    SurfaceRef _d(dp.get(), draw);
+    SurfaceRef _r(dp.get(), read);
+
+    // validate the context (if not EGL_NO_CONTEXT)
+    if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
+        // EGL_NO_CONTEXT is valid
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+    }
+
+    // these are the underlying implementation's object
+    EGLContext impl_ctx  = EGL_NO_CONTEXT;
+    EGLSurface impl_draw = EGL_NO_SURFACE;
+    EGLSurface impl_read = EGL_NO_SURFACE;
+
+    // these are our objects structs passed in
+    egl_context_t       * c = nullptr;
+    egl_surface_t const * d = nullptr;
+    egl_surface_t const * r = nullptr;
+
+    // these are the current objects structs
+    egl_context_t * cur_c = get_context(getContext());
+
+    if (ctx != EGL_NO_CONTEXT) {
+        c = get_context(ctx);
+        impl_ctx = c->context;
+    } else {
+        // no context given, use the implementation of the current context
+        if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
+            // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
+            return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE);
+        }
+        if (cur_c == nullptr) {
+            // no current context
+            // not an error, there is just no current context.
+            return EGL_TRUE;
+        }
+    }
+
+    // retrieve the underlying implementation's draw EGLSurface
+    if (draw != EGL_NO_SURFACE) {
+        if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        d = get_surface(draw);
+        impl_draw = d->surface;
+    }
+
+    // retrieve the underlying implementation's read EGLSurface
+    if (read != EGL_NO_SURFACE) {
+        if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        r = get_surface(read);
+        impl_read = r->surface;
+    }
+
+
+    EGLBoolean result = dp->makeCurrent(c, cur_c,
+            draw, read, ctx,
+            impl_draw, impl_read, impl_ctx);
+
+    if (result == EGL_TRUE) {
+        if (c) {
+            setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
+            egl_tls_t::setContext(ctx);
+            _c.acquire();
+            _r.acquire();
+            _d.acquire();
+        } else {
+            setGLHooksThreadSpecific(&gHooksNoContext);
+            egl_tls_t::setContext(EGL_NO_CONTEXT);
+        }
+    } else {
+        // this will ALOGE the error
+        egl_connection_t* const cnx = &gEGLImpl;
+        result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE);
+    }
+    return result;
+}
+
+EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx,
+                                EGLint attribute, EGLint *value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    ContextRef _c(dp.get(), ctx);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+    egl_context_t * const c = get_context(ctx);
+    return c->cnx->egl.eglQueryContext(
+            dp->disp.dpy, c->context, attribute, value);
+
+}
+
+EGLContext eglGetCurrentContextImpl(void)
+{
+    // could be called before eglInitialize(), but we wouldn't have a context
+    // then, and this function would correctly return EGL_NO_CONTEXT.
+    EGLContext ctx = getContext();
+    return ctx;
+}
+
+EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw)
+{
+    // could be called before eglInitialize(), but we wouldn't have a context
+    // then, and this function would correctly return EGL_NO_SURFACE.
+
+    EGLContext ctx = getContext();
+    if (ctx) {
+        egl_context_t const * const c = get_context(ctx);
+        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
+        switch (readdraw) {
+            case EGL_READ: return c->read;
+            case EGL_DRAW: return c->draw;
+            default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLDisplay eglGetCurrentDisplayImpl(void)
+{
+    // could be called before eglInitialize(), but we wouldn't have a context
+    // then, and this function would correctly return EGL_NO_DISPLAY.
+
+    EGLContext ctx = getContext();
+    if (ctx) {
+        egl_context_t const * const c = get_context(ctx);
+        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
+        return c->dpy;
+    }
+    return EGL_NO_DISPLAY;
+}
+
+EGLBoolean eglWaitGLImpl(void)
+{
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+    return cnx->egl.eglWaitGL();
+}
+
+EGLBoolean eglWaitNativeImpl(EGLint engine)
+{
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+    return cnx->egl.eglWaitNative(engine);
+}
+
+EGLint eglGetErrorImpl(void)
+{
+    EGLint err = EGL_SUCCESS;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso) {
+        err = cnx->egl.eglGetError();
+    }
+    if (err == EGL_SUCCESS) {
+        err = egl_tls_t::getError();
+    }
+    return err;
+}
+
+static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
+        const char* procname) {
+    const egl_connection_t* cnx = &gEGLImpl;
+    void* proc = nullptr;
+
+    proc = dlsym(cnx->libEgl, procname);
+    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+    proc = dlsym(cnx->libGles2, procname);
+    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+    proc = dlsym(cnx->libGles1, procname);
+    if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+    return nullptr;
+}
+
+__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname)
+{
+    if (FILTER_EXTENSIONS(procname)) {
+        return nullptr;
+    }
+
+    __eglMustCastToProperFunctionPointerType addr;
+    addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap));
+    if (addr) return addr;
+
+    addr = findBuiltinWrapper(procname);
+    if (addr) return addr;
+
+    // this protects accesses to sGLExtentionMap and sGLExtentionSlot
+    pthread_mutex_lock(&sExtensionMapMutex);
+
+    /*
+     * Since eglGetProcAddress() is not associated to anything, it needs
+     * to return a function pointer that "works" regardless of what
+     * the current context is.
+     *
+     * For this reason, we return a "forwarder", a small stub that takes
+     * care of calling the function associated with the context
+     * currently bound.
+     *
+     * We first look for extensions we've already resolved, if we're seeing
+     * this extension for the first time, we go through all our
+     * implementations and call eglGetProcAddress() and record the
+     * result in the appropriate implementation hooks and return the
+     * address of the forwarder corresponding to that hook set.
+     *
+     */
+
+    const std::string name(procname);
+
+    auto& extentionMap = sGLExtentionMap;
+    auto pos = extentionMap.find(name);
+    addr = (pos != extentionMap.end()) ? pos->second : nullptr;
+    const int slot = sGLExtentionSlot;
+
+    ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
+             "no more slots for eglGetProcAddress(\"%s\")",
+             procname);
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    LayerLoader& layer_loader(LayerLoader::getInstance());
+
+    if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
+
+        if (cnx->dso && cnx->egl.eglGetProcAddress) {
+
+            // Extensions are independent of the bound context
+            addr = cnx->egl.eglGetProcAddress(procname);
+            if (addr) {
+
+                // purposefully track the bottom of the stack in extensionMap
+                extentionMap[name] = addr;
+
+                // Apply layers
+                addr = layer_loader.ApplyLayers(procname, addr);
+
+                // Track the top most entry point
+                cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+                cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+                addr = gExtensionForwarders[slot];
+                sGLExtentionSlot++;
+            }
+        }
+
+    } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
+
+        // We've seen this func before, but we tracked the bottom, so re-apply layers
+        // More layers might have been enabled
+        addr = layer_loader.ApplyLayers(procname, addr);
+
+        // Track the top most entry point
+        cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+        cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+        addr = gExtensionForwarders[slot];
+    }
+
+    pthread_mutex_unlock(&sExtensionMapMutex);
+    return addr;
+}
+
+class FrameCompletionThread {
+public:
+
+    static void queueSync(EGLSyncKHR sync) {
+        static FrameCompletionThread thread;
+
+        char name[64];
+
+        std::lock_guard<std::mutex> lock(thread.mMutex);
+        snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued);
+        ATRACE_NAME(name);
+
+        thread.mQueue.push_back(sync);
+        thread.mCondition.notify_one();
+        thread.mFramesQueued++;
+        ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size()));
+    }
+
+private:
+
+    FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
+        std::thread thread(&FrameCompletionThread::loop, this);
+        thread.detach();
+    }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+    void loop() {
+        while (true) {
+            threadLoop();
+        }
+    }
+#pragma clang diagnostic pop
+
+    void threadLoop() {
+        EGLSyncKHR sync;
+        uint32_t frameNum;
+        {
+            std::unique_lock<std::mutex> lock(mMutex);
+            while (mQueue.empty()) {
+                mCondition.wait(lock);
+            }
+            sync = mQueue[0];
+            frameNum = mFramesCompleted;
+        }
+        EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        {
+            char name[64];
+            snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum);
+            ATRACE_NAME(name);
+
+            EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
+            if (result == EGL_FALSE) {
+                ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
+            } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+                ALOGE("FrameCompletion: timeout waiting for fence");
+            }
+            eglDestroySyncKHR(dpy, sync);
+        }
+        {
+            std::lock_guard<std::mutex> lock(mMutex);
+            mQueue.pop_front();
+            mFramesCompleted++;
+            ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size()));
+        }
+    }
+
+    uint32_t mFramesQueued;
+    uint32_t mFramesCompleted;
+    std::deque<EGLSyncKHR> mQueue;
+    std::condition_variable mCondition;
+    std::mutex mMutex;
+};
+
+EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw,
+        EGLint *rects, EGLint n_rects)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), draw);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t* const s = get_surface(draw);
+
+    if (CC_UNLIKELY(dp->traceGpuCompletion)) {
+        EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
+        if (sync != EGL_NO_SYNC_KHR) {
+            FrameCompletionThread::queueSync(sync);
+        }
+    }
+
+    if (CC_UNLIKELY(dp->finishOnSwap)) {
+        uint32_t pixel;
+        egl_context_t * const c = get_context( egl_tls_t::getContext() );
+        if (c) {
+            // glReadPixels() ensures that the frame is complete
+            s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
+                    GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
+        }
+    }
+
+    if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+        if (!sendSurfaceMetadata(s)) {
+            native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL);
+            return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE);
+        }
+    }
+
+    if (n_rects == 0) {
+        return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+    }
+
+    std::vector<android_native_rect_t> androidRects((size_t)n_rects);
+    for (int r = 0; r < n_rects; ++r) {
+        int offset = r * 4;
+        int x = rects[offset];
+        int y = rects[offset + 1];
+        int width = rects[offset + 2];
+        int height = rects[offset + 3];
+        android_native_rect_t androidRect;
+        androidRect.left = x;
+        androidRect.top = y + height;
+        androidRect.right = x + width;
+        androidRect.bottom = y;
+        androidRects.push_back(androidRect);
+    }
+    if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+        native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(),
+                                         androidRects.size());
+    }
+
+    if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
+        return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
+                rects, n_rects);
+    } else {
+        return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+    }
+}
+
+EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface)
+{
+    return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0);
+}
+
+EGLBoolean eglCopyBuffersImpl(  EGLDisplay dpy, EGLSurface surface,
+                                NativePixmapType target)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const * const s = get_surface(surface);
+    return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
+}
+
+const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name)
+{
+    if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) {
+        // Return list of client extensions
+        return gClientExtensionString;
+    }
+
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return (const char *) nullptr;
+
+    switch (name) {
+        case EGL_VENDOR:
+            return dp->getVendorString();
+        case EGL_VERSION:
+            return dp->getVersionString();
+        case EGL_EXTENSIONS:
+            return dp->getExtensionString();
+        case EGL_CLIENT_APIS:
+            return dp->getClientApiString();
+        default:
+            break;
+    }
+    return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+}
+
+EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return (const char *) nullptr;
+
+    switch (name) {
+        case EGL_VENDOR:
+            return dp->disp.queryString.vendor;
+        case EGL_VERSION:
+            return dp->disp.queryString.version;
+        case EGL_EXTENSIONS:
+            return dp->disp.queryString.extensions;
+        case EGL_CLIENT_APIS:
+            return dp->disp.queryString.clientApi;
+        default:
+            break;
+    }
+    return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+}
+
+// ----------------------------------------------------------------------------
+// EGL 1.1
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglSurfaceAttribImpl(
+        EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t * const s = get_surface(surface);
+
+    if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
+        if (!s->getNativeWindow()) {
+            setError(EGL_BAD_SURFACE, EGL_FALSE);
+        }
+        int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
+        return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    if (attribute == EGL_TIMESTAMPS_ANDROID) {
+        if (!s->getNativeWindow()) {
+            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        }
+        int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
+        return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    if (s->setSmpte2086Attribute(attribute, value)) {
+        return EGL_TRUE;
+    } else if (s->setCta8613Attribute(attribute, value)) {
+        return EGL_TRUE;
+    } else if (s->cnx->egl.eglSurfaceAttrib) {
+        return s->cnx->egl.eglSurfaceAttrib(
+                dp->disp.dpy, s->surface, attribute, value);
+    }
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglBindTexImageImpl(
+        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->egl.eglBindTexImage) {
+        return s->cnx->egl.eglBindTexImage(
+                dp->disp.dpy, s->surface, buffer);
+    }
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglReleaseTexImageImpl(
+        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->egl.eglReleaseTexImage) {
+        return s->cnx->egl.eglReleaseTexImage(
+                dp->disp.dpy, s->surface, buffer);
+    }
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean res = EGL_TRUE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglSwapInterval) {
+        res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval);
+    }
+
+    return res;
+}
+
+
+// ----------------------------------------------------------------------------
+// EGL 1.2
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglWaitClientImpl(void)
+{
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (!cnx->dso)
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+    EGLBoolean res;
+    if (cnx->egl.eglWaitClient) {
+        res = cnx->egl.eglWaitClient();
+    } else {
+        res = cnx->egl.eglWaitGL();
+    }
+    return res;
+}
+
+EGLBoolean eglBindAPIImpl(EGLenum api)
+{
+    // bind this API on all EGLs
+    EGLBoolean res = EGL_TRUE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglBindAPI) {
+        res = cnx->egl.eglBindAPI(api);
+    }
+    return res;
+}
+
+EGLenum eglQueryAPIImpl(void)
+{
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglQueryAPI) {
+        return cnx->egl.eglQueryAPI();
+    }
+
+    // or, it can only be OpenGL ES
+    return EGL_OPENGL_ES_API;
+}
+
+EGLBoolean eglReleaseThreadImpl(void)
+{
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglReleaseThread) {
+        cnx->egl.eglReleaseThread();
+    }
+
+    // If there is context bound to the thread, release it
+    egl_display_t::loseCurrent(get_context(getContext()));
+
+    egl_tls_t::clearTLS();
+    return EGL_TRUE;
+}
+
+EGLSurface eglCreatePbufferFromClientBufferImpl(
+          EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
+          EGLConfig config, const EGLint *attrib_list)
+{
+    egl_connection_t* cnx = nullptr;
+    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (!dp) return EGL_FALSE;
+    if (cnx->egl.eglCreatePbufferFromClientBuffer) {
+        return cnx->egl.eglCreatePbufferFromClientBuffer(
+                dp->disp.dpy, buftype, buffer, config, attrib_list);
+    }
+    return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 3
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface,
+        const EGLint *attrib_list)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->egl.eglLockSurfaceKHR) {
+        return s->cnx->egl.eglLockSurfaceKHR(
+                dp->disp.dpy, s->surface, attrib_list);
+    }
+    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get())
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->egl.eglUnlockSurfaceKHR) {
+        return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
+    }
+    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+}
+
+// Note: EGLImageKHR and EGLImage are the same thing so no need
+// to templatize that.
+template <typename AttrType, typename FuncType>
+EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+                               EGLClientBuffer buffer, const AttrType* attrib_list,
+                               FuncType eglCreateImageFunc) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_IMAGE_KHR;
+
+    ContextRef _c(dp.get(), ctx);
+    egl_context_t* const c = _c.get();
+
+    EGLImageKHR result = EGL_NO_IMAGE_KHR;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && eglCreateImageFunc) {
+        result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer,
+                                    attrib_list);
+    }
+    return result;
+}
+
+typedef EGLImage(EGLAPIENTRYP PFNEGLCREATEIMAGE)(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+                                                 EGLClientBuffer buffer,
+                                                 const EGLAttrib* attrib_list);
+
+EGLImageKHR eglCreateImageKHRImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+                                  EGLClientBuffer buffer, const EGLint* attrib_list) {
+    return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer,
+                                                                attrib_list,
+                                                                gEGLImpl.egl.eglCreateImageKHR);
+}
+
+EGLImage eglCreateImageImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
+                            const EGLAttrib* attrib_list) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglCreateImage) {
+            return eglCreateImageTmpl<EGLAttrib, PFNEGLCREATEIMAGE>(dpy, ctx, target, buffer,
+                                                                    attrib_list,
+                                                                    cnx->egl.eglCreateImage);
+        }
+        // driver doesn't support native function, return EGL_BAD_DISPLAY
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateImage");
+        return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE);
+    }
+
+    std::vector<EGLint> convertedAttribs;
+    convertAttribs(attrib_list, convertedAttribs);
+    return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer,
+                                                                convertedAttribs.data(),
+                                                                gEGLImpl.egl.eglCreateImageKHR);
+}
+
+EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img,
+                               PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && destroyImageFunc) {
+        result = destroyImageFunc(dp->disp.dpy, img);
+    }
+    return result;
+}
+
+EGLBoolean eglDestroyImageKHRImpl(EGLDisplay dpy, EGLImageKHR img) {
+    return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR);
+}
+
+EGLBoolean eglDestroyImageImpl(EGLDisplay dpy, EGLImageKHR img) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglDestroyImage) {
+            return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImage);
+        }
+        // driver doesn't support native function, return EGL_BAD_DISPLAY
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroyImage");
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    }
+
+    return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR);
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 5
+// ----------------------------------------------------------------------------
+
+// NOTE: EGLSyncKHR and EGLSync are identical, no need to templatize
+template <typename AttrType, typename FuncType>
+EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list,
+                             FuncType eglCreateSyncFunc) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_SYNC_KHR;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    EGLSyncKHR result = EGL_NO_SYNC_KHR;
+    if (cnx->dso && eglCreateSyncFunc) {
+        result = eglCreateSyncFunc(dp->disp.dpy, type, attrib_list);
+    }
+    return result;
+}
+
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATESYNC)(EGLDisplay dpy, EGLenum type,
+                                                  const EGLAttrib* attrib_list);
+
+EGLSyncKHR eglCreateSyncKHRImpl(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) {
+    return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, attrib_list,
+                                                              gEGLImpl.egl.eglCreateSyncKHR);
+}
+
+EGLSync eglCreateSyncImpl(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglCreateSync) {
+            return eglCreateSyncTmpl<EGLAttrib, PFNEGLCREATESYNC>(dpy, type, attrib_list,
+                                                                  cnx->egl.eglCreateSync);
+        }
+        // driver doesn't support native function, return EGL_BAD_DISPLAY
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateSync");
+        return setError(EGL_BAD_DISPLAY, EGL_NO_SYNC);
+    }
+
+    std::vector<EGLint> convertedAttribs;
+    convertAttribs(attrib_list, convertedAttribs);
+    return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, convertedAttribs.data(),
+                                                              cnx->egl.eglCreateSyncKHR);
+}
+
+EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync,
+                              PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && eglDestroySyncFunc) {
+        result = eglDestroySyncFunc(dp->disp.dpy, sync);
+    }
+    return result;
+}
+
+EGLBoolean eglDestroySyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+    return eglDestroySyncTmpl(dpy, sync, gEGLImpl.egl.eglDestroySyncKHR);
+}
+
+EGLBoolean eglDestroySyncImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglDestroySync) {
+            return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySync);
+        }
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroySync");
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    }
+
+    return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySyncKHR);
+}
+
+EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && gEGLImpl.egl.eglSignalSyncKHR) {
+        result = gEGLImpl.egl.eglSignalSyncKHR(dp->disp.dpy, sync, mode);
+    }
+    return result;
+}
+
+EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout,
+                             PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLint result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && eglClientWaitSyncFunc) {
+        result = eglClientWaitSyncFunc(dp->disp.dpy, sync, flags, timeout);
+    }
+    return result;
+}
+
+EGLint eglClientWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR);
+}
+
+EGLint eglClientWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglClientWaitSync) {
+            return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSync);
+        }
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglClientWaitSync");
+        return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+    }
+
+    return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR);
+}
+
+template <typename AttrType, typename FuncType>
+EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value,
+                                FuncType eglGetSyncAttribFunc) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && eglGetSyncAttribFunc) {
+        result = eglGetSyncAttribFunc(dp->disp.dpy, sync, attribute, value);
+    }
+    return result;
+}
+
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLGETSYNCATTRIB)(EGLDisplay dpy, EGLSync sync, EGLint attribute,
+                                                     EGLAttrib* value);
+
+EGLBoolean eglGetSyncAttribImpl(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglGetSyncAttrib) {
+            return eglGetSyncAttribTmpl<EGLAttrib, PFNEGLGETSYNCATTRIB>(dpy, sync, attribute, value,
+                                                                        cnx->egl.eglGetSyncAttrib);
+        }
+        ALOGE("Driver indicates EGL 1.5 support, but does not have eglGetSyncAttrib");
+        return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+    }
+
+    // Fallback to KHR, ask for EGLint attribute and cast back to EGLAttrib
+    EGLint attribValue;
+    EGLBoolean ret =
+            eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute,
+                                                                     &attribValue,
+                                                                     gEGLImpl.egl
+                                                                             .eglGetSyncAttribKHR);
+    if (ret) {
+        *value = static_cast<EGLAttrib>(attribValue);
+    }
+    return ret;
+}
+
+EGLBoolean eglGetSyncAttribKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute,
+                                   EGLint* value) {
+    return eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute, value,
+                                                                    gEGLImpl.egl
+                                                                            .eglGetSyncAttribKHR);
+}
+
+EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_STREAM_KHR;
+
+    EGLStreamKHR result = EGL_NO_STREAM_KHR;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
+        result = cnx->egl.eglCreateStreamKHR(
+                dp->disp.dpy, attrib_list);
+    }
+    return result;
+}
+
+EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
+        result = cnx->egl.eglDestroyStreamKHR(
+                dp->disp.dpy, stream);
+    }
+    return result;
+}
+
+EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+        EGLenum attribute, EGLint value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
+        result = cnx->egl.eglStreamAttribKHR(
+                dp->disp.dpy, stream, attribute, value);
+    }
+    return result;
+}
+
+EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+        EGLenum attribute, EGLint *value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
+        result = cnx->egl.eglQueryStreamKHR(
+                dp->disp.dpy, stream, attribute, value);
+    }
+    return result;
+}
+
+EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+        EGLenum attribute, EGLuint64KHR *value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
+        result = cnx->egl.eglQueryStreamu64KHR(
+                dp->disp.dpy, stream, attribute, value);
+    }
+    return result;
+}
+
+EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+        EGLenum attribute, EGLTimeKHR *value)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
+        result = cnx->egl.eglQueryStreamTimeKHR(
+                dp->disp.dpy, stream, attribute, value);
+    }
+    return result;
+}
+
+EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config,
+        EGLStreamKHR stream, const EGLint *attrib_list)
+{
+    egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_SURFACE;
+
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
+        EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
+                dp->disp.dpy, config, stream, attrib_list);
+        if (surface != EGL_NO_SURFACE) {
+            egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
+                                                 EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
+            return s;
+        }
+    }
+    return EGL_NO_SURFACE;
+}
+
+EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy,
+        EGLStreamKHR stream)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
+        result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
+                dp->disp.dpy, stream);
+    }
+    return result;
+}
+
+EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy,
+        EGLStreamKHR stream)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
+        result = cnx->egl.eglStreamConsumerAcquireKHR(
+                dp->disp.dpy, stream);
+    }
+    return result;
+}
+
+EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy,
+        EGLStreamKHR stream)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+
+    EGLBoolean result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
+        result = cnx->egl.eglStreamConsumerReleaseKHR(
+                dp->disp.dpy, stream);
+    }
+    return result;
+}
+
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(
+        EGLDisplay dpy, EGLStreamKHR stream)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
+
+    EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
+        result = cnx->egl.eglGetStreamFileDescriptorKHR(
+                dp->disp.dpy, stream);
+    }
+    return result;
+}
+
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(
+        EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_STREAM_KHR;
+
+    EGLStreamKHR result = EGL_NO_STREAM_KHR;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
+        result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
+                dp->disp.dpy, file_descriptor);
+    }
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 15
+// ----------------------------------------------------------------------------
+
+// Need to template function type because return type is different
+template <typename ReturnType, typename FuncType>
+ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags,
+                           FuncType eglWaitSyncFunc) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_FALSE;
+    ReturnType result = EGL_FALSE;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && eglWaitSyncFunc) {
+        result = eglWaitSyncFunc(dp->disp.dpy, sync, flags);
+    }
+    return result;
+}
+
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLWAITSYNC)(EGLDisplay dpy, EGLSync sync, EGLint flags);
+
+EGLint eglWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    return eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags,
+                                                          cnx->egl.eglWaitSyncKHR);
+}
+
+EGLBoolean eglWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags) {
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+        if (cnx->egl.eglWaitSync) {
+            return eglWaitSyncTmpl<EGLBoolean, PFNEGLWAITSYNC>(dpy, sync, flags,
+                                                               cnx->egl.eglWaitSync);
+        }
+        return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+    }
+
+    return static_cast<EGLBoolean>(
+            eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags,
+                                                           cnx->egl.eglWaitSyncKHR));
+}
+
+// ----------------------------------------------------------------------------
+// ANDROID extensions
+// ----------------------------------------------------------------------------
+
+EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
+
+    EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
+    egl_connection_t* const cnx = &gEGLImpl;
+    if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) {
+        result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync);
+    }
+    return result;
+}
+
+EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+        EGLnsecsANDROID time)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return EGL_FALSE;
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+    native_window_set_buffers_timestamp(s->getNativeWindow(), time);
+
+    return EGL_TRUE;
+}
+
+EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) {
+    // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
+    // this function cannot be implemented when this libEGL is built for
+    // vendors.
+#ifndef __ANDROID_VNDK__
+    if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
+    return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
+#else
+    return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// NVIDIA extensions
+// ----------------------------------------------------------------------------
+EGLuint64NV eglGetSystemTimeFrequencyNVImpl()
+{
+    EGLuint64NV ret = 0;
+    egl_connection_t* const cnx = &gEGLImpl;
+
+    if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) {
+        return cnx->egl.eglGetSystemTimeFrequencyNV();
+    }
+
+    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+}
+
+EGLuint64NV eglGetSystemTimeNVImpl()
+{
+    EGLuint64NV ret = 0;
+    egl_connection_t* const cnx = &gEGLImpl;
+
+    if (cnx->dso && cnx->egl.eglGetSystemTimeNV) {
+        return cnx->egl.eglGetSystemTimeNV();
+    }
+
+    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+}
+
+// ----------------------------------------------------------------------------
+// Partial update extension
+// ----------------------------------------------------------------------------
+EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface,
+        EGLint *rects, EGLint n_rects)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        setError(EGL_BAD_DISPLAY, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->egl.eglSetDamageRegionKHR) {
+        return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
+                rects, n_rects);
+    }
+
+    return EGL_FALSE;
+}
+
+EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+            EGLuint64KHR *frameId) {
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    if (!s->getNativeWindow()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    uint64_t nextFrameId = 0;
+    int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId);
+
+    if (ret != 0) {
+        // This should not happen. Return an error that is not in the spec
+        // so it's obvious something is very wrong.
+        ALOGE("eglGetNextFrameId: Unexpected error.");
+        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+
+    *frameId = nextFrameId;
+    return EGL_TRUE;
+}
+
+EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+        EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    if (!s->getNativeWindow()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    nsecs_t* compositeDeadline = nullptr;
+    nsecs_t* compositeInterval = nullptr;
+    nsecs_t* compositeToPresentLatency = nullptr;
+
+    for (int i = 0; i < numTimestamps; i++) {
+        switch (names[i]) {
+            case EGL_COMPOSITE_DEADLINE_ANDROID:
+                compositeDeadline = &values[i];
+                break;
+            case EGL_COMPOSITE_INTERVAL_ANDROID:
+                compositeInterval = &values[i];
+                break;
+            case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+                compositeToPresentLatency = &values[i];
+                break;
+            default:
+                return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+        }
+    }
+
+    int ret = native_window_get_compositor_timing(s->getNativeWindow(),
+            compositeDeadline, compositeInterval, compositeToPresentLatency);
+
+    switch (ret) {
+      case 0:
+        return EGL_TRUE;
+      case -ENOSYS:
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+      default:
+        // This should not happen. Return an error that is not in the spec
+        // so it's obvious something is very wrong.
+        ALOGE("eglGetCompositorTiming: Unexpected error.");
+        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+}
+
+EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(
+        EGLDisplay dpy, EGLSurface surface, EGLint name)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    ANativeWindow* window = s->getNativeWindow();
+    if (!window) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    switch (name) {
+        case EGL_COMPOSITE_DEADLINE_ANDROID:
+        case EGL_COMPOSITE_INTERVAL_ANDROID:
+        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+            return EGL_TRUE;
+        default:
+            return EGL_FALSE;
+    }
+}
+
+EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+        EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
+        EGLnsecsANDROID *values)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    if (!s->getNativeWindow()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    nsecs_t* requestedPresentTime = nullptr;
+    nsecs_t* acquireTime = nullptr;
+    nsecs_t* latchTime = nullptr;
+    nsecs_t* firstRefreshStartTime = nullptr;
+    nsecs_t* gpuCompositionDoneTime = nullptr;
+    nsecs_t* lastRefreshStartTime = nullptr;
+    nsecs_t* displayPresentTime = nullptr;
+    nsecs_t* dequeueReadyTime = nullptr;
+    nsecs_t* releaseTime = nullptr;
+
+    for (int i = 0; i < numTimestamps; i++) {
+        switch (timestamps[i]) {
+            case EGL_REQUESTED_PRESENT_TIME_ANDROID:
+                requestedPresentTime = &values[i];
+                break;
+            case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+                acquireTime = &values[i];
+                break;
+            case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+                latchTime = &values[i];
+                break;
+            case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+                firstRefreshStartTime = &values[i];
+                break;
+            case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+                lastRefreshStartTime = &values[i];
+                break;
+            case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
+                gpuCompositionDoneTime = &values[i];
+                break;
+            case EGL_DISPLAY_PRESENT_TIME_ANDROID:
+                displayPresentTime = &values[i];
+                break;
+            case EGL_DEQUEUE_READY_TIME_ANDROID:
+                dequeueReadyTime = &values[i];
+                break;
+            case EGL_READS_DONE_TIME_ANDROID:
+                releaseTime = &values[i];
+                break;
+            default:
+                return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+        }
+    }
+
+    int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
+            requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
+            lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
+            dequeueReadyTime, releaseTime);
+
+    switch (ret) {
+        case 0:
+            return EGL_TRUE;
+        case -ENOENT:
+            return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
+        case -ENOSYS:
+            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        case -EINVAL:
+            return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+        default:
+            // This should not happen. Return an error that is not in the spec
+            // so it's obvious something is very wrong.
+            ALOGE("eglGetFrameTimestamps: Unexpected error.");
+            return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+}
+
+EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(
+        EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
+{
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    ANativeWindow* window = s->getNativeWindow();
+    if (!window) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    switch (timestamp) {
+        case EGL_COMPOSITE_DEADLINE_ANDROID:
+        case EGL_COMPOSITE_INTERVAL_ANDROID:
+        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+        case EGL_REQUESTED_PRESENT_TIME_ANDROID:
+        case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+        case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+        case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+        case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+        case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
+        case EGL_DEQUEUE_READY_TIME_ANDROID:
+        case EGL_READS_DONE_TIME_ANDROID:
+            return EGL_TRUE;
+        case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
+            int value = 0;
+            window->query(window,
+                    NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+            return value == 0 ? EGL_FALSE : EGL_TRUE;
+        }
+        default:
+            return EGL_FALSE;
+    }
+}
+
+const GLubyte * glGetStringImpl(GLenum name) {
+    const GLubyte * ret = egl_get_string_for_current_context(name);
+    if (ret == NULL) {
+        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+        if(_c) ret = _c->glGetString(name);
+    }
+    return ret;
+}
+
+const GLubyte * glGetStringiImpl(GLenum name, GLuint index) {
+    const GLubyte * ret = egl_get_string_for_current_context(name, index);
+    if (ret == NULL) {
+        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+        if(_c) ret = _c->glGetStringi(name, index);
+    }
+    return ret;
+}
+
+void glGetBooleanvImpl(GLenum pname, GLboolean * data) {
+    if (pname == GL_NUM_EXTENSIONS) {
+        int num_exts = egl_get_num_extensions_for_current_context();
+        if (num_exts >= 0) {
+            *data = num_exts > 0 ? GL_TRUE : GL_FALSE;
+            return;
+        }
+    }
+
+    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    if (_c) _c->glGetBooleanv(pname, data);
+}
+
+void glGetFloatvImpl(GLenum pname, GLfloat * data) {
+    if (pname == GL_NUM_EXTENSIONS) {
+        int num_exts = egl_get_num_extensions_for_current_context();
+        if (num_exts >= 0) {
+            *data = (GLfloat)num_exts;
+            return;
+        }
+    }
+
+    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    if (_c) _c->glGetFloatv(pname, data);
+}
+
+void glGetIntegervImpl(GLenum pname, GLint * data) {
+    if (pname == GL_NUM_EXTENSIONS) {
+        int num_exts = egl_get_num_extensions_for_current_context();
+        if (num_exts >= 0) {
+            *data = (GLint)num_exts;
+            return;
+        }
+    }
+
+    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    if (_c) _c->glGetIntegerv(pname, data);
+}
+
+void glGetInteger64vImpl(GLenum pname, GLint64 * data) {
+    if (pname == GL_NUM_EXTENSIONS) {
+        int num_exts = egl_get_num_extensions_for_current_context();
+        if (num_exts >= 0) {
+            *data = (GLint64)num_exts;
+            return;
+        }
+    }
+
+    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+    if (_c) _c->glGetInteger64v(pname, data);
+}
+
+struct implementation_map_t {
+    const char* name;
+    EGLFuncPointer address;
+};
+
+static const implementation_map_t sPlatformImplMap[] = {
+        // clang-format off
+    { "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl },
+    { "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl },
+    { "eglInitialize", (EGLFuncPointer)&eglInitializeImpl },
+    { "eglTerminate", (EGLFuncPointer)&eglTerminateImpl },
+    { "eglGetConfigs", (EGLFuncPointer)&eglGetConfigsImpl },
+    { "eglChooseConfig", (EGLFuncPointer)&eglChooseConfigImpl },
+    { "eglGetConfigAttrib", (EGLFuncPointer)&eglGetConfigAttribImpl },
+    { "eglCreateWindowSurface", (EGLFuncPointer)&eglCreateWindowSurfaceImpl },
+    { "eglCreatePixmapSurface", (EGLFuncPointer)&eglCreatePixmapSurfaceImpl },
+    { "eglCreatePlatformWindowSurface", (EGLFuncPointer)&eglCreatePlatformWindowSurfaceImpl },
+    { "eglCreatePlatformPixmapSurface", (EGLFuncPointer)&eglCreatePlatformPixmapSurfaceImpl },
+    { "eglCreatePbufferSurface", (EGLFuncPointer)&eglCreatePbufferSurfaceImpl },
+    { "eglDestroySurface", (EGLFuncPointer)&eglDestroySurfaceImpl },
+    { "eglQuerySurface", (EGLFuncPointer)&eglQuerySurfaceImpl },
+    { "eglBeginFrame", (EGLFuncPointer)&eglBeginFrameImpl },
+    { "eglCreateContext", (EGLFuncPointer)&eglCreateContextImpl },
+    { "eglDestroyContext", (EGLFuncPointer)&eglDestroyContextImpl },
+    { "eglMakeCurrent", (EGLFuncPointer)&eglMakeCurrentImpl },
+    { "eglQueryContext", (EGLFuncPointer)&eglQueryContextImpl },
+    { "eglGetCurrentContext", (EGLFuncPointer)&eglGetCurrentContextImpl },
+    { "eglGetCurrentSurface", (EGLFuncPointer)&eglGetCurrentSurfaceImpl },
+    { "eglGetCurrentDisplay", (EGLFuncPointer)&eglGetCurrentDisplayImpl },
+    { "eglWaitGL", (EGLFuncPointer)&eglWaitGLImpl },
+    { "eglWaitNative", (EGLFuncPointer)&eglWaitNativeImpl },
+    { "eglGetError", (EGLFuncPointer)&eglGetErrorImpl },
+    { "eglSwapBuffersWithDamageKHR", (EGLFuncPointer)&eglSwapBuffersWithDamageKHRImpl },
+    { "eglGetProcAddress", (EGLFuncPointer)&eglGetProcAddressImpl },
+    { "eglSwapBuffers", (EGLFuncPointer)&eglSwapBuffersImpl },
+    { "eglCopyBuffers", (EGLFuncPointer)&eglCopyBuffersImpl },
+    { "eglQueryString", (EGLFuncPointer)&eglQueryStringImpl },
+    { "eglQueryStringImplementationANDROID", (EGLFuncPointer)&eglQueryStringImplementationANDROIDImpl },
+    { "eglSurfaceAttrib", (EGLFuncPointer)&eglSurfaceAttribImpl },
+    { "eglBindTexImage", (EGLFuncPointer)&eglBindTexImageImpl },
+    { "eglReleaseTexImage", (EGLFuncPointer)&eglReleaseTexImageImpl },
+    { "eglSwapInterval", (EGLFuncPointer)&eglSwapIntervalImpl },
+    { "eglWaitClient", (EGLFuncPointer)&eglWaitClientImpl },
+    { "eglBindAPI", (EGLFuncPointer)&eglBindAPIImpl },
+    { "eglQueryAPI", (EGLFuncPointer)&eglQueryAPIImpl },
+    { "eglReleaseThread", (EGLFuncPointer)&eglReleaseThreadImpl },
+    { "eglCreatePbufferFromClientBuffer", (EGLFuncPointer)&eglCreatePbufferFromClientBufferImpl },
+    { "eglLockSurfaceKHR", (EGLFuncPointer)&eglLockSurfaceKHRImpl },
+    { "eglUnlockSurfaceKHR", (EGLFuncPointer)&eglUnlockSurfaceKHRImpl },
+    { "eglCreateImageKHR", (EGLFuncPointer)&eglCreateImageKHRImpl },
+    { "eglDestroyImageKHR", (EGLFuncPointer)&eglDestroyImageKHRImpl },
+    { "eglCreateImage", (EGLFuncPointer)&eglCreateImageImpl },
+    { "eglDestroyImage", (EGLFuncPointer)&eglDestroyImageImpl },
+    { "eglCreateSync", (EGLFuncPointer)&eglCreateSyncImpl },
+    { "eglDestroySync", (EGLFuncPointer)&eglDestroySyncImpl },
+    { "eglClientWaitSync", (EGLFuncPointer)&eglClientWaitSyncImpl },
+    { "eglGetSyncAttrib", (EGLFuncPointer)&eglGetSyncAttribImpl },
+    { "eglCreateSyncKHR", (EGLFuncPointer)&eglCreateSyncKHRImpl },
+    { "eglDestroySyncKHR", (EGLFuncPointer)&eglDestroySyncKHRImpl },
+    { "eglSignalSyncKHR", (EGLFuncPointer)&eglSignalSyncKHRImpl },
+    { "eglClientWaitSyncKHR", (EGLFuncPointer)&eglClientWaitSyncKHRImpl },
+    { "eglGetSyncAttribKHR", (EGLFuncPointer)&eglGetSyncAttribKHRImpl },
+    { "eglCreateStreamKHR", (EGLFuncPointer)&eglCreateStreamKHRImpl },
+    { "eglDestroyStreamKHR", (EGLFuncPointer)&eglDestroyStreamKHRImpl },
+    { "eglStreamAttribKHR", (EGLFuncPointer)&eglStreamAttribKHRImpl },
+    { "eglQueryStreamKHR", (EGLFuncPointer)&eglQueryStreamKHRImpl },
+    { "eglQueryStreamu64KHR", (EGLFuncPointer)&eglQueryStreamu64KHRImpl },
+    { "eglQueryStreamTimeKHR", (EGLFuncPointer)&eglQueryStreamTimeKHRImpl },
+    { "eglCreateStreamProducerSurfaceKHR", (EGLFuncPointer)&eglCreateStreamProducerSurfaceKHRImpl },
+    { "eglStreamConsumerGLTextureExternalKHR", (EGLFuncPointer)&eglStreamConsumerGLTextureExternalKHRImpl },
+    { "eglStreamConsumerAcquireKHR", (EGLFuncPointer)&eglStreamConsumerAcquireKHRImpl },
+    { "eglStreamConsumerReleaseKHR", (EGLFuncPointer)&eglStreamConsumerReleaseKHRImpl },
+    { "eglGetStreamFileDescriptorKHR", (EGLFuncPointer)&eglGetStreamFileDescriptorKHRImpl },
+    { "eglCreateStreamFromFileDescriptorKHR", (EGLFuncPointer)&eglCreateStreamFromFileDescriptorKHRImpl },
+    { "eglWaitSync", (EGLFuncPointer)&eglWaitSyncImpl },
+    { "eglWaitSyncKHR", (EGLFuncPointer)&eglWaitSyncKHRImpl },
+    { "eglDupNativeFenceFDANDROID", (EGLFuncPointer)&eglDupNativeFenceFDANDROIDImpl },
+    { "eglPresentationTimeANDROID", (EGLFuncPointer)&eglPresentationTimeANDROIDImpl },
+    { "eglGetNativeClientBufferANDROID", (EGLFuncPointer)&eglGetNativeClientBufferANDROIDImpl },
+    { "eglGetSystemTimeFrequencyNV", (EGLFuncPointer)&eglGetSystemTimeFrequencyNVImpl },
+    { "eglGetSystemTimeNV", (EGLFuncPointer)&eglGetSystemTimeNVImpl },
+    { "eglSetDamageRegionKHR", (EGLFuncPointer)&eglSetDamageRegionKHRImpl },
+    { "eglGetNextFrameIdANDROID", (EGLFuncPointer)&eglGetNextFrameIdANDROIDImpl },
+    { "eglGetCompositorTimingANDROID", (EGLFuncPointer)&eglGetCompositorTimingANDROIDImpl },
+    { "eglGetCompositorTimingSupportedANDROID", (EGLFuncPointer)&eglGetCompositorTimingSupportedANDROIDImpl },
+    { "eglGetFrameTimestampsANDROID", (EGLFuncPointer)&eglGetFrameTimestampsANDROIDImpl },
+    { "eglGetFrameTimestampSupportedANDROID", (EGLFuncPointer)&eglGetFrameTimestampSupportedANDROIDImpl },
+    { "glGetString", (EGLFuncPointer)&glGetStringImpl },
+    { "glGetStringi", (EGLFuncPointer)&glGetStringiImpl },
+    { "glGetBooleanv", (EGLFuncPointer)&glGetBooleanvImpl },
+    { "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl },
+    { "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl },
+    { "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl },
+        // clang-format on
+};
+
+EGLFuncPointer FindPlatformImplAddr(const char* name)
+{
+    static const bool DEBUG = false;
+
+    if (name == nullptr) {
+        ALOGV("FindPlatformImplAddr called with null name");
+        return nullptr;
+    }
+
+    for (int i = 0; i < NELEM(sPlatformImplMap); i++) {
+        if (sPlatformImplMap[i].name == nullptr) {
+            ALOGV("FindPlatformImplAddr found nullptr for sPlatformImplMap[%i].name (%s)", i, name);
+            return nullptr;
+        }
+        if (!strcmp(name, sPlatformImplMap[i].name)) {
+            ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name);
+            return sPlatformImplMap[i].address;
+        }
+    }
+
+    ALOGV("FindPlatformImplAddr did not find an entry for %s", name);
+    return nullptr;
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/opengl/libs/EGL/egl_platform_entries.h
similarity index 69%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to opengl/libs/EGL/egl_platform_entries.h
index e6ac6bf..85b1db3 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.h
@@ -14,14 +14,19 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#ifndef ANDROID_EGLAPI_H
+#define ANDROID_EGLAPI_H
+
+#include <EGL/egl.h>
+
+typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
 
 namespace android {
-namespace mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+EGLint eglGetErrorImpl();
+EGLFuncPointer FindPlatformImplAddr(const char* name);
 
-} // namespace mock
-} // namespace android
+}; // namespace android
+
+#endif // ANDROID_EGLAPI_H
+
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index 8508c5f..aaecb62 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -21,6 +21,7 @@
 #include <cutils/properties.h>
 #include <log/log.h>
 #include "CallStack.h"
+#include "egl_platform_entries.h"
 
 namespace android {
 
@@ -28,7 +29,7 @@
 pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT;
 
 egl_tls_t::egl_tls_t()
-    : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(true) {
+    : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) {
 }
 
 const char *egl_tls_t::egl_strerror(EGLint err) {
@@ -55,13 +56,38 @@
 void egl_tls_t::validateTLSKey()
 {
     struct TlsKeyInitializer {
-        static void create() {
-            pthread_key_create(&sKey, (void (*)(void*))&eglReleaseThread);
-        }
+        static void create() { pthread_key_create(&sKey, destructTLSData); }
     };
     pthread_once(&sOnceKey, TlsKeyInitializer::create);
 }
 
+void egl_tls_t::destructTLSData(void* data) {
+    egl_tls_t* tls = static_cast<egl_tls_t*>(data);
+    if (!tls) return;
+
+    // Several things in the call tree of eglReleaseThread expect to be able to get the current
+    // thread state directly from TLS. That's a problem because Bionic has already cleared our
+    // TLS pointer before calling this function (pthread_getspecific(sKey) will return nullptr).
+    // Instead the data is passed as our parameter.
+    //
+    // Ideally we'd refactor this so we have thin wrappers that retrieve thread state from TLS and
+    // then pass it as a parameter (or 'this' pointer) to functions that do the real work without
+    // touching TLS. Then from here we could just call those implementation functions with the the
+    // TLS data we just received as a parameter.
+    //
+    // But that's a fairly invasive refactoring, so to do this robustly in the short term we just
+    // put the data *back* in TLS and call the top-level eglReleaseThread. It and it's call tree
+    // will retrieve the value from TLS, and then finally clear the TLS data. Bionic explicitly
+    // tolerates re-setting the value that it's currently trying to destruct (see
+    // pthread_key_clean_all()). Even if we forgot to clear the restored TLS data, bionic would
+    // call the destructor again, but eventually gives up and just leaks the data rather than
+    // enter an infinite loop.
+    pthread_setspecific(sKey, tls);
+    eglReleaseThread();
+    ALOGE_IF(pthread_getspecific(sKey) != nullptr,
+             "EGL TLS data still exists after eglReleaseThread");
+}
+
 void egl_tls_t::setErrorEtcImpl(
         const char* caller, int line, EGLint error, bool quiet) {
     validateTLSKey();
@@ -92,7 +118,7 @@
 
 egl_tls_t* egl_tls_t::getTLS() {
     egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey);
-    if (tls == 0) {
+    if (tls == nullptr) {
         tls = new egl_tls_t;
         pthread_setspecific(sKey, tls);
     }
@@ -103,7 +129,7 @@
     if (sKey != TLS_KEY_NOT_INITIALIZED) {
         egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey);
         if (tls) {
-            pthread_setspecific(sKey, 0);
+            pthread_setspecific(sKey, nullptr);
             delete tls;
         }
     }
@@ -112,7 +138,7 @@
 void egl_tls_t::clearError() {
     // This must clear the error from all the underlying EGL implementations as
     // well as the EGL wrapper layer.
-    eglGetError();
+    android::eglGetErrorImpl();
 }
 
 EGLint egl_tls_t::getError() {
diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h
index 9feae68..86a375c 100644
--- a/opengl/libs/EGL/egl_tls.h
+++ b/opengl/libs/EGL/egl_tls.h
@@ -38,6 +38,7 @@
 
     egl_tls_t();
     static void validateTLSKey();
+    static void destructTLSData(void* data);
     static void setErrorEtcImpl(
             const char* caller, int line, EGLint error, bool quiet);
 
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 9858276..9e112cc 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -18,9 +18,13 @@
 #define ANDROID_EGLDEFS_H
 
 #include "../hooks.h"
+#include "egl_platform_entries.h"
+
+#include <log/log.h>
 
 #define VERSION_MAJOR 1
 #define VERSION_MINOR 4
+#define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -31,23 +35,54 @@
 
 // ----------------------------------------------------------------------------
 
+extern char const * const platform_names[];
+
+// clang-format off
 struct egl_connection_t {
     enum {
         GLESv1_INDEX = 0,
         GLESv2_INDEX = 1
     };
 
-    inline egl_connection_t() : dso(0) { }
+    inline egl_connection_t() : dso(nullptr) {
+
+        char const* const* entries = platform_names;
+        EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform);
+        while (*entries) {
+            const char* name = *entries;
+            EGLFuncPointer f = FindPlatformImplAddr(name);
+
+            if (f == nullptr) {
+                // If no entry found, update the lookup table: sPlatformImplMap
+                ALOGE("No entry found in platform lookup table for %s", name);
+            }
+
+            *curr++ = f;
+            entries++;
+        }
+    }
+
     void *              dso;
     gl_hooks_t *        hooks[2];
     EGLint              major;
     EGLint              minor;
+    EGLint              driverVersion;
     egl_t               egl;
 
+    // Functions implemented or redirected by platform libraries
+    platform_impl_t     platform;
+
     void*               libEgl;
     void*               libGles1;
     void*               libGles2;
+
+    bool                shouldUseAngle; // Should we attempt to load ANGLE
+    bool                angleDecided;   // Have we tried to load ANGLE
+    bool                useAngle;       // Was ANGLE successfully loaded
+    EGLint              angleBackend;
+    void*               vendorEGL;
 };
+// clang-format on
 
 // ----------------------------------------------------------------------------
 
@@ -58,6 +93,7 @@
 extern "C" void gl_noop();
 
 extern char const * const gl_names[];
+extern char const * const gl_names_1[];
 extern char const * const egl_names[];
 
 extern egl_connection_t gEGLImpl;
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index f7fde96..65f50f5 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -301,71 +301,31 @@
 }
 
 const GLubyte * glGetString(GLenum name) {
-    const GLubyte * ret = egl_get_string_for_current_context(name);
-    if (ret == NULL) {
-        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-        if(_c) ret = _c->glGetString(name);
-    }
-    return ret;
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetString(name);
 }
 
 const GLubyte * glGetStringi(GLenum name, GLuint index) {
-    const GLubyte * ret = egl_get_string_for_current_context(name, index);
-    if (ret == NULL) {
-        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-        if(_c) ret = _c->glGetStringi(name, index);
-    }
-    return ret;
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetStringi(name, index);
 }
 
 void glGetBooleanv(GLenum pname, GLboolean * data) {
-    if (pname == GL_NUM_EXTENSIONS) {
-        int num_exts = egl_get_num_extensions_for_current_context();
-        if (num_exts >= 0) {
-            *data = num_exts > 0 ? GL_TRUE : GL_FALSE;
-            return;
-        }
-    }
-
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-    if (_c) _c->glGetBooleanv(pname, data);
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetBooleanv(pname, data);
 }
 
 void glGetFloatv(GLenum pname, GLfloat * data) {
-    if (pname == GL_NUM_EXTENSIONS) {
-        int num_exts = egl_get_num_extensions_for_current_context();
-        if (num_exts >= 0) {
-            *data = (GLfloat)num_exts;
-            return;
-        }
-    }
-
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-    if (_c) _c->glGetFloatv(pname, data);
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetFloatv(pname, data);
 }
 
 void glGetIntegerv(GLenum pname, GLint * data) {
-    if (pname == GL_NUM_EXTENSIONS) {
-        int num_exts = egl_get_num_extensions_for_current_context();
-        if (num_exts >= 0) {
-            *data = (GLint)num_exts;
-            return;
-        }
-    }
-
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-    if (_c) _c->glGetIntegerv(pname, data);
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetIntegerv(pname, data);
 }
 
 void glGetInteger64v(GLenum pname, GLint64 * data) {
-    if (pname == GL_NUM_EXTENSIONS) {
-        int num_exts = egl_get_num_extensions_for_current_context();
-        if (num_exts >= 0) {
-            *data = (GLint64)num_exts;
-            return;
-        }
-    }
-
-    gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
-    if (_c) _c->glGetInteger64v(pname, data);
+    egl_connection_t* const cnx = egl_get_connection();
+    return cnx->platform.glGetInteger64v(pname, data);
 }
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index a8855ef..0af0501 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -21,15 +21,18 @@
 #include <EGL/eglext.h>
 #include <EGL/eglplatform.h>
 
+#include "EGL/egldefs.h"
 #include "hooks.h"
 
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
 
+
 EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name);
 EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index);
 EGLAPI GLint egl_get_num_extensions_for_current_context();
+EGLAPI egl_connection_t* egl_get_connection();
 
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/opengl/libs/entries_gles1.in b/opengl/libs/entries_gles1.in
new file mode 100644
index 0000000..c9be593
--- /dev/null
+++ b/opengl/libs/entries_gles1.in
@@ -0,0 +1,298 @@
+GL_ENTRY(void, glActiveTexture, GLenum texture)
+GL_ENTRY(void, glAlphaFunc, GLenum func, GLfloat ref)
+GL_ENTRY(void, glAlphaFuncx, GLenum func, GLfixed ref)
+GL_ENTRY(void, glAlphaFuncxOES, GLenum func, GLfixed ref)
+GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer)
+GL_ENTRY(void, glBindFramebufferOES, GLenum target, GLuint framebuffer)
+GL_ENTRY(void, glBindRenderbufferOES, GLenum target, GLuint renderbuffer)
+GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture)
+GL_ENTRY(void, glBindVertexArrayOES, GLuint array)
+GL_ENTRY(void, glBlendEquationOES, GLenum mode)
+GL_ENTRY(void, glBlendEquationSeparateOES, GLenum modeRGB, GLenum modeAlpha)
+GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor)
+GL_ENTRY(void, glBlendFuncSeparateOES, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
+GL_ENTRY(void, glBufferData, GLenum target, GLsizeiptr size, const void *data, GLenum usage)
+GL_ENTRY(void, glBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void *data)
+GL_ENTRY(GLenum, glCheckFramebufferStatusOES, GLenum target)
+GL_ENTRY(void, glClear, GLbitfield mask)
+GL_ENTRY(void, glClearColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+GL_ENTRY(void, glClearColorx, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha)
+GL_ENTRY(void, glClearColorxOES, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha)
+GL_ENTRY(void, glClearDepthf, GLfloat d)
+GL_ENTRY(void, glClearDepthfOES, GLclampf depth)
+GL_ENTRY(void, glClearDepthx, GLfixed depth)
+GL_ENTRY(void, glClearDepthxOES, GLfixed depth)
+GL_ENTRY(void, glClearStencil, GLint s)
+GL_ENTRY(void, glClientActiveTexture, GLenum texture)
+GL_ENTRY(GLenum, glClientWaitSyncAPPLE, GLsync sync, GLbitfield flags, GLuint64 timeout)
+GL_ENTRY(void, glClipPlanef, GLenum p, const GLfloat *eqn)
+GL_ENTRY(void, glClipPlanefIMG, GLenum p, const GLfloat *eqn)
+GL_ENTRY(void, glClipPlanefOES, GLenum plane, const GLfloat *equation)
+GL_ENTRY(void, glClipPlanex, GLenum plane, const GLfixed *equation)
+GL_ENTRY(void, glClipPlanexIMG, GLenum p, const GLfixed *eqn)
+GL_ENTRY(void, glClipPlanexOES, GLenum plane, const GLfixed *equation)
+GL_ENTRY(void, glColor4f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+GL_ENTRY(void, glColor4ub, GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha)
+GL_ENTRY(void, glColor4x, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha)
+GL_ENTRY(void, glColor4xOES, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha)
+GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)
+GL_ENTRY(void, glColorPointer, GLint size, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data)
+GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data)
+GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
+GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glCopyTextureLevelsAPPLE, GLuint destinationTexture, GLuint sourceTexture, GLint sourceBaseLevel, GLsizei sourceLevelCount)
+GL_ENTRY(void, glCullFace, GLenum mode)
+GL_ENTRY(void, glCurrentPaletteMatrixOES, GLuint matrixpaletteindex)
+GL_ENTRY(void, glDeleteBuffers, GLsizei n, const GLuint *buffers)
+GL_ENTRY(void, glDeleteFencesNV, GLsizei n, const GLuint *fences)
+GL_ENTRY(void, glDeleteFramebuffersOES, GLsizei n, const GLuint *framebuffers)
+GL_ENTRY(void, glDeleteRenderbuffersOES, GLsizei n, const GLuint *renderbuffers)
+GL_ENTRY(void, glDeleteSyncAPPLE, GLsync sync)
+GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *textures)
+GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays)
+GL_ENTRY(void, glDepthFunc, GLenum func)
+GL_ENTRY(void, glDepthMask, GLboolean flag)
+GL_ENTRY(void, glDepthRangef, GLfloat n, GLfloat f)
+GL_ENTRY(void, glDepthRangefOES, GLclampf n, GLclampf f)
+GL_ENTRY(void, glDepthRangex, GLfixed n, GLfixed f)
+GL_ENTRY(void, glDepthRangexOES, GLfixed n, GLfixed f)
+GL_ENTRY(void, glDisable, GLenum cap)
+GL_ENTRY(void, glDisableClientState, GLenum array)
+GL_ENTRY(void, glDisableDriverControlQCOM, GLuint driverControl)
+GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments)
+GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count)
+GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const void *indices)
+GL_ENTRY(void, glDrawTexfOES, GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height)
+GL_ENTRY(void, glDrawTexfvOES, const GLfloat *coords)
+GL_ENTRY(void, glDrawTexiOES, GLint x, GLint y, GLint z, GLint width, GLint height)
+GL_ENTRY(void, glDrawTexivOES, const GLint *coords)
+GL_ENTRY(void, glDrawTexsOES, GLshort x, GLshort y, GLshort z, GLshort width, GLshort height)
+GL_ENTRY(void, glDrawTexsvOES, const GLshort *coords)
+GL_ENTRY(void, glDrawTexxOES, GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height)
+GL_ENTRY(void, glDrawTexxvOES, const GLfixed *coords)
+GL_ENTRY(void, glEGLImageTargetRenderbufferStorageOES, GLenum target, GLeglImageOES image)
+GL_ENTRY(void, glEGLImageTargetTexture2DOES, GLenum target, GLeglImageOES image)
+GL_ENTRY(void, glEnable, GLenum cap)
+GL_ENTRY(void, glEnableClientState, GLenum array)
+GL_ENTRY(void, glEnableDriverControlQCOM, GLuint driverControl)
+GL_ENTRY(void, glEndTilingQCOM, GLbitfield preserveMask)
+GL_ENTRY(void, glExtGetBufferPointervQCOM, GLenum target, void **params)
+GL_ENTRY(void, glExtGetBuffersQCOM, GLuint *buffers, GLint maxBuffers, GLint *numBuffers)
+GL_ENTRY(void, glExtGetFramebuffersQCOM, GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers)
+GL_ENTRY(void, glExtGetProgramBinarySourceQCOM, GLuint program, GLenum shadertype, GLchar *source, GLint *length)
+GL_ENTRY(void, glExtGetProgramsQCOM, GLuint *programs, GLint maxPrograms, GLint *numPrograms)
+GL_ENTRY(void, glExtGetRenderbuffersQCOM, GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers)
+GL_ENTRY(void, glExtGetShadersQCOM, GLuint *shaders, GLint maxShaders, GLint *numShaders)
+GL_ENTRY(void, glExtGetTexLevelParameterivQCOM, GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params)
+GL_ENTRY(void, glExtGetTexSubImageQCOM, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, void *texels)
+GL_ENTRY(void, glExtGetTexturesQCOM, GLuint *textures, GLint maxTextures, GLint *numTextures)
+GL_ENTRY(GLboolean, glExtIsProgramBinaryQCOM, GLuint program)
+GL_ENTRY(void, glExtTexObjectStateOverrideiQCOM, GLenum target, GLenum pname, GLint param)
+GL_ENTRY(GLsync, glFenceSyncAPPLE, GLenum condition, GLbitfield flags)
+GL_ENTRY(void, glFinish, void)
+GL_ENTRY(void, glFinishFenceNV, GLuint fence)
+GL_ENTRY(void, glFlush, void)
+GL_ENTRY(void, glFlushMappedBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length)
+GL_ENTRY(void, glFogf, GLenum pname, GLfloat param)
+GL_ENTRY(void, glFogfv, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glFogx, GLenum pname, GLfixed param)
+GL_ENTRY(void, glFogxOES, GLenum pname, GLfixed param)
+GL_ENTRY(void, glFogxv, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glFogxvOES, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glFramebufferRenderbufferOES, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
+GL_ENTRY(void, glFramebufferTexture2DMultisampleEXT, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples)
+GL_ENTRY(void, glFramebufferTexture2DMultisampleIMG, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples)
+GL_ENTRY(void, glFramebufferTexture2DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
+GL_ENTRY(void, glFrontFace, GLenum mode)
+GL_ENTRY(void, glFrustumf, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f)
+GL_ENTRY(void, glFrustumfOES, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f)
+GL_ENTRY(void, glFrustumx, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f)
+GL_ENTRY(void, glFrustumxOES, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f)
+GL_ENTRY(void, glGenBuffers, GLsizei n, GLuint *buffers)
+GL_ENTRY(void, glGenFencesNV, GLsizei n, GLuint *fences)
+GL_ENTRY(void, glGenFramebuffersOES, GLsizei n, GLuint *framebuffers)
+GL_ENTRY(void, glGenRenderbuffersOES, GLsizei n, GLuint *renderbuffers)
+GL_ENTRY(void, glGenTextures, GLsizei n, GLuint *textures)
+GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays)
+GL_ENTRY(void, glGenerateMipmapOES, GLenum target)
+GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean *data)
+GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, void **params)
+GL_ENTRY(void, glGetClipPlanef, GLenum plane, GLfloat *equation)
+GL_ENTRY(void, glGetClipPlanefOES, GLenum plane, GLfloat *equation)
+GL_ENTRY(void, glGetClipPlanex, GLenum plane, GLfixed *equation)
+GL_ENTRY(void, glGetClipPlanexOES, GLenum plane, GLfixed *equation)
+GL_ENTRY(void, glGetDriverControlStringQCOM, GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString)
+GL_ENTRY(void, glGetDriverControlsQCOM, GLint *num, GLsizei size, GLuint *driverControls)
+GL_ENTRY(GLenum, glGetError, void)
+GL_ENTRY(void, glGetFenceivNV, GLuint fence, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetFixedv, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetFixedvOES, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetFloatv, GLenum pname, GLfloat *data)
+GL_ENTRY(void, glGetFramebufferAttachmentParameterivOES, GLenum target, GLenum attachment, GLenum pname, GLint *params)
+GL_ENTRY(GLenum, glGetGraphicsResetStatusEXT, void)
+GL_ENTRY(void, glGetInteger64vAPPLE, GLenum pname, GLint64 *params)
+GL_ENTRY(void, glGetIntegerv, GLenum pname, GLint *data)
+GL_ENTRY(void, glGetLightfv, GLenum light, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetLightxv, GLenum light, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetLightxvOES, GLenum light, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetMaterialfv, GLenum face, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetMaterialxv, GLenum face, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetMaterialxvOES, GLenum face, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetPointerv, GLenum pname, void **params)
+GL_ENTRY(void, glGetRenderbufferParameterivOES, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(const GLubyte *, glGetString, GLenum name)
+GL_ENTRY(void, glGetSyncivAPPLE, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values)
+GL_ENTRY(void, glGetTexEnvfv, GLenum target, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetTexEnviv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetTexEnvxv, GLenum target, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetTexEnvxvOES, GLenum target, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetTexGenfvOES, GLenum coord, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetTexGenivOES, GLenum coord, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetTexGenxvOES, GLenum coord, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetTexParameterxv, GLenum target, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetTexParameterxvOES, GLenum target, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetnUniformfvEXT, GLuint program, GLint location, GLsizei bufSize, GLfloat *params)
+GL_ENTRY(void, glGetnUniformivEXT, GLuint program, GLint location, GLsizei bufSize, GLint *params)
+GL_ENTRY(void, glHint, GLenum target, GLenum mode)
+GL_ENTRY(void, glInsertEventMarkerEXT, GLsizei length, const GLchar *marker)
+GL_ENTRY(GLboolean, glIsBuffer, GLuint buffer)
+GL_ENTRY(GLboolean, glIsEnabled, GLenum cap)
+GL_ENTRY(GLboolean, glIsFenceNV, GLuint fence)
+GL_ENTRY(GLboolean, glIsFramebufferOES, GLuint framebuffer)
+GL_ENTRY(GLboolean, glIsRenderbufferOES, GLuint renderbuffer)
+GL_ENTRY(GLboolean, glIsSyncAPPLE, GLsync sync)
+GL_ENTRY(GLboolean, glIsTexture, GLuint texture)
+GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array)
+GL_ENTRY(void, glLightModelf, GLenum pname, GLfloat param)
+GL_ENTRY(void, glLightModelfv, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glLightModelx, GLenum pname, GLfixed param)
+GL_ENTRY(void, glLightModelxOES, GLenum pname, GLfixed param)
+GL_ENTRY(void, glLightModelxv, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glLightModelxvOES, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glLightf, GLenum light, GLenum pname, GLfloat param)
+GL_ENTRY(void, glLightfv, GLenum light, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glLightx, GLenum light, GLenum pname, GLfixed param)
+GL_ENTRY(void, glLightxOES, GLenum light, GLenum pname, GLfixed param)
+GL_ENTRY(void, glLightxv, GLenum light, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glLightxvOES, GLenum light, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glLineWidth, GLfloat width)
+GL_ENTRY(void, glLineWidthx, GLfixed width)
+GL_ENTRY(void, glLineWidthxOES, GLfixed width)
+GL_ENTRY(void, glLoadIdentity, void)
+GL_ENTRY(void, glLoadMatrixf, const GLfloat *m)
+GL_ENTRY(void, glLoadMatrixx, const GLfixed *m)
+GL_ENTRY(void, glLoadMatrixxOES, const GLfixed *m)
+GL_ENTRY(void, glLoadPaletteFromModelViewMatrixOES, void)
+GL_ENTRY(void, glLogicOp, GLenum opcode)
+GL_ENTRY(void *, glMapBufferOES, GLenum target, GLenum access)
+GL_ENTRY(void *, glMapBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
+GL_ENTRY(void, glMaterialf, GLenum face, GLenum pname, GLfloat param)
+GL_ENTRY(void, glMaterialfv, GLenum face, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glMaterialx, GLenum face, GLenum pname, GLfixed param)
+GL_ENTRY(void, glMaterialxOES, GLenum face, GLenum pname, GLfixed param)
+GL_ENTRY(void, glMaterialxv, GLenum face, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glMaterialxvOES, GLenum face, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glMatrixIndexPointerOES, GLint size, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glMatrixMode, GLenum mode)
+GL_ENTRY(void, glMultMatrixf, const GLfloat *m)
+GL_ENTRY(void, glMultMatrixx, const GLfixed *m)
+GL_ENTRY(void, glMultMatrixxOES, const GLfixed *m)
+GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount)
+GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount)
+GL_ENTRY(void, glMultiTexCoord4f, GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
+GL_ENTRY(void, glMultiTexCoord4x, GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q)
+GL_ENTRY(void, glMultiTexCoord4xOES, GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q)
+GL_ENTRY(void, glNormal3f, GLfloat nx, GLfloat ny, GLfloat nz)
+GL_ENTRY(void, glNormal3x, GLfixed nx, GLfixed ny, GLfixed nz)
+GL_ENTRY(void, glNormal3xOES, GLfixed nx, GLfixed ny, GLfixed nz)
+GL_ENTRY(void, glNormalPointer, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glOrthof, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f)
+GL_ENTRY(void, glOrthofOES, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f)
+GL_ENTRY(void, glOrthox, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f)
+GL_ENTRY(void, glOrthoxOES, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f)
+GL_ENTRY(void, glPixelStorei, GLenum pname, GLint param)
+GL_ENTRY(void, glPointParameterf, GLenum pname, GLfloat param)
+GL_ENTRY(void, glPointParameterfv, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glPointParameterx, GLenum pname, GLfixed param)
+GL_ENTRY(void, glPointParameterxOES, GLenum pname, GLfixed param)
+GL_ENTRY(void, glPointParameterxv, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glPointParameterxvOES, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glPointSize, GLfloat size)
+GL_ENTRY(void, glPointSizePointerOES, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glPointSizex, GLfixed size)
+GL_ENTRY(void, glPointSizexOES, GLfixed size)
+GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units)
+GL_ENTRY(void, glPolygonOffsetx, GLfixed factor, GLfixed units)
+GL_ENTRY(void, glPolygonOffsetxOES, GLfixed factor, GLfixed units)
+GL_ENTRY(void, glPopGroupMarkerEXT, void)
+GL_ENTRY(void, glPopMatrix, void)
+GL_ENTRY(void, glPushGroupMarkerEXT, GLsizei length, const GLchar *marker)
+GL_ENTRY(void, glPushMatrix, void)
+GL_ENTRY(GLbitfield, glQueryMatrixxOES, GLfixed *mantissa, GLint *exponent)
+GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels)
+GL_ENTRY(void, glReadnPixelsEXT, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data)
+GL_ENTRY(void, glRenderbufferStorageMultisampleAPPLE, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glRenderbufferStorageMultisampleEXT, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glRenderbufferStorageMultisampleIMG, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glRenderbufferStorageOES, GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glResolveMultisampleFramebufferAPPLE, void)
+GL_ENTRY(void, glRotatef, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
+GL_ENTRY(void, glRotatex, GLfixed angle, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(void, glRotatexOES, GLfixed angle, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(void, glSampleCoverage, GLfloat value, GLboolean invert)
+GL_ENTRY(void, glSampleCoveragex, GLclampx value, GLboolean invert)
+GL_ENTRY(void, glSampleCoveragexOES, GLclampx value, GLboolean invert)
+GL_ENTRY(void, glScalef, GLfloat x, GLfloat y, GLfloat z)
+GL_ENTRY(void, glScalex, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(void, glScalexOES, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glSetFenceNV, GLuint fence, GLenum condition)
+GL_ENTRY(void, glShadeModel, GLenum mode)
+GL_ENTRY(void, glStartTilingQCOM, GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask)
+GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask)
+GL_ENTRY(void, glStencilMask, GLuint mask)
+GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass)
+GL_ENTRY(GLboolean, glTestFenceNV, GLuint fence)
+GL_ENTRY(void, glTexCoordPointer, GLint size, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glTexEnvf, GLenum target, GLenum pname, GLfloat param)
+GL_ENTRY(void, glTexEnvfv, GLenum target, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glTexEnvi, GLenum target, GLenum pname, GLint param)
+GL_ENTRY(void, glTexEnviv, GLenum target, GLenum pname, const GLint *params)
+GL_ENTRY(void, glTexEnvx, GLenum target, GLenum pname, GLfixed param)
+GL_ENTRY(void, glTexEnvxOES, GLenum target, GLenum pname, GLfixed param)
+GL_ENTRY(void, glTexEnvxv, GLenum target, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glTexEnvxvOES, GLenum target, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glTexGenfOES, GLenum coord, GLenum pname, GLfloat param)
+GL_ENTRY(void, glTexGenfvOES, GLenum coord, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glTexGeniOES, GLenum coord, GLenum pname, GLint param)
+GL_ENTRY(void, glTexGenivOES, GLenum coord, GLenum pname, const GLint *params)
+GL_ENTRY(void, glTexGenxOES, GLenum coord, GLenum pname, GLfixed param)
+GL_ENTRY(void, glTexGenxvOES, GLenum coord, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels)
+GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param)
+GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param)
+GL_ENTRY(void, glTexParameteriv, GLenum target, GLenum pname, const GLint *params)
+GL_ENTRY(void, glTexParameterx, GLenum target, GLenum pname, GLfixed param)
+GL_ENTRY(void, glTexParameterxOES, GLenum target, GLenum pname, GLfixed param)
+GL_ENTRY(void, glTexParameterxv, GLenum target, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glTexParameterxvOES, GLenum target, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glTexStorage1DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width)
+GL_ENTRY(void, glTexStorage2DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glTexStorage3DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
+GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
+GL_ENTRY(void, glTextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width)
+GL_ENTRY(void, glTextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glTextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
+GL_ENTRY(void, glTranslatef, GLfloat x, GLfloat y, GLfloat z)
+GL_ENTRY(void, glTranslatex, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(void, glTranslatexOES, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(GLboolean, glUnmapBufferOES, GLenum target)
+GL_ENTRY(void, glVertexPointer, GLint size, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glWaitSyncAPPLE, GLsync sync, GLbitfield flags, GLuint64 timeout)
+GL_ENTRY(void, glWeightPointerOES, GLint size, GLenum type, GLsizei stride, const void *pointer)
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index 81dbe0e..63a0e14 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -59,6 +59,10 @@
 #define GL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__);
 #define EGL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__);
 
+struct platform_impl_t {
+    #include "platform_entries.in"
+};
+
 struct egl_t {
     #include "EGL/egl_entries.in"
 };
diff --git a/opengl/libs/libEGL.map.txt b/opengl/libs/libEGL.map.txt
index fa26e33..b2d7957 100644
--- a/opengl/libs/libEGL.map.txt
+++ b/opengl/libs/libEGL.map.txt
@@ -3,23 +3,30 @@
     eglBindAPI;
     eglBindTexImage;
     eglChooseConfig;
+    eglClientWaitSync; # introduced=29
     eglClientWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     eglCopyBuffers;
     eglCreateContext;
+    eglCreateImage; # introduced=29
     eglCreateImageKHR;
     eglCreateNativeClientBufferANDROID; # introduced=24
     eglCreatePbufferFromClientBuffer;
     eglCreatePbufferSurface;
     eglCreatePixmapSurface;
+    eglCreatePlatformPixmapSurface; # introduced=29
+    eglCreatePlatformWindowSurface; # introduced=29
     eglCreateStreamFromFileDescriptorKHR; # introduced=23
     eglCreateStreamKHR; # introduced=23
     eglCreateStreamProducerSurfaceKHR; # introduced=23
+    eglCreateSync; # introduced=29
     eglCreateSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     eglCreateWindowSurface;
     eglDestroyContext;
+    eglDestroyImage; # introduced=29
     eglDestroyImageKHR;
     eglDestroyStreamKHR; # introduced=23
     eglDestroySurface;
+    eglDestroySync; # introduced=29
     eglDestroySyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     eglDupNativeFenceFDANDROID; # vndk
     eglGetConfigAttrib;
@@ -30,8 +37,10 @@
     eglGetDisplay;
     eglGetError;
     eglGetNativeClientBufferANDROID; # introduced=26
+    eglGetPlatformDisplay; # introduced=29
     eglGetProcAddress;
     eglGetStreamFileDescriptorKHR; # introduced=23
+    eglGetSyncAttrib; # introduced=29
     eglGetSyncAttribKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     eglGetSystemTimeFrequencyNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21
     eglGetSystemTimeNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21
@@ -64,6 +73,7 @@
     eglWaitClient;
     eglWaitGL;
     eglWaitNative;
+    eglWaitSync; # introduced=29
     eglWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
   local:
     *;
diff --git a/opengl/libs/platform_entries.in b/opengl/libs/platform_entries.in
new file mode 100644
index 0000000..4673411
--- /dev/null
+++ b/opengl/libs/platform_entries.in
@@ -0,0 +1,86 @@
+EGL_ENTRY(EGLDisplay, eglGetDisplay, EGLNativeDisplayType)
+EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, EGLNativeDisplayType, const EGLAttrib*)
+EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*)
+EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay)
+EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*)
+EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*)
+EGL_ENTRY(EGLBoolean, eglGetConfigAttrib, EGLDisplay, EGLConfig, EGLint, EGLint*)
+EGL_ENTRY(EGLSurface, eglCreateWindowSurface, EGLDisplay, EGLConfig, NativeWindowType, const EGLint*)
+EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*)
+EGL_ENTRY(EGLSurface, eglCreatePixmapSurface, EGLDisplay, EGLConfig, NativePixmapType, const EGLint*)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*)
+EGL_ENTRY(EGLSurface, eglCreatePbufferSurface, EGLDisplay, EGLConfig, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroySurface, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglQuerySurface, EGLDisplay, EGLSurface, EGLint, EGLint*)
+EGL_ENTRY(void, eglBeginFrame, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLContext, eglCreateContext, EGLDisplay, EGLConfig, EGLContext, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyContext, EGLDisplay, EGLContext)
+EGL_ENTRY(EGLBoolean, eglMakeCurrent, EGLDisplay, EGLSurface, EGLSurface, EGLContext)
+EGL_ENTRY(EGLBoolean, eglQueryContext, EGLDisplay, EGLContext, EGLint, EGLint*)
+EGL_ENTRY(EGLContext, eglGetCurrentContext, void)
+EGL_ENTRY(EGLSurface, eglGetCurrentSurface, EGLint)
+EGL_ENTRY(EGLDisplay, eglGetCurrentDisplay, void)
+EGL_ENTRY(EGLBoolean, eglWaitGL, void)
+EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint)
+EGL_ENTRY(EGLint, eglGetError, void)
+EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char*)
+EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint*, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType)
+EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint)
+EGL_ENTRY(const char*, eglQueryStringImplementationANDROID, EGLDisplay, EGLint)
+EGL_ENTRY(EGLBoolean, eglSurfaceAttrib, EGLDisplay, EGLSurface, EGLint, EGLint)
+EGL_ENTRY(EGLBoolean, eglBindTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglReleaseTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapInterval, EGLDisplay, EGLint)
+EGL_ENTRY(EGLBoolean, eglWaitClient, void)
+EGL_ENTRY(EGLBoolean, eglBindAPI, EGLenum)
+EGL_ENTRY(EGLenum, eglQueryAPI, void)
+EGL_ENTRY(EGLBoolean, eglReleaseThread, void)
+EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglUnlockSurfaceKHR, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib*)
+EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage)
+EGL_ENTRY(EGLImageKHR, eglCreateImageKHR, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyImageKHR, EGLDisplay, EGLImageKHR)
+EGL_ENTRY(EGLSync, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib*)
+EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync)
+EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSyncKHR, EGLint, EGLAttrib*)
+EGL_ENTRY(EGLSyncKHR, eglCreateSyncKHR, EGLDisplay, EGLenum, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroySyncKHR, EGLDisplay, EGLSyncKHR)
+EGL_ENTRY(EGLBoolean, eglSignalSyncKHR, EGLDisplay, EGLSyncKHR, EGLenum)
+EGL_ENTRY(EGLint, eglClientWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttribKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLint*)
+EGL_ENTRY(EGLStreamKHR, eglCreateStreamKHR, EGLDisplay, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyStreamKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamAttribKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint)
+EGL_ENTRY(EGLBoolean, eglQueryStreamKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint*)
+EGL_ENTRY(EGLBoolean, eglQueryStreamu64KHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLuint64KHR*)
+EGL_ENTRY(EGLBoolean, eglQueryStreamTimeKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLTimeKHR*)
+EGL_ENTRY(EGLSurface, eglCreateStreamProducerSurfaceKHR, EGLDisplay, EGLConfig, EGLStreamKHR, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerGLTextureExternalKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerAcquireKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerReleaseKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLNativeFileDescriptorKHR, eglGetStreamFileDescriptorKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR, EGLDisplay, EGLNativeFileDescriptorKHR)
+EGL_ENTRY(EGLint, eglWaitSync, EGLDisplay, EGLSync, EGLint)
+EGL_ENTRY(EGLint, eglWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint)
+EGL_ENTRY(EGLint, eglDupNativeFenceFDANDROID, EGLDisplay, EGLSyncKHR)
+EGL_ENTRY(EGLBoolean, eglPresentationTimeANDROID, EGLDisplay, EGLSurface, EGLnsecsANDROID)
+EGL_ENTRY(EGLClientBuffer, eglGetNativeClientBufferANDROID, const AHardwareBuffer*)
+EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void)
+EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void)
+EGL_ENTRY(EGLBoolean, eglSetDamageRegionKHR, EGLDisplay, EGLSurface, EGLint*, EGLint)
+EGL_ENTRY(EGLBoolean, eglGetNextFrameIdANDROID, EGLDisplay, EGLSurface, EGLuint64KHR*)
+EGL_ENTRY(EGLBoolean, eglGetCompositorTimingANDROID, EGLDisplay, EGLSurface, EGLint, const EGLint*, EGLnsecsANDROID*)
+EGL_ENTRY(EGLBoolean, eglGetCompositorTimingSupportedANDROID, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglGetFrameTimestampsANDROID, EGLDisplay, EGLSurface, EGLuint64KHR, EGLint, const EGLint*, EGLnsecsANDROID*)
+EGL_ENTRY(EGLBoolean, eglGetFrameTimestampSupportedANDROID, EGLDisplay, EGLSurface, EGLint)
+GL_ENTRY(const GLubyte*, glGetString, GLenum)
+GL_ENTRY(const GLubyte*, glGetStringi, GLenum, GLuint)
+GL_ENTRY(void, glGetBooleanv, GLenum, GLboolean*)
+GL_ENTRY(void, glGetFloatv, GLenum, GLfloat*)
+GL_ENTRY(void, glGetIntegerv, GLenum, GLint*)
+GL_ENTRY(void, glGetInteger64v, GLenum, GLint64*)
diff --git a/opengl/libs/tools/genfiles b/opengl/libs/tools/genfiles
deleted file mode 100755
index feef318..0000000
--- a/opengl/libs/tools/genfiles
+++ /dev/null
@@ -1,50 +0,0 @@
-#! /bin/sh
-#
-# Copyright (C) 2008 Google Inc.
-#
-# 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.
-
-# Force a specific locale for sorting to avoid irrelevant differences
-# in the generated files that could hide real differences.
-export LC_ALL=POSIX
-
-./glapigen ../../include/GLES/gl.h      > ../GLES_CM/gl_api.in
-./glapigen ../../include/GLES/glext.h   > ../GLES_CM/glext_api.in
-./glapigen ../../include/GLES3/gl3.h    > ../GLES2/gl2_api.in
-./glapigen ../../include/GLES2/gl2ext.h > ../GLES2/gl2ext_api.in
-
-./glentrygen ../../include/GLES/gl.h      > /tmp/gl_entries.in
-./glentrygen ../../include/GLES/glext.h   > /tmp/glext_entries.in
-./glentrygen ../../include/GLES3/gl3.h    > /tmp/gl2_entries.in
-./glentrygen ../../include/GLES2/gl2ext.h > /tmp/gl2ext_entries.in
-
-# The awk command removes lines with the same function name as an earlier
-# line, even if the rest of the line differs. Although signatures of
-# functions with the same name should be the same, the different versions
-# have some irrelevant whitespace and parameter name differences.
-cat /tmp/gl_entries.in \
-    /tmp/glext_entries.in \
-    /tmp/gl2_entries.in \
-    /tmp/gl2ext_entries.in \
-        | sort -t, -k2 \
-        | awk -F, '!_[$2]++' \
-            > ../entries.in
-
-cat ../../include/GLES/gl.h \
-    ../../include/GLES/glext.h \
-    ../../include/GLES2/gl2ext.h \
-    ../../include/GLES3/gl3.h \
-        | ./glenumsgen \
-        | sort \
-        > ../enums.in
-
diff --git a/opengl/libs/tools/glapigen b/opengl/libs/tools/glapigen
deleted file mode 100755
index 4d8334f..0000000
--- a/opengl/libs/tools/glapigen
+++ /dev/null
@@ -1,76 +0,0 @@
-#! /usr/bin/perl
-#
-# Copyright (C) 2008 Google Inc.
-#
-# 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.
-
-use strict;
-
-sub rtrim($)
-{
-    my $string = shift;
-    $string =~ s/\s+$//;
-    return $string;
-}
-
-while (my $line = <>) {
-  next if $line =~ /^\//;
-  next if $line =~ /^#/;
-  next if $line =~ /^\s*$/;
-  if ($line !~ /^GL_API(CALL)?\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) {
-    next;
-  }
-  my $type = rtrim($2);
-  my $name = $3;
-  my $args = $4;
-
-  #printf("%s", $line);
-  
-  my $prefix = "";
-  if ($name eq "glGetString") {
-    $prefix = "__";
-  }
-  
-  printf("%s API_ENTRY(%s%s)(%s)", $type, $prefix, $name, $args);
-  
-  printf(" {\n");
-  if ($type eq "void") {
-    printf("    CALL_GL_API(%s", $name);
-  } else {
-    printf("    CALL_GL_API_RETURN(%s", $name);
-  }
-  my @args = split ',', $args;
-  my $len = scalar(@args);
-  for (my $num = 0; $num < $len; $num++) {
-    if ($args[$num] ne "void") {
-      print ", ";
-      #
-      # extract the name from the parameter
-      # type name
-      # const type *name
-      # type *name
-      # type name[4]
-      #
-      if ($args[$num] =~ /(\S+\s)+\**\s*([\w]+)/) {
-        printf("%s", $2);
-      }
-    }
-  }
-  printf(");\n");
-  printf("}\n");
-}
-
-
-
-
-
diff --git a/opengl/libs/tools/glentrygen b/opengl/libs/tools/glentrygen
deleted file mode 100755
index 170f041..0000000
--- a/opengl/libs/tools/glentrygen
+++ /dev/null
@@ -1,38 +0,0 @@
-#! /usr/bin/perl
-#
-# Copyright (C) 2008 Google Inc.
-#
-# 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.
-
-use strict;
-
-sub rtrim($)
-{
-    my $string = shift;
-    $string =~ s/\s+$//;
-    return $string;
-}
-
-while (my $line = <>) {
-  next if $line =~ /^\//;
-  next if $line =~ /^#/;
-  next if $line =~ /^\s*$/;
-  if ($line !~ /^GL_API(CALL)?\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) {
-    next;
-  }
-  my $type = rtrim($2);
-  my $name = $3;
-  my $args = $4;
-
-  printf("GL_ENTRY(%s, %s, %s)\n", $type, $name, $args);
-}
diff --git a/opengl/libs/tools/glenumsgen b/opengl/libs/tools/glenumsgen
deleted file mode 100755
index 2ae5fbf..0000000
--- a/opengl/libs/tools/glenumsgen
+++ /dev/null
@@ -1,38 +0,0 @@
-#! /usr/bin/perl
-#
-# Copyright (C) 2010 Google Inc.
-#
-# 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.
-
-use strict;
-
-my %enumHash = ();
-
-while (my $line = <STDIN>) {
-  next if $line =~ /^\//;
-  # Skip bitfield definitions.
-  next if $line =~ /_BIT(\d+_|\s+)/;
-  if ($line !~ /^#define\s+(\S+)\s+(0x\S+)/) {
-    next;
-  }
-  my $enumName = $1;
-  my $enumValue = $2;
-  next if exists($enumHash { $enumValue });
-  $enumHash { $enumValue } = $enumName;
-  printf("GL_ENUM(%s,%s)\n", $enumValue, $enumName);
-}
-
-
-
-
-
diff --git a/opengl/specs/README b/opengl/specs/README
index fdafb1b..6d597d5 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -19,10 +19,7 @@
 0x3145               EGL_SYNC_NATIVE_FENCE_FD_ANDROID (EGL_ANDROID_native_fence_sync)
 0x3146               EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID (EGL_ANDROID_native_fence_sync)
 0x3147               EGL_FRAMEBUFFER_TARGET_ANDROID (EGL_ANDROID_framebuffer_target)
-0x3148               EGL_IMAGE_CROP_LEFT_ANDROID (EGL_ANDROID_image_crop)
-0x3149               EGL_IMAGE_CROP_TOP_ANDROID (EGL_ANDROID_image_crop)
-0x314A               EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop)
-0x314B               EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop)
+0x3148 - 0x314B      previously used by the undocumented, deprecated extension EGL_ANDROID_image_crop
 0x314C               EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh)
 0x314D               EGL_GL_COLORSPACE_DEFAULT_EXT (EGL_EXT_image_gl_colorspace)
 0x314E - 0x314F      (unused)
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index 459b135..cca91c3 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -217,6 +217,7 @@
     // Test that display-p3 extensions exist
     ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3"));
     ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear"));
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough"));
 
     // Use 8-bit to keep forcus on Display-P3 aspect
     EGLint attrs[] = {
@@ -289,6 +290,59 @@
     EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
 }
 
+TEST_F(EGLTest, EGLDisplayP3Passthrough) {
+    EGLConfig config;
+    EGLBoolean success;
+
+    if (!hasWideColorDisplay) {
+        // skip this test if device does not have wide-color display
+        std::cerr << "[          ] Device does not support wide-color, test skipped" << std::endl;
+        return;
+    }
+
+    // Test that display-p3 extensions exist
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3"));
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear"));
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough"));
+
+    get8BitConfig(config);
+
+    struct DummyConsumer : public BnConsumerListener {
+        void onFrameAvailable(const BufferItem& /* item */) override {}
+        void onBuffersReleased() override {}
+        void onSidebandStreamChanged() override {}
+    };
+
+    // Create a EGLSurface
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+    consumer->consumerConnect(new DummyConsumer, false);
+    sp<Surface> mSTC = new Surface(producer);
+    sp<ANativeWindow> mANW = mSTC;
+    EGLint winAttrs[] = {
+            // clang-format off
+            EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT,
+            EGL_NONE,              EGL_NONE
+            // clang-format on
+    };
+
+    EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, config, mANW.get(), winAttrs);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_SURFACE, eglSurface);
+
+    android_dataspace dataspace =
+        static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(mANW.get()));
+    ASSERT_EQ(dataspace, HAL_DATASPACE_DISPLAY_P3);
+
+    EGLint value;
+    success = eglQuerySurface(mEglDisplay, eglSurface, EGL_GL_COLORSPACE_KHR, &value);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, value);
+
+    EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
+}
+
 TEST_F(EGLTest, EGLDisplayP31010102) {
     EGLint numConfigs;
     EGLConfig config;
@@ -303,6 +357,7 @@
     // Test that display-p3 extensions exist
     ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3"));
     ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear"));
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough"));
 
     // Use 8-bit to keep forcus on Display-P3 aspect
     EGLint attrs[] = {
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index 2b76279..b06422a 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -57,7 +57,7 @@
     sp<SurfaceControl> sc = surfaceComposerClient->createSurface(
             String8("Benchmark"), width, height,
             PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
-    if (sc == NULL || !sc->isValid()) {
+    if (sc == nullptr || !sc->isValid()) {
         fprintf(stderr, "Failed to create SurfaceControl\n");
         return;
     }
diff --git a/opengl/tests/lib/glTestLib.cpp b/opengl/tests/lib/glTestLib.cpp
index 213dffd..290d7a0 100644
--- a/opengl/tests/lib/glTestLib.cpp
+++ b/opengl/tests/lib/glTestLib.cpp
@@ -37,7 +37,7 @@
 {
     const char *v = (const char *) glGetString(s);
 
-    if (v == NULL) {
+    if (v == nullptr) {
         testPrintI("GL %s unknown", name);
     } else {
         testPrintI("GL %s = %s", name, v);
diff --git a/opengl/tests/lib/include/EGLUtils.h b/opengl/tests/lib/include/EGLUtils.h
index 9dc6bcf..cfa378f 100644
--- a/opengl/tests/lib/include/EGLUtils.h
+++ b/opengl/tests/lib/include/EGLUtils.h
@@ -100,11 +100,11 @@
     if (!attrs)
         return BAD_VALUE;
 
-    if (outConfig == NULL)
+    if (outConfig == nullptr)
         return BAD_VALUE;
 
     // Get all the "potential match" configs...
-    if (eglGetConfigs(dpy, NULL, 0, &numConfigs) == EGL_FALSE)
+    if (eglGetConfigs(dpy, nullptr, 0, &numConfigs) == EGL_FALSE)
         return BAD_VALUE;
 
     std::vector<EGLConfig> configs(numConfigs);
@@ -113,7 +113,7 @@
     }
 
     int i;
-    EGLConfig config = NULL;
+    EGLConfig config = nullptr;
     for (i=0 ; i<n ; i++) {
         EGLint nativeVisualId = 0;
         eglGetConfigAttrib(dpy, configs[i], EGL_NATIVE_VISUAL_ID, &nativeVisualId);
@@ -243,7 +243,7 @@
 
 bool EGLUtils::printEGLConfigurations(EGLDisplay dpy, String8& msg) {
     EGLint numConfig = 0;
-    EGLint returnVal = eglGetConfigs(dpy, NULL, 0, &numConfig);
+    EGLint returnVal = eglGetConfigs(dpy, nullptr, 0, &numConfig);
     msg.append(checkEglError("eglGetConfigs", returnVal));
     if (!returnVal) {
         return false;
@@ -280,6 +280,8 @@
             return String8("EGL_GL_COLORSPACE_SRGB_KHR");
         case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
             return String8("EGL_GL_COLORSPACE_DISPLAY_P3_EXT");
+        case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
+            return String8("EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT");
         case  EGL_GL_COLORSPACE_LINEAR_KHR:
             return String8("EGL_GL_COLORSPACE_LINEAR_KHR");
         default:
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index f9e96ea..9fa58e2 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -93,6 +93,7 @@
 pushd out > /dev/null
 mkdir classes
 javac -d classes    android/opengl/EGL14.java \
+                    android/opengl/EGL15.java \
                     android/opengl/EGLExt.java \
                     com/google/android/gles_jni/GLImpl.java \
                     javax/microedition/khronos/opengles/GL10.java \
@@ -155,13 +156,13 @@
     compareGenerated ../../../../base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x
 done
 
-for x in EGL14 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32
+for x in EGL14 EGL15 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32
 do
     compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
     compareGenerated ../../../../base/core/jni generated/C android_opengl_${x}.cpp
 done
 
-for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface
+for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface EGLImage EGLSync
 do
     compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
 done
diff --git a/opengl/tools/glgen/specs/egl/EGL15.spec b/opengl/tools/glgen/specs/egl/EGL15.spec
new file mode 100644
index 0000000..e0aad30
--- /dev/null
+++ b/opengl/tools/glgen/specs/egl/EGL15.spec
@@ -0,0 +1,14 @@
+EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list )
+EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync )
+EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout )
+EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value )
+// NOTE: native_display isn't actually an EGLAttrib. Using EGLAttrib
+// so that the generate creates mostly correct code (do not want a buffer)
+// have to manually change cast to (void *) in generated code that calls
+// the native function.
+EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list )
+EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list )
+EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags )
+EGLImage eglCreateImage ( EGLDisplay dpy, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list )
+EGLBoolean eglDestroyImage ( EGLDisplay dpy, EGLImage image )
diff --git a/opengl/tools/glgen/specs/egl/checks.spec b/opengl/tools/glgen/specs/egl/checks.spec
index ae531ee..e2bae48 100644
--- a/opengl/tools/glgen/specs/egl/checks.spec
+++ b/opengl/tools/glgen/specs/egl/checks.spec
@@ -11,3 +11,5 @@
 //STUB function: eglCreatePbufferFromClientBuffer nullAllowed attrib_list sentinel attrib_list EGL_NONE
 eglCreateContext sentinel attrib_list EGL_NONE
 eglQueryContext check value 1
+//unsupported: eglCreatePlatformPixmapSurface nullAllowed attrib_list sentinel attrib_list EGL_NONE
+eglCreatePlatformPixmapSurface unsupported
diff --git a/opengl/tools/glgen/src/CType.java b/opengl/tools/glgen/src/CType.java
index aba98af..b1f8e05 100644
--- a/opengl/tools/glgen/src/CType.java
+++ b/opengl/tools/glgen/src/CType.java
@@ -57,7 +57,9 @@
         if(baseType.equals("EGLContext")
            || baseType.equals("EGLConfig")
            || baseType.equals("EGLSurface")
-           || baseType.equals("EGLDisplay")) {
+           || baseType.equals("EGLDisplay")
+           || baseType.equals("EGLImage")
+           || baseType.equals("EGLSync")) {
                return true;
         }
         return false;
diff --git a/opengl/tools/glgen/src/GenerateEGL.java b/opengl/tools/glgen/src/GenerateEGL.java
index 2ef3970..57958c6 100644
--- a/opengl/tools/glgen/src/GenerateEGL.java
+++ b/opengl/tools/glgen/src/GenerateEGL.java
@@ -84,7 +84,7 @@
         ParameterChecker checker = new ParameterChecker(checksReader);
 
 
-        for(String suffix: new String[] {"EGL14", "EGLExt"}) {
+        for(String suffix: new String[] {"EGL14", "EGL15", "EGLExt"}) {
             BufferedReader specReader = new BufferedReader(new FileReader(
                     "specs/egl/" + suffix + ".spec"));
             String egljFilename = "android/opengl/" + suffix + ".java";
diff --git a/opengl/tools/glgen/src/JType.java b/opengl/tools/glgen/src/JType.java
index 7f08503..0b4401a 100644
--- a/opengl/tools/glgen/src/JType.java
+++ b/opengl/tools/glgen/src/JType.java
@@ -60,12 +60,16 @@
     typeMapping.put(new CType("EGLNativeDisplayType"), new JType("long"));
     typeMapping.put(new CType("EGLClientBuffer"), new JType("long"));
     typeMapping.put(new CType("EGLnsecsANDROID"), new JType("long"));
+    typeMapping.put(new CType("EGLAttrib"), new JType("long"));
+    typeMapping.put(new CType("EGLTime"), new JType("long"));
 
     // EGL nonprimitive types
     typeMapping.put(new CType("EGLConfig"), new JType("EGLConfig", true, false));
     typeMapping.put(new CType("EGLContext"), new JType("EGLContext", true, false));
     typeMapping.put(new CType("EGLDisplay"), new JType("EGLDisplay", true, false));
     typeMapping.put(new CType("EGLSurface"), new JType("EGLSurface", true, false));
+    typeMapping.put(new CType("EGLImage"), new JType("EGLImage", true, false));
+    typeMapping.put(new CType("EGLSync"), new JType("EGLSync", true, false));
 
 
     // Untyped pointers map to untyped Buffers
@@ -139,6 +143,8 @@
     arrayTypeMapping.put(new CType("EGLint", true, true), new JType("int", false, true));
     arrayTypeMapping.put(new CType("EGLConfig", false, true), new JType("EGLConfig", true, true));
     arrayTypeMapping.put(new CType("EGLConfig", true, true), new JType("EGLConfig", true, true));
+    arrayTypeMapping.put(new CType("EGLAttrib", false, true), new JType("long", false, true));
+    arrayTypeMapping.put(new CType("EGLAttrib", true, true), new JType("long", false, true));
 
     }
 
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index e8691bb..6697189 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -103,6 +103,12 @@
             if (cfunc.hasEGLHandleArg()) {
                 return;
             }
+            // eglGetPlatformDisplay does not have any EGLHandleArgs
+            // but we do not want to create IOBuffers of this, so
+            // exit
+            if (cfunc.getName().equals("eglGetPlatformDisplay")) {
+                return;
+            }
         }
 
         jfunc = JFunc.convert(cfunc, false);
diff --git a/opengl/tools/glgen/static/egl/EGLImage.java b/opengl/tools/glgen/static/egl/EGLImage.java
new file mode 100644
index 0000000..731ce72
--- /dev/null
+++ b/opengl/tools/glgen/static/egl/EGLImage.java
@@ -0,0 +1,37 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.opengl;
+
+/**
+ * Wrapper class for native EGLImage objects.
+ *
+ */
+public class EGLImage extends EGLObjectHandle {
+    private EGLImage(long handle) {
+        super(handle);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EGLImage)) return false;
+
+        EGLImage that = (EGLImage) o;
+        return getNativeHandle() == that.getNativeHandle();
+    }
+}
diff --git a/opengl/tools/glgen/static/egl/EGLSync.java b/opengl/tools/glgen/static/egl/EGLSync.java
new file mode 100644
index 0000000..472f9e7
--- /dev/null
+++ b/opengl/tools/glgen/static/egl/EGLSync.java
@@ -0,0 +1,37 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.opengl;
+
+/**
+ * Wrapper class for native EGLSync objects.
+ *
+ */
+public class EGLSync extends EGLObjectHandle {
+    private EGLSync(long handle) {
+        super(handle);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EGLSync)) return false;
+
+        EGLSync that = (EGLSync) o;
+        return getNativeHandle() == that.getNativeHandle();
+    }
+}
diff --git a/opengl/tools/glgen/stubs/egl/EGL15Header.java-if b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if
new file mode 100644
index 0000000..859380f
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if
@@ -0,0 +1,78 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.opengl;
+
+/**
+ * EGL 1.5
+ *
+ */
+public final class EGL15 {
+
+    private EGL15() {};
+
+    public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT            = 0x00000001;
+    public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT   = 0x00000002;
+    public static final int EGL_OPENGL_ES3_BIT                             = 0x00000040;
+    public static final int EGL_SYNC_FLUSH_COMMANDS_BIT                    = 0x0001;
+    public static final int EGL_GL_COLORSPACE_SRGB                         = 0x3089;
+    public static final int EGL_GL_COLORSPACE_LINEAR                       = 0x308A;
+    public static final int EGL_CONTEXT_MAJOR_VERSION                      = 0x3098;
+    public static final int EGL_CL_EVENT_HANDLE                            = 0x309C;
+    public static final int EGL_GL_COLORSPACE                              = 0x309D;
+    public static final int EGL_GL_TEXTURE_2D                              = 0x30B1;
+    public static final int EGL_GL_TEXTURE_3D                              = 0x30B2;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X             = 0x30B3;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X             = 0x30B4;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y             = 0x30B5;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y             = 0x30B6;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z             = 0x30B7;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z             = 0x30B8;
+    public static final int EGL_GL_RENDERBUFFER                            = 0x30B9;
+    public static final int EGL_GL_TEXTURE_LEVEL                           = 0x30BC;
+    public static final int EGL_GL_TEXTURE_ZOFFSET                         = 0x30BD;
+    public static final int EGL_IMAGE_PRESERVED                            = 0x30D2;
+    public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE               = 0x30F0;
+    public static final int EGL_SYNC_STATUS                                = 0x30F1;
+    public static final int EGL_SIGNALED                                   = 0x30F2;
+    public static final int EGL_UNSIGNALED                                 = 0x30F3;
+    public static final int EGL_TIMEOUT_EXPIRED                            = 0x30F5;
+    public static final int EGL_CONDITION_SATISFIED                        = 0x30F6;
+    public static final int EGL_SYNC_TYPE                                  = 0x30F7;
+    public static final int EGL_SYNC_CONDITION                             = 0x30F8;
+    public static final int EGL_SYNC_FENCE                                 = 0x30F9;
+    public static final int EGL_CONTEXT_MINOR_VERSION                      = 0x30FB;
+    public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK                = 0x30FD;
+    public static final int EGL_SYNC_CL_EVENT                              = 0x30FE;
+    public static final int EGL_SYNC_CL_EVENT_COMPLETE                     = 0x30FF;
+    public static final int EGL_CONTEXT_OPENGL_DEBUG                       = 0x31B0;
+    public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE          = 0x31B1;
+    public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS               = 0x31B2;
+    public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 0x31BD;
+    public static final int EGL_NO_RESET_NOTIFICATION                      = 0x31BE;
+    public static final int EGL_LOSE_CONTEXT_ON_RESET                      = 0x31BF;
+    public static final int EGL_PLATFORM_ANDROID_KHR                       = 0x3141;
+    public static final long EGL_FOREVER                                   = 0xFFFFFFFFFFFFFFFFL;
+    public static final EGLImage EGL_NO_IMAGE                              = null;
+    public static final EGLSync EGL_NO_SYNC                                = null;
+    public static final EGLContext EGL_NO_CONTEXT                          = null;
+    public static final EGLDisplay EGL_NO_DISPLAY                          = null;
+    public static final EGLSurface EGL_NO_SURFACE                          = null;
+
+    native private static void _nativeClassInit();
+    static {
+        _nativeClassInit();
+    }
diff --git a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
new file mode 100644
index 0000000..70b46f7
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
@@ -0,0 +1,205 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/misc.h>
+#include "jni.h"
+
+#include <EGL/egl.h>
+#include <assert.h>
+
+#include <ui/ANativeObjectBase.h>
+
+static int initialized = 0;
+
+// classes from EGL 1.4
+static jclass egldisplayClass;
+static jclass eglsurfaceClass;
+static jclass eglconfigClass;
+static jclass eglcontextClass;
+static jclass bufferClass;
+static jclass nioAccessClass;
+
+static jfieldID positionID;
+static jfieldID limitID;
+static jfieldID elementSizeShiftID;
+
+static jmethodID getBasePointerID;
+static jmethodID getBaseArrayID;
+static jmethodID getBaseArrayOffsetID;
+
+static jmethodID egldisplayGetHandleID;
+static jmethodID eglconfigGetHandleID;
+static jmethodID eglcontextGetHandleID;
+static jmethodID eglsurfaceGetHandleID;
+
+static jmethodID egldisplayConstructor;
+static jmethodID eglcontextConstructor;
+static jmethodID eglsurfaceConstructor;
+static jmethodID eglconfigConstructor;
+
+static jobject eglNoContextObject;
+static jobject eglNoDisplayObject;
+static jobject eglNoSurfaceObject;
+
+// classes from EGL 1.5
+static jclass eglimageClass;
+static jclass eglsyncClass;
+
+static jmethodID eglimageGetHandleID;
+static jmethodID eglsyncGetHandleID;
+
+static jmethodID eglimageConstructor;
+static jmethodID eglsyncConstructor;
+
+static jobject eglNoImageObject;
+static jobject eglNoSyncObject;
+
+/* Cache method IDs each time the class is loaded. */
+
+static void nativeClassInit(JNIEnv *_env, jclass glImplClass) {
+    // EGL 1.4 Init
+    jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig");
+    eglconfigClass = (jclass)_env->NewGlobalRef(eglconfigClassLocal);
+    jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext");
+    eglcontextClass = (jclass)_env->NewGlobalRef(eglcontextClassLocal);
+    jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay");
+    egldisplayClass = (jclass)_env->NewGlobalRef(egldisplayClassLocal);
+    jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface");
+    eglsurfaceClass = (jclass)_env->NewGlobalRef(eglsurfaceClassLocal);
+
+    eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J");
+    eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J");
+    egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
+    eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
+
+    eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V");
+    eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V");
+    egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V");
+    eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V");
+
+    jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor,
+                                                      reinterpret_cast<jlong>(EGL_NO_CONTEXT));
+    eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject);
+    jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor,
+                                                      reinterpret_cast<jlong>(EGL_NO_DISPLAY));
+    eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject);
+    jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor,
+                                                      reinterpret_cast<jlong>(EGL_NO_SURFACE));
+    eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject);
+
+    jclass eglClass = _env->FindClass("android/opengl/EGL15");
+    jfieldID noContextFieldID =
+            _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;");
+    _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject);
+
+    jfieldID noDisplayFieldID =
+            _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;");
+    _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject);
+
+    jfieldID noSurfaceFieldID =
+            _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;");
+    _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject);
+
+    // EGL 1.5 init
+    jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
+    nioAccessClass = (jclass)_env->NewGlobalRef(nioAccessClassLocal);
+
+    jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
+    bufferClass = (jclass)_env->NewGlobalRef(bufferClassLocal);
+
+    getBasePointerID =
+            _env->GetStaticMethodID(nioAccessClass, "getBasePointer", "(Ljava/nio/Buffer;)J");
+    getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, "getBaseArray",
+                                             "(Ljava/nio/Buffer;)Ljava/lang/Object;");
+    getBaseArrayOffsetID =
+            _env->GetStaticMethodID(nioAccessClass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
+
+    positionID = _env->GetFieldID(bufferClass, "position", "I");
+    limitID = _env->GetFieldID(bufferClass, "limit", "I");
+    elementSizeShiftID = _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
+
+    jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage");
+    eglimageClass = (jclass)_env->NewGlobalRef(eglimageClassLocal);
+    jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync");
+    eglsyncClass = (jclass)_env->NewGlobalRef(eglsyncClassLocal);
+
+    eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+    eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+    eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V");
+    eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V");
+
+    jfieldID noImageFieldID =
+            _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;");
+    _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject);
+
+    jfieldID noSyncFieldID =
+            _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;");
+    _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject);
+}
+
+static void *getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining,
+                        jint *offset) {
+    jint position;
+    jint limit;
+    jint elementSizeShift;
+    jlong pointer;
+
+    position = _env->GetIntField(buffer, positionID);
+    limit = _env->GetIntField(buffer, limitID);
+    elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+    *remaining = (limit - position) << elementSizeShift;
+    pointer = _env->CallStaticLongMethod(nioAccessClass, getBasePointerID, buffer);
+    if (pointer != 0L) {
+        *array = NULL;
+        return reinterpret_cast<void *>(pointer);
+    }
+    eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+    eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+    *array = (jarray)_env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer);
+    *offset = _env->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetID, buffer);
+
+    return NULL;
+}
+
+static void releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) {
+    _env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT);
+}
+
+static void *fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) {
+    if (obj == NULL) {
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null.");
+    }
+
+    jlong handle = _env->CallLongMethod(obj, mid);
+    return reinterpret_cast<void *>(handle);
+}
+
+static jobject toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void *handle) {
+    if (cls == eglimageClass && (EGLImage)handle == EGL_NO_IMAGE) {
+        return eglNoImageObject;
+    }
+
+    return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle));
+}
+
+// --------------------------------------------------------------------------
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp
new file mode 100644
index 0000000..fd44498
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp
@@ -0,0 +1,46 @@
+/* EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglGetPlatformDisplay
+  (JNIEnv *_env, jobject _this, jint platform, jlong native_display, jlongArray attrib_list_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    EGLDisplay _returnValue = (EGLDisplay) 0;
+    EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+    jint _remaining;
+    EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+    if (!attrib_list_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "attrib_list == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+    attrib_list_base = (EGLAttrib *)
+        _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+    attrib_list = attrib_list_base + offset;
+
+    _returnValue = eglGetPlatformDisplay(
+        (EGLenum)platform,
+        (void *)native_display,
+        (EGLAttrib *)attrib_list
+    );
+
+exit:
+    if (attrib_list_base) {
+        _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+            JNI_ABORT);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue);
+}
+
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java
new file mode 100644
index 0000000..28945e8
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java
@@ -0,0 +1,9 @@
+    // C function EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+
+    public static native EGLDisplay eglGetPlatformDisplay(
+        int platform,
+        long native_display,
+        long[] attrib_list,
+        int offset
+    );
+
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg
new file mode 100644
index 0000000..8a309bf
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg
@@ -0,0 +1 @@
+{"eglGetPlatformDisplay", "(IJ[JI)Landroid/opengl/EGLDisplay;", (void *) android_eglGetPlatformDisplay },
diff --git a/opengl/tools/glgen2/glgen.py b/opengl/tools/glgen2/glgen.py
index fa981a8..86fa28f 100755
--- a/opengl/tools/glgen2/glgen.py
+++ b/opengl/tools/glgen2/glgen.py
@@ -250,6 +250,27 @@
     for opts in TRAMPOLINE_OPTIONS:
         registry.apiGen(opts)
 
+    # Generate a GLESv1_CM entries separately to avoid extra driver loading time
+    apigen = ApiGenerator()
+    registry.setGenerator(apigen)
+    API_OPTIONS = [
+        # Generate non-extension versions of each API first, then extensions,
+        # so that if an extension enum was later standardized, we see the non-
+        # suffixed version first.
+        reg.GeneratorOptions(
+            apiname             = 'gles1',
+            profile             = 'common'),
+        reg.GeneratorOptions(
+            apiname             = 'gles1',
+            profile             = 'common',
+            emitversions        = None,
+            defaultExtensions   = 'gles1')]
+    for opts in API_OPTIONS:
+        registry.apiGen(opts)
+    apigen.finish()
+    with open('../../libs/entries_gles1.in', 'w') as f:
+        apigen.writeEntries(f)
+
     apigen = ApiGenerator()
     registry.setGenerator(apigen)
     API_OPTIONS = [
diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml
index e422e96..8d661ae 100644
--- a/opengl/tools/glgen2/registry/egl.xml
+++ b/opengl/tools/glgen2/registry/egl.xml
@@ -801,7 +801,9 @@
         <enum value="0x3361" name="EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT"/>
         <enum value="0x3362" name="EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT"/>
         <enum value="0x3363" name="EGL_GL_COLORSPACE_DISPLAY_P3_EXT"/>
-            <unused start="0x3364" end="0x339F"/>
+        <enum value="0x3364" name="EGL_SYNC_CLIENT_EXT"/>
+        <enum value="0x3365" name="EGL_SYNC_CLIENT_SIGNAL_EXT"/>
+            <unused start="0x3366" end="0x339F"/>
     </enums>
 
     <enums namespace="EGL" start="0x33A0" end="0x33AF" vendor="ANGLE" comment="Reserved for Shannon Woods (Bug 13175)">
@@ -887,6 +889,15 @@
         <enum value="0x3471" name="EGL_IMPORT_IMPLICIT_SYNC_EXT"/>
         <enum value="0x3472" name="EGL_IMPORT_EXPLICIT_SYNC_EXT"/>
     </enums>
+    <enums namespace="EGL" start="0x3480" end="0x348F" vendor="ANGLE" comment="Reserved for Courtney Goeltzenleuchter - ANGLE (gitlab EGL bug 7)">
+        <enum value="0x3480" name="EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE"/>
+            <unused start="0x3481" end="0x348F"/>
+    </enums>
+
+    <enums namespace="EGL" start="0x3490" end="0x349F" vendor="EXT" comment="Reserved for Courtney Goeltzenleuchter - Android (gitlab EGL bug 69)">
+        <enum value="0x3490" name="EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"/>
+            <unused start="0x3491" end="0x349F"/>
+    </enums>
 
 <!-- Please remember that new enumerant allocations must be obtained by
      request to the Khronos API registrar (see comments at the top of this
@@ -897,8 +908,8 @@
 
 <!-- Reservable for future use. To generate a new range, allocate multiples
      of 16 starting at the lowest available point in this block. -->
-    <enums namespace="EGL" start="0x3480" end="0x3FFF" vendor="KHR" comment="Reserved for future use">
-            <unused start="0x3480" end="0x3FFF"/>
+    <enums namespace="EGL" start="0x34A0" end="0x3FFF" vendor="KHR" comment="Reserved for future use">
+            <unused start="0x34A0" end="0x3FFF"/>
     </enums>
 
     <enums namespace="EGL" start="0x8F70" end="0x8F7F" vendor="HI" comment="For Mark Callow, Khronos bug 4055. Shared with GL.">
@@ -930,6 +941,12 @@
             <param><ptype>EGLint</ptype> *<name>num_config</name></param>
         </command>
         <command>
+            <proto><ptype>EGLBoolean</ptype> <name>eglClientSignalSyncEXT</name></proto>
+            <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+            <param><ptype>EGLSync</ptype> <name>sync</name></param>
+            <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
+        </command>
+        <command>
             <proto><ptype>EGLint</ptype> <name>eglClientWaitSync</name></proto>
             <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
             <param><ptype>EGLSync</ptype> <name>sync</name></param>
@@ -1647,6 +1664,11 @@
             <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
         </command>
         <command>
+            <proto><ptype>EGLBoolean</ptype> <name>eglStreamFlushNV</name></proto>
+            <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+            <param><ptype>EGLStreamKHR</ptype> <name>stream</name></param>
+        </command>
+        <command>
             <proto><ptype>EGLBoolean</ptype> <name>eglSurfaceAttrib</name></proto>
             <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
             <param><ptype>EGLSurface</ptype> <name>surface</name></param>
@@ -1701,6 +1723,12 @@
             <param><ptype>EGLSurface</ptype> <name>surface</name></param>
         </command>
         <command>
+            <proto><ptype>EGLBoolean</ptype> <name>eglUnsignalSyncEXT</name></proto>
+            <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+            <param><ptype>EGLSync</ptype> <name>sync</name></param>
+            <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
+        </command>
+        <command>
             <proto><ptype>EGLBoolean</ptype> <name>eglWaitClient</name></proto>
         </command>
         <command>
@@ -2146,6 +2174,13 @@
             </require>
         </extension>
         <extension name="EGL_EXT_client_extensions" supported="egl"/>
+        <extension name="EGL_EXT_client_sync" supported="egl">
+            <require>
+                <enum name="EGL_SYNC_CLIENT_EXT"/>
+                <enum name="EGL_SYNC_CLIENT_SIGNAL_EXT"/>
+                <command name="eglClientSignalSyncEXT"/>
+            </require>
+        </extension>
         <extension name="EGL_EXT_create_context_robustness" supported="egl">
             <require>
                 <enum name="EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT"/>
@@ -2220,6 +2255,11 @@
                 <enum name="EGL_GL_COLORSPACE_DISPLAY_P3_EXT"/>
             </require>
         </extension>
+        <extension name="EGL_EXT_gl_colorspace_display_p3_passthrough" supported="egl">
+            <require>
+                <enum name="EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"/>
+            </require>
+        </extension>
         <extension name="EGL_EXT_image_dma_buf_import" supported="egl">
             <require>
                 <enum name="EGL_LINUX_DMA_BUF_EXT"/>
@@ -2371,6 +2411,11 @@
                 <command name="eglSwapBuffersWithDamageEXT"/>
             </require>
         </extension>
+        <extension name="EGL_EXT_sync_reuse" supported="egl">
+            <require>
+                <command name="eglUnsignalSyncEXT"/>
+            </require>
+        </extension>
         <extension name="EGL_EXT_yuv_surface" supported="egl">
             <require>
                 <enum name="EGL_YUV_ORDER_EXT"/>
@@ -2932,6 +2977,11 @@
                 <enum name="EGL_STREAM_FIFO_SYNCHRONOUS_NV"/>
             </require>
         </extension>
+        <extension name="EGL_NV_stream_flush" supported="egl">
+            <require>
+                <command name="eglStreamFlushNV"/>
+            </require>
+        </extension>
         <extension name="EGL_NV_stream_frame_limits" supported="egl">
             <require>
                 <enum name="EGL_PRODUCER_MAX_FRAME_HINT_NV"/>
diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp
new file mode 100644
index 0000000..a4b81fe
--- /dev/null
+++ b/services/bufferhub/Android.bp
@@ -0,0 +1,80 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_shared {
+    name: "libbufferhubservice",
+    cflags: [
+        "-DLOG_TAG=\"libbufferhubservice\"",
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    srcs: [
+        "BufferClient.cpp",
+        "BufferHubIdGenerator.cpp",
+        "BufferHubService.cpp",
+        "BufferNode.cpp",
+    ],
+    header_libs: [
+        "libdvr_headers",
+        "libnativewindow_headers",
+    ],
+    shared_libs: [
+        "android.frameworks.bufferhub@1.0",
+        "libcrypto",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libui",
+        "libutils",
+    ],
+    export_include_dirs: [
+        "include"
+    ],
+}
+
+cc_binary {
+    name: "android.frameworks.bufferhub@1.0-service",
+    relative_install_path: "hw",
+    srcs: [
+        "main_bufferhub.cpp"
+    ],
+    header_libs: [
+        "libdvr_headers",
+        "libnativewindow_headers",
+    ],
+    shared_libs: [
+        "android.frameworks.bufferhub@1.0",
+        "libbufferhubservice",
+        "libcrypto",
+        "libcutils",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libui",
+        "libutils",
+    ],
+    cflags: [
+        "-DLOG_TAG=\"bufferhub\"",
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    init_rc: ["android.frameworks.bufferhub@1.0-service.rc"],
+    vintf_fragments: ["android.frameworks.bufferhub@1.0-service.xml"],
+}
diff --git a/services/bufferhub/BufferClient.cpp b/services/bufferhub/BufferClient.cpp
new file mode 100644
index 0000000..ec7e535
--- /dev/null
+++ b/services/bufferhub/BufferClient.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <bufferhub/BufferClient.h>
+#include <bufferhub/BufferHubService.h>
+#include <hidl/HidlSupport.h>
+#include <log/log.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+using hardware::hidl_handle;
+using hardware::Void;
+
+BufferClient* BufferClient::create(BufferHubService* service,
+                                   const std::shared_ptr<BufferNode>& node) {
+    if (!service) {
+        ALOGE("%s: service cannot be nullptr.", __FUNCTION__);
+        return nullptr;
+    } else if (!node) {
+        ALOGE("%s: node cannot be nullptr.", __FUNCTION__);
+        return nullptr;
+    }
+    return new BufferClient(service, node);
+}
+
+BufferClient::~BufferClient() {
+    {
+        std::lock_guard<std::mutex> lock(mClosedMutex);
+        if (!mClosed) {
+            ALOGW("%s: client of buffer #%d destroyed without close. Closing it now.", __FUNCTION__,
+                  mBufferNode->id());
+        }
+    }
+
+    close();
+}
+
+Return<BufferHubStatus> BufferClient::close() {
+    std::lock_guard<std::mutex> lock(mClosedMutex);
+    if (mClosed) {
+        return BufferHubStatus::CLIENT_CLOSED;
+    }
+
+    getService()->onClientClosed(this);
+    mBufferNode.reset();
+    mClosed = true;
+    return BufferHubStatus::NO_ERROR;
+}
+
+Return<void> BufferClient::duplicate(duplicate_cb _hidl_cb) {
+    std::lock_guard<std::mutex> lock(mClosedMutex);
+    if (mClosed) {
+        _hidl_cb(/*token=*/hidl_handle(), /*status=*/BufferHubStatus::CLIENT_CLOSED);
+        return Void();
+    }
+
+    if (!mBufferNode) {
+        // Should never happen
+        ALOGE("%s: node is missing.", __FUNCTION__);
+        _hidl_cb(/*token=*/hidl_handle(), /*status=*/BufferHubStatus::BUFFER_FREED);
+        return Void();
+    }
+
+    const hidl_handle token = getService()->registerToken(this);
+    _hidl_cb(/*token=*/token, /*status=*/BufferHubStatus::NO_ERROR);
+    return Void();
+}
+
+sp<BufferHubService> BufferClient::getService() {
+    sp<BufferHubService> service = mService.promote();
+    if (service == nullptr) {
+        // Should never happen. Kill the process.
+        LOG_FATAL("%s: service died.", __FUNCTION__);
+    }
+
+    return service;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
\ No newline at end of file
diff --git a/services/bufferhub/BufferHubIdGenerator.cpp b/services/bufferhub/BufferHubIdGenerator.cpp
new file mode 100644
index 0000000..2c12f0e
--- /dev/null
+++ b/services/bufferhub/BufferHubIdGenerator.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <bufferhub/BufferHubIdGenerator.h>
+#include <log/log.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+BufferHubIdGenerator& BufferHubIdGenerator::getInstance() {
+    static BufferHubIdGenerator generator;
+
+    return generator;
+}
+
+int BufferHubIdGenerator::getId() {
+    std::lock_guard<std::mutex> lock(mIdsInUseMutex);
+
+    do {
+        if (++mLastId >= std::numeric_limits<int>::max()) {
+            mLastId = 0;
+        }
+    } while (mIdsInUse.find(mLastId) != mIdsInUse.end());
+
+    mIdsInUse.insert(mLastId);
+    return mLastId;
+}
+
+void BufferHubIdGenerator::freeId(int id) {
+    std::lock_guard<std::mutex> lock(mIdsInUseMutex);
+    auto iter = mIdsInUse.find(id);
+    if (iter != mIdsInUse.end()) {
+        mIdsInUse.erase(iter);
+    } else {
+        ALOGW("%s: Cannot free nonexistent id #%d", __FUNCTION__, id);
+    }
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
new file mode 100644
index 0000000..6b802fb
--- /dev/null
+++ b/services/bufferhub/BufferHubService.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <array>
+#include <iomanip>
+#include <random>
+#include <sstream>
+
+#include <android/hardware_buffer.h>
+#include <bufferhub/BufferHubService.h>
+#include <cutils/native_handle.h>
+#include <log/log.h>
+#include <openssl/hmac.h>
+#include <system/graphics-base.h>
+#include <ui/BufferHubDefs.h>
+
+using ::android::BufferHubDefs::MetadataHeader;
+using ::android::hardware::Void;
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+BufferHubService::BufferHubService() {
+    std::mt19937_64 randomEngine;
+    randomEngine.seed(time(nullptr));
+
+    mKey = randomEngine();
+}
+
+Return<void> BufferHubService::allocateBuffer(const HardwareBufferDescription& description,
+                                              const uint32_t userMetadataSize,
+                                              allocateBuffer_cb _hidl_cb) {
+    AHardwareBuffer_Desc desc;
+    memcpy(&desc, &description, sizeof(AHardwareBuffer_Desc));
+
+    std::shared_ptr<BufferNode> node =
+            std::make_shared<BufferNode>(desc.width, desc.height, desc.layers, desc.format,
+                                         desc.usage, userMetadataSize,
+                                         BufferHubIdGenerator::getInstance().getId());
+    if (node == nullptr || !node->IsValid()) {
+        ALOGE("%s: creating BufferNode failed.", __FUNCTION__);
+        _hidl_cb(/*status=*/BufferHubStatus::ALLOCATION_FAILED, /*bufferClient=*/nullptr,
+                 /*bufferTraits=*/{});
+        return Void();
+    }
+
+    sp<BufferClient> client = BufferClient::create(this, node);
+    // Add it to list for bookkeeping and dumpsys.
+    std::lock_guard<std::mutex> lock(mClientSetMutex);
+    mClientSet.emplace(client);
+
+    // Allocate memory for bufferInfo of type hidl_handle on the stack. See
+    // http://aosp/286282 for the usage of NATIVE_HANDLE_DECLARE_STORAGE.
+    NATIVE_HANDLE_DECLARE_STORAGE(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+                                  BufferHubDefs::kBufferInfoNumInts);
+    hidl_handle bufferInfo =
+            buildBufferInfo(bufferInfoStorage, node->id(), node->AddNewActiveClientsBitToMask(),
+                            node->user_metadata_size(), node->metadata().ashmem_fd(),
+                            node->eventFd().get());
+    BufferTraits bufferTraits = {/*bufferDesc=*/description,
+                                 /*bufferHandle=*/hidl_handle(node->buffer_handle()),
+                                 /*bufferInfo=*/std::move(bufferInfo)};
+
+    _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
+             /*bufferTraits=*/std::move(bufferTraits));
+    return Void();
+}
+
+Return<void> BufferHubService::importBuffer(const hidl_handle& tokenHandle,
+                                            importBuffer_cb _hidl_cb) {
+    if (!tokenHandle.getNativeHandle() || tokenHandle->numFds != 0 || tokenHandle->numInts <= 1) {
+        // nullptr handle or wrong format
+        _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
+                 /*bufferTraits=*/{});
+        return Void();
+    }
+
+    int tokenId = tokenHandle->data[0];
+
+    wp<BufferClient> originClientWp;
+    {
+        std::lock_guard<std::mutex> lock(mTokenMutex);
+        auto iter = mTokenMap.find(tokenId);
+        if (iter == mTokenMap.end()) {
+            // Token Id not exist
+            ALOGD("%s: token #%d not found.", __FUNCTION__, tokenId);
+            _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
+                     /*bufferTraits=*/{});
+            return Void();
+        }
+
+        const std::vector<uint8_t>& tokenHMAC = iter->second.first;
+
+        int numIntsForHMAC = (int)ceil(tokenHMAC.size() * sizeof(uint8_t) / (double)sizeof(int));
+        if (tokenHandle->numInts - 1 != numIntsForHMAC) {
+            // HMAC size not match
+            ALOGD("%s: token #%d HMAC size not match. Expected: %d Actual: %d", __FUNCTION__,
+                  tokenId, numIntsForHMAC, tokenHandle->numInts - 1);
+            _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
+                     /*bufferTraits=*/{});
+            return Void();
+        }
+
+        size_t hmacSize = tokenHMAC.size() * sizeof(uint8_t);
+        if (memcmp(tokenHMAC.data(), &tokenHandle->data[1], hmacSize) != 0) {
+            // HMAC not match
+            ALOGD("%s: token #%d HMAC not match.", __FUNCTION__, tokenId);
+            _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
+                     /*bufferTraits=*/{});
+            return Void();
+        }
+
+        originClientWp = iter->second.second;
+        mTokenMap.erase(iter);
+    }
+
+    // Check if original client is dead
+    sp<BufferClient> originClient = originClientWp.promote();
+    if (!originClient) {
+        // Should not happen since token should be removed if already gone
+        ALOGE("%s: original client %p gone!", __FUNCTION__, originClientWp.unsafe_get());
+        _hidl_cb(/*status=*/BufferHubStatus::BUFFER_FREED, /*bufferClient=*/nullptr,
+                 /*bufferTraits=*/{});
+        return Void();
+    }
+
+    sp<BufferClient> client = new BufferClient(*originClient);
+    uint32_t clientStateMask = client->getBufferNode()->AddNewActiveClientsBitToMask();
+    if (clientStateMask == 0U) {
+        // Reach max client count
+        ALOGE("%s: import failed, BufferNode#%u reached maximum clients.", __FUNCTION__,
+              client->getBufferNode()->id());
+        _hidl_cb(/*status=*/BufferHubStatus::MAX_CLIENT, /*bufferClient=*/nullptr,
+                 /*bufferTraits=*/{});
+        return Void();
+    }
+
+    std::lock_guard<std::mutex> lock(mClientSetMutex);
+    mClientSet.emplace(client);
+
+    std::shared_ptr<BufferNode> node = client->getBufferNode();
+
+    HardwareBufferDescription bufferDesc;
+    memcpy(&bufferDesc, &node->buffer_desc(), sizeof(HardwareBufferDescription));
+
+    // Allocate memory for bufferInfo of type hidl_handle on the stack. See
+    // http://aosp/286282 for the usage of NATIVE_HANDLE_DECLARE_STORAGE.
+    NATIVE_HANDLE_DECLARE_STORAGE(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+                                  BufferHubDefs::kBufferInfoNumInts);
+    hidl_handle bufferInfo = buildBufferInfo(bufferInfoStorage, node->id(), clientStateMask,
+                                             node->user_metadata_size(),
+                                             node->metadata().ashmem_fd(), node->eventFd().get());
+    BufferTraits bufferTraits = {/*bufferDesc=*/bufferDesc,
+                                 /*bufferHandle=*/hidl_handle(node->buffer_handle()),
+                                 /*bufferInfo=*/std::move(bufferInfo)};
+
+    _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
+             /*bufferTraits=*/std::move(bufferTraits));
+    return Void();
+}
+
+Return<void> BufferHubService::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) {
+    if (fd.getNativeHandle() == nullptr || fd->numFds < 1) {
+        ALOGE("%s: missing fd for writing.", __FUNCTION__);
+        return Void();
+    }
+
+    FILE* out = fdopen(dup(fd->data[0]), "w");
+
+    if (args.size() != 0) {
+        fprintf(out,
+                "Note: lshal bufferhub currently does not support args. Input arguments are "
+                "ignored.\n");
+    }
+
+    std::ostringstream stream;
+
+    // Get the number of clients of each buffer.
+    // Map from bufferId to bufferNode_clientCount pair.
+    std::map<int, std::pair<const std::shared_ptr<BufferNode>, uint32_t>> clientCount;
+    {
+        std::lock_guard<std::mutex> lock(mClientSetMutex);
+        for (auto iter = mClientSet.begin(); iter != mClientSet.end(); ++iter) {
+            sp<BufferClient> client = iter->promote();
+            if (client != nullptr) {
+                const std::shared_ptr<BufferNode> node = client->getBufferNode();
+                auto mapIter = clientCount.find(node->id());
+                if (mapIter != clientCount.end()) {
+                    ++mapIter->second.second;
+                } else {
+                    clientCount.emplace(node->id(),
+                                        std::pair<std::shared_ptr<BufferNode>, uint32_t>(node, 1U));
+                }
+            }
+        }
+    }
+
+    stream << "Active Buffers:\n";
+    stream << std::right;
+    stream << std::setw(6) << "Id";
+    stream << " ";
+    stream << std::setw(9) << "#Clients";
+    stream << " ";
+    stream << std::setw(14) << "Geometry";
+    stream << " ";
+    stream << std::setw(6) << "Format";
+    stream << " ";
+    stream << std::setw(10) << "Usage";
+    stream << " ";
+    stream << std::setw(10) << "State";
+    stream << " ";
+    stream << std::setw(8) << "Index";
+    stream << std::endl;
+
+    for (auto iter = clientCount.begin(); iter != clientCount.end(); ++iter) {
+        const std::shared_ptr<BufferNode> node = std::move(iter->second.first);
+        const uint32_t clientCount = iter->second.second;
+        AHardwareBuffer_Desc desc = node->buffer_desc();
+
+        MetadataHeader* metadataHeader =
+                const_cast<BufferHubMetadata*>(&node->metadata())->metadata_header();
+        const uint32_t state = metadataHeader->buffer_state.load(std::memory_order_acquire);
+        const uint64_t index = metadataHeader->queue_index;
+
+        stream << std::right;
+        stream << std::setw(6) << /*Id=*/node->id();
+        stream << " ";
+        stream << std::setw(9) << /*#Clients=*/clientCount;
+        stream << " ";
+        if (desc.format == HAL_PIXEL_FORMAT_BLOB) {
+            std::string size = std::to_string(desc.width) + " B";
+            stream << std::setw(14) << /*Geometry=*/size;
+        } else {
+            std::string dimensions = std::to_string(desc.width) + "x" +
+                    std::to_string(desc.height) + "x" + std::to_string(desc.layers);
+            stream << std::setw(14) << /*Geometry=*/dimensions;
+        }
+        stream << " ";
+        stream << std::setw(6) << /*Format=*/desc.format;
+        stream << " ";
+        stream << "0x" << std::hex << std::setfill('0');
+        stream << std::setw(8) << /*Usage=*/desc.usage;
+        stream << std::dec << std::setfill(' ');
+        stream << " ";
+        stream << "0x" << std::hex << std::setfill('0');
+        stream << std::setw(8) << /*State=*/state;
+        stream << std::dec << std::setfill(' ');
+        stream << " ";
+        stream << std::setw(8) << /*Index=*/index;
+        stream << std::endl;
+    }
+
+    stream << std::endl;
+
+    // Get the number of tokens of each buffer.
+    // Map from bufferId to tokenCount
+    std::map<int, uint32_t> tokenCount;
+    {
+        std::lock_guard<std::mutex> lock(mTokenMutex);
+        for (auto iter = mTokenMap.begin(); iter != mTokenMap.end(); ++iter) {
+            sp<BufferClient> client = iter->second.second.promote();
+            if (client != nullptr) {
+                const std::shared_ptr<BufferNode> node = client->getBufferNode();
+                auto mapIter = tokenCount.find(node->id());
+                if (mapIter != tokenCount.end()) {
+                    ++mapIter->second;
+                } else {
+                    tokenCount.emplace(node->id(), 1U);
+                }
+            }
+        }
+    }
+
+    stream << "Unused Tokens:\n";
+    stream << std::right;
+    stream << std::setw(8) << "Buffer Id";
+    stream << " ";
+    stream << std::setw(7) << "#Tokens";
+    stream << std::endl;
+
+    for (auto iter = tokenCount.begin(); iter != tokenCount.end(); ++iter) {
+        stream << std::right;
+        stream << std::setw(8) << /*Buffer Id=*/iter->first;
+        stream << " ";
+        stream << std::setw(7) << /*#Tokens=*/iter->second;
+        stream << std::endl;
+    }
+
+    fprintf(out, "%s", stream.str().c_str());
+
+    fclose(out);
+    return Void();
+}
+
+hidl_handle BufferHubService::registerToken(const wp<BufferClient>& client) {
+    // Find next available token id
+    std::lock_guard<std::mutex> lock(mTokenMutex);
+    do {
+        ++mLastTokenId;
+    } while (mTokenMap.find(mLastTokenId) != mTokenMap.end());
+
+    std::array<uint8_t, EVP_MAX_MD_SIZE> hmac;
+    uint32_t hmacSize = 0U;
+
+    HMAC(/*evp_md=*/EVP_sha256(), /*key=*/&mKey, /*key_len=*/kKeyLen,
+         /*data=*/(uint8_t*)&mLastTokenId, /*data_len=*/mTokenIdSize,
+         /*out=*/hmac.data(), /*out_len=*/&hmacSize);
+
+    int numIntsForHMAC = (int)ceil(hmacSize / (double)sizeof(int));
+    native_handle_t* handle = native_handle_create(/*numFds=*/0, /*numInts=*/1 + numIntsForHMAC);
+    handle->data[0] = mLastTokenId;
+    // Set all the the bits of last int to 0 since it might not be fully overwritten
+    handle->data[numIntsForHMAC] = 0;
+    memcpy(&handle->data[1], hmac.data(), hmacSize);
+
+    // returnToken owns the native_handle_t* thus doing lifecycle management
+    hidl_handle returnToken;
+    returnToken.setTo(handle, /*shoudOwn=*/true);
+
+    std::vector<uint8_t> hmacVec;
+    hmacVec.resize(hmacSize);
+    memcpy(hmacVec.data(), hmac.data(), hmacSize);
+    mTokenMap.emplace(mLastTokenId, std::pair(hmacVec, client));
+
+    return returnToken;
+}
+
+void BufferHubService::onClientClosed(const BufferClient* client) {
+    removeTokenByClient(client);
+
+    std::lock_guard<std::mutex> lock(mClientSetMutex);
+    auto iter = std::find(mClientSet.begin(), mClientSet.end(), client);
+    if (iter != mClientSet.end()) {
+        mClientSet.erase(iter);
+    }
+}
+
+// Implementation of this function should be consistent with the definition of bufferInfo handle in
+// ui/BufferHubDefs.h.
+hidl_handle BufferHubService::buildBufferInfo(char* bufferInfoStorage, int bufferId,
+                                              uint32_t clientBitMask, uint32_t userMetadataSize,
+                                              int metadataFd, int eventFd) {
+    native_handle_t* infoHandle =
+            native_handle_init(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+                               BufferHubDefs::kBufferInfoNumInts);
+
+    infoHandle->data[0] = metadataFd;
+    infoHandle->data[1] = eventFd;
+    infoHandle->data[2] = bufferId;
+    // Use memcpy to convert to int without missing digit.
+    // TOOD(b/121345852): use bit_cast to unpack bufferInfo when C++20 becomes available.
+    memcpy(&infoHandle->data[3], &clientBitMask, sizeof(clientBitMask));
+    memcpy(&infoHandle->data[4], &userMetadataSize, sizeof(userMetadataSize));
+
+    hidl_handle bufferInfo;
+    bufferInfo.setTo(infoHandle, /*shouldOwn=*/false);
+
+    return bufferInfo;
+}
+
+void BufferHubService::removeTokenByClient(const BufferClient* client) {
+    std::lock_guard<std::mutex> lock(mTokenMutex);
+    auto iter = mTokenMap.begin();
+    while (iter != mTokenMap.end()) {
+        if (iter->second.second == client) {
+            auto oldIter = iter;
+            ++iter;
+            mTokenMap.erase(oldIter);
+        } else {
+            ++iter;
+        }
+    }
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
diff --git a/services/bufferhub/BufferNode.cpp b/services/bufferhub/BufferNode.cpp
new file mode 100644
index 0000000..5106390
--- /dev/null
+++ b/services/bufferhub/BufferNode.cpp
@@ -0,0 +1,114 @@
+#include <errno.h>
+
+#include <bufferhub/BufferHubService.h>
+#include <bufferhub/BufferNode.h>
+#include <log/log.h>
+#include <ui/GraphicBufferAllocator.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+void BufferNode::InitializeMetadata() {
+    // Using placement new here to reuse shared memory instead of new allocation
+    // Initialize the atomic variables to zero.
+    BufferHubDefs::MetadataHeader* metadata_header = metadata_.metadata_header();
+    buffer_state_ = new (&metadata_header->buffer_state) std::atomic<uint32_t>(0);
+    fence_state_ = new (&metadata_header->fence_state) std::atomic<uint32_t>(0);
+    active_clients_bit_mask_ =
+            new (&metadata_header->active_clients_bit_mask) std::atomic<uint32_t>(0);
+    // The C++ standard recommends (but does not require) that lock-free atomic operations are
+    // also address-free, that is, suitable for communication between processes using shared
+    // memory.
+    LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(buffer_state_) ||
+                                !std::atomic_is_lock_free(fence_state_) ||
+                                !std::atomic_is_lock_free(active_clients_bit_mask_),
+                        "Atomic variables in ashmen are not lock free.");
+}
+
+// Allocates a new BufferNode.
+BufferNode::BufferNode(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
+                       uint64_t usage, size_t user_metadata_size, int id)
+      : mId(id) {
+    uint32_t out_stride = 0;
+    // graphicBufferId is not used in GraphicBufferAllocator::allocate
+    // TODO(b/112338294) After move to the service folder, stop using the
+    // hardcoded service name "bufferhub".
+    int ret = GraphicBufferAllocator::get().allocate(width, height, format, layer_count, usage,
+                                                     const_cast<const native_handle_t**>(
+                                                             &buffer_handle_),
+                                                     &out_stride,
+                                                     /*graphicBufferId=*/0,
+                                                     /*requestor=*/"bufferhub");
+
+    if (ret != OK || buffer_handle_ == nullptr) {
+        ALOGE("%s: Failed to allocate buffer: %s", __FUNCTION__, strerror(-ret));
+        return;
+    }
+
+    buffer_desc_.width = width;
+    buffer_desc_.height = height;
+    buffer_desc_.layers = layer_count;
+    buffer_desc_.format = format;
+    buffer_desc_.usage = usage;
+    buffer_desc_.stride = out_stride;
+
+    metadata_ = BufferHubMetadata::Create(user_metadata_size);
+    if (!metadata_.IsValid()) {
+        ALOGE("%s: Failed to allocate metadata.", __FUNCTION__);
+        return;
+    }
+    InitializeMetadata();
+}
+
+BufferNode::~BufferNode() {
+    // Free the handle
+    if (buffer_handle_ != nullptr) {
+        status_t ret = GraphicBufferAllocator::get().free(buffer_handle_);
+        if (ret != OK) {
+            ALOGE("%s: Failed to free handle; Got error: %d", __FUNCTION__, ret);
+        }
+    }
+
+    // Free the id, if valid
+    if (mId >= 0) {
+        BufferHubIdGenerator::getInstance().freeId(mId);
+    }
+}
+
+uint32_t BufferNode::GetActiveClientsBitMask() const {
+    return active_clients_bit_mask_->load(std::memory_order_acquire);
+}
+
+uint32_t BufferNode::AddNewActiveClientsBitToMask() {
+    uint32_t current_active_clients_bit_mask = GetActiveClientsBitMask();
+    uint32_t client_state_mask = 0U;
+    uint32_t updated_active_clients_bit_mask = 0U;
+    do {
+        client_state_mask =
+                BufferHubDefs::FindNextAvailableClientStateMask(current_active_clients_bit_mask);
+        if (client_state_mask == 0U) {
+            ALOGE("%s: reached the maximum number of channels per buffer node: %d.", __FUNCTION__,
+                  BufferHubDefs::kMaxNumberOfClients);
+            errno = E2BIG;
+            return 0U;
+        }
+        updated_active_clients_bit_mask = current_active_clients_bit_mask | client_state_mask;
+    } while (!(active_clients_bit_mask_->compare_exchange_weak(current_active_clients_bit_mask,
+                                                               updated_active_clients_bit_mask,
+                                                               std::memory_order_acq_rel,
+                                                               std::memory_order_acquire)));
+    return client_state_mask;
+}
+
+void BufferNode::RemoveClientsBitFromMask(const uint32_t& value) {
+    active_clients_bit_mask_->fetch_and(~value);
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
diff --git a/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc b/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc
new file mode 100644
index 0000000..36fbede
--- /dev/null
+++ b/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc
@@ -0,0 +1,6 @@
+service system_bufferhub /system/bin/hw/android.frameworks.bufferhub@1.0-service
+  class hal animation
+  user system
+  group system graphics
+  onrestart restart surfaceflinger
+  writepid /dev/cpuset/system-background/tasks
diff --git a/services/bufferhub/android.frameworks.bufferhub@1.0-service.xml b/services/bufferhub/android.frameworks.bufferhub@1.0-service.xml
new file mode 100644
index 0000000..bd958d3
--- /dev/null
+++ b/services/bufferhub/android.frameworks.bufferhub@1.0-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+    <hal>
+        <name>android.frameworks.bufferhub</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+            <name>IBufferHub</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
new file mode 100644
index 0000000..66ed4bd
--- /dev/null
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_CLIENT_H
+#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_CLIENT_H
+
+#include <mutex>
+
+#include <android/frameworks/bufferhub/1.0/IBufferClient.h>
+#include <bufferhub/BufferNode.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+using hardware::hidl_handle;
+using hardware::Return;
+
+// Forward declaration to avoid circular dependency
+class BufferHubService;
+
+class BufferClient : public IBufferClient {
+public:
+    // Creates a server-side buffer client from an existing BufferNode. Note that
+    // this function takes ownership of the shared_ptr.
+    // Returns a raw pointer to the BufferClient on success, nullptr on failure.
+    static BufferClient* create(BufferHubService* service, const std::shared_ptr<BufferNode>& node);
+
+    // Creates a BufferClient from an existing BufferClient. Will share the same BufferNode.
+    explicit BufferClient(const BufferClient& other)
+          : mService(other.mService), mBufferNode(other.mBufferNode) {}
+    ~BufferClient();
+
+    Return<BufferHubStatus> close() override;
+    Return<void> duplicate(duplicate_cb _hidl_cb) override;
+
+    // Non-binder functions
+    const std::shared_ptr<BufferNode>& getBufferNode() const { return mBufferNode; }
+
+private:
+    BufferClient(wp<BufferHubService> service, const std::shared_ptr<BufferNode>& node)
+          : mService(service), mBufferNode(node) {}
+
+    sp<BufferHubService> getService();
+
+    wp<BufferHubService> mService;
+
+    std::mutex mClosedMutex;
+    bool mClosed GUARDED_BY(mClosedMutex) = false;
+
+    std::shared_ptr<BufferNode> mBufferNode;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
+
+#endif
\ No newline at end of file
diff --git a/services/bufferhub/include/bufferhub/BufferHubIdGenerator.h b/services/bufferhub/include/bufferhub/BufferHubIdGenerator.h
new file mode 100644
index 0000000..ef7c077
--- /dev/null
+++ b/services/bufferhub/include/bufferhub/BufferHubIdGenerator.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_ID_GENERATOR_H
+#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_ID_GENERATOR_H
+
+#include <mutex>
+#include <set>
+
+#include <utils/Mutex.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+// A thread-safe, non-negative, incremental, int id generator.
+class BufferHubIdGenerator {
+public:
+    // Get the singleton instance of this class
+    static BufferHubIdGenerator& getInstance();
+
+    // Gets next available id. If next id is greater than std::numeric_limits<int32_t>::max(), it
+    // will try to get an id start from 0 again.
+    int getId();
+
+    // Free a specific id.
+    void freeId(int id);
+
+private:
+    BufferHubIdGenerator() = default;
+    ~BufferHubIdGenerator() = default;
+
+    // Start from -1 so all valid ids will be >= 0
+    int mLastId = -1;
+
+    std::mutex mIdsInUseMutex;
+    std::set<int> mIdsInUse GUARDED_BY(mIdsInUseMutex);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
+
+#endif // ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_ID_GENERATOR_H
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
new file mode 100644
index 0000000..edad20b
--- /dev/null
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
+#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
+
+#include <map>
+#include <mutex>
+#include <set>
+#include <vector>
+
+#include <android/frameworks/bufferhub/1.0/IBufferHub.h>
+#include <bufferhub/BufferClient.h>
+#include <bufferhub/BufferHubIdGenerator.h>
+#include <utils/Mutex.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+using hardware::hidl_handle;
+using hardware::hidl_string;
+using hardware::hidl_vec;
+using hardware::Return;
+using hardware::graphics::common::V1_2::HardwareBufferDescription;
+
+class BufferHubService : public IBufferHub {
+public:
+    BufferHubService();
+
+    Return<void> allocateBuffer(const HardwareBufferDescription& description,
+                                const uint32_t userMetadataSize,
+                                allocateBuffer_cb _hidl_cb) override;
+    Return<void> importBuffer(const hidl_handle& tokenHandle, importBuffer_cb _hidl_cb) override;
+
+    Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
+
+    // Non-binder functions
+    // Internal help function for IBufferClient::duplicate.
+    hidl_handle registerToken(const wp<BufferClient>& client);
+
+    void onClientClosed(const BufferClient* client);
+
+private:
+    // Helper function to build BufferTraits.bufferInfo handle
+    hidl_handle buildBufferInfo(char* bufferInfoStorage, int bufferId, uint32_t clientBitMask,
+                                uint32_t userMetadataSize, int metadataFd, int eventFd);
+
+    // Helper function to remove all the token belongs to a specific client.
+    void removeTokenByClient(const BufferClient* client);
+
+    // List of active BufferClient for bookkeeping.
+    std::mutex mClientSetMutex;
+    std::set<wp<BufferClient>> mClientSet GUARDED_BY(mClientSetMutex);
+
+    // Token generation related
+    // A random number used as private key for HMAC
+    uint64_t mKey;
+    static constexpr size_t kKeyLen = sizeof(uint64_t);
+
+    std::mutex mTokenMutex;
+    // The first TokenId will be 1. TokenId could be negative.
+    int mLastTokenId GUARDED_BY(mTokenMutex) = 0;
+    static constexpr size_t mTokenIdSize = sizeof(int);
+    // A map from token id to the token-buffer_client pair. Using token id as the key to reduce
+    // looking up time
+    std::map<int, std::pair<std::vector<uint8_t>, const wp<BufferClient>>> mTokenMap
+            GUARDED_BY(mTokenMutex);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
+
+#endif // ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
diff --git a/services/bufferhub/include/bufferhub/BufferNode.h b/services/bufferhub/include/bufferhub/BufferNode.h
new file mode 100644
index 0000000..4729e1c
--- /dev/null
+++ b/services/bufferhub/include/bufferhub/BufferNode.h
@@ -0,0 +1,100 @@
+#ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_NODE_H_
+#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_NODE_H_
+
+#include <android/hardware_buffer.h>
+#include <bufferhub/BufferHubIdGenerator.h>
+#include <cutils/native_handle.h>
+#include <ui/BufferHubEventFd.h>
+#include <ui/BufferHubMetadata.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+class BufferNode {
+public:
+    // Allocates a new BufferNode.
+    BufferNode(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
+               uint64_t usage, size_t user_metadata_size, int id = -1);
+
+    ~BufferNode();
+
+    // Returns whether the object holds a valid metadata.
+    bool IsValid() const { return metadata_.IsValid(); }
+
+    int id() const { return mId; }
+
+    size_t user_metadata_size() const { return metadata_.user_metadata_size(); }
+
+    // Accessors of the buffer description and handle
+    const native_handle_t* buffer_handle() const { return buffer_handle_; }
+    const AHardwareBuffer_Desc& buffer_desc() const { return buffer_desc_; }
+
+    // Accessor of event fd.
+    const BufferHubEventFd& eventFd() const { return mEventFd; }
+
+    // Accessors of metadata.
+    const BufferHubMetadata& metadata() const { return metadata_; }
+
+    // Gets the current value of active_clients_bit_mask in metadata_ with
+    // std::memory_order_acquire, so that all previous releases of
+    // active_clients_bit_mask from all threads will be returned here.
+    uint32_t GetActiveClientsBitMask() const;
+
+    // Find and add a new client_state_mask to active_clients_bit_mask in
+    // metadata_.
+    // Return the new client_state_mask that is added to active_clients_bit_mask.
+    // Return 0U if there are already 16 clients of the buffer.
+    uint32_t AddNewActiveClientsBitToMask();
+
+    // Removes the value from active_clients_bit_mask in metadata_ with
+    // std::memory_order_release, so that the change will be visible to any
+    // acquire of active_clients_bit_mask_ in any threads after the succeed of
+    // this operation.
+    void RemoveClientsBitFromMask(const uint32_t& value);
+
+private:
+    // Helper method for constructors to initialize atomic metadata header
+    // variables in shared memory.
+    void InitializeMetadata();
+
+    // Gralloc buffer handles.
+    native_handle_t* buffer_handle_;
+    AHardwareBuffer_Desc buffer_desc_;
+
+    // Eventfd used for signalling buffer events among the clients of the buffer.
+    BufferHubEventFd mEventFd;
+
+    // Metadata in shared memory.
+    BufferHubMetadata metadata_;
+
+    // A system-unique id generated by bufferhub from 0 to std::numeric_limits<int>::max().
+    // BufferNodes not created by bufferhub will have id < 0, meaning "not specified".
+    // TODO(b/118891412): remove default id = -1 and update comments after pdx is no longer in use
+    const int mId = -1;
+
+    // The following variables are atomic variables in metadata_ that are visible
+    // to Bn object and Bp objects. Please find more info in
+    // BufferHubDefs::MetadataHeader.
+
+    // buffer_state_ tracks the state of the buffer. Buffer can be in one of these
+    // four states: gained, posted, acquired, released.
+    std::atomic<uint32_t>* buffer_state_ = nullptr;
+
+    // TODO(b/112012161): add comments to fence_state_.
+    std::atomic<uint32_t>* fence_state_ = nullptr;
+
+    // active_clients_bit_mask_ tracks all the bp clients of the buffer. It is the
+    // union of all client_state_mask of all bp clients.
+    std::atomic<uint32_t>* active_clients_bit_mask_ = nullptr;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
+
+#endif // ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_NODE_H_
diff --git a/services/bufferhub/main_bufferhub.cpp b/services/bufferhub/main_bufferhub.cpp
new file mode 100644
index 0000000..084460d
--- /dev/null
+++ b/services/bufferhub/main_bufferhub.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <bufferhub/BufferHubService.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
+#include <log/log.h>
+
+using android::sp;
+using android::frameworks::bufferhub::V1_0::IBufferHub;
+using android::frameworks::bufferhub::V1_0::implementation::BufferHubService;
+
+int main(int /*argc*/, char** /*argv*/) {
+    ALOGI("Bootstrap bufferhub HIDL service.");
+
+    android::hardware::configureRpcThreadpool(/*numThreads=*/1, /*willJoin=*/true);
+
+    sp<IBufferHub> service = new BufferHubService();
+    LOG_ALWAYS_FATAL_IF(service->registerAsService() != android::OK, "Failed to register service");
+
+    android::hardware::joinRpcThreadpool();
+
+    return 0;
+}
diff --git a/services/bufferhub/tests/Android.bp b/services/bufferhub/tests/Android.bp
new file mode 100644
index 0000000..8d29923
--- /dev/null
+++ b/services/bufferhub/tests/Android.bp
@@ -0,0 +1,25 @@
+cc_test {
+    name: "BufferHubServer_test",
+    srcs: [
+        "BufferNode_test.cpp",
+        "BufferHubIdGenerator_test.cpp",
+    ],
+    cflags: [
+        "-DLOG_TAG=\"BufferHubServer_test\"",
+        "-DTRACE=0",
+        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+        "-Wall",
+        "-Werror",
+    ],
+    header_libs: [
+        "libdvr_headers",
+        "libnativewindow_headers",
+    ],
+    shared_libs: [
+        "libbufferhubservice",
+        "libui",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+}
diff --git a/services/bufferhub/tests/BufferHubIdGenerator_test.cpp b/services/bufferhub/tests/BufferHubIdGenerator_test.cpp
new file mode 100644
index 0000000..fb6de0d
--- /dev/null
+++ b/services/bufferhub/tests/BufferHubIdGenerator_test.cpp
@@ -0,0 +1,48 @@
+#include <bufferhub/BufferHubIdGenerator.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+namespace {
+
+class BufferHubIdGeneratorTest : public testing::Test {
+protected:
+    BufferHubIdGenerator* mIdGenerator = &BufferHubIdGenerator::getInstance();
+};
+
+TEST_F(BufferHubIdGeneratorTest, TestGenerateAndFreeID) {
+    int id = mIdGenerator->getId();
+    EXPECT_GE(id, 0);
+
+    mIdGenerator->freeId(id);
+}
+
+TEST_F(BufferHubIdGeneratorTest, TestGenerateUniqueIncrementalID) {
+    // 10 IDs should not overflow the UniqueIdGenerator to cause a roll back to start, so the
+    // resulting IDs should still keep incresing.
+    const int kTestSize = 10;
+    int ids[kTestSize];
+    for (int i = 0; i < kTestSize; ++i) {
+        ids[i] = mIdGenerator->getId();
+        EXPECT_GE(ids[i], 0);
+        if (i >= 1) {
+            EXPECT_GT(ids[i], ids[i - 1]);
+        }
+    }
+
+    for (int i = 0; i < kTestSize; ++i) {
+        mIdGenerator->freeId(ids[i]);
+    }
+}
+
+} // namespace
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
\ No newline at end of file
diff --git a/services/bufferhub/tests/BufferNode_test.cpp b/services/bufferhub/tests/BufferNode_test.cpp
new file mode 100644
index 0000000..ccb1197
--- /dev/null
+++ b/services/bufferhub/tests/BufferNode_test.cpp
@@ -0,0 +1,111 @@
+#include <errno.h>
+
+#include <bufferhub/BufferNode.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/BufferHubDefs.h>
+#include <ui/GraphicBufferMapper.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+namespace {
+
+using testing::NotNull;
+
+const uint32_t kWidth = 640;
+const uint32_t kHeight = 480;
+const uint32_t kLayerCount = 1;
+const uint32_t kFormat = 1;
+const uint64_t kUsage = 0;
+const size_t kUserMetadataSize = 0;
+
+class BufferNodeTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        buffer_node =
+                new BufferNode(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
+        ASSERT_TRUE(buffer_node->IsValid());
+    }
+
+    void TearDown() override {
+        if (buffer_node != nullptr) {
+            delete buffer_node;
+        }
+    }
+
+    BufferNode* buffer_node = nullptr;
+};
+
+TEST_F(BufferNodeTest, TestCreateBufferNode) {
+    EXPECT_EQ(buffer_node->user_metadata_size(), kUserMetadataSize);
+    // Test the handle just allocated is good (i.e. able to be imported)
+    GraphicBufferMapper& mapper = GraphicBufferMapper::get();
+    const native_handle_t* outHandle;
+    status_t ret =
+            mapper.importBuffer(buffer_node->buffer_handle(), buffer_node->buffer_desc().width,
+                                buffer_node->buffer_desc().height,
+                                buffer_node->buffer_desc().layers,
+                                buffer_node->buffer_desc().format, buffer_node->buffer_desc().usage,
+                                buffer_node->buffer_desc().stride, &outHandle);
+    EXPECT_EQ(ret, OK);
+    EXPECT_THAT(outHandle, NotNull());
+}
+
+TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_twoNewClients) {
+    uint32_t new_client_state_mask_1 = buffer_node->AddNewActiveClientsBitToMask();
+    EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), new_client_state_mask_1);
+
+    // Request and add a new client_state_mask again.
+    // Active clients bit mask should be the union of the two new
+    // client_state_masks.
+    uint32_t new_client_state_mask_2 = buffer_node->AddNewActiveClientsBitToMask();
+    EXPECT_EQ(buffer_node->GetActiveClientsBitMask(),
+              new_client_state_mask_1 | new_client_state_mask_2);
+}
+
+TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_32NewClients) {
+    uint32_t new_client_state_mask = 0U;
+    uint32_t current_mask = 0U;
+    uint32_t expected_mask = 0U;
+
+    for (int i = 0; i < BufferHubDefs::kMaxNumberOfClients; ++i) {
+        new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask();
+        EXPECT_NE(new_client_state_mask, 0U);
+        EXPECT_FALSE(new_client_state_mask & current_mask);
+        expected_mask = current_mask | new_client_state_mask;
+        current_mask = buffer_node->GetActiveClientsBitMask();
+        EXPECT_EQ(current_mask, expected_mask);
+    }
+
+    // Method should fail upon requesting for more than maximum allowable clients.
+    new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask();
+    EXPECT_EQ(new_client_state_mask, 0U);
+    EXPECT_EQ(errno, E2BIG);
+}
+
+TEST_F(BufferNodeTest, TestRemoveActiveClientsBitFromMask) {
+    buffer_node->AddNewActiveClientsBitToMask();
+    uint32_t current_mask = buffer_node->GetActiveClientsBitMask();
+    uint32_t new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask();
+    EXPECT_NE(buffer_node->GetActiveClientsBitMask(), current_mask);
+
+    buffer_node->RemoveClientsBitFromMask(new_client_state_mask);
+    EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask);
+
+    // Remove the test_mask again to the active client bit mask should not modify
+    // the value of active clients bit mask.
+    buffer_node->RemoveClientsBitFromMask(new_client_state_mask);
+    EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask);
+}
+
+} // namespace
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 8871199..f73d498 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -16,18 +16,65 @@
     name: "libinputflinger",
 
     srcs: [
-        "EventHub.cpp",
-        "InputApplication.cpp",
+        "InputClassifier.cpp",
         "InputDispatcher.cpp",
-        "InputListener.cpp",
         "InputManager.cpp",
-        "InputReader.cpp",
-        "InputWindow.cpp",
     ],
 
     shared_libs: [
+        "android.hardware.input.classifier@1.0",
+        "libinputflinger_base",
+        "libinputreporter",
+        "libinputreader",
         "libbase",
         "libbinder",
+        "libcutils",
+        "libhidlbase",
+        "libinput",
+        "liblog",
+        "libutils",
+        "libui",
+        "server_configurable_flags",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wno-unused-parameter",
+        // TODO(b/123097103): annotate InputDispatcher and uncomment the following line
+        //"-Wthread-safety",
+        // TODO(b/23084678): Move inputflinger to its own process and mark it hidden
+        //-fvisibility=hidden
+    ],
+
+    export_include_dirs: [
+        ".",
+        "include",
+    ],
+
+}
+
+
+cc_library_headers {
+   name: "libinputflinger_headers",
+
+   export_include_dirs: ["include"],
+}
+
+cc_library_shared {
+    name: "libinputreader",
+
+    srcs: [
+        "EventHub.cpp",
+        "InputReader.cpp",
+        "InputReaderFactory.cpp",
+        "TouchVideoDevice.cpp",
+    ],
+
+    shared_libs: [
+        "libinputflinger_base",
+        "libbase",
         "libcrypto",
         "libcutils",
         "libinput",
@@ -35,20 +82,84 @@
         "libutils",
         "libui",
         "libhardware_legacy",
+        "libstatslog",
+        "libutils",
+    ],
+
+    header_libs: [
+        "libinputflinger_headers",
+    ],
+
+    export_header_lib_headers: [
+        "libinputflinger_headers",
     ],
 
     cflags: [
         "-Wall",
         "-Wextra",
         "-Werror",
-        // Allow implicit fallthroughs in InputReader.cpp until they are fixed.
-        "-Wno-error=implicit-fallthrough",
         "-Wno-unused-parameter",
-        // TODO: Move inputflinger to its own process and mark it hidden
-        //-fvisibility=hidden
+    ],
+}
+
+cc_library_shared {
+    name: "libinputflinger_base",
+
+    srcs: [
+        "InputListener.cpp",
+        "InputReaderBase.cpp",
     ],
 
-    export_include_dirs: ["."],
+    shared_libs: [
+        "libbase",
+        "libinput",
+        "liblog",
+        "libutils",
+    ],
+
+    header_libs: [
+        "libinputflinger_headers",
+    ],
+
+    export_header_lib_headers: [
+        "libinputflinger_headers",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+}
+
+cc_library_shared {
+    name: "libinputreporter",
+
+    srcs: [
+        "InputReporter.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libutils",
+    ],
+
+    header_libs: [
+        "libinputflinger_headers",
+    ],
+
+    export_header_lib_headers: [
+        "libinputflinger_headers",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
 }
 
 subdirs = [
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
new file mode 100644
index 0000000..b892120
--- /dev/null
+++ b/services/inputflinger/BlockingQueue.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_BLOCKING_QUEUE_H
+#define _UI_INPUT_BLOCKING_QUEUE_H
+
+#include <condition_variable>
+#include <mutex>
+#include <vector>
+
+namespace android {
+
+/**
+ * A FIFO queue that stores up to <i>capacity</i> objects.
+ * Objects can always be added. Objects are added immediately.
+ * If the queue is full, new objects cannot be added.
+ *
+ * The action of retrieving an object will block until an element is available.
+ */
+template <class T>
+class BlockingQueue {
+public:
+    BlockingQueue(size_t capacity) : mCapacity(capacity) {
+        mQueue.reserve(mCapacity);
+    };
+
+    /**
+     * Retrieve and remove the oldest object.
+     * Blocks execution while queue is empty.
+     */
+    T pop() {
+        std::unique_lock<std::mutex> lock(mLock);
+        mHasElements.wait(lock, [this]{ return !this->mQueue.empty(); });
+        T t = std::move(mQueue.front());
+        mQueue.erase(mQueue.begin());
+        return std::move(t);
+    };
+
+    /**
+     * Add a new object to the queue.
+     * Does not block.
+     * Return true if an element was successfully added.
+     * Return false if the queue is full.
+     */
+    bool push(T&& t) {
+        std::unique_lock<std::mutex> lock(mLock);
+        if (mQueue.size() == mCapacity) {
+            return false;
+        }
+        mQueue.push_back(std::move(t));
+        mHasElements.notify_one();
+        return true;
+    };
+
+    void erase(const std::function<bool(const T&)>& lambda) {
+        std::unique_lock<std::mutex> lock(mLock);
+        mQueue.erase(std::remove_if(mQueue.begin(), mQueue.end(),
+                [&lambda](const T& t) { return lambda(t); }), mQueue.end());
+    }
+
+    /**
+     * Remove all elements.
+     * Does not block.
+     */
+    void clear() {
+        std::scoped_lock lock(mLock);
+        mQueue.clear();
+    };
+
+private:
+    size_t mCapacity;
+    /**
+     * Used to signal that mQueue is non-empty.
+     */
+    std::condition_variable mHasElements;
+    /**
+     * Lock for accessing and waiting on elements.
+     */
+    std::mutex mLock;
+    std::vector<T> mQueue; //GUARDED_BY(mLock)
+};
+
+
+} // namespace android
+#endif
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp
index ccc24b9..cf9d3c7 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/EventHub.cpp
@@ -69,23 +69,27 @@
 
 namespace android {
 
+static constexpr bool DEBUG = false;
+
 static const char *WAKE_LOCK_ID = "KeyEvents";
 static const char *DEVICE_PATH = "/dev/input";
+// v4l2 devices go directly into /dev
+static const char *VIDEO_DEVICE_PATH = "/dev";
 
 static inline const char* toString(bool value) {
     return value ? "true" : "false";
 }
 
-static String8 sha1(const String8& in) {
+static std::string sha1(const std::string& in) {
     SHA_CTX ctx;
     SHA1_Init(&ctx);
-    SHA1_Update(&ctx, reinterpret_cast<const u_char*>(in.string()), in.size());
+    SHA1_Update(&ctx, reinterpret_cast<const u_char*>(in.c_str()), in.size());
     u_char digest[SHA_DIGEST_LENGTH];
     SHA1_Final(digest, &ctx);
 
-    String8 out;
+    std::string out;
     for (size_t i = 0; i < SHA_DIGEST_LENGTH; i++) {
-        out.appendFormat("%02x", digest[i]);
+        out += StringPrintf("%02x", digest[i]);
     }
     return out;
 }
@@ -98,6 +102,44 @@
     }
 }
 
+/**
+ * Return true if name matches "v4l-touch*"
+ */
+static bool isV4lTouchNode(const char* name) {
+    return strstr(name, "v4l-touch") == name;
+}
+
+/**
+ * Returns true if V4L devices should be scanned.
+ *
+ * The system property ro.input.video_enabled can be used to control whether
+ * EventHub scans and opens V4L devices. As V4L does not support multiple
+ * clients, EventHub effectively blocks access to these devices when it opens
+ * them. This property enables other clients to read these devices for testing
+ * and development.
+ */
+static bool isV4lScanningEnabled() {
+  return property_get_bool("ro.input.video_enabled", true /* default_value */);
+}
+
+static nsecs_t processEventTimestamp(const struct input_event& event) {
+    // Use the time specified in the event instead of the current time
+    // so that downstream code can get more accurate estimates of
+    // event dispatch latency from the time the event is enqueued onto
+    // the evdev client buffer.
+    //
+    // The event's timestamp fortuitously uses the same monotonic clock
+    // time base as the rest of Android. The kernel event device driver
+    // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
+    // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
+    // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
+    // system call that also queries ktime_get_ts().
+
+    const nsecs_t inputEventTime = seconds_to_nanoseconds(event.time.tv_sec) +
+            microseconds_to_nanoseconds(event.time.tv_usec);
+    return inputEventTime;
+}
+
 // --- Global Functions ---
 
 uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
@@ -141,14 +183,13 @@
 
 // --- EventHub::Device ---
 
-EventHub::Device::Device(int fd, int32_t id, const String8& path,
+EventHub::Device::Device(int fd, int32_t id, const std::string& path,
         const InputDeviceIdentifier& identifier) :
-        next(NULL),
+        next(nullptr),
         fd(fd), id(id), path(path), identifier(identifier),
-        classes(0), configuration(NULL), virtualKeyMap(NULL),
+        classes(0), configuration(nullptr), virtualKeyMap(nullptr),
         ffEffectPlaying(false), ffEffectId(-1), controllerNumber(0),
-        timestampOverrideSec(0), timestampOverrideUsec(0), enabled(true),
-        isVirtual(fd < 0) {
+        enabled(true), isVirtual(fd < 0) {
     memset(keyBitmask, 0, sizeof(keyBitmask));
     memset(absBitmask, 0, sizeof(absBitmask));
     memset(relBitmask, 0, sizeof(relBitmask));
@@ -172,9 +213,9 @@
 }
 
 status_t EventHub::Device::enable() {
-    fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
+    fd = open(path.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
     if(fd < 0) {
-        ALOGE("could not open %s, %s\n", path.string(), strerror(errno));
+        ALOGE("could not open %s, %s\n", path.c_str(), strerror(errno));
         return -errno;
     }
     enabled = true;
@@ -193,32 +234,37 @@
 
 // --- EventHub ---
 
-const uint32_t EventHub::EPOLL_ID_INOTIFY;
-const uint32_t EventHub::EPOLL_ID_WAKE;
-const int EventHub::EPOLL_SIZE_HINT;
 const int EventHub::EPOLL_MAX_EVENTS;
 
 EventHub::EventHub(void) :
         mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
-        mOpeningDevices(0), mClosingDevices(0),
+        mOpeningDevices(nullptr), mClosingDevices(nullptr),
         mNeedToSendFinishedDeviceScan(false),
         mNeedToReopenDevices(false), mNeedToScanDevices(true),
         mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 
     mEpollFd = epoll_create1(EPOLL_CLOEXEC);
-    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
+    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
     mINotifyFd = inotify_init();
-    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
-    LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s.  errno=%d",
-            DEVICE_PATH, errno);
+    mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
+    LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s",
+            DEVICE_PATH, strerror(errno));
+    if (isV4lScanningEnabled()) {
+        mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
+        LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
+                VIDEO_DEVICE_PATH, strerror(errno));
+    } else {
+        mVideoWd = -1;
+        ALOGI("Video device scanning disabled");
+    }
 
     struct epoll_event eventItem;
     memset(&eventItem, 0, sizeof(eventItem));
     eventItem.events = EPOLLIN;
-    eventItem.data.u32 = EPOLL_ID_INOTIFY;
-    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
+    eventItem.data.fd = mINotifyFd;
+    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
 
     int wakeFds[2];
@@ -236,7 +282,7 @@
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
             errno);
 
-    eventItem.data.u32 = EPOLL_ID_WAKE;
+    eventItem.data.fd = mWakeReadPipeFd;
     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
             errno);
@@ -267,21 +313,21 @@
 InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device == NULL) return InputDeviceIdentifier();
+    if (device == nullptr) return InputDeviceIdentifier();
     return device->identifier;
 }
 
 uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device == NULL) return 0;
+    if (device == nullptr) return 0;
     return device->classes;
 }
 
 int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device == NULL) return 0;
+    if (device == nullptr) return 0;
     return device->controllerNumber;
 }
 
@@ -307,7 +353,7 @@
             struct input_absinfo info;
             if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
                 ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
-                     axis, device->identifier.name.string(), device->fd, errno);
+                     axis, device->identifier.name.c_str(), device->fd, errno);
                 return -errno;
             }
 
@@ -416,7 +462,7 @@
             struct input_absinfo info;
             if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
                 ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
-                     axis, device->identifier.name.string(), device->fd, errno);
+                     axis, device->identifier.name.c_str(), device->fd, errno);
                 return -errno;
             }
 
@@ -465,7 +511,7 @@
     if (device) {
         // Check the key character map first.
         sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
-        if (kcm != NULL) {
+        if (kcm != nullptr) {
             if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
                 *outFlags = 0;
                 status = NO_ERROR;
@@ -474,14 +520,13 @@
 
         // Check the key layout next.
         if (status != NO_ERROR && device->keyMap.haveKeyLayout()) {
-            if (!device->keyMap.keyLayoutMap->mapKey(
-                    scanCode, usageCode, outKeycode, outFlags)) {
+            if (!device->keyMap.keyLayoutMap->mapKey(scanCode, usageCode, outKeycode, outFlags)) {
                 status = NO_ERROR;
             }
         }
 
         if (status == NO_ERROR) {
-            if (kcm != NULL) {
+            if (kcm != nullptr) {
                 kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
             } else {
                 *outMetaState = metaState;
@@ -512,7 +557,7 @@
     return NAME_NOT_FOUND;
 }
 
-void EventHub::setExcludedDevices(const Vector<String8>& devices) {
+void EventHub::setExcludedDevices(const std::vector<std::string>& devices) {
     AutoMutex _l(mLock);
 
     mExcludedDevices = devices;
@@ -581,7 +626,7 @@
     if (device) {
         return device->getKeyCharacterMap();
     }
-    return NULL;
+    return nullptr;
 }
 
 bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
@@ -599,16 +644,16 @@
     return false;
 }
 
-static String8 generateDescriptor(InputDeviceIdentifier& identifier) {
-    String8 rawDescriptor;
-    rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor,
+static std::string generateDescriptor(InputDeviceIdentifier& identifier) {
+    std::string rawDescriptor;
+    rawDescriptor += StringPrintf(":%04x:%04x:", identifier.vendor,
             identifier.product);
     // TODO add handling for USB devices to not uniqueify kbs that show up twice
-    if (!identifier.uniqueId.isEmpty()) {
-        rawDescriptor.append("uniqueId:");
-        rawDescriptor.append(identifier.uniqueId);
+    if (!identifier.uniqueId.empty()) {
+        rawDescriptor += "uniqueId:";
+        rawDescriptor += identifier.uniqueId;
     } else if (identifier.nonce != 0) {
-        rawDescriptor.appendFormat("nonce:%04x", identifier.nonce);
+        rawDescriptor += StringPrintf("nonce:%04x", identifier.nonce);
     }
 
     if (identifier.vendor == 0 && identifier.product == 0) {
@@ -616,12 +661,12 @@
         // built-in so we need to rely on other information to uniquely identify
         // the input device.  Usually we try to avoid relying on the device name or
         // location but for built-in input device, they are unlikely to ever change.
-        if (!identifier.name.isEmpty()) {
-            rawDescriptor.append("name:");
-            rawDescriptor.append(identifier.name);
-        } else if (!identifier.location.isEmpty()) {
-            rawDescriptor.append("location:");
-            rawDescriptor.append(identifier.location);
+        if (!identifier.name.empty()) {
+            rawDescriptor += "name:";
+            rawDescriptor += identifier.name;
+        } else if (!identifier.location.empty()) {
+            rawDescriptor += "location:";
+            rawDescriptor += identifier.location;
         }
     }
     identifier.descriptor = sha1(rawDescriptor);
@@ -637,17 +682,17 @@
     // Ideally, we also want the descriptor to be short and relatively opaque.
 
     identifier.nonce = 0;
-    String8 rawDescriptor = generateDescriptor(identifier);
-    if (identifier.uniqueId.isEmpty()) {
+    std::string rawDescriptor = generateDescriptor(identifier);
+    if (identifier.uniqueId.empty()) {
         // If it didn't have a unique id check for conflicts and enforce
         // uniqueness if necessary.
-        while(getDeviceByDescriptorLocked(identifier.descriptor) != NULL) {
+        while(getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) {
             identifier.nonce++;
             rawDescriptor = generateDescriptor(identifier);
         }
     }
-    ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(),
-            identifier.descriptor.string());
+    ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.c_str(),
+            identifier.descriptor.c_str());
 }
 
 void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
@@ -664,7 +709,7 @@
         effect.replay.delay = 0;
         if (ioctl(device->fd, EVIOCSFF, &effect)) {
             ALOGW("Could not upload force feedback effect to device %s due to error %d.",
-                    device->identifier.name.string(), errno);
+                    device->identifier.name.c_str(), errno);
             return;
         }
         device->ffEffectId = effect.id;
@@ -677,7 +722,7 @@
         ev.value = 1;
         if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
             ALOGW("Could not start force feedback effect on device %s due to error %d.",
-                    device->identifier.name.string(), errno);
+                    device->identifier.name.c_str(), errno);
             return;
         }
         device->ffEffectPlaying = true;
@@ -699,22 +744,22 @@
             ev.value = 0;
             if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
                 ALOGW("Could not stop force feedback effect on device %s due to error %d.",
-                        device->identifier.name.string(), errno);
+                        device->identifier.name.c_str(), errno);
                 return;
             }
         }
     }
 }
 
-EventHub::Device* EventHub::getDeviceByDescriptorLocked(String8& descriptor) const {
+EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const {
     size_t size = mDevices.size();
     for (size_t i = 0; i < size; i++) {
         Device* device = mDevices.valueAt(i);
-        if (descriptor.compare(device->identifier.descriptor) == 0) {
+        if (descriptor == device->identifier.descriptor) {
             return device;
         }
     }
-    return NULL;
+    return nullptr;
 }
 
 EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
@@ -732,7 +777,31 @@
             return device;
         }
     }
-    return NULL;
+    return nullptr;
+}
+
+/**
+ * The file descriptor could be either input device, or a video device (associated with a
+ * specific input device). Check both cases here, and return the device that this event
+ * belongs to. Caller can compare the fd's once more to determine event type.
+ * Looks through all input devices, and only attached video devices. Unattached video
+ * devices are ignored.
+ */
+EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        Device* device = mDevices.valueAt(i);
+        if (device->fd == fd) {
+            // This is an input device event
+            return device;
+        }
+        if (device->videoDevice && device->videoDevice->getFd() == fd) {
+            // This is a video device event
+            return device;
+        }
+    }
+    // We do not check mUnattachedVideoDevices here because they should not participate in epoll,
+    // and therefore should never be looked up by fd.
+    return nullptr;
 }
 
 size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
@@ -763,7 +832,7 @@
         while (mClosingDevices) {
             Device* device = mClosingDevices;
             ALOGV("Reporting device closed: id=%d, name=%s\n",
-                 device->id, device->path.string());
+                 device->id, device->path.c_str());
             mClosingDevices = device->next;
             event->when = now;
             event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
@@ -782,10 +851,10 @@
             mNeedToSendFinishedDeviceScan = true;
         }
 
-        while (mOpeningDevices != NULL) {
+        while (mOpeningDevices != nullptr) {
             Device* device = mOpeningDevices;
             ALOGV("Reporting device opened: id=%d, name=%s\n",
-                 device->id, device->path.string());
+                 device->id, device->path.c_str());
             mOpeningDevices = device->next;
             event->when = now;
             event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
@@ -811,7 +880,7 @@
         bool deviceChanged = false;
         while (mPendingEventIndex < mPendingEventCount) {
             const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
-            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
+            if (eventItem.data.fd == mINotifyFd) {
                 if (eventItem.events & EPOLLIN) {
                     mPendingINotify = true;
                 } else {
@@ -820,7 +889,7 @@
                 continue;
             }
 
-            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
+            if (eventItem.data.fd == mWakeReadPipeFd) {
                 if (eventItem.events & EPOLLIN) {
                     ALOGV("awoken after wake()");
                     awoken = true;
@@ -836,14 +905,34 @@
                 continue;
             }
 
-            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
-            if (deviceIndex < 0) {
-                ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
-                        eventItem.events, eventItem.data.u32);
+            Device* device = getDeviceByFdLocked(eventItem.data.fd);
+            if (!device) {
+                ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.",
+                        eventItem.events, eventItem.data.fd);
+                ALOG_ASSERT(!DEBUG);
                 continue;
             }
-
-            Device* device = mDevices.valueAt(deviceIndex);
+            if (device->videoDevice && eventItem.data.fd == device->videoDevice->getFd()) {
+                if (eventItem.events & EPOLLIN) {
+                    size_t numFrames = device->videoDevice->readAndQueueFrames();
+                    if (numFrames == 0) {
+                        ALOGE("Received epoll event for video device %s, but could not read frame",
+                                device->videoDevice->getName().c_str());
+                    }
+                } else if (eventItem.events & EPOLLHUP) {
+                    // TODO(b/121395353) - consider adding EPOLLRDHUP
+                    ALOGI("Removing video device %s due to epoll hang-up event.",
+                            device->videoDevice->getName().c_str());
+                    unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
+                    device->videoDevice = nullptr;
+                } else {
+                    ALOGW("Received unexpected epoll event 0x%08x for device %s.",
+                            eventItem.events, device->videoDevice->getName().c_str());
+                    ALOG_ASSERT(!DEBUG);
+                }
+                continue;
+            }
+            // This must be an input event
             if (eventItem.events & EPOLLIN) {
                 int32_t readSize = read(device->fd, readBuffer,
                         sizeof(struct input_event) * capacity);
@@ -866,86 +955,7 @@
                     size_t count = size_t(readSize) / sizeof(struct input_event);
                     for (size_t i = 0; i < count; i++) {
                         struct input_event& iev = readBuffer[i];
-                        ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
-                                device->path.string(),
-                                (int) iev.time.tv_sec, (int) iev.time.tv_usec,
-                                iev.type, iev.code, iev.value);
-
-                        // Some input devices may have a better concept of the time
-                        // when an input event was actually generated than the kernel
-                        // which simply timestamps all events on entry to evdev.
-                        // This is a custom Android extension of the input protocol
-                        // mainly intended for use with uinput based device drivers.
-                        if (iev.type == EV_MSC) {
-                            if (iev.code == MSC_ANDROID_TIME_SEC) {
-                                device->timestampOverrideSec = iev.value;
-                                continue;
-                            } else if (iev.code == MSC_ANDROID_TIME_USEC) {
-                                device->timestampOverrideUsec = iev.value;
-                                continue;
-                            }
-                        }
-                        if (device->timestampOverrideSec || device->timestampOverrideUsec) {
-                            iev.time.tv_sec = device->timestampOverrideSec;
-                            iev.time.tv_usec = device->timestampOverrideUsec;
-                            if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
-                                device->timestampOverrideSec = 0;
-                                device->timestampOverrideUsec = 0;
-                            }
-                            ALOGV("applied override time %d.%06d",
-                                    int(iev.time.tv_sec), int(iev.time.tv_usec));
-                        }
-
-                        // Use the time specified in the event instead of the current time
-                        // so that downstream code can get more accurate estimates of
-                        // event dispatch latency from the time the event is enqueued onto
-                        // the evdev client buffer.
-                        //
-                        // The event's timestamp fortuitously uses the same monotonic clock
-                        // time base as the rest of Android.  The kernel event device driver
-                        // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
-                        // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
-                        // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
-                        // system call that also queries ktime_get_ts().
-                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
-                                + nsecs_t(iev.time.tv_usec) * 1000LL;
-                        ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);
-
-                        // Bug 7291243: Add a guard in case the kernel generates timestamps
-                        // that appear to be far into the future because they were generated
-                        // using the wrong clock source.
-                        //
-                        // This can happen because when the input device is initially opened
-                        // it has a default clock source of CLOCK_REALTIME.  Any input events
-                        // enqueued right after the device is opened will have timestamps
-                        // generated using CLOCK_REALTIME.  We later set the clock source
-                        // to CLOCK_MONOTONIC but it is already too late.
-                        //
-                        // Invalid input event timestamps can result in ANRs, crashes and
-                        // and other issues that are hard to track down.  We must not let them
-                        // propagate through the system.
-                        //
-                        // Log a warning so that we notice the problem and recover gracefully.
-                        if (event->when >= now + 10 * 1000000000LL) {
-                            // Double-check.  Time may have moved on.
-                            nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC);
-                            if (event->when > time) {
-                                ALOGW("An input event from %s has a timestamp that appears to "
-                                        "have been generated using the wrong clock source "
-                                        "(expected CLOCK_MONOTONIC): "
-                                        "event time %" PRId64 ", current time %" PRId64
-                                        ", call time %" PRId64 ".  "
-                                        "Using current time instead.",
-                                        device->path.string(), event->when, time, now);
-                                event->when = time;
-                            } else {
-                                ALOGV("Event time is ok but failed the fast path and required "
-                                        "an extra call to systemTime: "
-                                        "event time %" PRId64 ", current time %" PRId64
-                                        ", call time %" PRId64 ".",
-                                        event->when, time, now);
-                            }
-                        }
+                        event->when = processEventTimestamp(iev);
                         event->deviceId = deviceId;
                         event->type = iev.type;
                         event->code = iev.code;
@@ -962,12 +972,12 @@
                 }
             } else if (eventItem.events & EPOLLHUP) {
                 ALOGI("Removing device %s due to epoll hang-up event.",
-                        device->identifier.name.string());
+                        device->identifier.name.c_str());
                 deviceChanged = true;
                 closeDeviceLocked(device);
             } else {
                 ALOGW("Received unexpected epoll event 0x%08x for device %s.",
-                        eventItem.events, device->identifier.name.string());
+                        eventItem.events, device->identifier.name.c_str());
             }
         }
 
@@ -1037,6 +1047,16 @@
     return event - buffer;
 }
 
+std::vector<TouchVideoFrame> EventHub::getVideoFrames(int32_t deviceId) {
+    AutoMutex _l(mLock);
+
+    Device* device = getDeviceLocked(deviceId);
+    if (!device || !device->videoDevice) {
+        return {};
+    }
+    return device->videoDevice->consumeFrames();
+}
+
 void EventHub::wake() {
     ALOGV("wake() called");
 
@@ -1046,14 +1066,20 @@
     } while (nWrite == -1 && errno == EINTR);
 
     if (nWrite != 1 && errno != EAGAIN) {
-        ALOGW("Could not write wake signal, errno=%d", errno);
+        ALOGW("Could not write wake signal: %s", strerror(errno));
     }
 }
 
 void EventHub::scanDevicesLocked() {
-    status_t res = scanDirLocked(DEVICE_PATH);
-    if(res < 0) {
-        ALOGE("scan dir failed for %s\n", DEVICE_PATH);
+    status_t result = scanDirLocked(DEVICE_PATH);
+    if(result < 0) {
+        ALOGE("scan dir failed for %s", DEVICE_PATH);
+    }
+    if (isV4lScanningEnabled()) {
+        result = scanVideoDirLocked(VIDEO_DEVICE_PATH);
+        if (result != OK) {
+            ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH);
+        }
     }
     if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
         createVirtualKeyboardLocked();
@@ -1082,32 +1108,76 @@
         AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
 };
 
-status_t EventHub::registerDeviceForEpollLocked(Device* device) {
-    struct epoll_event eventItem;
-    memset(&eventItem, 0, sizeof(eventItem));
-    eventItem.events = EPOLLIN;
-    if (mUsingEpollWakeup) {
-        eventItem.events |= EPOLLWAKEUP;
-    }
-    eventItem.data.u32 = device->id;
-    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, device->fd, &eventItem)) {
-        ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
+status_t EventHub::registerFdForEpoll(int fd) {
+    // TODO(b/121395353) - consider adding EPOLLRDHUP
+    struct epoll_event eventItem = {};
+    eventItem.events = EPOLLIN | EPOLLWAKEUP;
+    eventItem.data.fd = fd;
+    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
+        ALOGE("Could not add fd to epoll instance: %s", strerror(errno));
         return -errno;
     }
     return OK;
 }
 
-status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) {
-    if (device->hasValidFd()) {
-        if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) {
-            ALOGW("Could not remove device fd from epoll instance.  errno=%d", errno);
-            return -errno;
-        }
+status_t EventHub::unregisterFdFromEpoll(int fd) {
+    if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, nullptr)) {
+        ALOGW("Could not remove fd from epoll instance: %s", strerror(errno));
+        return -errno;
     }
     return OK;
 }
 
-status_t EventHub::openDeviceLocked(const char *devicePath) {
+status_t EventHub::registerDeviceForEpollLocked(Device* device) {
+    if (device == nullptr) {
+        if (DEBUG) {
+            LOG_ALWAYS_FATAL("Cannot call registerDeviceForEpollLocked with null Device");
+        }
+        return BAD_VALUE;
+    }
+    status_t result = registerFdForEpoll(device->fd);
+    if (result != OK) {
+        ALOGE("Could not add input device fd to epoll for device %" PRId32, device->id);
+        return result;
+    }
+    if (device->videoDevice) {
+        registerVideoDeviceForEpollLocked(*device->videoDevice);
+    }
+    return result;
+}
+
+void EventHub::registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice) {
+    status_t result = registerFdForEpoll(videoDevice.getFd());
+    if (result != OK) {
+        ALOGE("Could not add video device %s to epoll", videoDevice.getName().c_str());
+    }
+}
+
+status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) {
+    if (device->hasValidFd()) {
+        status_t result = unregisterFdFromEpoll(device->fd);
+        if (result != OK) {
+            ALOGW("Could not remove input device fd from epoll for device %" PRId32, device->id);
+            return result;
+        }
+    }
+    if (device->videoDevice) {
+        unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
+    }
+    return OK;
+}
+
+void EventHub::unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice) {
+    if (videoDevice.hasValidFd()) {
+        status_t result = unregisterFdFromEpoll(videoDevice.getFd());
+        if (result != OK) {
+            ALOGW("Could not remove video device fd from epoll for device: %s",
+                    videoDevice.getName().c_str());
+        }
+    }
+}
+
+status_t EventHub::openDeviceLocked(const char* devicePath) {
     char buffer[80];
 
     ALOGV("Opening device: %s", devicePath);
@@ -1122,17 +1192,17 @@
 
     // Get device name.
     if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
-        //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
+        ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno));
     } else {
         buffer[sizeof(buffer) - 1] = '\0';
-        identifier.name.setTo(buffer);
+        identifier.name = buffer;
     }
 
     // Check to see if the device is on our excluded list
     for (size_t i = 0; i < mExcludedDevices.size(); i++) {
-        const String8& item = mExcludedDevices.itemAt(i);
+        const std::string& item = mExcludedDevices[i];
         if (identifier.name == item) {
-            ALOGI("ignoring event id %s driver %s\n", devicePath, item.string());
+            ALOGI("ignoring event id %s driver %s\n", devicePath, item.c_str());
             close(fd);
             return -1;
         }
@@ -1163,7 +1233,7 @@
         //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
     } else {
         buffer[sizeof(buffer) - 1] = '\0';
-        identifier.location.setTo(buffer);
+        identifier.location = buffer;
     }
 
     // Get device unique id.
@@ -1171,7 +1241,7 @@
         //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
     } else {
         buffer[sizeof(buffer) - 1] = '\0';
-        identifier.uniqueId.setTo(buffer);
+        identifier.uniqueId = buffer;
     }
 
     // Fill in the descriptor.
@@ -1179,7 +1249,7 @@
 
     // Allocate device.  (The device object takes ownership of the fd at this point.)
     int32_t deviceId = mNextDeviceId++;
-    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
+    Device* device = new Device(fd, deviceId, devicePath, identifier);
 
     ALOGV("add device %d: %s\n", deviceId, devicePath);
     ALOGV("  bus:        %04x\n"
@@ -1187,10 +1257,10 @@
          "  product     %04x\n"
          "  version     %04x\n",
         identifier.bus, identifier.vendor, identifier.product, identifier.version);
-    ALOGV("  name:       \"%s\"\n", identifier.name.string());
-    ALOGV("  location:   \"%s\"\n", identifier.location.string());
-    ALOGV("  unique id:  \"%s\"\n", identifier.uniqueId.string());
-    ALOGV("  descriptor: \"%s\"\n", identifier.descriptor.string());
+    ALOGV("  name:       \"%s\"\n", identifier.name.c_str());
+    ALOGV("  location:   \"%s\"\n", identifier.location.c_str());
+    ALOGV("  unique id:  \"%s\"\n", identifier.uniqueId.c_str());
+    ALOGV("  descriptor: \"%s\"\n", identifier.descriptor.c_str());
     ALOGV("  driver:     v%d.%d.%d\n",
         driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
 
@@ -1343,7 +1413,7 @@
     // If the device isn't recognized as something we handle, don't monitor it.
     if (device->classes == 0) {
         ALOGV("Dropping device: id=%d, path='%s', name='%s'",
-                deviceId, devicePath, device->identifier.name.string());
+                deviceId, devicePath, device->identifier.name.c_str());
         delete device;
         return -1;
     }
@@ -1364,6 +1434,18 @@
         setLedForControllerLocked(device);
     }
 
+    // Find a matching video device by comparing device names
+    // This should be done before registerDeviceForEpollLocked, so that both fds are added to epoll
+    for (std::unique_ptr<TouchVideoDevice>& videoDevice : mUnattachedVideoDevices) {
+        if (device->identifier.name == videoDevice->getName()) {
+            device->videoDevice = std::move(videoDevice);
+            break;
+        }
+    }
+    mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(),
+            mUnattachedVideoDevices.end(),
+            [](const std::unique_ptr<TouchVideoDevice>& videoDevice){
+            return videoDevice == nullptr; }), mUnattachedVideoDevices.end());
 
     if (registerDeviceForEpollLocked(device) != OK) {
         delete device;
@@ -1374,11 +1456,11 @@
 
     ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
             "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
-         deviceId, fd, devicePath, device->identifier.name.string(),
+         deviceId, fd, devicePath, device->identifier.name.c_str(),
          device->classes,
-         device->configurationFile.string(),
-         device->keyMap.keyLayoutFile.string(),
-         device->keyMap.keyCharacterMapFile.string(),
+         device->configurationFile.c_str(),
+         device->keyMap.keyLayoutFile.c_str(),
+         device->keyMap.keyCharacterMapFile.c_str(),
          toString(mBuiltInKeyboardId == deviceId));
 
     addDeviceLocked(device);
@@ -1392,11 +1474,11 @@
         unsigned int repeatRate[] = {0, 0};
         if (ioctl(device->fd, EVIOCSREP, repeatRate)) {
             ALOGW("Unable to disable kernel key repeat for %s: %s",
-                  device->path.string(), strerror(errno));
+                  device->path.c_str(), strerror(errno));
         }
     }
 
-    String8 wakeMechanism("EPOLLWAKEUP");
+    std::string wakeMechanism = "EPOLLWAKEUP";
     if (!mUsingEpollWakeup) {
 #ifndef EVIOCSSUSPENDBLOCK
         // uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels
@@ -1416,14 +1498,39 @@
     // clock.
     int clockId = CLOCK_MONOTONIC;
     bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId);
-    ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.string(),
+    ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.c_str(),
           toString(usingClockIoctl));
 }
 
+void EventHub::openVideoDeviceLocked(const std::string& devicePath) {
+    std::unique_ptr<TouchVideoDevice> videoDevice = TouchVideoDevice::create(devicePath);
+    if (!videoDevice) {
+        ALOGE("Could not create touch video device for %s. Ignoring", devicePath.c_str());
+        return;
+    }
+    // Transfer ownership of this video device to a matching input device
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        Device* device = mDevices.valueAt(i);
+        if (videoDevice->getName() == device->identifier.name) {
+            device->videoDevice = std::move(videoDevice);
+            if (device->enabled) {
+                registerVideoDeviceForEpollLocked(*device->videoDevice);
+            }
+            return;
+        }
+    }
+
+    // Couldn't find a matching input device, so just add it to a temporary holding queue.
+    // A matching input device may appear later.
+    ALOGI("Adding video device %s to list of unattached video devices",
+            videoDevice->getName().c_str());
+    mUnattachedVideoDevices.push_back(std::move(videoDevice));
+}
+
 bool EventHub::isDeviceEnabled(int32_t deviceId) {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device == NULL) {
+    if (device == nullptr) {
         ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
         return false;
     }
@@ -1433,7 +1540,7 @@
 status_t EventHub::enableDevice(int32_t deviceId) {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device == NULL) {
+    if (device == nullptr) {
         ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
         return BAD_VALUE;
     }
@@ -1455,7 +1562,7 @@
 status_t EventHub::disableDevice(int32_t deviceId) {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device == NULL) {
+    if (device == nullptr) {
         ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
         return BAD_VALUE;
     }
@@ -1473,7 +1580,7 @@
     identifier.uniqueId = "<virtual>";
     assignDescriptorLocked(identifier);
 
-    Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, String8("<virtual>"), identifier);
+    Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, "<virtual>", identifier);
     device->classes = INPUT_DEVICE_CLASS_KEYBOARD
             | INPUT_DEVICE_CLASS_ALPHAKEY
             | INPUT_DEVICE_CLASS_DPAD
@@ -1491,26 +1598,26 @@
 void EventHub::loadConfigurationLocked(Device* device) {
     device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
             device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
-    if (device->configurationFile.isEmpty()) {
+    if (device->configurationFile.empty()) {
         ALOGD("No input device configuration file found for device '%s'.",
-                device->identifier.name.string());
+                device->identifier.name.c_str());
     } else {
-        status_t status = PropertyMap::load(device->configurationFile,
+        status_t status = PropertyMap::load(String8(device->configurationFile.c_str()),
                 &device->configuration);
         if (status) {
             ALOGE("Error loading input device configuration file for device '%s'.  "
                     "Using default configuration.",
-                    device->identifier.name.string());
+                    device->identifier.name.c_str());
         }
     }
 }
 
 status_t EventHub::loadVirtualKeyMapLocked(Device* device) {
     // The virtual key map is supplied by the kernel as a system board property file.
-    String8 path;
-    path.append("/sys/board_properties/virtualkeys.");
-    path.append(device->identifier.name);
-    if (access(path.string(), R_OK)) {
+    std::string path;
+    path += "/sys/board_properties/virtualkeys.";
+    path += device->identifier.name;
+    if (access(path.c_str(), R_OK)) {
         return NAME_NOT_FOUND;
     }
     return VirtualKeyMap::load(path, &device->virtualKeyMap);
@@ -1543,7 +1650,7 @@
 int32_t EventHub::getNextControllerNumberLocked(Device* device) {
     if (mControllerNumbers.isFull()) {
         ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s",
-                device->identifier.name.string());
+                device->identifier.name.c_str());
         return 0;
     }
     // Since the controller number 0 is reserved for non-controllers, translate all numbers up by
@@ -1599,34 +1706,60 @@
     return NAME_NOT_FOUND;
 }
 
-status_t EventHub::closeDeviceByPathLocked(const char *devicePath) {
+void EventHub::closeDeviceByPathLocked(const char *devicePath) {
     Device* device = getDeviceByPathLocked(devicePath);
     if (device) {
         closeDeviceLocked(device);
-        return 0;
+        return;
     }
     ALOGV("Remove device: %s not found, device may already have been removed.", devicePath);
-    return -1;
+}
+
+/**
+ * Find the video device by filename, and close it.
+ * The video device is closed by path during an inotify event, where we don't have the
+ * additional context about the video device fd, or the associated input device.
+ */
+void EventHub::closeVideoDeviceByPathLocked(const std::string& devicePath) {
+    // A video device may be owned by an existing input device, or it may be stored in
+    // the mUnattachedVideoDevices queue. Check both locations.
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        Device* device = mDevices.valueAt(i);
+        if (device->videoDevice && device->videoDevice->getPath() == devicePath) {
+            unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
+            device->videoDevice = nullptr;
+            return;
+        }
+    }
+    mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(),
+            mUnattachedVideoDevices.end(), [&devicePath](
+            const std::unique_ptr<TouchVideoDevice>& videoDevice) {
+            return videoDevice->getPath() == devicePath; }), mUnattachedVideoDevices.end());
 }
 
 void EventHub::closeAllDevicesLocked() {
+    mUnattachedVideoDevices.clear();
     while (mDevices.size() > 0) {
         closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1));
     }
 }
 
 void EventHub::closeDeviceLocked(Device* device) {
-    ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n",
-         device->path.string(), device->identifier.name.string(), device->id,
+    ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x",
+         device->path.c_str(), device->identifier.name.c_str(), device->id,
          device->fd, device->classes);
 
     if (device->id == mBuiltInKeyboardId) {
         ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
-                device->path.string(), mBuiltInKeyboardId);
+                device->path.c_str(), mBuiltInKeyboardId);
         mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
     }
 
     unregisterDeviceFromEpollLocked(device);
+    if (device->videoDevice) {
+        // This must be done after the video device is removed from epoll
+        mUnattachedVideoDevices.push_back(std::move(device->videoDevice));
+    }
 
     releaseControllerNumberLocked(device);
 
@@ -1634,9 +1767,9 @@
     device->close();
 
     // Unlink for opening devices list if it is present.
-    Device* pred = NULL;
+    Device* pred = nullptr;
     bool found = false;
-    for (Device* entry = mOpeningDevices; entry != NULL; ) {
+    for (Device* entry = mOpeningDevices; entry != nullptr; ) {
         if (entry == device) {
             found = true;
             break;
@@ -1648,7 +1781,7 @@
         // Unlink the device from the opening devices list then delete it.
         // We don't need to tell the client that the device was closed because
         // it does not even know it was opened in the first place.
-        ALOGI("Device %s was immediately closed after opening.", device->path.string());
+        ALOGI("Device %s was immediately closed after opening.", device->path.c_str());
         if (pred) {
             pred->next = device->next;
         } else {
@@ -1665,8 +1798,6 @@
 
 status_t EventHub::readNotifyLocked() {
     int res;
-    char devname[PATH_MAX];
-    char *filename;
     char event_buf[512];
     int event_size;
     int event_pos = 0;
@@ -1680,22 +1811,32 @@
         ALOGW("could not get event, %s\n", strerror(errno));
         return -1;
     }
-    //printf("got %d bytes of event information\n", res);
-
-    strcpy(devname, DEVICE_PATH);
-    filename = devname + strlen(devname);
-    *filename++ = '/';
 
     while(res >= (int)sizeof(*event)) {
         event = (struct inotify_event *)(event_buf + event_pos);
-        //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
         if(event->len) {
-            strcpy(filename, event->name);
-            if(event->mask & IN_CREATE) {
-                openDeviceLocked(devname);
-            } else {
-                ALOGI("Removing device '%s' due to inotify event\n", devname);
-                closeDeviceByPathLocked(devname);
+            if (event->wd == mInputWd) {
+                std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
+                if(event->mask & IN_CREATE) {
+                    openDeviceLocked(filename.c_str());
+                } else {
+                    ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
+                    closeDeviceByPathLocked(filename.c_str());
+                }
+            }
+            else if (event->wd == mVideoWd) {
+                if (isV4lTouchNode(event->name)) {
+                    std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name);
+                    if (event->mask & IN_CREATE) {
+                        openVideoDeviceLocked(filename);
+                    } else {
+                        ALOGI("Removing video device '%s' due to inotify event", filename.c_str());
+                        closeVideoDeviceByPathLocked(filename);
+                    }
+                }
+            }
+            else {
+                LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd);
             }
         }
         event_size = sizeof(*event) + event->len;
@@ -1712,7 +1853,7 @@
     DIR *dir;
     struct dirent *de;
     dir = opendir(dirname);
-    if(dir == NULL)
+    if(dir == nullptr)
         return -1;
     strcpy(devname, dirname);
     filename = devname + strlen(devname);
@@ -1729,6 +1870,30 @@
     return 0;
 }
 
+/**
+ * Look for all dirname/v4l-touch* devices, and open them.
+ */
+status_t EventHub::scanVideoDirLocked(const std::string& dirname)
+{
+    DIR* dir;
+    struct dirent* de;
+    dir = opendir(dirname.c_str());
+    if(!dir) {
+        ALOGE("Could not open video directory %s", dirname.c_str());
+        return BAD_VALUE;
+    }
+
+    while((de = readdir(dir))) {
+        const char* name = de->d_name;
+        if (isV4lTouchNode(name)) {
+            ALOGI("Found touch video device %s", name);
+            openVideoDeviceLocked(dirname + "/" + name);
+        }
+    }
+    closedir(dir);
+    return OK;
+}
+
 void EventHub::requestReopenDevices() {
     ALOGV("requestReopenDevices() called");
 
@@ -1750,30 +1915,44 @@
             const Device* device = mDevices.valueAt(i);
             if (mBuiltInKeyboardId == device->id) {
                 dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n",
-                        device->id, device->identifier.name.string());
+                        device->id, device->identifier.name.c_str());
             } else {
                 dump += StringPrintf(INDENT2 "%d: %s\n", device->id,
-                        device->identifier.name.string());
+                        device->identifier.name.c_str());
             }
             dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes);
-            dump += StringPrintf(INDENT3 "Path: %s\n", device->path.string());
+            dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str());
             dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(device->enabled));
-            dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.string());
-            dump += StringPrintf(INDENT3 "Location: %s\n", device->identifier.location.string());
+            dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.c_str());
+            dump += StringPrintf(INDENT3 "Location: %s\n", device->identifier.location.c_str());
             dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber);
-            dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string());
+            dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str());
             dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
                     "product=0x%04x, version=0x%04x\n",
                     device->identifier.bus, device->identifier.vendor,
                     device->identifier.product, device->identifier.version);
             dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n",
-                    device->keyMap.keyLayoutFile.string());
+                    device->keyMap.keyLayoutFile.c_str());
             dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n",
-                    device->keyMap.keyCharacterMapFile.string());
+                    device->keyMap.keyCharacterMapFile.c_str());
             dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n",
-                    device->configurationFile.string());
+                    device->configurationFile.c_str());
             dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n",
-                    toString(device->overlayKeyMap != NULL));
+                    toString(device->overlayKeyMap != nullptr));
+            dump += INDENT3 "VideoDevice: ";
+            if (device->videoDevice) {
+                dump += device->videoDevice->dump() + "\n";
+            } else {
+                dump += "<none>\n";
+            }
+        }
+
+        dump += INDENT "Unattached video devices:\n";
+        for (const std::unique_ptr<TouchVideoDevice>& videoDevice : mUnattachedVideoDevices) {
+            dump += INDENT2 + videoDevice->dump() + "\n";
+        }
+        if (mUnattachedVideoDevices.empty()) {
+            dump += INDENT2 "<none>\n";
         }
     } // release lock
 }
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
new file mode 100644
index 0000000..11427c3
--- /dev/null
+++ b/services/inputflinger/InputClassifier.cpp
@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2019 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 "InputClassifier"
+
+#include "InputClassifier.h"
+
+#include <algorithm>
+#include <cmath>
+#include <inttypes.h>
+#include <log/log.h>
+#if defined(__linux__)
+    #include <pthread.h>
+#endif
+#include <server_configurable_flags/get_flags.h>
+
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+
+using android::hardware::hidl_bitfield;
+using android::hardware::hidl_vec;
+using namespace android::hardware::input;
+
+namespace android {
+
+static constexpr bool DEBUG = false;
+
+// Category (=namespace) name for the input settings that are applied at boot time
+static const char* INPUT_NATIVE_BOOT = "input_native_boot";
+// Feature flag name for the deep press feature
+static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
+
+//Max number of elements to store in mEvents.
+static constexpr size_t MAX_EVENTS = 5;
+
+template<class K, class V>
+static V getValueForKey(const std::unordered_map<K, V>& map, K key, V defaultValue) {
+    auto it = map.find(key);
+    if (it == map.end()) {
+        return defaultValue;
+    }
+    return it->second;
+}
+
+static common::V1_0::Source getSource(uint32_t source) {
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_UNKNOWN) ==
+            common::V1_0::Source::UNKNOWN, "SOURCE_UNKNOWN mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_KEYBOARD) ==
+            common::V1_0::Source::KEYBOARD, "SOURCE_KEYBOARD mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_DPAD) ==
+            common::V1_0::Source::DPAD, "SOURCE_DPAD mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_GAMEPAD) ==
+            common::V1_0::Source::GAMEPAD, "SOURCE_GAMEPAD mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCHSCREEN) ==
+            common::V1_0::Source::TOUCHSCREEN, "SOURCE_TOUCHSCREEN mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_MOUSE) ==
+            common::V1_0::Source::MOUSE, "SOURCE_MOUSE mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_STYLUS) ==
+            common::V1_0::Source::STYLUS, "SOURCE_STYLUS mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_BLUETOOTH_STYLUS) ==
+            common::V1_0::Source::BLUETOOTH_STYLUS, "SOURCE_BLUETOOTH_STYLUS mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TRACKBALL) ==
+            common::V1_0::Source::TRACKBALL, "SOURCE_TRACKBALL mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_MOUSE_RELATIVE) ==
+            common::V1_0::Source::MOUSE_RELATIVE, "SOURCE_MOUSE_RELATIVE mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCHPAD) ==
+            common::V1_0::Source::TOUCHPAD, "SOURCE_TOUCHPAD mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCH_NAVIGATION) ==
+            common::V1_0::Source::TOUCH_NAVIGATION, "SOURCE_TOUCH_NAVIGATION mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_JOYSTICK) ==
+            common::V1_0::Source::JOYSTICK, "SOURCE_JOYSTICK mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_ROTARY_ENCODER) ==
+            common::V1_0::Source::ROTARY_ENCODER, "SOURCE_ROTARY_ENCODER mismatch");
+    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_ANY) ==
+            common::V1_0::Source::ANY, "SOURCE_ANY mismatch");
+    return static_cast<common::V1_0::Source>(source);
+}
+
+static common::V1_0::Action getAction(int32_t actionMasked) {
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_DOWN) ==
+            common::V1_0::Action::DOWN, "ACTION_DOWN mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_UP) ==
+            common::V1_0::Action::UP, "ACTION_UP mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_MOVE) ==
+            common::V1_0::Action::MOVE, "ACTION_MOVE mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_CANCEL) ==
+            common::V1_0::Action::CANCEL, "ACTION_CANCEL mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_OUTSIDE) ==
+            common::V1_0::Action::OUTSIDE, "ACTION_OUTSIDE mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_POINTER_DOWN) ==
+            common::V1_0::Action::POINTER_DOWN, "ACTION_POINTER_DOWN mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_POINTER_UP) ==
+            common::V1_0::Action::POINTER_UP, "ACTION_POINTER_UP mismatch");
+    static_assert(static_cast<common::V1_0::Action>( AMOTION_EVENT_ACTION_HOVER_MOVE) ==
+            common::V1_0::Action::HOVER_MOVE, "ACTION_HOVER_MOVE mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_SCROLL) ==
+            common::V1_0::Action::SCROLL, "ACTION_SCROLL mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_HOVER_ENTER) ==
+            common::V1_0::Action::HOVER_ENTER, "ACTION_HOVER_ENTER mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_HOVER_EXIT) ==
+            common::V1_0::Action::HOVER_EXIT, "ACTION_HOVER_EXIT mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_BUTTON_PRESS) ==
+            common::V1_0::Action::BUTTON_PRESS, "ACTION_BUTTON_PRESS mismatch");
+    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_BUTTON_RELEASE) ==
+            common::V1_0::Action::BUTTON_RELEASE, "ACTION_BUTTON_RELEASE mismatch");
+    return static_cast<common::V1_0::Action>(actionMasked);
+}
+
+static common::V1_0::Button getActionButton(int32_t actionButton) {
+    static_assert(static_cast<common::V1_0::Button>(0) ==
+            common::V1_0::Button::NONE, "BUTTON_NONE mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_PRIMARY) ==
+            common::V1_0::Button::PRIMARY, "BUTTON_PRIMARY mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_SECONDARY) ==
+            common::V1_0::Button::SECONDARY, "BUTTON_SECONDARY mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_TERTIARY) ==
+            common::V1_0::Button::TERTIARY, "BUTTON_TERTIARY mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_BACK) ==
+            common::V1_0::Button::BACK, "BUTTON_BACK mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_FORWARD) ==
+            common::V1_0::Button::FORWARD, "BUTTON_FORWARD mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) ==
+            common::V1_0::Button::STYLUS_PRIMARY, "BUTTON_STYLUS_PRIMARY mismatch");
+    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY) ==
+            common::V1_0::Button::STYLUS_SECONDARY, "BUTTON_STYLUS_SECONDARY mismatch");
+    return static_cast<common::V1_0::Button>(actionButton);
+}
+
+static hidl_bitfield<common::V1_0::Flag> getFlags(int32_t flags) {
+    static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED) ==
+            common::V1_0::Flag::WINDOW_IS_OBSCURED);
+    static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE) ==
+            common::V1_0::Flag::IS_GENERATED_GESTURE);
+    static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_TAINTED) ==
+            common::V1_0::Flag::TAINTED);
+    return static_cast<hidl_bitfield<common::V1_0::Flag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::PolicyFlag> getPolicyFlags(int32_t flags) {
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_WAKE) ==
+            common::V1_0::PolicyFlag::WAKE);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_VIRTUAL) ==
+            common::V1_0::PolicyFlag::VIRTUAL);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_FUNCTION) ==
+            common::V1_0::PolicyFlag::FUNCTION);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_GESTURE) ==
+            common::V1_0::PolicyFlag::GESTURE);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_INJECTED) ==
+            common::V1_0::PolicyFlag::INJECTED);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_TRUSTED) ==
+            common::V1_0::PolicyFlag::TRUSTED);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_FILTERED) ==
+            common::V1_0::PolicyFlag::FILTERED);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_DISABLE_KEY_REPEAT) ==
+            common::V1_0::PolicyFlag::DISABLE_KEY_REPEAT);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_INTERACTIVE) ==
+            common::V1_0::PolicyFlag::INTERACTIVE);
+    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_PASS_TO_USER) ==
+            common::V1_0::PolicyFlag::PASS_TO_USER);
+    return static_cast<hidl_bitfield<common::V1_0::PolicyFlag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::EdgeFlag> getEdgeFlags(int32_t flags) {
+    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_NONE) ==
+            common::V1_0::EdgeFlag::NONE);
+    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_TOP) ==
+            common::V1_0::EdgeFlag::TOP);
+    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_BOTTOM) ==
+            common::V1_0::EdgeFlag::BOTTOM);
+    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_LEFT) ==
+            common::V1_0::EdgeFlag::LEFT);
+    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_RIGHT) ==
+            common::V1_0::EdgeFlag::RIGHT);
+    return static_cast<hidl_bitfield<common::V1_0::EdgeFlag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::Meta> getMetastate(int32_t state) {
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_NONE) ==
+            common::V1_0::Meta::NONE);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_ON) ==
+            common::V1_0::Meta::ALT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_LEFT_ON) ==
+            common::V1_0::Meta::ALT_LEFT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_RIGHT_ON) ==
+            common::V1_0::Meta::ALT_RIGHT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_ON) ==
+            common::V1_0::Meta::SHIFT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_LEFT_ON) ==
+            common::V1_0::Meta::SHIFT_LEFT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_RIGHT_ON) ==
+            common::V1_0::Meta::SHIFT_RIGHT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_SYM_ON) ==
+            common::V1_0::Meta::SYM_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_FUNCTION_ON) ==
+            common::V1_0::Meta::FUNCTION_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_ON) ==
+            common::V1_0::Meta::CTRL_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_LEFT_ON) ==
+            common::V1_0::Meta::CTRL_LEFT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_RIGHT_ON) ==
+            common::V1_0::Meta::CTRL_RIGHT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_META_ON) ==
+            common::V1_0::Meta::META_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_META_LEFT_ON) ==
+            common::V1_0::Meta::META_LEFT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_META_RIGHT_ON) ==
+            common::V1_0::Meta::META_RIGHT_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_CAPS_LOCK_ON) ==
+            common::V1_0::Meta::CAPS_LOCK_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_NUM_LOCK_ON) ==
+            common::V1_0::Meta::NUM_LOCK_ON);
+    static_assert(static_cast<common::V1_0::Meta>(AMETA_SCROLL_LOCK_ON) ==
+            common::V1_0::Meta::SCROLL_LOCK_ON);
+    return static_cast<hidl_bitfield<common::V1_0::Meta>>(state);
+}
+
+static hidl_bitfield<common::V1_0::Button> getButtonState(int32_t buttonState) {
+    // No need for static_assert here.
+    // The button values have already been asserted in getActionButton(..) above
+    return static_cast<hidl_bitfield<common::V1_0::Button>>(buttonState);
+}
+
+static common::V1_0::ToolType getToolType(int32_t toolType) {
+    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_UNKNOWN) ==
+            common::V1_0::ToolType::UNKNOWN);
+    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_FINGER) ==
+            common::V1_0::ToolType::FINGER);
+    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_STYLUS) ==
+            common::V1_0::ToolType::STYLUS);
+    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_MOUSE) ==
+            common::V1_0::ToolType::MOUSE);
+    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_ERASER) ==
+            common::V1_0::ToolType::ERASER);
+    return static_cast<common::V1_0::ToolType>(toolType);
+}
+
+static common::V1_0::Axis getAxis(uint64_t axis) {
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_X) ==
+            common::V1_0::Axis::X);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_Y) ==
+            common::V1_0::Axis::Y);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_PRESSURE) ==
+            common::V1_0::Axis::PRESSURE);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_SIZE) ==
+            common::V1_0::Axis::SIZE);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOUCH_MAJOR) ==
+            common::V1_0::Axis::TOUCH_MAJOR);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOUCH_MINOR) ==
+            common::V1_0::Axis::TOUCH_MINOR);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOOL_MAJOR) ==
+            common::V1_0::Axis::TOOL_MAJOR);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOOL_MINOR) ==
+            common::V1_0::Axis::TOOL_MINOR);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_ORIENTATION) ==
+            common::V1_0::Axis::ORIENTATION);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_VSCROLL) ==
+            common::V1_0::Axis::VSCROLL);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HSCROLL) ==
+            common::V1_0::Axis::HSCROLL);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_Z) ==
+            common::V1_0::Axis::Z);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RX) ==
+            common::V1_0::Axis::RX);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RY) ==
+            common::V1_0::Axis::RY);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RZ) ==
+            common::V1_0::Axis::RZ);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HAT_X) ==
+            common::V1_0::Axis::HAT_X);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HAT_Y) ==
+            common::V1_0::Axis::HAT_Y);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_LTRIGGER) ==
+            common::V1_0::Axis::LTRIGGER);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RTRIGGER) ==
+            common::V1_0::Axis::RTRIGGER);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_THROTTLE) ==
+            common::V1_0::Axis::THROTTLE);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RUDDER) ==
+            common::V1_0::Axis::RUDDER);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_WHEEL) ==
+            common::V1_0::Axis::WHEEL);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GAS) ==
+            common::V1_0::Axis::GAS);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_BRAKE) ==
+            common::V1_0::Axis::BRAKE);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_DISTANCE) ==
+            common::V1_0::Axis::DISTANCE);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TILT) ==
+            common::V1_0::Axis::TILT);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_SCROLL) ==
+            common::V1_0::Axis::SCROLL);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RELATIVE_X) ==
+            common::V1_0::Axis::RELATIVE_X);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RELATIVE_Y) ==
+            common::V1_0::Axis::RELATIVE_Y);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_1) ==
+            common::V1_0::Axis::GENERIC_1);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_2) ==
+            common::V1_0::Axis::GENERIC_2);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_3) ==
+            common::V1_0::Axis::GENERIC_3);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_4) ==
+            common::V1_0::Axis::GENERIC_4);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_5) ==
+            common::V1_0::Axis::GENERIC_5);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_6) ==
+            common::V1_0::Axis::GENERIC_6);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_7) ==
+            common::V1_0::Axis::GENERIC_7);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_8) ==
+            common::V1_0::Axis::GENERIC_8);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_9) ==
+            common::V1_0::Axis::GENERIC_9);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_10) ==
+            common::V1_0::Axis::GENERIC_10);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_11) ==
+            common::V1_0::Axis::GENERIC_11);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_12) ==
+            common::V1_0::Axis::GENERIC_12);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_13) ==
+            common::V1_0::Axis::GENERIC_13);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) ==
+            common::V1_0::Axis::GENERIC_14);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) ==
+            common::V1_0::Axis::GENERIC_15);
+    static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) ==
+            common::V1_0::Axis::GENERIC_16);
+    return static_cast<common::V1_0::Axis>(axis);
+}
+
+static common::V1_0::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) {
+    common::V1_0::VideoFrame out;
+    out.width = frame.getWidth();
+    out.height = frame.getHeight();
+    out.data = frame.getData();
+    struct timeval timestamp = frame.getTimestamp();
+    out.timestamp = seconds_to_nanoseconds(timestamp.tv_sec) +
+             microseconds_to_nanoseconds(timestamp.tv_usec);
+    return out;
+}
+
+static std::vector<common::V1_0::VideoFrame> convertVideoFrames(
+        const std::vector<TouchVideoFrame>& frames) {
+    std::vector<common::V1_0::VideoFrame> out;
+    for (const TouchVideoFrame& frame : frames) {
+        out.push_back(getHalVideoFrame(frame));
+    }
+    return out;
+}
+
+static uint8_t getActionIndex(int32_t action) {
+    return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
+            AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+}
+
+static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args,
+        std::vector<common::V1_0::PointerProperties>* outPointerProperties,
+        std::vector<common::V1_0::PointerCoords>* outPointerCoords) {
+    outPointerProperties->reserve(args.pointerCount);
+    outPointerCoords->reserve(args.pointerCount);
+    for (size_t i = 0; i < args.pointerCount; i++) {
+        common::V1_0::PointerProperties properties;
+        properties.id = args.pointerProperties[i].id;
+        properties.toolType = getToolType(args.pointerProperties[i].toolType);
+        outPointerProperties->push_back(properties);
+
+        common::V1_0::PointerCoords coords;
+        BitSet64 bits (args.pointerCoords[i].bits);
+        std::vector<float> values;
+        size_t index = 0;
+        while (!bits.isEmpty()) {
+            uint32_t axis = bits.clearFirstMarkedBit();
+            coords.bits |= 1 << static_cast<uint64_t>(getAxis(axis));
+            float value = args.pointerCoords[i].values[index++];
+            values.push_back(value);
+        }
+        coords.values = values;
+        outPointerCoords->push_back(coords);
+    }
+}
+
+static common::V1_0::MotionEvent getMotionEvent(const NotifyMotionArgs& args) {
+    common::V1_0::MotionEvent event;
+    event.deviceId = args.deviceId;
+    event.source = getSource(args.source);
+    event.displayId = args.displayId;
+    event.downTime = args.downTime;
+    event.eventTime = args.eventTime;
+    event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK);
+    event.actionIndex = getActionIndex(args.action);
+    event.actionButton = getActionButton(args.actionButton);
+    event.flags = getFlags(args.flags);
+    event.policyFlags = getPolicyFlags(args.policyFlags);
+    event.edgeFlags = getEdgeFlags(args.edgeFlags);
+    event.metaState = getMetastate(args.metaState);
+    event.buttonState = getButtonState(args.buttonState);
+    event.xPrecision = args.xPrecision;
+    event.yPrecision = args.yPrecision;
+
+    std::vector<common::V1_0::PointerProperties> pointerProperties;
+    std::vector<common::V1_0::PointerCoords> pointerCoords;
+    getHidlPropertiesAndCoords(args, /*out*/&pointerProperties, /*out*/&pointerCoords);
+    event.pointerProperties = pointerProperties;
+    event.pointerCoords = pointerCoords;
+
+    event.deviceTimestamp = args.deviceTimestamp;
+    event.frames = convertVideoFrames(args.videoFrames);
+
+    return event;
+}
+
+static MotionClassification getMotionClassification(common::V1_0::Classification classification) {
+    static_assert(MotionClassification::NONE ==
+            static_cast<MotionClassification>(common::V1_0::Classification::NONE));
+    static_assert(MotionClassification::AMBIGUOUS_GESTURE ==
+            static_cast<MotionClassification>(common::V1_0::Classification::AMBIGUOUS_GESTURE));
+    static_assert(MotionClassification::DEEP_PRESS ==
+            static_cast<MotionClassification>(common::V1_0::Classification::DEEP_PRESS));
+    return static_cast<MotionClassification>(classification);
+}
+
+static bool isTouchEvent(const NotifyMotionArgs& args) {
+    return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
+}
+
+// Check if the "deep touch" feature is on.
+static bool deepPressEnabled() {
+    std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
+            INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
+    std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
+    if (flag_value == "1" || flag_value == "true") {
+        ALOGI("Deep press feature enabled.");
+        return true;
+    }
+    ALOGI("Deep press feature is not enabled.");
+    return false;
+}
+
+
+// --- ClassifierEvent ---
+
+ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
+        type(ClassifierEventType::MOTION), args(std::move(args)) { };
+ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
+        type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
+ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
+        type(type), args(std::move(args)) { };
+
+ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
+        type(other.type), args(std::move(other.args)) { };
+
+ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
+    type = other.type;
+    args = std::move(other.args);
+    return *this;
+}
+
+ClassifierEvent ClassifierEvent::createHalResetEvent() {
+    return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
+}
+
+ClassifierEvent ClassifierEvent::createExitEvent() {
+    return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
+}
+
+std::optional<int32_t> ClassifierEvent::getDeviceId() const {
+    switch (type) {
+        case ClassifierEventType::MOTION: {
+            NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
+            return motionArgs->deviceId;
+        }
+        case ClassifierEventType::DEVICE_RESET: {
+            NotifyDeviceResetArgs* deviceResetArgs =
+                    static_cast<NotifyDeviceResetArgs*>(args.get());
+            return deviceResetArgs->deviceId;
+        }
+        case ClassifierEventType::HAL_RESET: {
+            return std::nullopt;
+        }
+        case ClassifierEventType::EXIT: {
+            return std::nullopt;
+        }
+    }
+}
+
+// --- MotionClassifier ---
+
+MotionClassifier::MotionClassifier(
+        sp<android::hardware::input::classifier::V1_0::IInputClassifier> service) :
+        mEvents(MAX_EVENTS), mService(service) {
+    mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
+#if defined(__linux__)
+    // Set the thread name for debugging
+    pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
+#endif
+}
+
+MotionClassifier::~MotionClassifier() {
+    requestExit();
+    mHalThread.join();
+}
+
+void MotionClassifier::ensureHalThread(const char* function) {
+    if (DEBUG) {
+        if (std::this_thread::get_id() != mHalThread.get_id()) {
+            ALOGE("Function %s should only be called from InputClassifier thread", function);
+        }
+    }
+}
+
+/**
+ * Obtain the classification from the HAL for a given MotionEvent.
+ * Should only be called from the InputClassifier thread (mHalThread).
+ * Should not be called from the thread that notifyMotion runs on.
+ *
+ * There is no way to provide a timeout for a HAL call. So if the HAL takes too long
+ * to return a classification, this would directly impact the touch latency.
+ * To remove any possibility of negatively affecting the touch latency, the HAL
+ * is called from a dedicated thread.
+ */
+void MotionClassifier::callInputClassifierHal() {
+    ensureHalThread(__func__);
+    while (true) {
+        ClassifierEvent event = mEvents.pop();
+        switch (event.type) {
+            case ClassifierEventType::MOTION: {
+                NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
+                common::V1_0::MotionEvent motionEvent = getMotionEvent(*motionArgs);
+                common::V1_0::Classification halClassification = mService->classify(motionEvent);
+                updateClassification(motionArgs->deviceId, motionArgs->eventTime,
+                        getMotionClassification(halClassification));
+                break;
+            }
+            case ClassifierEventType::DEVICE_RESET: {
+                const int32_t deviceId = *(event.getDeviceId());
+                mService->resetDevice(deviceId);
+                setClassification(deviceId, MotionClassification::NONE);
+                break;
+            }
+            case ClassifierEventType::HAL_RESET: {
+                mService->reset();
+                clearClassifications();
+                break;
+            }
+            case ClassifierEventType::EXIT: {
+                clearClassifications();
+                return;
+            }
+        }
+    }
+}
+
+void MotionClassifier::requestExit() {
+    reset();
+    mEvents.push(ClassifierEvent::createExitEvent());
+}
+
+void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime,
+        MotionClassification classification) {
+    std::scoped_lock lock(mLock);
+    const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
+    if (eventTime < lastDownTime) {
+        // HAL just finished processing an event that belonged to an earlier gesture,
+        // but new gesture is already in progress. Drop this classification.
+        ALOGW("Received late classification. Late by at least %" PRId64 " ms.",
+                nanoseconds_to_milliseconds(lastDownTime - eventTime));
+        return;
+    }
+    mClassifications[deviceId] = classification;
+}
+
+void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
+    std::scoped_lock lock(mLock);
+    mClassifications[deviceId] = classification;
+}
+
+void MotionClassifier::clearClassifications() {
+    std::scoped_lock lock(mLock);
+    mClassifications.clear();
+}
+
+MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
+    std::scoped_lock lock(mLock);
+    return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
+}
+
+void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) {
+    std::scoped_lock lock(mLock);
+    mLastDownTimes[deviceId] = downTime;
+    mClassifications[deviceId] = MotionClassification::NONE;
+}
+
+MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
+    if (!mService) {
+        // If HAL is not present, do nothing
+        return MotionClassification::NONE;
+    }
+    if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
+        updateLastDownTime(args.deviceId, args.downTime);
+    }
+
+    ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
+    bool elementAdded = mEvents.push(std::move(event));
+    if (!elementAdded) {
+        // Queue should not ever overfill. Suspect HAL is slow.
+        ALOGE("Dropped element with eventTime %" PRIu64, args.eventTime);
+        reset();
+        return MotionClassification::NONE;
+    }
+    return getClassification(args.deviceId);
+}
+
+void MotionClassifier::reset() {
+    mEvents.clear();
+    mEvents.push(ClassifierEvent::createHalResetEvent());
+}
+
+/**
+ * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
+ * Request InputClassifier thread to call resetDevice for this particular device.
+ */
+void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
+    int32_t deviceId = args.deviceId;
+    // Clear the pending events right away, to avoid unnecessary work done by the HAL.
+    mEvents.erase([deviceId](const ClassifierEvent& event) {
+            std::optional<int32_t> eventDeviceId = event.getDeviceId();
+            return eventDeviceId && (*eventDeviceId == deviceId);
+    });
+    mEvents.push(std::make_unique<NotifyDeviceResetArgs>(args));
+}
+
+// --- InputClassifier ---
+
+InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
+        mListener(listener) {
+    if (deepPressEnabled()) {
+        sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+                classifier::V1_0::IInputClassifier::getService();
+        if (service) {
+            mMotionClassifier = std::make_unique<MotionClassifier>(service);
+        } else {
+            ALOGI("Could not obtain InputClassifier HAL");
+        }
+    }
+};
+
+void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+    // pass through
+    mListener->notifyConfigurationChanged(args);
+}
+
+void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
+    // pass through
+    mListener->notifyKey(args);
+}
+
+void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
+    if (mMotionClassifier && isTouchEvent(*args)) {
+        // We only cover touch events, for now.
+        NotifyMotionArgs newArgs = NotifyMotionArgs(*args);
+        newArgs.classification = mMotionClassifier->classify(newArgs);
+        args = &newArgs;
+    }
+    mListener->notifyMotion(args);
+}
+
+void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
+    // pass through
+    mListener->notifySwitch(args);
+}
+
+void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+    if (mMotionClassifier) {
+        mMotionClassifier->reset(*args);
+    }
+    // continue to next stage
+    mListener->notifyDeviceReset(args);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
new file mode 100644
index 0000000..1f7dd41
--- /dev/null
+++ b/services/inputflinger/InputClassifier.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_CLASSIFIER_H
+#define _UI_INPUT_CLASSIFIER_H
+
+#include <utils/RefBase.h>
+#include <unordered_map>
+#include <thread>
+
+#include "BlockingQueue.h"
+#include "InputListener.h"
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+
+namespace android {
+
+enum class ClassifierEventType : uint8_t {
+    MOTION = 0,
+    DEVICE_RESET = 1,
+    HAL_RESET = 2,
+    EXIT = 3,
+};
+
+struct ClassifierEvent {
+    ClassifierEventType type;
+    std::unique_ptr<NotifyArgs> args;
+
+    ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args);
+    ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args);
+    ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args);
+    ClassifierEvent(ClassifierEvent&& other);
+    ClassifierEvent& operator=(ClassifierEvent&& other);
+
+    // Convenience function to create a HAL_RESET event
+    static ClassifierEvent createHalResetEvent();
+    // Convenience function to create an EXIT event
+    static ClassifierEvent createExitEvent();
+
+    std::optional<int32_t> getDeviceId() const;
+};
+
+// --- Interfaces ---
+
+/**
+ * Interface for adding a MotionClassification to NotifyMotionArgs.
+ *
+ * To implement, override the classify function.
+ */
+class MotionClassifierInterface {
+public:
+    MotionClassifierInterface() { }
+    virtual ~MotionClassifierInterface() { }
+    /**
+     * Based on the motion event described by NotifyMotionArgs,
+     * provide a MotionClassification for the current gesture.
+     */
+    virtual MotionClassification classify(const NotifyMotionArgs& args) = 0;
+    virtual void reset() = 0;
+    virtual void reset(const NotifyDeviceResetArgs& args) = 0;
+};
+
+/**
+ * Base interface for an InputListener stage.
+ * Provides classification to events.
+ */
+class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
+protected:
+    InputClassifierInterface() { }
+    virtual ~InputClassifierInterface() { }
+};
+
+// --- Implementations ---
+
+/**
+ * Implementation of MotionClassifierInterface that calls the InputClassifier HAL
+ * in order to determine the classification for the current gesture.
+ *
+ * The InputClassifier HAL may keep track of the entire gesture in order to determine
+ * the classification, and may be hardware-specific. It may use the data in
+ * NotifyMotionArgs::videoFrames field to drive the classification decisions.
+ * The HAL is called from a separate thread.
+ */
+class MotionClassifier final : public MotionClassifierInterface {
+public:
+    MotionClassifier(sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
+    ~MotionClassifier();
+    /**
+     * Classifies events asynchronously; that is, it doesn't block events on a classification,
+     * but instead sends them over to the classifier HAL
+     * and after a classification is determined,
+     * it then marks the next event it sees in the stream with it.
+     *
+     * Therefore, it is acceptable to have the classifications be delayed by 1-2 events
+     * in a particular gesture.
+     */
+    virtual MotionClassification classify(const NotifyMotionArgs& args) override;
+    virtual void reset() override;
+    virtual void reset(const NotifyDeviceResetArgs& args) override;
+
+private:
+    // The events that need to be sent to the HAL.
+    BlockingQueue<ClassifierEvent> mEvents;
+    /**
+     * Thread that will communicate with InputClassifier HAL.
+     * This should be the only thread that communicates with InputClassifier HAL,
+     * because this thread is allowed to block on the HAL calls.
+     */
+    std::thread mHalThread;
+    /**
+     * Print an error message if the caller is not on the InputClassifier thread.
+     * Caller must supply the name of the calling function as __function__
+     */
+    void ensureHalThread(const char* function);
+    /**
+     * Call the InputClassifier HAL
+     */
+    void callInputClassifierHal();
+    /**
+     * Access to the InputClassifier HAL
+     */
+    sp<android::hardware::input::classifier::V1_0::IInputClassifier> mService;
+    std::mutex mLock;
+    /**
+     * Per-device input classifications. Should only be accessed using the
+     * getClassification / setClassification methods.
+     */
+    std::unordered_map<int32_t /*deviceId*/, MotionClassification>
+            mClassifications; //GUARDED_BY(mLock);
+    /**
+     * Set the current classification for a given device.
+     */
+    void setClassification(int32_t deviceId, MotionClassification classification);
+    /**
+     * Get the current classification for a given device.
+     */
+    MotionClassification getClassification(int32_t deviceId);
+    void updateClassification(int32_t deviceId, nsecs_t eventTime,
+        MotionClassification classification);
+    /**
+     * Clear all current classifications
+     */
+    void clearClassifications();
+    /**
+     * Per-device times when the last ACTION_DOWN was received.
+     * Used to reject late classifications that do not belong to the current gesture.
+     *
+     * Accessed indirectly by both InputClassifier thread and the thread that receives notifyMotion.
+     */
+    std::unordered_map<int32_t /*deviceId*/, nsecs_t /*downTime*/>
+            mLastDownTimes; //GUARDED_BY(mLock);
+    void updateLastDownTime(int32_t deviceId, nsecs_t downTime);
+    // Should only be accessed through isResetNeeded() and setResetNeeded()
+    bool mResetNeeded = false; //GUARDED_BY(mLock);
+    /**
+     * Check whether reset should be performed. Reset should be performed
+     * if the eventTime of the current event is older than mLastDownTime,
+     * i.e. a new gesture has already begun, but an older gesture is still being processed.
+     */
+    bool isResetNeeded(nsecs_t eventTime);
+    void setResetNeeded(bool isNeeded);
+
+    /**
+     * Exit the InputClassifier HAL thread.
+     * Useful for tests to ensure proper cleanup.
+     */
+    void requestExit();
+};
+
+/**
+ * Implementation of the InputClassifierInterface.
+ * Represents a separate stage of input processing. All of the input events go through this stage.
+ * Acts as a passthrough for all input events except for motion events.
+ * The events of motion type are sent to MotionClassifier.
+ */
+class InputClassifier : public InputClassifierInterface {
+public:
+    explicit InputClassifier(const sp<InputListenerInterface>& listener);
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
+    virtual void notifyKey(const NotifyKeyArgs* args);
+    virtual void notifyMotion(const NotifyMotionArgs* args);
+    virtual void notifySwitch(const NotifySwitchArgs* args);
+    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+
+private:
+    std::unique_ptr<MotionClassifierInterface> mMotionClassifier = nullptr;
+    // The next stage to pass input events to
+    sp<InputListenerInterface> mListener;
+};
+
+} // namespace android
+#endif
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 9a449fa..9702fb2 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -58,6 +58,7 @@
 #include <utils/Trace.h>
 #include <powermanager/PowerManager.h>
 #include <ui/Region.h>
+#include <binder/Binder.h>
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -96,6 +97,9 @@
 // Number of recent events to keep for debugging purposes.
 constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
 
+// Sequence number for synthesized or injected events.
+constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
+
 
 static inline nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
@@ -211,10 +215,6 @@
     return true;
 }
 
-static bool isMainDisplay(int32_t displayId) {
-    return displayId == ADISPLAY_ID_DEFAULT || displayId == ADISPLAY_ID_NONE;
-}
-
 static void dumpRegion(std::string& dump, const Region& region) {
     if (region.isEmpty()) {
         dump += "<empty>";
@@ -235,19 +235,27 @@
     }
 }
 
+template<typename T, typename U>
+static T getValueByKey(std::unordered_map<U, T>& map, U key) {
+    typename std::unordered_map<U, T>::const_iterator it = map.find(key);
+    return it != map.end() ? it->second : T{};
+}
+
 
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
     mPolicy(policy),
-    mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
+    mPendingEvent(nullptr), mLastDropReason(DROP_REASON_NOT_DROPPED),
     mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
-    mNextUnblockedEvent(NULL),
+    mNextUnblockedEvent(nullptr),
     mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
+    mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
     mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
     mLooper = new Looper(false);
+    mReporter = createInputReporter();
 
-    mKeyRepeatState.lastKeyEntry = NULL;
+    mKeyRepeatState.lastKeyEntry = nullptr;
 
     policy->getDispatcherConfiguration(&mConfig);
 }
@@ -360,7 +368,7 @@
 
     // Now we have an event to dispatch.
     // All events are eventually dequeued and processed this way, even if we intend to drop them.
-    ALOG_ASSERT(mPendingEvent != NULL);
+    ALOG_ASSERT(mPendingEvent != nullptr);
     bool done = false;
     DropReason dropReason = DROP_REASON_NOT_DROPPED;
     if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
@@ -370,7 +378,7 @@
     }
 
     if (mNextUnblockedEvent == mPendingEvent) {
-        mNextUnblockedEvent = NULL;
+        mNextUnblockedEvent = nullptr;
     }
 
     switch (mPendingEvent->type) {
@@ -481,16 +489,16 @@
         if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
                 && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                 && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
-                && mInputTargetWaitApplicationHandle != NULL) {
+                && mInputTargetWaitApplicationToken != nullptr) {
             int32_t displayId = motionEntry->displayId;
             int32_t x = int32_t(motionEntry->pointerCoords[0].
                     getAxisValue(AMOTION_EVENT_AXIS_X));
             int32_t y = int32_t(motionEntry->pointerCoords[0].
                     getAxisValue(AMOTION_EVENT_AXIS_Y));
             sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
-            if (touchedWindowHandle != NULL
-                    && touchedWindowHandle->inputApplicationHandle
-                            != mInputTargetWaitApplicationHandle) {
+            if (touchedWindowHandle != nullptr
+                    && touchedWindowHandle->getApplicationToken()
+                            != mInputTargetWaitApplicationToken) {
                 // User touched a different application than the one we are waiting on.
                 // Flag the event, and start pruning the input queue.
                 mNextUnblockedEvent = motionEntry;
@@ -513,11 +521,12 @@
 }
 
 sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
-        int32_t x, int32_t y) {
+        int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) {
     // Traverse windows from front to back to find touched window.
-    size_t numWindows = mWindowHandles.size();
+    const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+    size_t numWindows = windowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
-        sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
+        sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i);
         const InputWindowInfo* windowInfo = windowHandle->getInfo();
         if (windowInfo->displayId == displayId) {
             int32_t flags = windowInfo->layoutParamsFlags;
@@ -527,14 +536,29 @@
                     bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
                             | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
                     if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
+                        int32_t portalToDisplayId = windowInfo->portalToDisplayId;
+                        if (portalToDisplayId != ADISPLAY_ID_NONE
+                                && portalToDisplayId != displayId) {
+                            if (addPortalWindows) {
+                                // For the monitoring channels of the display.
+                                mTempTouchState.addPortalWindow(windowHandle);
+                            }
+                            return findTouchedWindowAtLocked(
+                                    portalToDisplayId, x, y, addOutsideTargets, addPortalWindows);
+                        }
                         // Found window.
                         return windowHandle;
                     }
                 }
+
+                if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
+                    mTempTouchState.addOrUpdateWindow(
+                            windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
+                }
             }
         }
     }
-    return NULL;
+    return nullptr;
 }
 
 void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
@@ -663,7 +687,7 @@
     if (mPendingEvent) {
         resetANRTimeoutsLocked();
         releaseInboundEventLocked(mPendingEvent);
-        mPendingEvent = NULL;
+        mPendingEvent = nullptr;
     }
 }
 
@@ -676,7 +700,7 @@
         setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
     }
     if (entry == mNextUnblockedEvent) {
-        mNextUnblockedEvent = NULL;
+        mNextUnblockedEvent = nullptr;
     }
     addRecentEventLocked(entry);
     entry->release();
@@ -685,7 +709,7 @@
 void InputDispatcher::resetKeyRepeatLocked() {
     if (mKeyRepeatState.lastKeyEntry) {
         mKeyRepeatState.lastKeyEntry->release();
-        mKeyRepeatState.lastKeyEntry = NULL;
+        mKeyRepeatState.lastKeyEntry = nullptr;
     }
 }
 
@@ -701,8 +725,8 @@
         entry->policyFlags = policyFlags;
         entry->repeatCount += 1;
     } else {
-        KeyEntry* newEntry = new KeyEntry(currentTime,
-                entry->deviceId, entry->source, policyFlags,
+        KeyEntry* newEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
+                entry->deviceId, entry->source, entry->displayId, policyFlags,
                 entry->action, entry->flags, entry->keyCode, entry->scanCode,
                 entry->metaState, entry->repeatCount + 1, entry->downTime);
 
@@ -807,8 +831,11 @@
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
             CommandEntry* commandEntry = postCommandLocked(
                     & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
-            if (mFocusedWindowHandle != NULL) {
-                commandEntry->inputWindowHandle = mFocusedWindowHandle;
+            sp<InputWindowHandle> focusedWindowHandle =
+                    getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
+            if (focusedWindowHandle != nullptr) {
+                commandEntry->inputChannel =
+                    getInputChannelLocked(focusedWindowHandle->getToken());
             }
             commandEntry->keyEntry = entry;
             entry->refCount += 1;
@@ -826,6 +853,7 @@
     if (*dropReason != DROP_REASON_NOT_DROPPED) {
         setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
                 ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
+        mReporter->reportDroppedKey(entry->sequenceNum);
         return true;
     }
 
@@ -842,7 +870,8 @@
         return true;
     }
 
-    addMonitoringTargetsLocked(inputTargets);
+    // Add monitor channels from event's or focused display.
+    addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
 
     // Dispatch the key.
     dispatchEventLocked(currentTime, entry, inputTargets);
@@ -851,11 +880,11 @@
 
 void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, "
-            "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
-            "repeatCount=%d, downTime=%" PRId64,
+    ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
+            "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
+            "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
             prefix,
-            entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+            entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags,
             entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
             entry->repeatCount, entry->downTime);
 #endif
@@ -909,7 +938,24 @@
         return true;
     }
 
-    addMonitoringTargetsLocked(inputTargets);
+    // Add monitor channels from event's or focused display.
+    addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
+
+    if (isPointerEvent) {
+        ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
+        if (stateIndex >= 0) {
+            const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex);
+            if (!state.portalWindows.isEmpty()) {
+                // The event has gone through these portal windows, so we add monitoring targets of
+                // the corresponding displays as well.
+                for (size_t i = 0; i < state.portalWindows.size(); i++) {
+                    const InputWindowInfo* windowInfo = state.portalWindows.itemAt(i)->getInfo();
+                    addMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
+                            -windowInfo->frameLeft, -windowInfo->frameTop);
+                }
+            }
+        }
+    }
 
     // Dispatch the motion.
     if (conflictingPointerActions) {
@@ -924,12 +970,13 @@
 
 void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-    ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, "
+    ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+            ", policyFlags=0x%x, "
             "action=0x%x, actionButton=0x%x, flags=0x%x, "
             "metaState=0x%x, buttonState=0x%x,"
             "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
             prefix,
-            entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+            entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags,
             entry->action, entry->actionButton, entry->flags,
             entry->metaState, entry->buttonState,
             entry->edgeFlags, entry->xPrecision, entry->yPrecision,
@@ -987,7 +1034,7 @@
         const sp<InputApplicationHandle>& applicationHandle,
         const sp<InputWindowHandle>& windowHandle,
         nsecs_t* nextWakeupTime, const char* reason) {
-    if (applicationHandle == NULL && windowHandle == NULL) {
+    if (applicationHandle == nullptr && windowHandle == nullptr) {
         if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
 #if DEBUG_FOCUS
             ALOGD("Waiting for system to become ready for input.  Reason: %s", reason);
@@ -996,7 +1043,7 @@
             mInputTargetWaitStartTime = currentTime;
             mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
             mInputTargetWaitTimeoutExpired = false;
-            mInputTargetWaitApplicationHandle.clear();
+            mInputTargetWaitApplicationToken.clear();
         }
     } else {
         if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
@@ -1006,9 +1053,9 @@
                     reason);
 #endif
             nsecs_t timeout;
-            if (windowHandle != NULL) {
+            if (windowHandle != nullptr) {
                 timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
-            } else if (applicationHandle != NULL) {
+            } else if (applicationHandle != nullptr) {
                 timeout = applicationHandle->getDispatchingTimeout(
                         DEFAULT_INPUT_DISPATCHING_TIMEOUT);
             } else {
@@ -1019,13 +1066,13 @@
             mInputTargetWaitStartTime = currentTime;
             mInputTargetWaitTimeoutTime = currentTime + timeout;
             mInputTargetWaitTimeoutExpired = false;
-            mInputTargetWaitApplicationHandle.clear();
+            mInputTargetWaitApplicationToken.clear();
 
-            if (windowHandle != NULL) {
-                mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
+            if (windowHandle != nullptr) {
+                mInputTargetWaitApplicationToken = windowHandle->getApplicationToken();
             }
-            if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) {
-                mInputTargetWaitApplicationHandle = applicationHandle;
+            if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) {
+                mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken();
             }
         }
     }
@@ -1051,6 +1098,13 @@
     }
 }
 
+void InputDispatcher::removeWindowByTokenLocked(const sp<IBinder>& token) {
+    for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
+        TouchState& state = mTouchStatesByDisplay.editValueAt(d);
+        state.removeWindowByToken(token);
+    }
+}
+
 void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
         const sp<InputChannel>& inputChannel) {
     if (newTimeout > 0) {
@@ -1065,17 +1119,10 @@
             ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
             if (connectionIndex >= 0) {
                 sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
-                sp<InputWindowHandle> windowHandle = connection->inputWindowHandle;
+                sp<IBinder> token = connection->inputChannel->getToken();
 
-                if (windowHandle != NULL) {
-                    const InputWindowInfo* info = windowHandle->getInfo();
-                    if (info) {
-                        ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(info->displayId);
-                        if (stateIndex >= 0) {
-                            mTouchStatesByDisplay.editValueAt(stateIndex).removeWindow(
-                                    windowHandle);
-                        }
-                    }
+                if (token != nullptr) {
+                    removeWindowByTokenLocked(token);
                 }
 
                 if (connection->status == Connection::STATUS_NORMAL) {
@@ -1103,7 +1150,33 @@
 
     // Reset input target wait timeout.
     mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
-    mInputTargetWaitApplicationHandle.clear();
+    mInputTargetWaitApplicationToken.clear();
+}
+
+/**
+ * Get the display id that the given event should go to. If this event specifies a valid display id,
+ * then it should be dispatched to that display. Otherwise, the event goes to the focused display.
+ * Focused display is the display that the user most recently interacted with.
+ */
+int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) {
+    int32_t displayId;
+    switch (entry->type) {
+    case EventEntry::TYPE_KEY: {
+        const KeyEntry* typedEntry = static_cast<const KeyEntry*>(entry);
+        displayId = typedEntry->displayId;
+        break;
+    }
+    case EventEntry::TYPE_MOTION: {
+        const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry);
+        displayId = typedEntry->displayId;
+        break;
+    }
+    default: {
+        ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type);
+        return ADISPLAY_ID_NONE;
+    }
+    }
+    return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
 }
 
 int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
@@ -1111,41 +1184,48 @@
     int32_t injectionResult;
     std::string reason;
 
+    int32_t displayId = getTargetDisplayId(entry);
+    sp<InputWindowHandle> focusedWindowHandle =
+            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+    sp<InputApplicationHandle> focusedApplicationHandle =
+            getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
+
     // If there is no currently focused window and no focused application
     // then drop the event.
-    if (mFocusedWindowHandle == NULL) {
-        if (mFocusedApplicationHandle != NULL) {
+    if (focusedWindowHandle == nullptr) {
+        if (focusedApplicationHandle != nullptr) {
             injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    mFocusedApplicationHandle, NULL, nextWakeupTime,
+                    focusedApplicationHandle, nullptr, nextWakeupTime,
                     "Waiting because no window has focus but there is a "
                     "focused application that may eventually add a window "
                     "when it finishes starting up.");
             goto Unresponsive;
         }
 
-        ALOGI("Dropping event because there is no focused window or focused application.");
+        ALOGI("Dropping event because there is no focused window or focused application in display "
+                "%" PRId32 ".", displayId);
         injectionResult = INPUT_EVENT_INJECTION_FAILED;
         goto Failed;
     }
 
     // Check permissions.
-    if (! checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) {
+    if (!checkInjectionPermission(focusedWindowHandle, entry->injectionState)) {
         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
         goto Failed;
     }
 
     // Check whether the window is ready for more input.
     reason = checkWindowReadyForMoreInputLocked(currentTime,
-            mFocusedWindowHandle, entry, "focused");
+            focusedWindowHandle, entry, "focused");
     if (!reason.empty()) {
         injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.c_str());
+                focusedApplicationHandle, focusedWindowHandle, nextWakeupTime, reason.c_str());
         goto Unresponsive;
     }
 
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-    addWindowTargetLocked(mFocusedWindowHandle,
+    addWindowTargetLocked(focusedWindowHandle,
             InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
             inputTargets);
 
@@ -1186,7 +1266,7 @@
     // Copy current touch state into mTempTouchState.
     // This state is always reset at the end of this function, so if we don't find state
     // for the specified display then our initial state will be empty.
-    const TouchState* oldState = NULL;
+    const TouchState* oldState = nullptr;
     ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
     if (oldStateIndex >= 0) {
         oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex);
@@ -1209,7 +1289,8 @@
         bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
         if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) {
 #if DEBUG_FOCUS
-            ALOGD("Dropping event because a pointer for a different device is already down.");
+            ALOGD("Dropping event because a pointer for a different device is already down "
+                    "in display %" PRId32, displayId);
 #endif
             // TODO: test multiple simultaneous input streams.
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
@@ -1225,7 +1306,8 @@
         isSplit = false;
     } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
 #if DEBUG_FOCUS
-        ALOGI("Dropping move event because a pointer for a different device is already active.");
+        ALOGI("Dropping move event because a pointer for a different device is already active "
+                "in display %" PRId32, displayId);
 #endif
         // TODO: test multiple simultaneous input streams.
         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
@@ -1242,54 +1324,27 @@
                 getAxisValue(AMOTION_EVENT_AXIS_X));
         int32_t y = int32_t(entry->pointerCoords[pointerIndex].
                 getAxisValue(AMOTION_EVENT_AXIS_Y));
-        sp<InputWindowHandle> newTouchedWindowHandle;
-        bool isTouchModal = false;
-
-        // Traverse windows from front to back to find touched window and outside targets.
-        size_t numWindows = mWindowHandles.size();
-        for (size_t i = 0; i < numWindows; i++) {
-            sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
-            const InputWindowInfo* windowInfo = windowHandle->getInfo();
-            if (windowInfo->displayId != displayId) {
-                continue; // wrong display
-            }
-
-            int32_t flags = windowInfo->layoutParamsFlags;
-            if (windowInfo->visible) {
-                if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
-                    isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
-                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
-                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
-                        newTouchedWindowHandle = windowHandle;
-                        break; // found touched window, exit window loop
-                    }
-                }
-
-                if (maskedAction == AMOTION_EVENT_ACTION_DOWN
-                        && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
-                    mTempTouchState.addOrUpdateWindow(
-                            windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
-                }
-            }
-        }
+        sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
+                displayId, x, y, maskedAction == AMOTION_EVENT_ACTION_DOWN, true);
 
         // Figure out whether splitting will be allowed for this window.
-        if (newTouchedWindowHandle != NULL
+        if (newTouchedWindowHandle != nullptr
                 && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
             // New window supports splitting.
             isSplit = true;
         } else if (isSplit) {
             // New window does not support splitting but we have already split events.
             // Ignore the new window.
-            newTouchedWindowHandle = NULL;
+            newTouchedWindowHandle = nullptr;
         }
 
         // Handle the case where we did not find a window.
-        if (newTouchedWindowHandle == NULL) {
+        if (newTouchedWindowHandle == nullptr) {
             // Try to assign the pointer to the first foreground window we find, if there is one.
             newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
-            if (newTouchedWindowHandle == NULL) {
-                ALOGI("Dropping event because there is no touchable window at (%d, %d).", x, y);
+            if (newTouchedWindowHandle == nullptr) {
+                ALOGI("Dropping event because there is no touchable window at (%d, %d) in display "
+                        "%" PRId32 ".", x, y, displayId);
                 injectionResult = INPUT_EVENT_INJECTION_FAILED;
                 goto Failed;
             }
@@ -1327,7 +1382,7 @@
         if (! mTempTouchState.down) {
 #if DEBUG_FOCUS
             ALOGD("Dropping event because the pointer is not down or we previously "
-                    "dropped the pointer down event.");
+                    "dropped the pointer down event in display %" PRId32, displayId);
 #endif
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
@@ -1345,11 +1400,12 @@
             sp<InputWindowHandle> newTouchedWindowHandle =
                     findTouchedWindowAtLocked(displayId, x, y);
             if (oldTouchedWindowHandle != newTouchedWindowHandle
-                    && newTouchedWindowHandle != NULL) {
+                    && newTouchedWindowHandle != nullptr) {
 #if DEBUG_FOCUS
-                ALOGD("Touch is slipping out of window %s into window %s.",
+                ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
                         oldTouchedWindowHandle->getName().c_str(),
-                        newTouchedWindowHandle->getName().c_str());
+                        newTouchedWindowHandle->getName().c_str(),
+                        displayId);
 #endif
                 // Make a slippery exit from the old window.
                 mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
@@ -1380,7 +1436,7 @@
 
     if (newHoverWindowHandle != mLastHoverWindowHandle) {
         // Let the previous window know that the hover sequence is over.
-        if (mLastHoverWindowHandle != NULL) {
+        if (mLastHoverWindowHandle != nullptr) {
 #if DEBUG_HOVER
             ALOGD("Sending hover exit event to window %s.",
                     mLastHoverWindowHandle->getName().c_str());
@@ -1390,7 +1446,7 @@
         }
 
         // Let the new window know that the hover sequence is starting.
-        if (newHoverWindowHandle != NULL) {
+        if (newHoverWindowHandle != nullptr) {
 #if DEBUG_HOVER
             ALOGD("Sending hover enter event to window %s.",
                     newHoverWindowHandle->getName().c_str());
@@ -1418,7 +1474,8 @@
         }
         if (! haveForegroundWindow) {
 #if DEBUG_FOCUS
-            ALOGD("Dropping event because there is no touched foreground window to receive it.");
+            ALOGD("Dropping event because there is no touched foreground window in display %" PRId32
+                    " to receive it.", displayId);
 #endif
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
@@ -1455,7 +1512,7 @@
                     touchedWindow.windowHandle, entry, "touched");
             if (!reason.empty()) {
                 injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                        NULL, touchedWindow.windowHandle, nextWakeupTime, reason.c_str());
+                        nullptr, touchedWindow.windowHandle, nextWakeupTime, reason.c_str());
                 goto Unresponsive;
             }
         }
@@ -1471,8 +1528,10 @@
         sp<InputWindowHandle> foregroundWindowHandle =
                 mTempTouchState.getFirstForegroundWindowHandle();
         if (foregroundWindowHandle->getInfo()->hasWallpaper) {
-            for (size_t i = 0; i < mWindowHandles.size(); i++) {
-                sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
+            const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+            size_t numWindows = windowHandles.size();
+            for (size_t i = 0; i < numWindows; i++) {
+                sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i);
                 const InputWindowInfo* info = windowHandle->getInfo();
                 if (info->displayId == displayId
                         && windowHandle->getInfo()->layoutParamsType
@@ -1503,7 +1562,7 @@
 Failed:
     // Check injection permission once and for all.
     if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
-        if (checkInjectionPermission(NULL, entry->injectionState)) {
+        if (checkInjectionPermission(nullptr, entry->injectionState)) {
             injectionPermission = INJECTION_PERMISSION_GRANTED;
         } else {
             injectionPermission = INJECTION_PERMISSION_DENIED;
@@ -1607,39 +1666,59 @@
 
 void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
         int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {
+    sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+    if (inputChannel == nullptr) {
+        ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
+        return;
+    }
+
     inputTargets.push();
 
     const InputWindowInfo* windowInfo = windowHandle->getInfo();
     InputTarget& target = inputTargets.editTop();
-    target.inputChannel = windowInfo->inputChannel;
+    target.inputChannel = inputChannel;
     target.flags = targetFlags;
     target.xOffset = - windowInfo->frameLeft;
     target.yOffset = - windowInfo->frameTop;
-    target.scaleFactor = windowInfo->scaleFactor;
+    target.globalScaleFactor = windowInfo->globalScaleFactor;
+    target.windowXScale = windowInfo->windowXScale;
+    target.windowYScale = windowInfo->windowYScale;
     target.pointerIds = pointerIds;
 }
 
-void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets) {
-    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-        inputTargets.push();
+void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets,
+        int32_t displayId, float xOffset, float yOffset) {
+    std::unordered_map<int32_t, Vector<sp<InputChannel>>>::const_iterator it =
+            mMonitoringChannelsByDisplay.find(displayId);
 
-        InputTarget& target = inputTargets.editTop();
-        target.inputChannel = mMonitoringChannels[i];
-        target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-        target.xOffset = 0;
-        target.yOffset = 0;
-        target.pointerIds.clear();
-        target.scaleFactor = 1.0f;
+    if (it != mMonitoringChannelsByDisplay.end()) {
+        const Vector<sp<InputChannel>>& monitoringChannels = it->second;
+        const size_t numChannels = monitoringChannels.size();
+        for (size_t i = 0; i < numChannels; i++) {
+            inputTargets.push();
+
+            InputTarget& target = inputTargets.editTop();
+            target.inputChannel = monitoringChannels[i];
+            target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+            target.xOffset = xOffset;
+            target.yOffset = yOffset;
+            target.pointerIds.clear();
+            target.globalScaleFactor = 1.0f;
+        }
+    } else {
+        // If there is no monitor channel registered or all monitor channel unregistered,
+        // the display can't detect the extra system gesture by a copy of input events.
+        ALOGW("There is no monitor channel found in display %" PRId32, displayId);
     }
 }
 
 bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
         const InjectionState* injectionState) {
     if (injectionState
-            && (windowHandle == NULL
+            && (windowHandle == nullptr
                     || windowHandle->getInfo()->ownerUid != injectionState->injectorUid)
             && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
-        if (windowHandle != NULL) {
+        if (windowHandle != nullptr) {
             ALOGW("Permission denied: injecting event from pid %d uid %d to window %s "
                     "owned by uid %d",
                     injectionState->injectorPid, injectionState->injectorUid,
@@ -1657,9 +1736,10 @@
 bool InputDispatcher::isWindowObscuredAtPointLocked(
         const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
-    size_t numWindows = mWindowHandles.size();
+    const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+    size_t numWindows = windowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
-        sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i);
+        sp<InputWindowHandle> otherHandle = windowHandles.itemAt(i);
         if (otherHandle == windowHandle) {
             break;
         }
@@ -1677,10 +1757,11 @@
 
 bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
+    const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
     const InputWindowInfo* windowInfo = windowHandle->getInfo();
-    size_t numWindows = mWindowHandles.size();
+    size_t numWindows = windowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
-        sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i);
+        sp<InputWindowHandle> otherHandle = windowHandles.itemAt(i);
         if (otherHandle == windowHandle) {
             break;
         }
@@ -1704,7 +1785,8 @@
     }
 
     // If the window's connection is not registered then keep waiting.
-    ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
+    ssize_t connectionIndex = getConnectionIndexLocked(
+            getInputChannelLocked(windowHandle->getToken()));
     if (connectionIndex < 0) {
         return StringPrintf("Waiting because the %s window's input channel is not "
                 "registered with the input dispatcher.  The window may be in the process "
@@ -1778,8 +1860,8 @@
 std::string InputDispatcher::getApplicationWindowLabelLocked(
         const sp<InputApplicationHandle>& applicationHandle,
         const sp<InputWindowHandle>& windowHandle) {
-    if (applicationHandle != NULL) {
-        if (windowHandle != NULL) {
+    if (applicationHandle != nullptr) {
+        if (windowHandle != nullptr) {
             std::string label(applicationHandle->getName());
             label += " - ";
             label += windowHandle->getName();
@@ -1787,7 +1869,7 @@
         } else {
             return applicationHandle->getName();
         }
-    } else if (windowHandle != NULL) {
+    } else if (windowHandle != nullptr) {
         return windowHandle->getName();
     } else {
         return "<unknown application or window>";
@@ -1795,8 +1877,11 @@
 }
 
 void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
-    if (mFocusedWindowHandle != NULL) {
-        const InputWindowInfo* info = mFocusedWindowHandle->getInfo();
+    int32_t displayId = getTargetDisplayId(eventEntry);
+    sp<InputWindowHandle> focusedWindowHandle =
+            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+    if (focusedWindowHandle != nullptr) {
+        const InputWindowInfo* info = focusedWindowHandle->getInfo();
         if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) {
 #if DEBUG_DISPATCH_CYCLE
             ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
@@ -1838,11 +1923,13 @@
         const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
-            "xOffset=%f, yOffset=%f, scaleFactor=%f, "
-            "pointerIds=0x%x",
+            "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
+            "windowScaleFactor=(%f, %f), pointerIds=0x%x",
             connection->getInputChannelName().c_str(), inputTarget->flags,
             inputTarget->xOffset, inputTarget->yOffset,
-            inputTarget->scaleFactor, inputTarget->pointerIds.value);
+            inputTarget->globalScaleFactor,
+            inputTarget->windowXScale, inputTarget->windowYScale,
+            inputTarget->pointerIds.value);
 #endif
 
     // Skip this event if the connection status is not normal.
@@ -1919,7 +2006,8 @@
     // Enqueue a new dispatch entry onto the outbound queue for this connection.
     DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
             inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
-            inputTarget->scaleFactor);
+            inputTarget->globalScaleFactor, inputTarget->windowXScale,
+            inputTarget->windowYScale);
 
     // Apply target flags and update the connection's input state.
     switch (eventEntry->type) {
@@ -2017,7 +2105,7 @@
 
             // Publish the key event.
             status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
-                    keyEntry->deviceId, keyEntry->source,
+                    keyEntry->deviceId, keyEntry->source, keyEntry->displayId,
                     dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                     keyEntry->keyCode, keyEntry->scanCode,
                     keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
@@ -2035,13 +2123,15 @@
             float xOffset, yOffset;
             if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                     && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
-                float scaleFactor = dispatchEntry->scaleFactor;
-                xOffset = dispatchEntry->xOffset * scaleFactor;
-                yOffset = dispatchEntry->yOffset * scaleFactor;
-                if (scaleFactor != 1.0f) {
+                float globalScaleFactor = dispatchEntry->globalScaleFactor;
+                float wxs = dispatchEntry->windowXScale;
+                float wys = dispatchEntry->windowYScale;
+                xOffset = dispatchEntry->xOffset * wxs;
+                yOffset = dispatchEntry->yOffset * wys;
+                if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
                     for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
                         scaledCoords[i] = motionEntry->pointerCoords[i];
-                        scaledCoords[i].scale(scaleFactor);
+                        scaledCoords[i].scale(globalScaleFactor, wxs, wys);
                     }
                     usingCoords = scaledCoords;
                 }
@@ -2063,7 +2153,7 @@
                     motionEntry->deviceId, motionEntry->source, motionEntry->displayId,
                     dispatchEntry->resolvedAction, motionEntry->actionButton,
                     dispatchEntry->resolvedFlags, motionEntry->edgeFlags,
-                    motionEntry->metaState, motionEntry->buttonState,
+                    motionEntry->metaState, motionEntry->buttonState, motionEntry->classification,
                     xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
                     motionEntry->downTime, motionEntry->eventTime,
                     motionEntry->pointerCount, motionEntry->pointerProperties,
@@ -2243,8 +2333,12 @@
 
 void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
         const CancelationOptions& options) {
-    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-        synthesizeCancelationEventsForInputChannelLocked(mMonitoringChannels[i], options);
+    for (auto& it : mMonitoringChannelsByDisplay) {
+        const Vector<sp<InputChannel>>& monitoringChannels = it.second;
+        const size_t numChannels = monitoringChannels.size();
+        for (size_t i = 0; i < numChannels; i++) {
+            synthesizeCancelationEventsForInputChannelLocked(monitoringChannels[i], options);
+        }
     }
 }
 
@@ -2290,16 +2384,19 @@
             }
 
             InputTarget target;
-            sp<InputWindowHandle> windowHandle = getWindowHandleLocked(connection->inputChannel);
-            if (windowHandle != NULL) {
+            sp<InputWindowHandle> windowHandle = getWindowHandleLocked(
+                    connection->inputChannel->getToken());
+            if (windowHandle != nullptr) {
                 const InputWindowInfo* windowInfo = windowHandle->getInfo();
                 target.xOffset = -windowInfo->frameLeft;
                 target.yOffset = -windowInfo->frameTop;
-                target.scaleFactor = windowInfo->scaleFactor;
+                target.globalScaleFactor = windowInfo->globalScaleFactor;
+                target.windowXScale = windowInfo->windowXScale;
+                target.windowYScale = windowInfo->windowYScale;
             } else {
                 target.xOffset = 0;
                 target.yOffset = 0;
-                target.scaleFactor = 1.0f;
+                target.globalScaleFactor = 1.0f;
             }
             target.inputChannel = connection->inputChannel;
             target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
@@ -2349,7 +2446,7 @@
                 "we expected there to be %d pointers.  This probably means we received "
                 "a broken sequence of pointer ids from the input device.",
                 splitPointerCount, pointerIds.count());
-        return NULL;
+        return nullptr;
     }
 
     int32_t action = originalMotionEntry->action;
@@ -2381,20 +2478,22 @@
     }
 
     MotionEntry* splitMotionEntry = new MotionEntry(
+            originalMotionEntry->sequenceNum,
             originalMotionEntry->eventTime,
             originalMotionEntry->deviceId,
             originalMotionEntry->source,
+            originalMotionEntry->displayId,
             originalMotionEntry->policyFlags,
             action,
             originalMotionEntry->actionButton,
             originalMotionEntry->flags,
             originalMotionEntry->metaState,
             originalMotionEntry->buttonState,
+            originalMotionEntry->classification,
             originalMotionEntry->edgeFlags,
             originalMotionEntry->xPrecision,
             originalMotionEntry->yPrecision,
             originalMotionEntry->downTime,
-            originalMotionEntry->displayId,
             splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0);
 
     if (originalMotionEntry->injectionState) {
@@ -2414,7 +2513,8 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        ConfigurationChangedEntry* newEntry = new ConfigurationChangedEntry(args->eventTime);
+        ConfigurationChangedEntry* newEntry =
+                new ConfigurationChangedEntry(args->sequenceNum, args->eventTime);
         needWake = enqueueInboundEventLocked(newEntry);
     } // release lock
 
@@ -2423,12 +2523,49 @@
     }
 }
 
+/**
+ * If one of the meta shortcuts is detected, process them here:
+ *     Meta + Backspace -> generate BACK
+ *     Meta + Enter -> generate HOME
+ * This will potentially overwrite keyCode and metaState.
+ */
+void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action,
+        int32_t& keyCode, int32_t& metaState) {
+    if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) {
+        int32_t newKeyCode = AKEYCODE_UNKNOWN;
+        if (keyCode == AKEYCODE_DEL) {
+            newKeyCode = AKEYCODE_BACK;
+        } else if (keyCode == AKEYCODE_ENTER) {
+            newKeyCode = AKEYCODE_HOME;
+        }
+        if (newKeyCode != AKEYCODE_UNKNOWN) {
+            AutoMutex _l(mLock);
+            struct KeyReplacement replacement = {keyCode, deviceId};
+            mReplacedKeys.add(replacement, newKeyCode);
+            keyCode = newKeyCode;
+            metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
+        }
+    } else if (action == AKEY_EVENT_ACTION_UP) {
+        // In order to maintain a consistent stream of up and down events, check to see if the key
+        // going up is one we've replaced in a down event and haven't yet replaced in an up event,
+        // even if the modifier was released between the down and the up events.
+        AutoMutex _l(mLock);
+        struct KeyReplacement replacement = {keyCode, deviceId};
+        ssize_t index = mReplacedKeys.indexOfKey(replacement);
+        if (index >= 0) {
+            keyCode = mReplacedKeys.valueAt(index);
+            mReplacedKeys.removeItemsAt(index);
+            metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
+        }
+    }
+}
+
 void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("notifyKey - eventTime=%" PRId64
-            ", deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, "
+            ", deviceId=%d, source=0x%x, displayId=%" PRId32 "policyFlags=0x%x, action=0x%x, "
             "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
-            args->eventTime, args->deviceId, args->source, args->policyFlags,
+            args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
             args->action, args->flags, args->keyCode, args->scanCode,
             args->metaState, args->downTime);
 #endif
@@ -2439,6 +2576,9 @@
     uint32_t policyFlags = args->policyFlags;
     int32_t flags = args->flags;
     int32_t metaState = args->metaState;
+    // InputDispatcher tracks and generates key repeats on behalf of
+    // whatever notifies it, so repeatCount should always be set to 0
+    constexpr int32_t repeatCount = 0;
     if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
         policyFlags |= POLICY_FLAG_VIRTUAL;
         flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
@@ -2450,37 +2590,11 @@
     policyFlags |= POLICY_FLAG_TRUSTED;
 
     int32_t keyCode = args->keyCode;
-    if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) {
-        int32_t newKeyCode = AKEYCODE_UNKNOWN;
-        if (keyCode == AKEYCODE_DEL) {
-            newKeyCode = AKEYCODE_BACK;
-        } else if (keyCode == AKEYCODE_ENTER) {
-            newKeyCode = AKEYCODE_HOME;
-        }
-        if (newKeyCode != AKEYCODE_UNKNOWN) {
-            AutoMutex _l(mLock);
-            struct KeyReplacement replacement = {keyCode, args->deviceId};
-            mReplacedKeys.add(replacement, newKeyCode);
-            keyCode = newKeyCode;
-            metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
-        }
-    } else if (args->action == AKEY_EVENT_ACTION_UP) {
-        // In order to maintain a consistent stream of up and down events, check to see if the key
-        // going up is one we've replaced in a down event and haven't yet replaced in an up event,
-        // even if the modifier was released between the down and the up events.
-        AutoMutex _l(mLock);
-        struct KeyReplacement replacement = {keyCode, args->deviceId};
-        ssize_t index = mReplacedKeys.indexOfKey(replacement);
-        if (index >= 0) {
-            keyCode = mReplacedKeys.valueAt(index);
-            mReplacedKeys.removeItemsAt(index);
-            metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
-        }
-    }
+    accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);
 
     KeyEvent event;
-    event.initialize(args->deviceId, args->source, args->action,
-            flags, keyCode, args->scanCode, metaState, 0,
+    event.initialize(args->deviceId, args->source, args->displayId, args->action,
+            flags, keyCode, args->scanCode, metaState, repeatCount,
             args->downTime, args->eventTime);
 
     android::base::Timer t;
@@ -2505,9 +2619,8 @@
             mLock.lock();
         }
 
-        int32_t repeatCount = 0;
-        KeyEntry* newEntry = new KeyEntry(args->eventTime,
-                args->deviceId, args->source, policyFlags,
+        KeyEntry* newEntry = new KeyEntry(args->sequenceNum, args->eventTime,
+                args->deviceId, args->source, args->displayId, policyFlags,
                 args->action, flags, keyCode, args->scanCode,
                 metaState, repeatCount, args->downTime);
 
@@ -2526,10 +2639,11 @@
 
 void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, "
+    ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+            ", policyFlags=0x%x, "
             "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x,"
             "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
-            args->eventTime, args->deviceId, args->source, args->policyFlags,
+            args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
             args->action, args->actionButton, args->flags, args->metaState, args->buttonState,
             args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime);
     for (uint32_t i = 0; i < args->pointerCount; i++) {
@@ -2573,9 +2687,10 @@
             mLock.unlock();
 
             MotionEvent event;
-            event.initialize(args->deviceId, args->source, args->action, args->actionButton,
+            event.initialize(args->deviceId, args->source, args->displayId,
+                    args->action, args->actionButton,
                     args->flags, args->edgeFlags, args->metaState, args->buttonState,
-                    0, 0, args->xPrecision, args->yPrecision,
+                    args->classification, 0, 0, args->xPrecision, args->yPrecision,
                     args->downTime, args->eventTime,
                     args->pointerCount, args->pointerProperties, args->pointerCoords);
 
@@ -2588,12 +2703,11 @@
         }
 
         // Just enqueue a new motion event.
-        MotionEntry* newEntry = new MotionEntry(args->eventTime,
-                args->deviceId, args->source, policyFlags,
+        MotionEntry* newEntry = new MotionEntry(args->sequenceNum, args->eventTime,
+                args->deviceId, args->source, args->displayId, policyFlags,
                 args->action, args->actionButton, args->flags,
-                args->metaState, args->buttonState,
+                args->metaState, args->buttonState, args->classification,
                 args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
-                args->displayId,
                 args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
 
         needWake = enqueueInboundEventLocked(newEntry);
@@ -2606,8 +2720,7 @@
 }
 
 bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) {
-    // TODO: support sending secondary display events to input filter
-    return mInputFilterEnabled && isMainDisplay(args->displayId);
+    return mInputFilterEnabled;
 }
 
 void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
@@ -2633,7 +2746,8 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        DeviceResetEntry* newEntry = new DeviceResetEntry(args->eventTime, args->deviceId);
+        DeviceResetEntry* newEntry =
+                new DeviceResetEntry(args->sequenceNum, args->eventTime, args->deviceId);
         needWake = enqueueInboundEventLocked(newEntry);
     } // release lock
 
@@ -2642,14 +2756,13 @@
     }
 }
 
-int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displayId,
+int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
         int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
         uint32_t policyFlags) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
-            "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x, displayId=%d",
-            event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags,
-            displayId);
+            "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
+            event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
 #endif
 
     nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
@@ -2663,20 +2776,29 @@
     EventEntry* lastInjectedEntry;
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY: {
-        const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
-        int32_t action = keyEvent->getAction();
+        KeyEvent keyEvent;
+        keyEvent.initialize(*static_cast<const KeyEvent*>(event));
+        int32_t action = keyEvent.getAction();
         if (! validateKeyEvent(action)) {
             return INPUT_EVENT_INJECTION_FAILED;
         }
 
-        int32_t flags = keyEvent->getFlags();
+        int32_t flags = keyEvent.getFlags();
+        int32_t keyCode = keyEvent.getKeyCode();
+        int32_t metaState = keyEvent.getMetaState();
+        accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
+                /*byref*/ keyCode, /*byref*/ metaState);
+        keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(),
+            action, flags, keyCode, keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(),
+            keyEvent.getDownTime(), keyEvent.getEventTime());
+
         if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
             policyFlags |= POLICY_FLAG_VIRTUAL;
         }
 
         if (!(policyFlags & POLICY_FLAG_FILTERED)) {
             android::base::Timer t;
-            mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
+            mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags);
             if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
                 ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
                         std::to_string(t.duration().count()).c_str());
@@ -2684,11 +2806,11 @@
         }
 
         mLock.lock();
-        firstInjectedEntry = new KeyEntry(keyEvent->getEventTime(),
-                keyEvent->getDeviceId(), keyEvent->getSource(),
+        firstInjectedEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(),
+                keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(),
                 policyFlags, action, flags,
-                keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
-                keyEvent->getRepeatCount(), keyEvent->getDownTime());
+                keyEvent.getKeyCode(), keyEvent.getScanCode(), keyEvent.getMetaState(),
+                keyEvent.getRepeatCount(), keyEvent.getDownTime());
         lastInjectedEntry = firstInjectedEntry;
         break;
     }
@@ -2716,26 +2838,29 @@
         mLock.lock();
         const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
         const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
-        firstInjectedEntry = new MotionEntry(*sampleEventTimes,
-                motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
+        firstInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
+                motionEvent->getDeviceId(), motionEvent->getSource(), motionEvent->getDisplayId(),
+                policyFlags,
                 action, actionButton, motionEvent->getFlags(),
                 motionEvent->getMetaState(), motionEvent->getButtonState(),
-                motionEvent->getEdgeFlags(),
+                motionEvent->getClassification(), motionEvent->getEdgeFlags(),
                 motionEvent->getXPrecision(), motionEvent->getYPrecision(),
-                motionEvent->getDownTime(), displayId,
+                motionEvent->getDownTime(),
                 uint32_t(pointerCount), pointerProperties, samplePointerCoords,
                 motionEvent->getXOffset(), motionEvent->getYOffset());
         lastInjectedEntry = firstInjectedEntry;
         for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
             sampleEventTimes += 1;
             samplePointerCoords += pointerCount;
-            MotionEntry* nextInjectedEntry = new MotionEntry(*sampleEventTimes,
-                    motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
+            MotionEntry* nextInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM,
+                    *sampleEventTimes,
+                    motionEvent->getDeviceId(), motionEvent->getSource(),
+                    motionEvent->getDisplayId(), policyFlags,
                     action, actionButton, motionEvent->getFlags(),
                     motionEvent->getMetaState(), motionEvent->getButtonState(),
-                    motionEvent->getEdgeFlags(),
+                    motionEvent->getClassification(), motionEvent->getEdgeFlags(),
                     motionEvent->getXPrecision(), motionEvent->getYPrecision(),
-                    motionEvent->getDownTime(), displayId,
+                    motionEvent->getDownTime(),
                     uint32_t(pointerCount), pointerProperties, samplePointerCoords,
                     motionEvent->getXOffset(), motionEvent->getYOffset());
             lastInjectedEntry->next = nextInjectedEntry;
@@ -2758,7 +2883,7 @@
     lastInjectedEntry->injectionState = injectionState;
 
     bool needWake = false;
-    for (EventEntry* entry = firstInjectedEntry; entry != NULL; ) {
+    for (EventEntry* entry = firstInjectedEntry; entry != nullptr; ) {
         EventEntry* nextEntry = entry->next;
         needWake |= enqueueInboundEventLocked(entry);
         entry = nextEntry;
@@ -2886,94 +3011,189 @@
     }
 }
 
+Vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(int32_t displayId) const {
+    std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>>::const_iterator it =
+        mWindowHandlesByDisplay.find(displayId);
+    if(it != mWindowHandlesByDisplay.end()) {
+        return it->second;
+    }
+
+    // Return an empty one if nothing found.
+    return Vector<sp<InputWindowHandle>>();
+}
+
 sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
-        const sp<InputChannel>& inputChannel) const {
-    size_t numWindows = mWindowHandles.size();
-    for (size_t i = 0; i < numWindows; i++) {
-        const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
-        if (windowHandle->getInputChannel() == inputChannel) {
-            return windowHandle;
+        const sp<IBinder>& windowHandleToken) const {
+    for (auto& it : mWindowHandlesByDisplay) {
+        const Vector<sp<InputWindowHandle>> windowHandles = it.second;
+        size_t numWindows = windowHandles.size();
+        for (size_t i = 0; i < numWindows; i++) {
+            const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i);
+            if (windowHandle->getToken() == windowHandleToken) {
+                return windowHandle;
+            }
         }
     }
-    return NULL;
+    return nullptr;
 }
 
 bool InputDispatcher::hasWindowHandleLocked(
         const sp<InputWindowHandle>& windowHandle) const {
-    size_t numWindows = mWindowHandles.size();
-    for (size_t i = 0; i < numWindows; i++) {
-        if (mWindowHandles.itemAt(i) == windowHandle) {
-            return true;
+    for (auto& it : mWindowHandlesByDisplay) {
+        const Vector<sp<InputWindowHandle>> windowHandles = it.second;
+        size_t numWindows = windowHandles.size();
+        for (size_t i = 0; i < numWindows; i++) {
+            if (windowHandles.itemAt(i)->getToken()
+                    == windowHandle->getToken()) {
+                if (windowHandle->getInfo()->displayId != it.first) {
+                    ALOGE("Found window %s in display %" PRId32
+                            ", but it should belong to display %" PRId32,
+                            windowHandle->getName().c_str(), it.first,
+                            windowHandle->getInfo()->displayId);
+                }
+                return true;
+            }
         }
     }
     return false;
 }
 
-void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
+sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token) const {
+    size_t count = mInputChannelsByToken.count(token);
+    if (count == 0) {
+        return nullptr;
+    }
+    return mInputChannelsByToken.at(token);
+}
+
+/**
+ * Called from InputManagerService, update window handle list by displayId that can receive input.
+ * A window handle contains information about InputChannel, Touch Region, Types, Focused,...
+ * If set an empty list, remove all handles from the specific display.
+ * For focused handle, check if need to change and send a cancel event to previous one.
+ * For removed handle, check if need to send a cancel event if already in touch.
+ */
+void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle>>& inputWindowHandles,
+        int32_t displayId) {
 #if DEBUG_FOCUS
-    ALOGD("setInputWindows");
+    ALOGD("setInputWindows displayId=%" PRId32, displayId);
 #endif
     { // acquire lock
         AutoMutex _l(mLock);
 
-        Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
-        mWindowHandles = inputWindowHandles;
+        // Copy old handles for release if they are no longer present.
+        const Vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
 
-        sp<InputWindowHandle> newFocusedWindowHandle;
+        sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
         bool foundHoveredWindow = false;
-        for (size_t i = 0; i < mWindowHandles.size(); i++) {
-            const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
-            if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
-                mWindowHandles.removeAt(i--);
-                continue;
+
+        if (inputWindowHandles.isEmpty()) {
+            // Remove all handles on a display if there are no windows left.
+            mWindowHandlesByDisplay.erase(displayId);
+        } else {
+            // Since we compare the pointer of input window handles across window updates, we need
+            // to make sure the handle object for the same window stays unchanged across updates.
+            const Vector<sp<InputWindowHandle>>& oldHandles = mWindowHandlesByDisplay[displayId];
+            std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens;
+            for (size_t i = 0; i < oldHandles.size(); i++) {
+                const sp<InputWindowHandle>& handle = oldHandles.itemAt(i);
+                oldHandlesByTokens[handle->getToken()] = handle;
             }
-            if (windowHandle->getInfo()->hasFocus) {
-                newFocusedWindowHandle = windowHandle;
+
+            const size_t numWindows = inputWindowHandles.size();
+            Vector<sp<InputWindowHandle>> newHandles;
+            for (size_t i = 0; i < numWindows; i++) {
+                const sp<InputWindowHandle>& handle = inputWindowHandles.itemAt(i);
+                if (!handle->updateInfo() || (getInputChannelLocked(handle->getToken()) == nullptr
+                        && handle->getInfo()->portalToDisplayId == ADISPLAY_ID_NONE)) {
+                    ALOGE("Window handle %s has no registered input channel",
+                            handle->getName().c_str());
+                    continue;
+                }
+
+                if (handle->getInfo()->displayId != displayId) {
+                    ALOGE("Window %s updated by wrong display %d, should belong to display %d",
+                        handle->getName().c_str(), displayId,
+                        handle->getInfo()->displayId);
+                    continue;
+                }
+
+                if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) {
+                    const sp<InputWindowHandle> oldHandle =
+                            oldHandlesByTokens.at(handle->getToken());
+                    oldHandle->updateFrom(handle);
+                    newHandles.push_back(oldHandle);
+                } else {
+                    newHandles.push_back(handle);
+                }
             }
-            if (windowHandle == mLastHoverWindowHandle) {
-                foundHoveredWindow = true;
+
+            for (size_t i = 0; i < newHandles.size(); i++) {
+                const sp<InputWindowHandle>& windowHandle = newHandles.itemAt(i);
+                // Set newFocusedWindowHandle to the top most focused window instead of the last one
+                if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus
+                        && windowHandle->getInfo()->visible) {
+                    newFocusedWindowHandle = windowHandle;
+                }
+                if (windowHandle == mLastHoverWindowHandle) {
+                    foundHoveredWindow = true;
+                }
             }
+
+            // Insert or replace
+            mWindowHandlesByDisplay[displayId] = newHandles;
         }
 
         if (!foundHoveredWindow) {
-            mLastHoverWindowHandle = NULL;
+            mLastHoverWindowHandle = nullptr;
         }
 
-        if (mFocusedWindowHandle != newFocusedWindowHandle) {
-            if (mFocusedWindowHandle != NULL) {
+        sp<InputWindowHandle> oldFocusedWindowHandle =
+                getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+
+        if (oldFocusedWindowHandle != newFocusedWindowHandle) {
+            if (oldFocusedWindowHandle != nullptr) {
 #if DEBUG_FOCUS
-                ALOGD("Focus left window: %s",
-                        mFocusedWindowHandle->getName().c_str());
+                ALOGD("Focus left window: %s in display %" PRId32,
+                        oldFocusedWindowHandle->getName().c_str(), displayId);
 #endif
-                sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel();
-                if (focusedInputChannel != NULL) {
+                sp<InputChannel> focusedInputChannel = getInputChannelLocked(
+                        oldFocusedWindowHandle->getToken());
+                if (focusedInputChannel != nullptr) {
                     CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                             "focus left window");
                     synthesizeCancelationEventsForInputChannelLocked(
                             focusedInputChannel, options);
                 }
+                mFocusedWindowHandlesByDisplay.erase(displayId);
             }
-            if (newFocusedWindowHandle != NULL) {
+            if (newFocusedWindowHandle != nullptr) {
 #if DEBUG_FOCUS
-                ALOGD("Focus entered window: %s",
-                        newFocusedWindowHandle->getName().c_str());
+                ALOGD("Focus entered window: %s in display %" PRId32,
+                        newFocusedWindowHandle->getName().c_str(), displayId);
 #endif
+                mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
             }
-            mFocusedWindowHandle = newFocusedWindowHandle;
+
+            if (mFocusedDisplayId == displayId) {
+                onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+            }
+
         }
 
-        for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
-            TouchState& state = mTouchStatesByDisplay.editValueAt(d);
+        ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
+        if (stateIndex >= 0) {
+            TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
             for (size_t i = 0; i < state.windows.size(); ) {
                 TouchedWindow& touchedWindow = state.windows.editItemAt(i);
                 if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
 #if DEBUG_FOCUS
-                    ALOGD("Touched window was removed: %s",
-                            touchedWindow.windowHandle->getName().c_str());
+                    ALOGD("Touched window was removed: %s in display %" PRId32,
+                            touchedWindow.windowHandle->getName().c_str(), displayId);
 #endif
                     sp<InputChannel> touchedInputChannel =
-                            touchedWindow.windowHandle->getInputChannel();
-                    if (touchedInputChannel != NULL) {
+                            getInputChannelLocked(touchedWindow.windowHandle->getToken());
+                    if (touchedInputChannel != nullptr) {
                         CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                                 "touched window was removed");
                         synthesizeCancelationEventsForInputChannelLocked(
@@ -2990,13 +3210,14 @@
         // This ensures that unused input channels are released promptly.
         // Otherwise, they might stick around until the window handle is destroyed
         // which might not happen until the next GC.
-        for (size_t i = 0; i < oldWindowHandles.size(); i++) {
+        size_t numWindows = oldWindowHandles.size();
+        for (size_t i = 0; i < numWindows; i++) {
             const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i);
             if (!hasWindowHandleLocked(oldWindowHandle)) {
 #if DEBUG_FOCUS
                 ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
 #endif
-                oldWindowHandle->releaseInfo();
+                oldWindowHandle->releaseChannel();
             }
         }
     } // release lock
@@ -3006,25 +3227,28 @@
 }
 
 void InputDispatcher::setFocusedApplication(
-        const sp<InputApplicationHandle>& inputApplicationHandle) {
+        int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
 #if DEBUG_FOCUS
-    ALOGD("setFocusedApplication");
+    ALOGD("setFocusedApplication displayId=%" PRId32, displayId);
 #endif
     { // acquire lock
         AutoMutex _l(mLock);
 
-        if (inputApplicationHandle != NULL && inputApplicationHandle->updateInfo()) {
-            if (mFocusedApplicationHandle != inputApplicationHandle) {
-                if (mFocusedApplicationHandle != NULL) {
+        sp<InputApplicationHandle> oldFocusedApplicationHandle =
+                getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
+        if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
+            if (oldFocusedApplicationHandle != inputApplicationHandle) {
+                if (oldFocusedApplicationHandle != nullptr) {
                     resetANRTimeoutsLocked();
-                    mFocusedApplicationHandle->releaseInfo();
+                    oldFocusedApplicationHandle->releaseInfo();
                 }
-                mFocusedApplicationHandle = inputApplicationHandle;
+                mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
             }
-        } else if (mFocusedApplicationHandle != NULL) {
+        } else if (oldFocusedApplicationHandle != nullptr) {
             resetANRTimeoutsLocked();
-            mFocusedApplicationHandle->releaseInfo();
-            mFocusedApplicationHandle.clear();
+            oldFocusedApplicationHandle->releaseInfo();
+            oldFocusedApplicationHandle.clear();
+            mFocusedApplicationHandlesByDisplay.erase(displayId);
         }
 
 #if DEBUG_FOCUS
@@ -3036,6 +3260,65 @@
     mLooper->wake();
 }
 
+/**
+ * Sets the focused display, which is responsible for receiving focus-dispatched input events where
+ * the display not specified.
+ *
+ * We track any unreleased events for each window. If a window loses the ability to receive the
+ * released event, we will send a cancel event to it. So when the focused display is changed, we
+ * cancel all the unreleased display-unspecified events for the focused window on the old focused
+ * display. The display-specified events won't be affected.
+ */
+void InputDispatcher::setFocusedDisplay(int32_t displayId) {
+#if DEBUG_FOCUS
+    ALOGD("setFocusedDisplay displayId=%" PRId32, displayId);
+#endif
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        if (mFocusedDisplayId != displayId) {
+            sp<InputWindowHandle> oldFocusedWindowHandle =
+                    getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
+            if (oldFocusedWindowHandle != nullptr) {
+                sp<InputChannel> inputChannel =
+                    getInputChannelLocked(oldFocusedWindowHandle->getToken());
+                if (inputChannel != nullptr) {
+                    CancelationOptions options(
+                            CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS,
+                            "The display which contains this window no longer has focus.");
+                    synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
+                }
+            }
+            mFocusedDisplayId = displayId;
+
+            // Sanity check
+            sp<InputWindowHandle> newFocusedWindowHandle =
+                    getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+            onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+
+            if (newFocusedWindowHandle == nullptr) {
+                ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
+                if (!mFocusedWindowHandlesByDisplay.empty()) {
+                    ALOGE("But another display has a focused window:");
+                    for (auto& it : mFocusedWindowHandlesByDisplay) {
+                        const int32_t displayId = it.first;
+                        const sp<InputWindowHandle>& windowHandle = it.second;
+                        ALOGE("Display #%" PRId32 " has focused window: '%s'\n",
+                                displayId, windowHandle->getName().c_str());
+                    }
+                }
+            }
+        }
+
+#if DEBUG_FOCUS
+        logDispatchStateLocked();
+#endif
+    } // release lock
+
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mLooper->wake();
+}
+
 void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
 #if DEBUG_FOCUS
     ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
@@ -3062,7 +3345,7 @@
         }
 
 #if DEBUG_FOCUS
-        //logDispatchStateLocked();
+        logDispatchStateLocked();
 #endif
     } // release lock
 
@@ -3092,29 +3375,27 @@
     mLooper->wake();
 }
 
-bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
-        const sp<InputChannel>& toChannel) {
+bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+    if (fromToken == toToken) {
 #if DEBUG_FOCUS
-    ALOGD("transferTouchFocus: fromChannel=%s, toChannel=%s",
-            fromChannel->getName().c_str(), toChannel->getName().c_str());
+        ALOGD("Trivial transfer to same window.");
 #endif
+        return true;
+    }
+
     { // acquire lock
         AutoMutex _l(mLock);
 
-        sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromChannel);
-        sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toChannel);
-        if (fromWindowHandle == NULL || toWindowHandle == NULL) {
-#if DEBUG_FOCUS
-            ALOGD("Cannot transfer focus because from or to window not found.");
-#endif
+        sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
+        sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toToken);
+        if (fromWindowHandle == nullptr || toWindowHandle == nullptr) {
+            ALOGW("Cannot transfer focus because from or to window not found.");
             return false;
         }
-        if (fromWindowHandle == toWindowHandle) {
 #if DEBUG_FOCUS
-            ALOGD("Trivial transfer to same window.");
+        ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
+            fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
 #endif
-            return true;
-        }
         if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
 #if DEBUG_FOCUS
             ALOGD("Cannot transfer focus because windows are on different displays.");
@@ -3152,6 +3433,9 @@
             return false;
         }
 
+
+        sp<InputChannel> fromChannel = getInputChannelLocked(fromToken);
+        sp<InputChannel> toChannel = getInputChannelLocked(toToken);
         ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel);
         ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel);
         if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) {
@@ -3207,17 +3491,35 @@
 void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
     dump += StringPrintf(INDENT "DispatchEnabled: %d\n", mDispatchEnabled);
     dump += StringPrintf(INDENT "DispatchFrozen: %d\n", mDispatchFrozen);
+    dump += StringPrintf(INDENT "FocusedDisplayId: %" PRId32 "\n", mFocusedDisplayId);
 
-    if (mFocusedApplicationHandle != NULL) {
-        dump += StringPrintf(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
-                mFocusedApplicationHandle->getName().c_str(),
-                mFocusedApplicationHandle->getDispatchingTimeout(
-                        DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0);
+    if (!mFocusedApplicationHandlesByDisplay.empty()) {
+        dump += StringPrintf(INDENT "FocusedApplications:\n");
+        for (auto& it : mFocusedApplicationHandlesByDisplay) {
+            const int32_t displayId = it.first;
+            const sp<InputApplicationHandle>& applicationHandle = it.second;
+            dump += StringPrintf(
+                    INDENT2 "displayId=%" PRId32 ", name='%s', dispatchingTimeout=%0.3fms\n",
+                    displayId,
+                    applicationHandle->getName().c_str(),
+                    applicationHandle->getDispatchingTimeout(
+                            DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0);
+        }
     } else {
-        dump += StringPrintf(INDENT "FocusedApplication: <null>\n");
+        dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
     }
-    dump += StringPrintf(INDENT "FocusedWindow: name='%s'\n",
-            mFocusedWindowHandle != NULL ? mFocusedWindowHandle->getName().c_str() : "<null>");
+
+    if (!mFocusedWindowHandlesByDisplay.empty()) {
+        dump += StringPrintf(INDENT "FocusedWindows:\n");
+        for (auto& it : mFocusedWindowHandlesByDisplay) {
+            const int32_t displayId = it.first;
+            const sp<InputWindowHandle>& windowHandle = it.second;
+            dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n",
+                    displayId, windowHandle->getName().c_str());
+        }
+    } else {
+        dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
+    }
 
     if (!mTouchStatesByDisplay.isEmpty()) {
         dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
@@ -3238,49 +3540,71 @@
             } else {
                 dump += INDENT3 "Windows: <none>\n";
             }
+            if (!state.portalWindows.isEmpty()) {
+                dump += INDENT3 "Portal windows:\n";
+                for (size_t i = 0; i < state.portalWindows.size(); i++) {
+                    const sp<InputWindowHandle> portalWindowHandle = state.portalWindows.itemAt(i);
+                    dump += StringPrintf(INDENT4 "%zu: name='%s'\n",
+                            i, portalWindowHandle->getName().c_str());
+                }
+            }
         }
     } else {
         dump += INDENT "TouchStates: <no displays touched>\n";
     }
 
-    if (!mWindowHandles.isEmpty()) {
-        dump += INDENT "Windows:\n";
-        for (size_t i = 0; i < mWindowHandles.size(); i++) {
-            const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
-            const InputWindowInfo* windowInfo = windowHandle->getInfo();
+    if (!mWindowHandlesByDisplay.empty()) {
+       for (auto& it : mWindowHandlesByDisplay) {
+            const Vector<sp<InputWindowHandle>> windowHandles = it.second;
+            dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first);
+            if (!windowHandles.isEmpty()) {
+                dump += INDENT2 "Windows:\n";
+                for (size_t i = 0; i < windowHandles.size(); i++) {
+                    const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i);
+                    const InputWindowInfo* windowInfo = windowHandle->getInfo();
 
-            dump += StringPrintf(INDENT2 "%zu: name='%s', displayId=%d, "
-                    "paused=%s, hasFocus=%s, hasWallpaper=%s, "
-                    "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
-                    "frame=[%d,%d][%d,%d], scale=%f, "
-                    "touchableRegion=",
-                    i, windowInfo->name.c_str(), windowInfo->displayId,
-                    toString(windowInfo->paused),
-                    toString(windowInfo->hasFocus),
-                    toString(windowInfo->hasWallpaper),
-                    toString(windowInfo->visible),
-                    toString(windowInfo->canReceiveKeys),
-                    windowInfo->layoutParamsFlags, windowInfo->layoutParamsType,
-                    windowInfo->layer,
-                    windowInfo->frameLeft, windowInfo->frameTop,
-                    windowInfo->frameRight, windowInfo->frameBottom,
-                    windowInfo->scaleFactor);
-            dumpRegion(dump, windowInfo->touchableRegion);
-            dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
-            dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
-                    windowInfo->ownerPid, windowInfo->ownerUid,
-                    windowInfo->dispatchingTimeout / 1000000.0);
+                    dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
+                            "portalToDisplayId=%d, paused=%s, hasFocus=%s, hasWallpaper=%s, "
+                            "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
+                            "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), "
+                            "touchableRegion=",
+                            i, windowInfo->name.c_str(), windowInfo->displayId,
+                            windowInfo->portalToDisplayId,
+                            toString(windowInfo->paused),
+                            toString(windowInfo->hasFocus),
+                            toString(windowInfo->hasWallpaper),
+                            toString(windowInfo->visible),
+                            toString(windowInfo->canReceiveKeys),
+                            windowInfo->layoutParamsFlags, windowInfo->layoutParamsType,
+                            windowInfo->layer,
+                            windowInfo->frameLeft, windowInfo->frameTop,
+                            windowInfo->frameRight, windowInfo->frameBottom,
+                            windowInfo->globalScaleFactor,
+                            windowInfo->windowXScale, windowInfo->windowYScale);
+                    dumpRegion(dump, windowInfo->touchableRegion);
+                    dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
+                    dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+                            windowInfo->ownerPid, windowInfo->ownerUid,
+                            windowInfo->dispatchingTimeout / 1000000.0);
+                }
+            } else {
+                dump += INDENT2 "Windows: <none>\n";
+            }
         }
     } else {
-        dump += INDENT "Windows: <none>\n";
+        dump += INDENT "Displays: <none>\n";
     }
 
-    if (!mMonitoringChannels.isEmpty()) {
-        dump += INDENT "MonitoringChannels:\n";
-        for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-            const sp<InputChannel>& channel = mMonitoringChannels[i];
-            dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str());
-        }
+    if (!mMonitoringChannelsByDisplay.empty()) {
+       for (auto& it : mMonitoringChannelsByDisplay) {
+            const Vector<sp<InputChannel>>& monitoringChannels = it.second;
+            dump += StringPrintf(INDENT "MonitoringChannels in display %" PRId32 ":\n", it.first);
+            const size_t numChannels = monitoringChannels.size();
+            for (size_t i = 0; i < numChannels; i++) {
+                const sp<InputChannel>& channel = monitoringChannels[i];
+                dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str());
+            }
+       }
     } else {
         dump += INDENT "MonitoringChannels: <none>\n";
     }
@@ -3397,29 +3721,39 @@
             mConfig.keyRepeatTimeout * 0.000001f);
 }
 
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
-        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, int32_t displayId) {
 #if DEBUG_REGISTRATION
-    ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().c_str(),
-            toString(monitor));
+    ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32,
+            inputChannel->getName().c_str(), displayId);
 #endif
 
     { // acquire lock
         AutoMutex _l(mLock);
 
+        // If InputWindowHandle is null and displayId is not ADISPLAY_ID_NONE,
+        // treat inputChannel as monitor channel for displayId.
+        bool monitor = inputChannel->getToken() == nullptr && displayId != ADISPLAY_ID_NONE;
+        if (monitor) {
+            inputChannel->setToken(new BBinder());
+        }
+
         if (getConnectionIndexLocked(inputChannel) >= 0) {
             ALOGW("Attempted to register already registered input channel '%s'",
                     inputChannel->getName().c_str());
             return BAD_VALUE;
         }
 
-        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
+        sp<Connection> connection = new Connection(inputChannel, monitor);
 
         int fd = inputChannel->getFd();
         mConnectionsByFd.add(fd, connection);
+        mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
 
+        // Store monitor channel by displayId.
         if (monitor) {
-            mMonitoringChannels.push(inputChannel);
+            Vector<sp<InputChannel>>& monitoringChannels =
+                    mMonitoringChannelsByDisplay[displayId];
+            monitoringChannels.push(inputChannel);
         }
 
         mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
@@ -3462,6 +3796,8 @@
     sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
     mConnectionsByFd.removeItemsAt(connectionIndex);
 
+    mInputChannelsByToken.erase(inputChannel->getToken());
+
     if (connection->monitor) {
         removeMonitorChannelLocked(inputChannel);
     }
@@ -3476,20 +3812,33 @@
 }
 
 void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
-    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
-         if (mMonitoringChannels[i] == inputChannel) {
-             mMonitoringChannels.removeAt(i);
-             break;
-         }
+    for (auto it = mMonitoringChannelsByDisplay.begin();
+            it != mMonitoringChannelsByDisplay.end(); ) {
+        Vector<sp<InputChannel>>& monitoringChannels = it->second;
+        const size_t numChannels = monitoringChannels.size();
+        for (size_t i = 0; i < numChannels; i++) {
+             if (monitoringChannels[i] == inputChannel) {
+                 monitoringChannels.removeAt(i);
+                 break;
+             }
+        }
+        if (monitoringChannels.empty()) {
+            it = mMonitoringChannelsByDisplay.erase(it);
+        } else {
+            ++it;
+        }
     }
 }
 
 ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
-    ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
-    if (connectionIndex >= 0) {
-        sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
-        if (connection->inputChannel.get() == inputChannel.get()) {
-            return connectionIndex;
+    if (inputChannel == nullptr) {
+        return -1;
+    }
+
+    for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
+        sp<Connection> connection = mConnectionsByFd.valueAt(i);
+        if (connection->inputChannel->getToken() == inputChannel->getToken()) {
+            return i;
         }
     }
 
@@ -3516,6 +3865,16 @@
     commandEntry->connection = connection;
 }
 
+void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
+        const sp<InputWindowHandle>& newFocus) {
+    sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr;
+    sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr;
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doNotifyFocusChangedLockedInterruptible);
+    commandEntry->oldToken = oldToken;
+    commandEntry->newToken = newToken;
+}
+
 void InputDispatcher::onANRLocked(
         nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
         const sp<InputWindowHandle>& windowHandle,
@@ -3528,7 +3887,7 @@
             dispatchLatency, waitDuration, reason);
 
     // Capture a record of the InputDispatcher state at the time of the ANR.
-    time_t t = time(NULL);
+    time_t t = time(nullptr);
     struct tm tm;
     localtime_r(&t, &tm);
     char timestr[64];
@@ -3546,7 +3905,8 @@
     CommandEntry* commandEntry = postCommandLocked(
             & InputDispatcher::doNotifyANRLockedInterruptible);
     commandEntry->inputApplicationHandle = applicationHandle;
-    commandEntry->inputWindowHandle = windowHandle;
+    commandEntry->inputChannel = windowHandle != nullptr ?
+            getInputChannelLocked(windowHandle->getToken()) : nullptr;
     commandEntry->reason = reason;
 }
 
@@ -3566,25 +3926,34 @@
     if (connection->status != Connection::STATUS_ZOMBIE) {
         mLock.unlock();
 
-        mPolicy->notifyInputChannelBroken(connection->inputWindowHandle);
+        mPolicy->notifyInputChannelBroken(connection->inputChannel->getToken());
 
         mLock.lock();
     }
 }
 
+void InputDispatcher::doNotifyFocusChangedLockedInterruptible(
+        CommandEntry* commandEntry) {
+    sp<IBinder> oldToken = commandEntry->oldToken;
+    sp<IBinder> newToken = commandEntry->newToken;
+    mLock.unlock();
+    mPolicy->notifyFocusChanged(oldToken, newToken);
+    mLock.lock();
+}
+
 void InputDispatcher::doNotifyANRLockedInterruptible(
         CommandEntry* commandEntry) {
     mLock.unlock();
 
     nsecs_t newTimeout = mPolicy->notifyANR(
-            commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,
+            commandEntry->inputApplicationHandle,
+            commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr,
             commandEntry->reason);
 
     mLock.lock();
 
     resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
-            commandEntry->inputWindowHandle != NULL
-                    ? commandEntry->inputWindowHandle->getInputChannel() : NULL);
+            commandEntry->inputChannel);
 }
 
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -3597,7 +3966,9 @@
     mLock.unlock();
 
     android::base::Timer t;
-    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
+    sp<IBinder> token = commandEntry->inputChannel != nullptr ?
+        commandEntry->inputChannel->getToken() : nullptr;
+    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token,
             &event, entry->policyFlags);
     if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
         ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
@@ -3671,171 +4042,181 @@
 
 bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
         DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) {
-    if (!(keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK)) {
-        // Get the fallback key state.
-        // Clear it out after dispatching the UP.
-        int32_t originalKeyCode = keyEntry->keyCode;
-        int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
-        if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
-            connection->inputState.removeFallbackKey(originalKeyCode);
+    if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) {
+        if (!handled) {
+            // Report the key as unhandled, since the fallback was not handled.
+            mReporter->reportUnhandledKey(keyEntry->sequenceNum);
         }
+        return false;
+    }
 
-        if (handled || !dispatchEntry->hasForegroundTarget()) {
-            // If the application handles the original key for which we previously
-            // generated a fallback or if the window is not a foreground window,
-            // then cancel the associated fallback key, if any.
-            if (fallbackKeyCode != -1) {
-                // Dispatch the unhandled key to the policy with the cancel flag.
+    // Get the fallback key state.
+    // Clear it out after dispatching the UP.
+    int32_t originalKeyCode = keyEntry->keyCode;
+    int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
+    if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+        connection->inputState.removeFallbackKey(originalKeyCode);
+    }
+
+    if (handled || !dispatchEntry->hasForegroundTarget()) {
+        // If the application handles the original key for which we previously
+        // generated a fallback or if the window is not a foreground window,
+        // then cancel the associated fallback key, if any.
+        if (fallbackKeyCode != -1) {
+            // Dispatch the unhandled key to the policy with the cancel flag.
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-                ALOGD("Unhandled key event: Asking policy to cancel fallback action.  "
-                        "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-                        keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
-                        keyEntry->policyFlags);
-#endif
-                KeyEvent event;
-                initializeKeyEvent(&event, keyEntry);
-                event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
-
-                mLock.unlock();
-
-                mPolicy->dispatchUnhandledKey(connection->inputWindowHandle,
-                        &event, keyEntry->policyFlags, &event);
-
-                mLock.lock();
-
-                // Cancel the fallback key.
-                if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
-                    CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
-                            "application handled the original non-fallback key "
-                            "or is no longer a foreground target, "
-                            "canceling previously dispatched fallback key");
-                    options.keyCode = fallbackKeyCode;
-                    synthesizeCancelationEventsForConnectionLocked(connection, options);
-                }
-                connection->inputState.removeFallbackKey(originalKeyCode);
-            }
-        } else {
-            // If the application did not handle a non-fallback key, first check
-            // that we are in a good state to perform unhandled key event processing
-            // Then ask the policy what to do with it.
-            bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN
-                    && keyEntry->repeatCount == 0;
-            if (fallbackKeyCode == -1 && !initialDown) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-                ALOGD("Unhandled key event: Skipping unhandled key event processing "
-                        "since this is not an initial down.  "
-                        "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
-                        originalKeyCode, keyEntry->action, keyEntry->repeatCount,
-                        keyEntry->policyFlags);
-#endif
-                return false;
-            }
-
-            // Dispatch the unhandled key to the policy.
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-            ALOGD("Unhandled key event: Asking policy to perform fallback action.  "
+            ALOGD("Unhandled key event: Asking policy to cancel fallback action.  "
                     "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
                     keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
                     keyEntry->policyFlags);
 #endif
             KeyEvent event;
             initializeKeyEvent(&event, keyEntry);
+            event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
 
             mLock.unlock();
 
-            bool fallback = mPolicy->dispatchUnhandledKey(connection->inputWindowHandle,
-                    &event, keyEntry->policyFlags, &event);
+            mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(),
+                                          &event, keyEntry->policyFlags, &event);
 
             mLock.lock();
 
-            if (connection->status != Connection::STATUS_NORMAL) {
-                connection->inputState.removeFallbackKey(originalKeyCode);
-                return false;
-            }
-
-            // Latch the fallback keycode for this key on an initial down.
-            // The fallback keycode cannot change at any other point in the lifecycle.
-            if (initialDown) {
-                if (fallback) {
-                    fallbackKeyCode = event.getKeyCode();
-                } else {
-                    fallbackKeyCode = AKEYCODE_UNKNOWN;
-                }
-                connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
-            }
-
-            ALOG_ASSERT(fallbackKeyCode != -1);
-
-            // Cancel the fallback key if the policy decides not to send it anymore.
-            // We will continue to dispatch the key to the policy but we will no
-            // longer dispatch a fallback key to the application.
-            if (fallbackKeyCode != AKEYCODE_UNKNOWN
-                    && (!fallback || fallbackKeyCode != event.getKeyCode())) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-                if (fallback) {
-                    ALOGD("Unhandled key event: Policy requested to send key %d"
-                            "as a fallback for %d, but on the DOWN it had requested "
-                            "to send %d instead.  Fallback canceled.",
-                            event.getKeyCode(), originalKeyCode, fallbackKeyCode);
-                } else {
-                    ALOGD("Unhandled key event: Policy did not request fallback for %d, "
-                            "but on the DOWN it had requested to send %d.  "
-                            "Fallback canceled.",
-                            originalKeyCode, fallbackKeyCode);
-                }
-#endif
-
+            // Cancel the fallback key.
+            if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
                 CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
-                        "canceling fallback, policy no longer desires it");
+                                           "application handled the original non-fallback key "
+                                           "or is no longer a foreground target, "
+                                           "canceling previously dispatched fallback key");
                 options.keyCode = fallbackKeyCode;
                 synthesizeCancelationEventsForConnectionLocked(connection, options);
-
-                fallback = false;
-                fallbackKeyCode = AKEYCODE_UNKNOWN;
-                if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
-                    connection->inputState.setFallbackKey(originalKeyCode,
-                            fallbackKeyCode);
-                }
             }
-
+            connection->inputState.removeFallbackKey(originalKeyCode);
+        }
+    } else {
+        // If the application did not handle a non-fallback key, first check
+        // that we are in a good state to perform unhandled key event processing
+        // Then ask the policy what to do with it.
+        bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN
+                && keyEntry->repeatCount == 0;
+        if (fallbackKeyCode == -1 && !initialDown) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-            {
-                std::string msg;
-                const KeyedVector<int32_t, int32_t>& fallbackKeys =
-                        connection->inputState.getFallbackKeys();
-                for (size_t i = 0; i < fallbackKeys.size(); i++) {
-                    msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i),
-                            fallbackKeys.valueAt(i));
-                }
-                ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
-                        fallbackKeys.size(), msg.c_str());
-            }
+            ALOGD("Unhandled key event: Skipping unhandled key event processing "
+                    "since this is not an initial down.  "
+                    "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+                    originalKeyCode, keyEntry->action, keyEntry->repeatCount,
+                    keyEntry->policyFlags);
 #endif
+            return false;
+        }
 
+        // Dispatch the unhandled key to the policy.
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+        ALOGD("Unhandled key event: Asking policy to perform fallback action.  "
+                "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+                keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
+                keyEntry->policyFlags);
+#endif
+        KeyEvent event;
+        initializeKeyEvent(&event, keyEntry);
+
+        mLock.unlock();
+
+        bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(),
+                                                      &event, keyEntry->policyFlags, &event);
+
+        mLock.lock();
+
+        if (connection->status != Connection::STATUS_NORMAL) {
+            connection->inputState.removeFallbackKey(originalKeyCode);
+            return false;
+        }
+
+        // Latch the fallback keycode for this key on an initial down.
+        // The fallback keycode cannot change at any other point in the lifecycle.
+        if (initialDown) {
             if (fallback) {
-                // Restart the dispatch cycle using the fallback key.
-                keyEntry->eventTime = event.getEventTime();
-                keyEntry->deviceId = event.getDeviceId();
-                keyEntry->source = event.getSource();
-                keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
-                keyEntry->keyCode = fallbackKeyCode;
-                keyEntry->scanCode = event.getScanCode();
-                keyEntry->metaState = event.getMetaState();
-                keyEntry->repeatCount = event.getRepeatCount();
-                keyEntry->downTime = event.getDownTime();
-                keyEntry->syntheticRepeat = false;
+                fallbackKeyCode = event.getKeyCode();
+            } else {
+                fallbackKeyCode = AKEYCODE_UNKNOWN;
+            }
+            connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
+        }
+
+        ALOG_ASSERT(fallbackKeyCode != -1);
+
+        // Cancel the fallback key if the policy decides not to send it anymore.
+        // We will continue to dispatch the key to the policy but we will no
+        // longer dispatch a fallback key to the application.
+        if (fallbackKeyCode != AKEYCODE_UNKNOWN
+                && (!fallback || fallbackKeyCode != event.getKeyCode())) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+            if (fallback) {
+                ALOGD("Unhandled key event: Policy requested to send key %d"
+                        "as a fallback for %d, but on the DOWN it had requested "
+                        "to send %d instead.  Fallback canceled.",
+                        event.getKeyCode(), originalKeyCode, fallbackKeyCode);
+            } else {
+                ALOGD("Unhandled key event: Policy did not request fallback for %d, "
+                        "but on the DOWN it had requested to send %d.  "
+                        "Fallback canceled.",
+                        originalKeyCode, fallbackKeyCode);
+            }
+#endif
+
+            CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+                                       "canceling fallback, policy no longer desires it");
+            options.keyCode = fallbackKeyCode;
+            synthesizeCancelationEventsForConnectionLocked(connection, options);
+
+            fallback = false;
+            fallbackKeyCode = AKEYCODE_UNKNOWN;
+            if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
+                connection->inputState.setFallbackKey(originalKeyCode,
+                                                      fallbackKeyCode);
+            }
+        }
 
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-                ALOGD("Unhandled key event: Dispatching fallback key.  "
-                        "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
-                        originalKeyCode, fallbackKeyCode, keyEntry->metaState);
-#endif
-                return true; // restart the event
-            } else {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-                ALOGD("Unhandled key event: No fallback key.");
-#endif
+        {
+            std::string msg;
+            const KeyedVector<int32_t, int32_t>& fallbackKeys =
+                    connection->inputState.getFallbackKeys();
+            for (size_t i = 0; i < fallbackKeys.size(); i++) {
+                msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i),
+                        fallbackKeys.valueAt(i));
             }
+            ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
+                    fallbackKeys.size(), msg.c_str());
+        }
+#endif
+
+        if (fallback) {
+            // Restart the dispatch cycle using the fallback key.
+            keyEntry->eventTime = event.getEventTime();
+            keyEntry->deviceId = event.getDeviceId();
+            keyEntry->source = event.getSource();
+            keyEntry->displayId = event.getDisplayId();
+            keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
+            keyEntry->keyCode = fallbackKeyCode;
+            keyEntry->scanCode = event.getScanCode();
+            keyEntry->metaState = event.getMetaState();
+            keyEntry->repeatCount = event.getRepeatCount();
+            keyEntry->downTime = event.getDownTime();
+            keyEntry->syntheticRepeat = false;
+
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+            ALOGD("Unhandled key event: Dispatching fallback key.  "
+                    "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
+                    originalKeyCode, fallbackKeyCode, keyEntry->metaState);
+#endif
+            return true; // restart the event
+        } else {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+            ALOGD("Unhandled key event: No fallback key.");
+#endif
+
+            // Report the key as unhandled, since there is no fallback key.
+            mReporter->reportUnhandledKey(keyEntry->sequenceNum);
         }
     }
     return false;
@@ -3855,7 +4236,7 @@
 }
 
 void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) {
-    event->initialize(entry->deviceId, entry->source, entry->action, entry->flags,
+    event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags,
             entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
             entry->downTime, entry->eventTime);
 }
@@ -3932,9 +4313,10 @@
 
 // --- InputDispatcher::EventEntry ---
 
-InputDispatcher::EventEntry::EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags) :
-        refCount(1), type(type), eventTime(eventTime), policyFlags(policyFlags),
-        injectionState(NULL), dispatchInProgress(false) {
+InputDispatcher::EventEntry::EventEntry(uint32_t sequenceNum, int32_t type,
+        nsecs_t eventTime, uint32_t policyFlags) :
+        sequenceNum(sequenceNum), refCount(1), type(type), eventTime(eventTime),
+        policyFlags(policyFlags), injectionState(nullptr), dispatchInProgress(false) {
 }
 
 InputDispatcher::EventEntry::~EventEntry() {
@@ -3953,15 +4335,16 @@
 void InputDispatcher::EventEntry::releaseInjectionState() {
     if (injectionState) {
         injectionState->release();
-        injectionState = NULL;
+        injectionState = nullptr;
     }
 }
 
 
 // --- InputDispatcher::ConfigurationChangedEntry ---
 
-InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry(nsecs_t eventTime) :
-        EventEntry(TYPE_CONFIGURATION_CHANGED, eventTime, 0) {
+InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry(
+        uint32_t sequenceNum, nsecs_t eventTime) :
+        EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {
 }
 
 InputDispatcher::ConfigurationChangedEntry::~ConfigurationChangedEntry() {
@@ -3974,8 +4357,9 @@
 
 // --- InputDispatcher::DeviceResetEntry ---
 
-InputDispatcher::DeviceResetEntry::DeviceResetEntry(nsecs_t eventTime, int32_t deviceId) :
-        EventEntry(TYPE_DEVICE_RESET, eventTime, 0),
+InputDispatcher::DeviceResetEntry::DeviceResetEntry(
+        uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) :
+        EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0),
         deviceId(deviceId) {
 }
 
@@ -3990,12 +4374,12 @@
 
 // --- InputDispatcher::KeyEntry ---
 
-InputDispatcher::KeyEntry::KeyEntry(nsecs_t eventTime,
-        int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
+InputDispatcher::KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime,
+        int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
         int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
         int32_t repeatCount, nsecs_t downTime) :
-        EventEntry(TYPE_KEY, eventTime, policyFlags),
-        deviceId(deviceId), source(source), action(action), flags(flags),
+        EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags),
+        deviceId(deviceId), source(source), displayId(displayId), action(action), flags(flags),
         keyCode(keyCode), scanCode(scanCode), metaState(metaState),
         repeatCount(repeatCount), downTime(downTime),
         syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
@@ -4006,10 +4390,10 @@
 }
 
 void InputDispatcher::KeyEntry::appendDescription(std::string& msg) const {
-    msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, action=%s, "
+    msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
             "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
             "repeatCount=%d), policyFlags=0x%08x",
-            deviceId, source, keyActionToString(action).c_str(), flags, keyCode,
+            deviceId, source, displayId, keyActionToString(action).c_str(), flags, keyCode,
             scanCode, metaState, repeatCount, policyFlags);
 }
 
@@ -4025,19 +4409,21 @@
 
 // --- InputDispatcher::MotionEntry ---
 
-InputDispatcher::MotionEntry::MotionEntry(nsecs_t eventTime, int32_t deviceId,
-        uint32_t source, uint32_t policyFlags, int32_t action, int32_t actionButton,
-        int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
-        float xPrecision, float yPrecision, nsecs_t downTime,
-        int32_t displayId, uint32_t pointerCount,
+InputDispatcher::MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId,
+        uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+        int32_t actionButton,
+        int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
+        int32_t edgeFlags, float xPrecision, float yPrecision, nsecs_t downTime,
+        uint32_t pointerCount,
         const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
         float xOffset, float yOffset) :
-        EventEntry(TYPE_MOTION, eventTime, policyFlags),
+        EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags),
         eventTime(eventTime),
-        deviceId(deviceId), source(source), action(action), actionButton(actionButton),
-        flags(flags), metaState(metaState), buttonState(buttonState),
-        edgeFlags(edgeFlags), xPrecision(xPrecision), yPrecision(yPrecision),
-        downTime(downTime), displayId(displayId), pointerCount(pointerCount) {
+        deviceId(deviceId), source(source), displayId(displayId), action(action),
+        actionButton(actionButton), flags(flags), metaState(metaState), buttonState(buttonState),
+        classification(classification), edgeFlags(edgeFlags),
+        xPrecision(xPrecision), yPrecision(yPrecision),
+        downTime(downTime), pointerCount(pointerCount) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
         this->pointerCoords[i].copyFrom(pointerCoords[i]);
@@ -4051,11 +4437,13 @@
 }
 
 void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const {
-    msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, action=%s, actionButton=0x%08x, "
-            "flags=0x%08x, metaState=0x%08x, buttonState=0x%08x, "
-            "edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, displayId=%d, pointers=[",
-            deviceId, source, motionActionToString(action).c_str(), actionButton, flags, metaState,
-            buttonState, edgeFlags, xPrecision, yPrecision, displayId);
+    msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32
+            ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, buttonState=0x%08x, "
+            "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, pointers=[",
+            deviceId, source, displayId, motionActionToString(action).c_str(), actionButton, flags,
+            metaState, buttonState, motionClassificationToString(classification), edgeFlags,
+            xPrecision, yPrecision);
+
     for (uint32_t i = 0; i < pointerCount; i++) {
         if (i) {
             msg += ", ";
@@ -4072,10 +4460,12 @@
 volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic;
 
 InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry,
-        int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) :
+        int32_t targetFlags, float xOffset, float yOffset, float globalScaleFactor,
+        float windowXScale, float windowYScale) :
         seq(nextSeq()),
         eventEntry(eventEntry), targetFlags(targetFlags),
-        xOffset(xOffset), yOffset(yOffset), scaleFactor(scaleFactor),
+        xOffset(xOffset), yOffset(yOffset), globalScaleFactor(globalScaleFactor),
+        windowXScale(windowXScale), windowYScale(windowYScale),
         deliveryTime(0), resolvedAction(0), resolvedFlags(0) {
     eventEntry->refCount += 1;
 }
@@ -4184,8 +4574,8 @@
         }
 #if DEBUG_OUTBOUND_EVENT_DETAILS
         ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
-                "actionMasked=%d",
-                entry->deviceId, entry->source, actionMasked);
+                "displayId=%" PRId32 ", actionMasked=%d",
+                entry->deviceId, entry->source, entry->displayId, actionMasked);
 #endif
         return false;
     }
@@ -4237,8 +4627,8 @@
         }
 #if DEBUG_OUTBOUND_EVENT_DETAILS
         ALOGD("Dropping inconsistent motion pointer up/down or move event: "
-                "deviceId=%d, source=%08x, actionMasked=%d",
-                entry->deviceId, entry->source, actionMasked);
+                "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
+                entry->deviceId, entry->source, entry->displayId, actionMasked);
 #endif
         return false;
     }
@@ -4250,8 +4640,9 @@
             return true;
         }
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-        ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x",
-                entry->deviceId, entry->source);
+        ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, "
+                "displayId=%" PRId32,
+                entry->deviceId, entry->source, entry->displayId);
 #endif
         return false;
     }
@@ -4276,6 +4667,7 @@
         const KeyMemento& memento = mKeyMementos.itemAt(i);
         if (memento.deviceId == entry->deviceId
                 && memento.source == entry->source
+                && memento.displayId == entry->displayId
                 && memento.keyCode == entry->keyCode
                 && memento.scanCode == entry->scanCode) {
             return i;
@@ -4303,6 +4695,7 @@
     KeyMemento& memento = mKeyMementos.editTop();
     memento.deviceId = entry->deviceId;
     memento.source = entry->source;
+    memento.displayId = entry->displayId;
     memento.keyCode = entry->keyCode;
     memento.scanCode = entry->scanCode;
     memento.metaState = entry->metaState;
@@ -4317,11 +4710,11 @@
     MotionMemento& memento = mMotionMementos.editTop();
     memento.deviceId = entry->deviceId;
     memento.source = entry->source;
+    memento.displayId = entry->displayId;
     memento.flags = flags;
     memento.xPrecision = entry->xPrecision;
     memento.yPrecision = entry->yPrecision;
     memento.downTime = entry->downTime;
-    memento.displayId = entry->displayId;
     memento.setPointers(entry);
     memento.hovering = hovering;
     memento.policyFlags = entry->policyFlags;
@@ -4340,8 +4733,8 @@
     for (size_t i = 0; i < mKeyMementos.size(); i++) {
         const KeyMemento& memento = mKeyMementos.itemAt(i);
         if (shouldCancelKey(memento, options)) {
-            outEvents.push(new KeyEntry(currentTime,
-                    memento.deviceId, memento.source, memento.policyFlags,
+            outEvents.push(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
+                    memento.deviceId, memento.source, memento.displayId, memento.policyFlags,
                     AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED,
                     memento.keyCode, memento.scanCode, memento.metaState, 0, memento.downTime));
         }
@@ -4350,16 +4743,15 @@
     for (size_t i = 0; i < mMotionMementos.size(); i++) {
         const MotionMemento& memento = mMotionMementos.itemAt(i);
         if (shouldCancelMotion(memento, options)) {
-            outEvents.push(new MotionEntry(currentTime,
-                    memento.deviceId, memento.source, memento.policyFlags,
-                    memento.hovering
-                            ? AMOTION_EVENT_ACTION_HOVER_EXIT
-                            : AMOTION_EVENT_ACTION_CANCEL,
-                    memento.flags, 0, 0, 0, 0,
+            const int32_t action = memento.hovering ?
+                    AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL;
+            outEvents.push(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
+                    memento.deviceId, memento.source, memento.displayId, memento.policyFlags,
+                    action, 0 /*actionButton*/, memento.flags, AMETA_NONE, 0 /*buttonState*/,
+                    MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
                     memento.xPrecision, memento.yPrecision, memento.downTime,
-                    memento.displayId,
                     memento.pointerCount, memento.pointerProperties, memento.pointerCoords,
-                    0, 0));
+                    0 /*xOffset*/, 0 /*yOffset*/));
         }
     }
 }
@@ -4424,6 +4816,8 @@
         return true;
     case CancelationOptions::CANCEL_FALLBACK_EVENTS:
         return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
+    case CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS:
+        return memento.displayId == ADISPLAY_ID_NONE;
     default:
         return false;
     }
@@ -4442,6 +4836,8 @@
         return memento.source & AINPUT_SOURCE_CLASS_POINTER;
     case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
         return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
+    case CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS:
+        return memento.displayId == ADISPLAY_ID_NONE;
     default:
         return false;
     }
@@ -4450,9 +4846,8 @@
 
 // --- InputDispatcher::Connection ---
 
-InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
-        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) :
-        status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
+InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor) :
+        status(STATUS_NORMAL), inputChannel(inputChannel),
         monitor(monitor),
         inputPublisher(inputChannel), inputPublisherBlocked(false) {
 }
@@ -4461,8 +4856,8 @@
 }
 
 const std::string InputDispatcher::Connection::getWindowName() const {
-    if (inputWindowHandle != NULL) {
-        return inputWindowHandle->getName();
+    if (inputChannel != nullptr) {
+        return inputChannel->getName();
     }
     if (monitor) {
         return "monitor";
@@ -4487,19 +4882,19 @@
 }
 
 InputDispatcher::DispatchEntry* InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) {
-    for (DispatchEntry* entry = waitQueue.head; entry != NULL; entry = entry->next) {
+    for (DispatchEntry* entry = waitQueue.head; entry != nullptr; entry = entry->next) {
         if (entry->seq == seq) {
             return entry;
         }
     }
-    return NULL;
+    return nullptr;
 }
 
 
 // --- InputDispatcher::CommandEntry ---
 
 InputDispatcher::CommandEntry::CommandEntry(Command command) :
-    command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0),
+    command(command), eventTime(0), keyEntry(nullptr), userActivityEventType(0),
     seq(0), handled(false) {
 }
 
@@ -4510,7 +4905,7 @@
 // --- InputDispatcher::TouchState ---
 
 InputDispatcher::TouchState::TouchState() :
-    down(false), split(false), deviceId(-1), source(0), displayId(-1) {
+    down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {
 }
 
 InputDispatcher::TouchState::~TouchState() {
@@ -4521,8 +4916,9 @@
     split = false;
     deviceId = -1;
     source = 0;
-    displayId = -1;
+    displayId = ADISPLAY_ID_NONE;
     windows.clear();
+    portalWindows.clear();
 }
 
 void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
@@ -4532,6 +4928,7 @@
     source = other.source;
     displayId = other.displayId;
     windows = other.windows;
+    portalWindows = other.portalWindows;
 }
 
 void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
@@ -4560,6 +4957,17 @@
     touchedWindow.pointerIds = pointerIds;
 }
 
+void InputDispatcher::TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) {
+    size_t numWindows = portalWindows.size();
+    for (size_t i = 0; i < numWindows; i++) {
+        sp<InputWindowHandle> portalWindowHandle = portalWindows.itemAt(i);
+        if (portalWindowHandle == windowHandle) {
+            return;
+        }
+    }
+    portalWindows.push_back(windowHandle);
+}
+
 void InputDispatcher::TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
     for (size_t i = 0; i < windows.size(); i++) {
         if (windows.itemAt(i).windowHandle == windowHandle) {
@@ -4569,6 +4977,15 @@
     }
 }
 
+void InputDispatcher::TouchState::removeWindowByToken(const sp<IBinder>& token) {
+    for (size_t i = 0; i < windows.size(); i++) {
+        if (windows.itemAt(i).windowHandle->getToken() == token) {
+            windows.removeAt(i);
+            return;
+        }
+    }
+}
+
 void InputDispatcher::TouchState::filterNonAsIsTouchWindows() {
     for (size_t i = 0 ; i < windows.size(); ) {
         TouchedWindow& window = windows.editItemAt(i);
@@ -4590,7 +5007,7 @@
             return window.windowHandle;
         }
     }
-    return NULL;
+    return nullptr;
 }
 
 bool InputDispatcher::TouchState::isSlippery() const {
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 8da8450..2d8df5c 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -18,7 +18,9 @@
 #define _UI_INPUT_DISPATCHER_H
 
 #include <input/Input.h>
+#include <input/InputApplication.h>
 #include <input/InputTransport.h>
+#include <input/InputWindow.h>
 #include <utils/KeyedVector.h>
 #include <utils/Vector.h>
 #include <utils/threads.h>
@@ -27,14 +29,15 @@
 #include <utils/Looper.h>
 #include <utils/BitSet.h>
 #include <cutils/atomic.h>
+#include <unordered_map>
 
 #include <stddef.h>
 #include <unistd.h>
 #include <limits.h>
+#include <unordered_map>
 
-#include "InputWindow.h"
-#include "InputApplication.h"
 #include "InputListener.h"
+#include "InputReporterInterface.h"
 
 
 namespace android {
@@ -158,7 +161,9 @@
 
     // Scaling factor to apply to MotionEvent as it is delivered.
     // (ignored for KeyEvents)
-    float scaleFactor;
+    float globalScaleFactor;
+    float windowXScale = 1.0f;
+    float windowYScale = 1.0f;
 
     // The subset of pointer ids to include in motion events dispatched to this input target
     // if FLAG_SPLIT is set.
@@ -207,11 +212,12 @@
     /* Notifies the system that an application is not responding.
      * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
     virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
-            const sp<InputWindowHandle>& inputWindowHandle,
+            const sp<IBinder>& token,
             const std::string& reason) = 0;
 
     /* Notifies the system that an input channel is unrecoverably broken. */
-    virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle) = 0;
+    virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
+    virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0;
 
     /* Gets the input dispatcher configuration. */
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
@@ -242,12 +248,12 @@
     virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) = 0;
 
     /* Allows the policy a chance to intercept a key before dispatching. */
-    virtual nsecs_t interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
+    virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
             const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
 
     /* Allows the policy a chance to perform default processing for an unhandled key.
      * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */
-    virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
+    virtual bool dispatchUnhandledKey(const sp<IBinder>& token,
             const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0;
 
     /* Notifies the policy about switch events.
@@ -299,7 +305,7 @@
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual int32_t injectInputEvent(const InputEvent* event, int32_t displayId,
+    virtual int32_t injectInputEvent(const InputEvent* event,
             int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
             uint32_t policyFlags) = 0;
 
@@ -307,14 +313,21 @@
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) = 0;
+    virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles,
+            int32_t displayId) = 0;
 
-    /* Sets the focused application.
+    /* Sets the focused application on the given display.
      *
      * This method may be called on any thread (usually by the input manager).
      */
     virtual void setFocusedApplication(
-            const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+            int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+
+    /* Sets the focused display.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void setFocusedDisplay(int32_t displayId) = 0;
 
     /* Sets the input dispatching mode.
      *
@@ -330,21 +343,25 @@
      */
     virtual void setInputFilterEnabled(bool enabled) = 0;
 
-    /* Transfers touch focus from the window associated with one channel to the
-     * window associated with the other channel.
+    /* Transfers touch focus from one window to another window.
      *
      * Returns true on success.  False if the window did not actually have touch focus.
      */
-    virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
-            const sp<InputChannel>& toChannel) = 0;
+    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
 
-    /* Registers or unregister input channels that may be used as targets for input events.
-     * If monitor is true, the channel will receive a copy of all input events.
+    /* Registers input channels that may be used as targets for input events.
+     * If inputWindowHandle is null, and displayId is not ADISPLAY_ID_NONE,
+     * the channel will receive a copy of all input events form the specific displayId.
      *
-     * These methods may be called on any thread (usually by the input manager).
+     * This method may be called on any thread (usually by the input manager).
      */
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
-            const sp<InputWindowHandle>& inputWindowHandle, bool monitor) = 0;
+    virtual status_t registerInputChannel(
+            const sp<InputChannel>& inputChannel, int32_t displayId) = 0;
+
+    /* Unregister input channels that will no longer receive input events.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
 };
 
@@ -383,20 +400,22 @@
     virtual void notifySwitch(const NotifySwitchArgs* args);
     virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
 
-    virtual int32_t injectInputEvent(const InputEvent* event, int32_t displayId,
+    virtual int32_t injectInputEvent(const InputEvent* event,
             int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
             uint32_t policyFlags);
 
-    virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles);
-    virtual void setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle);
+    virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles,
+            int32_t displayId);
+    virtual void setFocusedApplication(int32_t displayId,
+            const sp<InputApplicationHandle>& inputApplicationHandle);
+    virtual void setFocusedDisplay(int32_t displayId);
     virtual void setInputDispatchMode(bool enabled, bool frozen);
     virtual void setInputFilterEnabled(bool enabled);
 
-    virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
-            const sp<InputChannel>& toChannel);
+    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
 
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
-            const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
+            int32_t displayId);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
 private:
@@ -406,7 +425,7 @@
         T* prev;
 
     protected:
-        inline Link() : next(NULL), prev(NULL) { }
+        inline Link() : next(nullptr), prev(nullptr) { }
     };
 
     struct InjectionState {
@@ -433,6 +452,7 @@
             TYPE_MOTION
         };
 
+        uint32_t sequenceNum;
         mutable int32_t refCount;
         int32_t type;
         nsecs_t eventTime;
@@ -441,20 +461,20 @@
 
         bool dispatchInProgress; // initially false, set to true while dispatching
 
-        inline bool isInjected() const { return injectionState != NULL; }
+        inline bool isInjected() const { return injectionState != nullptr; }
 
         void release();
 
         virtual void appendDescription(std::string& msg) const = 0;
 
     protected:
-        EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags);
+        EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags);
         virtual ~EventEntry();
         void releaseInjectionState();
     };
 
     struct ConfigurationChangedEntry : EventEntry {
-        explicit ConfigurationChangedEntry(nsecs_t eventTime);
+        explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime);
         virtual void appendDescription(std::string& msg) const;
 
     protected:
@@ -464,7 +484,7 @@
     struct DeviceResetEntry : EventEntry {
         int32_t deviceId;
 
-        DeviceResetEntry(nsecs_t eventTime, int32_t deviceId);
+        DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId);
         virtual void appendDescription(std::string& msg) const;
 
     protected:
@@ -474,6 +494,7 @@
     struct KeyEntry : EventEntry {
         int32_t deviceId;
         uint32_t source;
+        int32_t displayId;
         int32_t action;
         int32_t flags;
         int32_t keyCode;
@@ -493,9 +514,9 @@
         InterceptKeyResult interceptKeyResult; // set based on the interception result
         nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
 
-        KeyEntry(nsecs_t eventTime,
-                int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
-                int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+        KeyEntry(uint32_t sequenceNum, nsecs_t eventTime,
+                int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+                int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
                 int32_t repeatCount, nsecs_t downTime);
         virtual void appendDescription(std::string& msg) const;
         void recycle();
@@ -508,26 +529,27 @@
         nsecs_t eventTime;
         int32_t deviceId;
         uint32_t source;
+        int32_t displayId;
         int32_t action;
         int32_t actionButton;
         int32_t flags;
         int32_t metaState;
         int32_t buttonState;
+        MotionClassification classification;
         int32_t edgeFlags;
         float xPrecision;
         float yPrecision;
         nsecs_t downTime;
-        int32_t displayId;
         uint32_t pointerCount;
         PointerProperties pointerProperties[MAX_POINTERS];
         PointerCoords pointerCoords[MAX_POINTERS];
 
-        MotionEntry(nsecs_t eventTime,
-                int32_t deviceId, uint32_t source, uint32_t policyFlags,
+        MotionEntry(uint32_t sequenceNum, nsecs_t eventTime,
+                int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
                 int32_t action, int32_t actionButton, int32_t flags,
-                int32_t metaState, int32_t buttonState, int32_t edgeFlags,
-                float xPrecision, float yPrecision, nsecs_t downTime,
-                int32_t displayId, uint32_t pointerCount,
+                int32_t metaState, int32_t buttonState, MotionClassification classification,
+                int32_t edgeFlags, float xPrecision, float yPrecision,
+                nsecs_t downTime, uint32_t pointerCount,
                 const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
                 float xOffset, float yOffset);
         virtual void appendDescription(std::string& msg) const;
@@ -544,7 +566,9 @@
         int32_t targetFlags;
         float xOffset;
         float yOffset;
-        float scaleFactor;
+        float globalScaleFactor;
+        float windowXScale = 1.0f;
+        float windowYScale = 1.0f;
         nsecs_t deliveryTime; // time when the event was actually delivered
 
         // Set to the resolved action and flags when the event is enqueued.
@@ -552,7 +576,8 @@
         int32_t resolvedFlags;
 
         DispatchEntry(EventEntry* eventEntry,
-                int32_t targetFlags, float xOffset, float yOffset, float scaleFactor);
+                int32_t targetFlags, float xOffset, float yOffset,
+                float globalScaleFactor, float windowXScale, float windowYScale);
         ~DispatchEntry();
 
         inline bool hasForegroundTarget() const {
@@ -600,11 +625,13 @@
         nsecs_t eventTime;
         KeyEntry* keyEntry;
         sp<InputApplicationHandle> inputApplicationHandle;
-        sp<InputWindowHandle> inputWindowHandle;
         std::string reason;
         int32_t userActivityEventType;
         uint32_t seq;
         bool handled;
+        sp<InputChannel> inputChannel;
+        sp<IBinder> oldToken;
+        sp<IBinder> newToken;
     };
 
     // Generic queue implementation.
@@ -614,7 +641,7 @@
         T* tail;
         uint32_t entryCount;
 
-        inline Queue() : head(NULL), tail(NULL), entryCount(0) {
+        inline Queue() : head(nullptr), tail(nullptr), entryCount(0) {
         }
 
         inline bool isEmpty() const {
@@ -629,7 +656,7 @@
             } else {
                 head = entry;
             }
-            entry->next = NULL;
+            entry->next = nullptr;
             tail = entry;
         }
 
@@ -641,7 +668,7 @@
             } else {
                 tail = entry;
             }
-            entry->prev = NULL;
+            entry->prev = nullptr;
             head = entry;
         }
 
@@ -664,9 +691,9 @@
             T* entry = head;
             head = entry->next;
             if (head) {
-                head->prev = NULL;
+                head->prev = nullptr;
             } else {
-                tail = NULL;
+                tail = nullptr;
             }
             return entry;
         }
@@ -683,6 +710,10 @@
             CANCEL_POINTER_EVENTS = 1,
             CANCEL_NON_POINTER_EVENTS = 2,
             CANCEL_FALLBACK_EVENTS = 3,
+
+            /* Cancel events where the display not specified. These events would go to the focused
+             * display. */
+            CANCEL_DISPLAY_UNSPECIFIED_EVENTS = 4,
         };
 
         // The criterion to use to determine which events should be canceled.
@@ -754,6 +785,7 @@
         struct KeyMemento {
             int32_t deviceId;
             uint32_t source;
+            int32_t displayId;
             int32_t keyCode;
             int32_t scanCode;
             int32_t metaState;
@@ -765,11 +797,11 @@
         struct MotionMemento {
             int32_t deviceId;
             uint32_t source;
+            int32_t displayId;
             int32_t flags;
             float xPrecision;
             float yPrecision;
             nsecs_t downTime;
-            int32_t displayId;
             uint32_t pointerCount;
             PointerProperties pointerProperties[MAX_POINTERS];
             PointerCoords pointerCoords[MAX_POINTERS];
@@ -812,7 +844,6 @@
 
         Status status;
         sp<InputChannel> inputChannel; // never null
-        sp<InputWindowHandle> inputWindowHandle; // may be null
         bool monitor;
         InputPublisher inputPublisher;
         InputState inputState;
@@ -828,8 +859,7 @@
         // yet received a "finished" response from the application.
         Queue<DispatchEntry> waitQueue;
 
-        explicit Connection(const sp<InputChannel>& inputChannel,
-                const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
+        explicit Connection(const sp<InputChannel>& inputChannel, bool monitor);
 
         inline const std::string getInputChannelName() const { return inputChannel->getName(); }
 
@@ -891,15 +921,23 @@
     // to transfer focus to a new application.
     EventEntry* mNextUnblockedEvent;
 
-    sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y);
+    sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+            bool addOutsideTargets = false, bool addPortalWindows = false);
 
     // All registered connections mapped by channel file descriptor.
     KeyedVector<int, sp<Connection> > mConnectionsByFd;
 
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& b) const {
+            return std::hash<IBinder *>{}(b.get());
+        }
+    };
+    std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken;
+
     ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
 
-    // Input channels that will receive a copy of all input events.
-    Vector<sp<InputChannel> > mMonitoringChannels;
+    // Input channels that will receive a copy of all input events sent to the provided display.
+    std::unordered_map<int32_t, Vector<sp<InputChannel>>> mMonitoringChannelsByDisplay;
 
     // Event injection and synchronization.
     Condition mInjectionResultAvailableCondition;
@@ -932,6 +970,9 @@
     };
     // Maps the key code replaced, device id tuple to the key code it was replaced with
     KeyedVector<KeyReplacement, int32_t> mReplacedKeys;
+    // Process certain Meta + Key combinations
+    void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action,
+            int32_t& keyCode, int32_t& metaState);
 
     // Deferred command processing.
     bool haveCommandsLocked() const;
@@ -952,13 +993,15 @@
     bool mDispatchFrozen;
     bool mInputFilterEnabled;
 
-    Vector<sp<InputWindowHandle> > mWindowHandles;
-
-    sp<InputWindowHandle> getWindowHandleLocked(const sp<InputChannel>& inputChannel) const;
+    std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay;
+    // Get window handles by display, return an empty vector if not found.
+    Vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const;
+    sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const;
+    sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const;
     bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const;
 
     // Focus tracking for keys, trackball, etc.
-    sp<InputWindowHandle> mFocusedWindowHandle;
+    std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay;
 
     // Focus tracking for touch.
     struct TouchedWindow {
@@ -974,13 +1017,20 @@
         int32_t displayId; // id to the display that currently has a touch, others are rejected
         Vector<TouchedWindow> windows;
 
+        // This collects the portal windows that the touch has gone through. Each portal window
+        // targets a display (embedded display for most cases). With this info, we can add the
+        // monitoring channels of the displays touched.
+        Vector<sp<InputWindowHandle>> portalWindows;
+
         TouchState();
         ~TouchState();
         void reset();
         void copyFrom(const TouchState& other);
         void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
                 int32_t targetFlags, BitSet32 pointerIds);
+        void addPortalWindow(const sp<InputWindowHandle>& windowHandle);
         void removeWindow(const sp<InputWindowHandle>& windowHandle);
+        void removeWindowByToken(const sp<IBinder>& token);
         void filterNonAsIsTouchWindows();
         sp<InputWindowHandle> getFirstForegroundWindowHandle() const;
         bool isSlippery() const;
@@ -989,8 +1039,11 @@
     KeyedVector<int32_t, TouchState> mTouchStatesByDisplay;
     TouchState mTempTouchState;
 
-    // Focused application.
-    sp<InputApplicationHandle> mFocusedApplicationHandle;
+    // Focused applications.
+    std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay;
+
+    // Top focused display.
+    int32_t mFocusedDisplayId;
 
     // Dispatcher state at time of last ANR.
     std::string mLastANRState;
@@ -1023,7 +1076,7 @@
     nsecs_t mInputTargetWaitStartTime;
     nsecs_t mInputTargetWaitTimeoutTime;
     bool mInputTargetWaitTimeoutExpired;
-    sp<InputApplicationHandle> mInputTargetWaitApplicationHandle;
+    sp<IBinder> mInputTargetWaitApplicationToken;
 
     // Contains the last window which received a hover event.
     sp<InputWindowHandle> mLastHoverWindowHandle;
@@ -1033,11 +1086,15 @@
             const sp<InputApplicationHandle>& applicationHandle,
             const sp<InputWindowHandle>& windowHandle,
             nsecs_t* nextWakeupTime, const char* reason);
+
+    void removeWindowByTokenLocked(const sp<IBinder>& token);
+
     void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
             const sp<InputChannel>& inputChannel);
     nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
     void resetANRTimeoutsLocked();
 
+    int32_t getTargetDisplayId(const EventEntry* entry);
     int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
             Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime);
     int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
@@ -1046,7 +1103,8 @@
 
     void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
             int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
-    void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets);
+    void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId,
+            float xOffset = 0, float yOffset = 0);
 
     void pokeUserActivityLocked(const EventEntry* eventEntry);
     bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
@@ -1111,6 +1169,8 @@
             nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled);
     void onDispatchCycleBrokenLocked(
             nsecs_t currentTime, const sp<Connection>& connection);
+    void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
+            const sp<InputWindowHandle>& newFocus);
     void onANRLocked(
             nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
             const sp<InputWindowHandle>& windowHandle,
@@ -1119,6 +1179,7 @@
     // Outbound policy interactions.
     void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
     void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
+    void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry);
     void doNotifyANRLockedInterruptible(CommandEntry* commandEntry);
     void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
     void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry);
@@ -1135,6 +1196,8 @@
     void traceInboundQueueLengthLocked();
     void traceOutboundQueueLengthLocked(const sp<Connection>& connection);
     void traceWaitQueueLengthLocked(const sp<Connection>& connection);
+
+    sp<InputReporterInterface> mReporter;
 };
 
 /* Enqueues and dispatches input events, endlessly. */
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 520fea4..e537e09 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -26,13 +26,18 @@
 
 // --- NotifyConfigurationChangedArgs ---
 
-NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(nsecs_t eventTime) :
-        eventTime(eventTime) {
+NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
+        uint32_t sequenceNum, nsecs_t eventTime) :
+        NotifyArgs(sequenceNum), eventTime(eventTime) {
 }
 
 NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
         const NotifyConfigurationChangedArgs& other) :
-        eventTime(other.eventTime) {
+        NotifyArgs(other.sequenceNum), eventTime(other.eventTime) {
+}
+
+bool NotifyConfigurationChangedArgs::operator==(const NotifyConfigurationChangedArgs& rhs) const {
+    return sequenceNum == rhs.sequenceNum && eventTime == rhs.eventTime;
 }
 
 void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
@@ -42,23 +47,39 @@
 
 // --- NotifyKeyArgs ---
 
-NotifyKeyArgs::NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-        uint32_t policyFlags,
+NotifyKeyArgs::NotifyKeyArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId,
+        uint32_t source, int32_t displayId, uint32_t policyFlags,
         int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
         int32_t metaState, nsecs_t downTime) :
-        eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
+        NotifyArgs(sequenceNum), eventTime(eventTime), deviceId(deviceId), source(source),
+        displayId(displayId), policyFlags(policyFlags),
         action(action), flags(flags), keyCode(keyCode), scanCode(scanCode),
         metaState(metaState), downTime(downTime) {
 }
 
 NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other) :
-        eventTime(other.eventTime), deviceId(other.deviceId), source(other.source),
-        policyFlags(other.policyFlags),
+        NotifyArgs(other.sequenceNum), eventTime(other.eventTime), deviceId(other.deviceId),
+        source(other.source), displayId(other.displayId), policyFlags(other.policyFlags),
         action(other.action), flags(other.flags),
         keyCode(other.keyCode), scanCode(other.scanCode),
         metaState(other.metaState), downTime(other.downTime) {
 }
 
+bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const {
+    return sequenceNum == rhs.sequenceNum
+            && eventTime == rhs.eventTime
+            && deviceId == rhs.deviceId
+            && source == rhs.source
+            && displayId == rhs.displayId
+            && policyFlags == rhs.policyFlags
+            && action == rhs.action
+            && flags == rhs.flags
+            && keyCode == rhs.keyCode
+            && scanCode == rhs.scanCode
+            && metaState == rhs.metaState
+            && downTime == rhs.downTime;
+}
+
 void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
     listener->notifyKey(this);
 }
@@ -66,19 +87,22 @@
 
 // --- NotifyMotionArgs ---
 
-NotifyMotionArgs::NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-        uint32_t policyFlags,
+NotifyMotionArgs::NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId,
+        uint32_t source, int32_t displayId, uint32_t policyFlags,
         int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
-        int32_t buttonState, int32_t edgeFlags, int32_t displayId, uint32_t deviceTimestamp,
-        uint32_t pointerCount,
+        int32_t buttonState, MotionClassification classification,
+        int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount,
         const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-        float xPrecision, float yPrecision, nsecs_t downTime) :
-        eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
+        float xPrecision, float yPrecision, nsecs_t downTime,
+        const std::vector<TouchVideoFrame>& videoFrames) :
+        NotifyArgs(sequenceNum), eventTime(eventTime), deviceId(deviceId), source(source),
+        displayId(displayId), policyFlags(policyFlags),
         action(action), actionButton(actionButton),
         flags(flags), metaState(metaState), buttonState(buttonState),
-        edgeFlags(edgeFlags), displayId(displayId), deviceTimestamp(deviceTimestamp),
+        classification(classification), edgeFlags(edgeFlags), deviceTimestamp(deviceTimestamp),
         pointerCount(pointerCount),
-        xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime) {
+        xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime),
+        videoFrames(videoFrames) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
         this->pointerCoords[i].copyFrom(pointerCoords[i]);
@@ -86,19 +110,57 @@
 }
 
 NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) :
-        eventTime(other.eventTime), deviceId(other.deviceId), source(other.source),
-        policyFlags(other.policyFlags),
+        NotifyArgs(other.sequenceNum), eventTime(other.eventTime), deviceId(other.deviceId),
+        source(other.source), displayId(other.displayId), policyFlags(other.policyFlags),
         action(other.action), actionButton(other.actionButton), flags(other.flags),
         metaState(other.metaState), buttonState(other.buttonState),
-        edgeFlags(other.edgeFlags), displayId(other.displayId),
+        classification(other.classification), edgeFlags(other.edgeFlags),
         deviceTimestamp(other.deviceTimestamp), pointerCount(other.pointerCount),
-        xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime) {
+        xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime),
+        videoFrames(other.videoFrames) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         pointerProperties[i].copyFrom(other.pointerProperties[i]);
         pointerCoords[i].copyFrom(other.pointerCoords[i]);
     }
 }
 
+bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
+    bool equal =
+            sequenceNum == rhs.sequenceNum
+            && eventTime == rhs.eventTime
+            && deviceId == rhs.deviceId
+            && source == rhs.source
+            && displayId == rhs.displayId
+            && policyFlags == rhs.policyFlags
+            && action == rhs.action
+            && actionButton == rhs.actionButton
+            && flags == rhs.flags
+            && metaState == rhs.metaState
+            && buttonState == rhs.buttonState
+            && classification == rhs.classification
+            && edgeFlags == rhs.edgeFlags
+            && deviceTimestamp == rhs.deviceTimestamp
+            && pointerCount == rhs.pointerCount
+            // PointerProperties and PointerCoords are compared separately below
+            && xPrecision == rhs.xPrecision
+            && yPrecision == rhs.yPrecision
+            && downTime == rhs.downTime
+            && videoFrames == rhs.videoFrames;
+    if (!equal) {
+        return false;
+    }
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        equal =
+                pointerProperties[i] == rhs.pointerProperties[i]
+                && pointerCoords[i] == rhs.pointerCoords[i];
+        if (!equal) {
+            return false;
+        }
+    }
+    return true;
+}
+
 void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
     listener->notifyMotion(this);
 }
@@ -106,17 +168,25 @@
 
 // --- NotifySwitchArgs ---
 
-NotifySwitchArgs::NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
+NotifySwitchArgs::NotifySwitchArgs(uint32_t sequenceNum, nsecs_t eventTime, uint32_t policyFlags,
         uint32_t switchValues, uint32_t switchMask) :
-        eventTime(eventTime), policyFlags(policyFlags),
+        NotifyArgs(sequenceNum), eventTime(eventTime), policyFlags(policyFlags),
         switchValues(switchValues), switchMask(switchMask) {
 }
 
 NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) :
-        eventTime(other.eventTime), policyFlags(other.policyFlags),
+        NotifyArgs(other.sequenceNum), eventTime(other.eventTime), policyFlags(other.policyFlags),
         switchValues(other.switchValues), switchMask(other.switchMask) {
 }
 
+bool NotifySwitchArgs::operator==(const NotifySwitchArgs rhs) const {
+    return sequenceNum == rhs.sequenceNum
+            && eventTime == rhs.eventTime
+            && policyFlags == rhs.policyFlags
+            && switchValues == rhs.switchValues
+            && switchMask == rhs.switchMask;
+}
+
 void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
     listener->notifySwitch(this);
 }
@@ -124,12 +194,19 @@
 
 // --- NotifyDeviceResetArgs ---
 
-NotifyDeviceResetArgs::NotifyDeviceResetArgs(nsecs_t eventTime, int32_t deviceId) :
-        eventTime(eventTime), deviceId(deviceId) {
+NotifyDeviceResetArgs::NotifyDeviceResetArgs(
+        uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) :
+        NotifyArgs(sequenceNum), eventTime(eventTime), deviceId(deviceId) {
 }
 
 NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) :
-        eventTime(other.eventTime), deviceId(other.deviceId) {
+        NotifyArgs(other.sequenceNum), eventTime(other.eventTime), deviceId(other.deviceId) {
+}
+
+bool NotifyDeviceResetArgs::operator==(const NotifyDeviceResetArgs& rhs) const {
+    return sequenceNum == rhs.sequenceNum
+            && eventTime == rhs.eventTime
+            && deviceId == rhs.deviceId;
 }
 
 void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) const {
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 519faa6..b3b9e3e 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -19,25 +19,23 @@
 //#define LOG_NDEBUG 0
 
 #include "InputManager.h"
+#include "InputReaderFactory.h"
+
+#include <binder/IPCThreadState.h>
 
 #include <log/log.h>
+#include <unordered_map>
+
+#include <private/android_filesystem_config.h>
 
 namespace android {
 
 InputManager::InputManager(
-        const sp<EventHubInterface>& eventHub,
         const sp<InputReaderPolicyInterface>& readerPolicy,
         const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
     mDispatcher = new InputDispatcher(dispatcherPolicy);
-    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
-    initialize();
-}
-
-InputManager::InputManager(
-        const sp<InputReaderInterface>& reader,
-        const sp<InputDispatcherInterface>& dispatcher) :
-        mReader(reader),
-        mDispatcher(dispatcher) {
+    mClassifier = new InputClassifier(mDispatcher);
+    mReader = createInputReader(readerPolicy, mClassifier);
     initialize();
 }
 
@@ -90,4 +88,48 @@
     return mDispatcher;
 }
 
+class BinderWindowHandle : public InputWindowHandle {
+public:
+    BinderWindowHandle(const InputWindowInfo& info) {
+        mInfo = info;
+    }
+
+    bool updateInfo() override {
+        return true;
+    }
+};
+
+void InputManager::setInputWindows(const Vector<InputWindowInfo>& infos) {
+    std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>> handlesPerDisplay;
+
+    Vector<sp<InputWindowHandle>> handles;
+    for (const auto& info : infos) {
+        handlesPerDisplay.emplace(info.displayId, Vector<sp<InputWindowHandle>>());
+        handlesPerDisplay[info.displayId].add(new BinderWindowHandle(info));
+    }
+    for (auto const& i : handlesPerDisplay) {
+        mDispatcher->setInputWindows(i.second, i.first);
+    }
+}
+
+void InputManager::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+    mDispatcher->transferTouchFocus(fromToken, toToken);
+}
+
+// Used by tests only.
+void InputManager::registerInputChannel(const sp<InputChannel>& channel) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int uid = ipc->getCallingUid();
+    if (uid != AID_SHELL && uid != AID_ROOT) {
+        ALOGE("Invalid attempt to register input channel over IPC"
+                "from non shell/root entity (PID: %d)", ipc->getCallingPid());
+        return;
+    }
+    mDispatcher->registerInputChannel(channel, false);
+}
+
+void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
+    mDispatcher->unregisterInputChannel(channel);
+}
+
 } // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index a213b2d..142ec0c 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -22,18 +22,22 @@
  */
 
 #include "EventHub.h"
-#include "InputReader.h"
+#include "InputReaderBase.h"
+#include "InputClassifier.h"
 #include "InputDispatcher.h"
+#include "InputReader.h"
 
 #include <input/Input.h>
 #include <input/InputTransport.h>
+
+#include <input/IInputFlinger.h>
 #include <utils/Errors.h>
 #include <utils/Vector.h>
 #include <utils/Timers.h>
 #include <utils/RefBase.h>
-#include <utils/String8.h>
 
 namespace android {
+class InputChannel;
 
 /*
  * The input manager is the core of the system event processing.
@@ -73,31 +77,33 @@
     virtual sp<InputDispatcherInterface> getDispatcher() = 0;
 };
 
-class InputManager : public InputManagerInterface {
+class InputManager : public InputManagerInterface, public BnInputFlinger {
 protected:
     virtual ~InputManager();
 
 public:
     InputManager(
-            const sp<EventHubInterface>& eventHub,
             const sp<InputReaderPolicyInterface>& readerPolicy,
             const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
 
-    // (used for testing purposes)
-    InputManager(
-            const sp<InputReaderInterface>& reader,
-            const sp<InputDispatcherInterface>& dispatcher);
-
     virtual status_t start();
     virtual status_t stop();
 
     virtual sp<InputReaderInterface> getReader();
     virtual sp<InputDispatcherInterface> getDispatcher();
 
+    virtual void setInputWindows(const Vector<InputWindowInfo>& handles);
+    virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
+
+    virtual void registerInputChannel(const sp<InputChannel>& channel);
+    virtual void unregisterInputChannel(const sp<InputChannel>& channel);
+
 private:
     sp<InputReaderInterface> mReader;
     sp<InputReaderThread> mReaderThread;
 
+    sp<InputClassifierInterface> mClassifier;
+
     sp<InputDispatcherInterface> mDispatcher;
     sp<InputDispatcherThread> mDispatcherThread;
 
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index efa6f88..f84aa34 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -57,6 +57,7 @@
 #include <android-base/stringprintf.h>
 #include <input/Keyboard.h>
 #include <input/VirtualKeyMap.h>
+#include <statslog.h>
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -71,18 +72,21 @@
 // --- Constants ---
 
 // Maximum number of slots supported when using the slot-based Multitouch Protocol B.
-static const size_t MAX_SLOTS = 32;
+static constexpr size_t MAX_SLOTS = 32;
 
 // Maximum amount of latency to add to touch events while waiting for data from an
 // external stylus.
-static const nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
+static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
 
 // Maximum amount of time to wait on touch data before pushing out new pressure data.
-static const nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
+static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
 
 // Artificial latency on synthetic events created from stylus data without corresponding touch
 // data.
-static const nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+
+// How often to report input event statistics
+static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60);
 
 // --- Static Functions ---
 
@@ -226,7 +230,7 @@
 }
 
 static void synthesizeButtonKey(InputReaderContext* context, int32_t action,
-        nsecs_t when, int32_t deviceId, uint32_t source,
+        nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId,
         uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState,
         int32_t buttonState, int32_t keyCode) {
     if (
@@ -236,106 +240,31 @@
             || (action == AKEY_EVENT_ACTION_UP
                     && (lastButtonState & buttonState)
                     && !(currentButtonState & buttonState))) {
-        NotifyKeyArgs args(when, deviceId, source, policyFlags,
-                action, 0, keyCode, 0, context->getGlobalMetaState(), when);
+        NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId,
+                policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
         context->getListener()->notifyKey(&args);
     }
 }
 
 static void synthesizeButtonKeys(InputReaderContext* context, int32_t action,
-        nsecs_t when, int32_t deviceId, uint32_t source,
+        nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId,
         uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState) {
-    synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,
+    synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
             lastButtonState, currentButtonState,
             AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
-    synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,
+    synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
             lastButtonState, currentButtonState,
             AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
 }
 
 
-// --- InputReaderConfiguration ---
-
-bool InputReaderConfiguration::getDisplayViewport(ViewportType viewportType,
-        const String8* uniqueDisplayId, DisplayViewport* outViewport) const {
-    const DisplayViewport* viewport = NULL;
-    if (viewportType == ViewportType::VIEWPORT_VIRTUAL && uniqueDisplayId != NULL) {
-        for (const DisplayViewport& currentViewport : mVirtualDisplays) {
-            if (currentViewport.uniqueId == *uniqueDisplayId) {
-                viewport = &currentViewport;
-                break;
-            }
-        }
-    } else if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
-        viewport = &mExternalDisplay;
-    } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
-        viewport = &mInternalDisplay;
-    }
-
-    if (viewport != NULL && viewport->displayId >= 0) {
-        *outViewport = *viewport;
-        return true;
-    }
-    return false;
-}
-
-void InputReaderConfiguration::setPhysicalDisplayViewport(ViewportType viewportType,
-        const DisplayViewport& viewport) {
-    if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
-        mExternalDisplay = viewport;
-    } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
-        mInternalDisplay = viewport;
-    }
-}
-
-void InputReaderConfiguration::setVirtualDisplayViewports(
-        const Vector<DisplayViewport>& viewports) {
-    mVirtualDisplays = viewports;
-}
-
-void InputReaderConfiguration::dump(std::string& dump) const {
-    dump += INDENT4 "ViewportInternal:\n";
-    dumpViewport(dump, mInternalDisplay);
-    dump += INDENT4 "ViewportExternal:\n";
-    dumpViewport(dump, mExternalDisplay);
-    dump += INDENT4 "ViewportVirtual:\n";
-    for (const DisplayViewport& viewport : mVirtualDisplays) {
-        dumpViewport(dump, viewport);
-    }
-}
-
-void InputReaderConfiguration::dumpViewport(std::string& dump, const DisplayViewport& viewport) const {
-    dump += StringPrintf(INDENT5 "Viewport: displayId=%d, orientation=%d, uniqueId='%s', "
-            "logicalFrame=[%d, %d, %d, %d], "
-            "physicalFrame=[%d, %d, %d, %d], "
-            "deviceSize=[%d, %d]\n",
-            viewport.displayId, viewport.orientation, viewport.uniqueId.c_str(),
-            viewport.logicalLeft, viewport.logicalTop,
-            viewport.logicalRight, viewport.logicalBottom,
-            viewport.physicalLeft, viewport.physicalTop,
-            viewport.physicalRight, viewport.physicalBottom,
-            viewport.deviceWidth, viewport.deviceHeight);
-}
-
-
-// -- TouchAffineTransformation --
-void TouchAffineTransformation::applyTo(float& x, float& y) const {
-    float newX, newY;
-    newX = x * x_scale + y * x_ymix + x_offset;
-    newY = x * y_xmix + y * y_scale + y_offset;
-
-    x = newX;
-    y = newY;
-}
-
-
 // --- InputReader ---
 
 InputReader::InputReader(const sp<EventHubInterface>& eventHub,
         const sp<InputReaderPolicyInterface>& policy,
         const sp<InputListenerInterface>& listener) :
         mContext(this), mEventHub(eventHub), mPolicy(policy),
-        mGlobalMetaState(0), mGeneration(1),
+        mNextSequenceNum(1), mGlobalMetaState(0), mGeneration(1),
         mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
         mConfigurationChangesToRefresh(0) {
     mQueuedListener = new QueuedInputListener(listener);
@@ -473,10 +402,10 @@
 
     if (device->isIgnored()) {
         ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
-                identifier.name.string());
+                identifier.name.c_str());
     } else {
         ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
-                identifier.name.string(), device->getSources());
+                identifier.name.c_str(), device->getSources());
     }
 
     mDevices.add(deviceId, device);
@@ -488,7 +417,7 @@
 }
 
 void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
-    InputDevice* device = NULL;
+    InputDevice* device = nullptr;
     ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
     if (deviceIndex < 0) {
         ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
@@ -501,10 +430,10 @@
 
     if (device->isIgnored()) {
         ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)",
-                device->getId(), device->getName().string());
+                device->getId(), device->getName().c_str());
     } else {
         ALOGI("Device removed: id=%d, name='%s', sources=0x%08x",
-                device->getId(), device->getName().string(), device->getSources());
+                device->getId(), device->getName().c_str(), device->getSources());
     }
 
     if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
@@ -621,7 +550,7 @@
     updateGlobalMetaStateLocked();
 
     // Enqueue configuration changed.
-    NotifyConfigurationChangedArgs args(when);
+    NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when);
     mQueuedListener->notifyConfigurationChanged(&args);
 }
 
@@ -687,7 +616,7 @@
     if (now < mDisableVirtualKeysTimeout) {
         ALOGI("Dropping virtual key from device %s because virtual keys are "
                 "temporarily disabled for the next %0.3fms.  keyCode=%d, scanCode=%d",
-                device->getName().string(),
+                device->getName().c_str(),
                 (mDisableVirtualKeysTimeout - now) * 0.000001,
                 keyCode, scanCode);
         return true;
@@ -876,6 +805,30 @@
     return false;
 }
 
+bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
+    AutoMutex _l(mLock);
+
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex < 0) {
+        ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
+        return false;
+    }
+
+    InputDevice* device = mDevices.valueAt(deviceIndex);
+    std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplay();
+    // No associated display. By default, can dispatch to all displays.
+    if (!associatedDisplayId) {
+        return true;
+    }
+
+    if (*associatedDisplayId == ADISPLAY_ID_NONE) {
+        ALOGW("Device has associated, but no associated display id.");
+        return true;
+    }
+
+    return *associatedDisplayId == displayId;
+}
+
 void InputReader::dump(std::string& dump) {
     AutoMutex _l(mLock);
 
@@ -894,7 +847,7 @@
         if (i != 0) {
             dump += ", ";
         }
-        dump += mConfig.excludedDeviceNames.itemAt(i).string();
+        dump += mConfig.excludedDeviceNames[i];
     }
     dump += "]\n";
     dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
@@ -1019,22 +972,10 @@
     return mReader->mEventHub.get();
 }
 
-
-// --- InputReaderThread ---
-
-InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
-        Thread(/*canCallJava*/ true), mReader(reader) {
+uint32_t InputReader::ContextImpl::getNextSequenceNum() {
+    return (mReader->mNextSequenceNum)++;
 }
 
-InputReaderThread::~InputReaderThread() {
-}
-
-bool InputReaderThread::threadLoop() {
-    mReader->loopOnce();
-    return true;
-}
-
-
 // --- InputDevice ---
 
 InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
@@ -1074,12 +1015,18 @@
 
 void InputDevice::dump(std::string& dump) {
     InputDeviceInfo deviceInfo;
-    getDeviceInfo(& deviceInfo);
+    getDeviceInfo(&deviceInfo);
 
     dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(),
-            deviceInfo.getDisplayName().string());
+            deviceInfo.getDisplayName().c_str());
     dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
     dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
+    dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
+    if (mAssociatedDisplayPort) {
+        dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
+    } else {
+        dump += "<none>\n";
+    }
     dump += StringPrintf(INDENT2 "HasMic:     %s\n", toString(mHasMic));
     dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
     dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
@@ -1135,7 +1082,7 @@
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
             if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
-                String8 alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
+                std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
                 if (mAlias != alias) {
                     mAlias = alias;
                     bumpGeneration();
@@ -1149,6 +1096,20 @@
             setEnabled(enabled, when);
         }
 
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+             // In most situations, no port will be specified.
+            mAssociatedDisplayPort = std::nullopt;
+            // Find the display port that corresponds to the current input port.
+            const std::string& inputPort = mIdentifier.location;
+            if (!inputPort.empty()) {
+                const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations;
+                const auto& displayPort = ports.find(inputPort);
+                if (displayPort != ports.end()) {
+                    mAssociatedDisplayPort = std::make_optional(displayPort->second);
+                }
+            }
+        }
+
         size_t numMappers = mMappers.size();
         for (size_t i = 0; i < numMappers; i++) {
             InputMapper* mapper = mMappers[i];
@@ -1196,7 +1157,7 @@
 #endif
             }
         } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
-            ALOGI("Detected input event buffer overrun for device %s.", getName().string());
+            ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
             mDropUntilNextSync = true;
             reset(rawEvent->when);
         } else {
@@ -1334,10 +1295,22 @@
 }
 
 void InputDevice::notifyReset(nsecs_t when) {
-    NotifyDeviceResetArgs args(when, mId);
+    NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId);
     mContext->getListener()->notifyDeviceReset(&args);
 }
 
+std::optional<int32_t> InputDevice::getAssociatedDisplay() {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplay();
+        if (associatedDisplayId) {
+            return associatedDisplayId;
+        }
+    }
+
+    return std::nullopt;
+}
 
 // --- CursorButtonAccumulator ---
 
@@ -1786,7 +1759,7 @@
 // --- MultiTouchMotionAccumulator ---
 
 MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() :
-        mCurrentSlot(-1), mSlots(NULL), mSlotCount(0), mUsingSlotsProtocol(false),
+        mCurrentSlot(-1), mSlots(nullptr), mSlotCount(0), mUsingSlotsProtocol(false),
         mHaveStylus(false), mDeviceTimestamp(0) {
 }
 
@@ -2107,7 +2080,8 @@
 void SwitchInputMapper::sync(nsecs_t when) {
     if (mUpdatedSwitchMask) {
         uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
-        NotifySwitchArgs args(when, 0, updatedSwitchValues, mUpdatedSwitchMask);
+        NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues,
+                mUpdatedSwitchMask);
         getListener()->notifySwitch(&args);
 
         mUpdatedSwitchMask = 0;
@@ -2240,8 +2214,7 @@
 
 KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
         uint32_t source, int32_t keyboardType) :
-        InputMapper(device), mSource(source),
-        mKeyboardType(keyboardType) {
+        InputMapper(device), mSource(source), mKeyboardType(keyboardType) {
 }
 
 KeyboardInputMapper::~KeyboardInputMapper() {
@@ -2251,6 +2224,20 @@
     return mSource;
 }
 
+int32_t KeyboardInputMapper::getOrientation() {
+    if (mViewport) {
+        return mViewport->orientation;
+    }
+    return DISPLAY_ORIENTATION_0;
+}
+
+int32_t KeyboardInputMapper::getDisplayId() {
+    if (mViewport) {
+        return mViewport->displayId;
+    }
+    return ADISPLAY_ID_NONE;
+}
+
 void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
     InputMapper::populateDeviceInfo(info);
 
@@ -2262,13 +2249,12 @@
     dump += INDENT2 "Keyboard Input Mapper:\n";
     dumpParameters(dump);
     dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType);
-    dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
+    dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation());
     dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
     dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState);
     dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
 }
 
-
 void KeyboardInputMapper::configure(nsecs_t when,
         const InputReaderConfiguration* config, uint32_t changes) {
     InputMapper::configure(when, config, changes);
@@ -2279,15 +2265,8 @@
     }
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-        if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
-            DisplayViewport v;
-            if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) {
-                mOrientation = v.orientation;
-            } else {
-                mOrientation = DISPLAY_ORIENTATION_0;
-            }
-        } else {
-            mOrientation = DISPLAY_ORIENTATION_0;
+        if (mParameters.orientationAware) {
+            mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
         }
     }
 }
@@ -2310,10 +2289,7 @@
     config.tryGetProperty(String8("keyboard.orientationAware"),
             mParameters.orientationAware);
 
-    mParameters.hasAssociatedDisplay = false;
     if (mParameters.orientationAware) {
-        mParameters.hasAssociatedDisplay = true;
-
         mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
         mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1");
         mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2");
@@ -2327,8 +2303,6 @@
 
 void KeyboardInputMapper::dumpParameters(std::string& dump) {
     dump += INDENT3 "Parameters:\n";
-    dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
-            toString(mParameters.hasAssociatedDisplay));
     dump += StringPrintf(INDENT4 "OrientationAware: %s\n",
             toString(mParameters.orientationAware));
     dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n",
@@ -2423,8 +2397,8 @@
 
     if (down) {
         // Rotate key codes according to orientation if needed.
-        if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
-            keyCode = rotateKeyCode(keyCode, mOrientation);
+        if (mParameters.orientationAware) {
+            keyCode = rotateKeyCode(keyCode, getOrientation());
         }
 
         // Add key down.
@@ -2461,7 +2435,7 @@
             // key was not actually down
             ALOGI("Dropping key up from device %s because the key was not down.  "
                     "keyCode=%d, scanCode=%d",
-                    getDeviceName().string(), keyCode, scanCode);
+                    getDeviceName().c_str(), keyCode, scanCode);
             return;
         }
     }
@@ -2489,8 +2463,8 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
-            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+            getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
             AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
     getListener()->notifyKey(&args);
 }
@@ -2699,15 +2673,18 @@
     }
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+        mOrientation = DISPLAY_ORIENTATION_0;
         if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
-            DisplayViewport v;
-            if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) {
-                mOrientation = v.orientation;
-            } else {
-                mOrientation = DISPLAY_ORIENTATION_0;
+            std::optional<DisplayViewport> internalViewport =
+                    config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+            if (internalViewport) {
+                mOrientation = internalViewport->orientation;
             }
-        } else {
-            mOrientation = DISPLAY_ORIENTATION_0;
+        }
+
+        // Update the PointerController if viewports changed.
+        if (mParameters.mode == Parameters::MODE_POINTER) {
+            getPolicy()->obtainPointerController(getDeviceId());
         }
         bumpGeneration();
     }
@@ -2826,8 +2803,8 @@
     float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
     bool scrolled = vscroll != 0 || hscroll != 0;
 
-    mWheelYVelocityControl.move(when, NULL, &vscroll);
-    mWheelXVelocityControl.move(when, &hscroll, NULL);
+    mWheelYVelocityControl.move(when, nullptr, &vscroll);
+    mWheelXVelocityControl.move(when, &hscroll, nullptr);
 
     mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
@@ -2854,7 +2831,7 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
-        displayId = ADISPLAY_ID_DEFAULT;
+        displayId = mPointerController->getDisplayId();
     } else {
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
@@ -2874,7 +2851,7 @@
 
     // Synthesize key down from buttons if needed.
     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
-            policyFlags, lastButtonState, currentButtonState);
+            displayId, policyFlags, lastButtonState, currentButtonState);
 
     // Send motion event.
     if (downChanged || moved || scrolled || buttonsChanged) {
@@ -2894,20 +2871,22 @@
             while (!released.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
                 buttonState &= ~actionButton;
-                NotifyMotionArgs releaseArgs(when, getDeviceId(), mSource, policyFlags,
+                NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+                        mSource, displayId, policyFlags,
                         AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
-                        metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                        displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                        mXPrecision, mYPrecision, downTime);
+                        metaState, buttonState,
+                        MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                        /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+                        mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
                 getListener()->notifyMotion(&releaseArgs);
             }
         }
 
-        NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                motionEventAction, 0, 0, metaState, currentButtonState,
-                AMOTION_EVENT_EDGE_FLAG_NONE,
-                displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                mXPrecision, mYPrecision, downTime);
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+                displayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
+                MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+                mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
 
         if (buttonsPressed) {
@@ -2915,11 +2894,12 @@
             while (!pressed.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
                 buttonState |= actionButton;
-                NotifyMotionArgs pressArgs(when, getDeviceId(), mSource, policyFlags,
-                        AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
-                        metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                        displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                        mXPrecision, mYPrecision, downTime);
+                NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+                        mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+                        actionButton, 0, metaState, buttonState,
+                        MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                        /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+                        mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
                 getListener()->notifyMotion(&pressArgs);
             }
         }
@@ -2929,11 +2909,12 @@
         // Send hover move after UP to tell the application that the mouse is hovering now.
         if (motionEventAction == AMOTION_EVENT_ACTION_UP
                 && (mSource == AINPUT_SOURCE_MOUSE)) {
-            NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, policyFlags,
-                    AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
-                    metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                    displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                    mXPrecision, mYPrecision, downTime);
+            NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+                    mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+                    metaState, currentButtonState,
+                    MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                    /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+                    mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&hoverArgs);
         }
 
@@ -2942,18 +2923,19 @@
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-            NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags,
+            NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+                    mSource, displayId, policyFlags,
                     AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState,
-                    AMOTION_EVENT_EDGE_FLAG_NONE,
-                    displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                    mXPrecision, mYPrecision, downTime);
+                    MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                    /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+                    mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&scrollArgs);
         }
     }
 
     // Synthesize key up from buttons if needed.
     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
-            policyFlags, lastButtonState, currentButtonState);
+            displayId, policyFlags, lastButtonState, currentButtonState);
 
     mCursorMotionAccumulator.finishSync();
     mCursorScrollAccumulator.finishSync();
@@ -2968,11 +2950,24 @@
 }
 
 void CursorInputMapper::fadePointer() {
-    if (mPointerController != NULL) {
+    if (mPointerController != nullptr) {
         mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
     }
 }
 
+std::optional<int32_t> CursorInputMapper::getAssociatedDisplay() {
+    if (mParameters.hasAssociatedDisplay) {
+        if (mParameters.mode == Parameters::MODE_POINTER) {
+            return std::make_optional(mPointerController->getDisplayId());
+        } else {
+            // If the device is orientationAware and not a mouse,
+            // it expects to dispatch events to any display
+            return std::make_optional(ADISPLAY_ID_NONE);
+        }
+    }
+    return std::nullopt;
+}
+
 // --- RotaryEncoderInputMapper ---
 
 RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) :
@@ -3019,9 +3014,10 @@
         mRotaryEncoderScrollAccumulator.configure(getDevice());
     }
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-        DisplayViewport v;
-        if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) {
-            mOrientation = v.orientation;
+        std::optional<DisplayViewport> internalViewport =
+                config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+        if (internalViewport) {
+            mOrientation = internalViewport->orientation;
         } else {
             mOrientation = DISPLAY_ORIENTATION_0;
         }
@@ -3072,11 +3068,12 @@
         int32_t metaState = mContext->getGlobalMetaState();
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
 
-        NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags,
-                AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, 0,
-                AMOTION_EVENT_EDGE_FLAG_NONE,
-                displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                0, 0, 0);
+        NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+                mSource, displayId, policyFlags,
+                AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, /* buttonState */ 0,
+                MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+                0, 0, 0, /* videoFrames */ {});
         getListener()->notifyMotion(&scrollArgs);
     }
 
@@ -3394,10 +3391,15 @@
         mParameters.hasAssociatedDisplay = true;
         if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
             mParameters.associatedDisplayIsExternal = getDevice()->isExternal();
+            String8 uniqueDisplayId;
             getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"),
-                    mParameters.uniqueDisplayId);
+                    uniqueDisplayId);
+            mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
         }
     }
+    if (getDevice()->getAssociatedDisplayPort()) {
+        mParameters.hasAssociatedDisplay = true;
+    }
 
     // Initial downs on external touch devices should wake the device.
     // Normally we don't do this for internal touch screens to prevent them from waking
@@ -3472,6 +3474,59 @@
     return mExternalStylusConnected;
 }
 
+/**
+ * Determine which DisplayViewport to use.
+ * 1. If display port is specified, return the matching viewport. If matching viewport not
+ * found, then return.
+ * 2. If a device has associated display, get the matching viewport by either unique id or by
+ * the display type (internal or external).
+ * 3. Otherwise, use a non-display viewport.
+ */
+std::optional<DisplayViewport> TouchInputMapper::findViewport() {
+    if (mParameters.hasAssociatedDisplay) {
+        const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+        if (displayPort) {
+            // Find the viewport that contains the same port
+            std::optional<DisplayViewport> v = mConfig.getDisplayViewportByPort(*displayPort);
+            if (!v) {
+                ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
+                        "but the corresponding viewport is not found.",
+                        getDeviceName().c_str(), *displayPort);
+            }
+            return v;
+        }
+
+        if (!mParameters.uniqueDisplayId.empty()) {
+            return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
+        }
+
+        ViewportType viewportTypeToUse;
+        if (mParameters.associatedDisplayIsExternal) {
+            viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+        } else {
+            viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+        }
+
+        std::optional<DisplayViewport> viewport =
+                mConfig.getDisplayViewportByType(viewportTypeToUse);
+        if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) {
+            ALOGW("Input device %s should be associated with external display, "
+                    "fallback to internal one for the external viewport is not found.",
+                        getDeviceName().c_str());
+            viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+        }
+
+        return viewport;
+    }
+
+    DisplayViewport newViewport;
+    // Raw width and height in the natural orientation.
+    int32_t rawWidth = mRawPointerAxes.getRawWidth();
+    int32_t rawHeight = mRawPointerAxes.getRawHeight();
+    newViewport.setNonDisplayViewport(rawWidth, rawHeight);
+    return std::make_optional(newViewport);
+}
+
 void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
     int32_t oldDeviceMode = mDeviceMode;
 
@@ -3505,47 +3560,30 @@
 
     // Ensure we have valid X and Y axes.
     if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
-        ALOGW(INDENT "Touch device '%s' did not report support for X or Y axis!  "
-                "The device will be inoperable.", getDeviceName().string());
+        ALOGW("Touch device '%s' did not report support for X or Y axis!  "
+                "The device will be inoperable.", getDeviceName().c_str());
+        mDeviceMode = DEVICE_MODE_DISABLED;
+        return;
+    }
+
+    // Get associated display dimensions.
+    std::optional<DisplayViewport> newViewport = findViewport();
+    if (!newViewport) {
+        ALOGI("Touch device '%s' could not query the properties of its associated "
+                "display.  The device will be inoperable until the display size "
+                "becomes available.",
+                getDeviceName().c_str());
         mDeviceMode = DEVICE_MODE_DISABLED;
         return;
     }
 
     // Raw width and height in the natural orientation.
-    int32_t rawWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
-    int32_t rawHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
+    int32_t rawWidth = mRawPointerAxes.getRawWidth();
+    int32_t rawHeight = mRawPointerAxes.getRawHeight();
 
-    // Get associated display dimensions.
-    DisplayViewport newViewport;
-    if (mParameters.hasAssociatedDisplay) {
-        const String8* uniqueDisplayId = NULL;
-        ViewportType viewportTypeToUse;
-
-        if (mParameters.associatedDisplayIsExternal) {
-            viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
-        } else if (!mParameters.uniqueDisplayId.isEmpty()) {
-            // If the IDC file specified a unique display Id, then it expects to be linked to a
-            // virtual display with the same unique ID.
-            uniqueDisplayId = &mParameters.uniqueDisplayId;
-            viewportTypeToUse = ViewportType::VIEWPORT_VIRTUAL;
-        } else {
-            viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
-        }
-
-        if (!mConfig.getDisplayViewport(viewportTypeToUse, uniqueDisplayId, &newViewport)) {
-            ALOGI(INDENT "Touch device '%s' could not query the properties of its associated "
-                    "display.  The device will be inoperable until the display size "
-                    "becomes available.",
-                    getDeviceName().string());
-            mDeviceMode = DEVICE_MODE_DISABLED;
-            return;
-        }
-    } else {
-        newViewport.setNonDisplayViewport(rawWidth, rawHeight);
-    }
-    bool viewportChanged = mViewport != newViewport;
+    bool viewportChanged = mViewport != *newViewport;
     if (viewportChanged) {
-        mViewport = newViewport;
+        mViewport = *newViewport;
 
         if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
             // Convert rotated viewport to natural surface coordinates.
@@ -3597,6 +3635,12 @@
                 break;
             }
 
+            if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) {
+                ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str());
+                naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight;
+                naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth;
+            }
+
             mPhysicalWidth = naturalPhysicalWidth;
             mPhysicalHeight = naturalPhysicalHeight;
             mPhysicalLeft = naturalPhysicalLeft;
@@ -3629,10 +3673,10 @@
         mOrientedRanges.clear();
     }
 
-    // Create pointer controller if needed.
+    // Create or update pointer controller if needed.
     if (mDeviceMode == DEVICE_MODE_POINTER ||
             (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
-        if (mPointerController == NULL) {
+        if (mPointerController == nullptr || viewportChanged) {
             mPointerController = getPolicy()->obtainPointerController(getDeviceId());
         }
     } else {
@@ -3642,7 +3686,7 @@
     if (viewportChanged || deviceModeChanged) {
         ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
                 "display id %d",
-                getDeviceId(), getDeviceName().string(), mSurfaceWidth, mSurfaceHeight,
+                getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight,
                 mSurfaceOrientation, mDeviceMode, mViewport.displayId);
 
         // Configure X and Y factors.
@@ -3910,17 +3954,7 @@
 }
 
 void TouchInputMapper::dumpSurface(std::string& dump) {
-    dump += StringPrintf(INDENT3 "Viewport: displayId=%d, orientation=%d, "
-            "logicalFrame=[%d, %d, %d, %d], "
-            "physicalFrame=[%d, %d, %d, %d], "
-            "deviceSize=[%d, %d]\n",
-            mViewport.displayId, mViewport.orientation,
-            mViewport.logicalLeft, mViewport.logicalTop,
-            mViewport.logicalRight, mViewport.logicalBottom,
-            mViewport.physicalLeft, mViewport.physicalTop,
-            mViewport.physicalRight, mViewport.physicalBottom,
-            mViewport.deviceWidth, mViewport.deviceHeight);
-
+    dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
     dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
     dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
     dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
@@ -3946,8 +3980,8 @@
 
     int32_t touchScreenLeft = mRawPointerAxes.x.minValue;
     int32_t touchScreenTop = mRawPointerAxes.y.minValue;
-    int32_t touchScreenWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
-    int32_t touchScreenHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
+    int32_t touchScreenWidth = mRawPointerAxes.getRawWidth();
+    int32_t touchScreenHeight = mRawPointerAxes.getRawHeight();
 
     for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
         const VirtualKeyDefinition& virtualKeyDefinition =
@@ -4286,7 +4320,7 @@
     mPointerSimple.reset();
     resetExternalStylus();
 
-    if (mPointerController != NULL) {
+    if (mPointerController != nullptr) {
         mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
         mPointerController->clearSpots();
     }
@@ -4306,12 +4340,25 @@
     mExternalStylusFusionTimeout = LLONG_MAX;
 }
 
+void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) {
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    nsecs_t latency = now - evdevTime;
+    mStatistics.addValue(nanoseconds_to_microseconds(latency));
+    nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime;
+    if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) {
+        android::util::stats_write(android::util::TOUCH_EVENT_REPORTED,
+                mStatistics.min, mStatistics.max, mStatistics.mean(), mStatistics.stdev());
+        mStatistics.reset(now);
+    }
+}
+
 void TouchInputMapper::process(const RawEvent* rawEvent) {
     mCursorButtonAccumulator.process(rawEvent);
     mCursorScrollAccumulator.process(rawEvent);
     mTouchButtonAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+        reportEventForStatistics(rawEvent->when);
         sync(rawEvent->when);
     }
 }
@@ -4448,7 +4495,8 @@
 
     // Synthesize key down from raw buttons if needed.
     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
-            policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);
+            mViewport.displayId, policyFlags,
+            mLastCookedState.buttonState, mCurrentCookedState.buttonState);
 
     // Dispatch the touches either directly or by translation through a pointer on screen.
     if (mDeviceMode == DEVICE_MODE_POINTER) {
@@ -4495,7 +4543,7 @@
         dispatchPointerUsage(when, policyFlags, pointerUsage);
     } else {
         if (mDeviceMode == DEVICE_MODE_DIRECT
-                && mConfig.showTouches && mPointerController != NULL) {
+                && mConfig.showTouches && mPointerController != nullptr) {
             mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
             mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
 
@@ -4520,7 +4568,8 @@
 
     // Synthesize key up from raw buttons if needed.
     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
-            policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);
+            mViewport.displayId, policyFlags,
+            mLastCookedState.buttonState, mCurrentCookedState.buttonState);
 
     // Clear some transient state.
     mCurrentRawState.rawVScroll = 0;
@@ -4735,8 +4784,9 @@
     int32_t metaState = mContext->getGlobalMetaState();
     policyFlags |= POLICY_FLAG_VIRTUAL;
 
-    NotifyKeyArgs args(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
-            keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+            mViewport.displayId,
+            policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
     getListener()->notifyKey(&args);
 }
 
@@ -5428,11 +5478,13 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
-        NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
+        const int32_t displayId = mPointerController->getDisplayId();
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+                mSource, displayId, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
-                metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                mViewport.displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                0, 0, mPointerGesture.downTime);
+                metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+                0, 0, mPointerGesture.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -5473,7 +5525,7 @@
     mPointerVelocityControl.reset();
 
     // Remove any current spots.
-    if (mPointerController != NULL) {
+    if (mPointerController != nullptr) {
         mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
         mPointerController->clearSpots();
     }
@@ -6335,8 +6387,9 @@
 void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
         bool down, bool hovering) {
     int32_t metaState = getContext()->getGlobalMetaState();
+    int32_t displayId = mViewport.displayId;
 
-    if (mPointerController != NULL) {
+    if (mPointerController != nullptr) {
         if (down || hovering) {
             mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
             mPointerController->clearSpots();
@@ -6345,18 +6398,20 @@
         } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
             mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
         }
+        displayId = mPointerController->getDisplayId();
     }
 
     if (mPointerSimple.down && !down) {
         mPointerSimple.down = false;
 
         // Send up.
-        NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                 AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, 0,
-                 mViewport.displayId, /* deviceTimestamp */ 0,
-                 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
-                 mOrientedXPrecision, mOrientedYPrecision,
-                 mPointerSimple.downTime);
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+                mSource, displayId, policyFlags,
+                AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState,
+                MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
+                1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
+                mOrientedXPrecision, mOrientedYPrecision,
+                mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -6364,12 +6419,13 @@
         mPointerSimple.hovering = false;
 
         // Send hover exit.
-        NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, 0,
-                mViewport.displayId, /* deviceTimestamp */ 0,
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+                mSource, displayId, policyFlags,
+                AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState,
+                MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
                 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
-                mPointerSimple.downTime);
+                mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -6379,22 +6435,25 @@
             mPointerSimple.downTime = when;
 
             // Send down.
-            NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                    AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, 0,
-                    mViewport.displayId, /* deviceTimestamp */ 0,
+            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+                    mSource, displayId, policyFlags,
+                    AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState,
+                    MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                    /* deviceTimestamp */ 0,
                     1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                     mOrientedXPrecision, mOrientedYPrecision,
-                    mPointerSimple.downTime);
+                    mPointerSimple.downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&args);
         }
 
         // Send move.
-        NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0,
-                mViewport.displayId, /* deviceTimestamp */ 0,
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+                mSource, displayId, policyFlags,
+                AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState,
+                MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
                 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
-                mPointerSimple.downTime);
+                mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -6403,32 +6462,34 @@
             mPointerSimple.hovering = true;
 
             // Send hover enter.
-            NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
+            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+                    mSource, displayId, policyFlags,
                     AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
-                    mCurrentRawState.buttonState, 0,
-                    mViewport.displayId, /* deviceTimestamp */ 0,
+                    mCurrentRawState.buttonState, MotionClassification::NONE,
+                    AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
                     1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                     mOrientedXPrecision, mOrientedYPrecision,
-                    mPointerSimple.downTime);
+                    mPointerSimple.downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&args);
         }
 
         // Send hover move.
-        NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+                mSource, displayId, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                mCurrentRawState.buttonState, 0,
-                mViewport.displayId, /* deviceTimestamp */ 0,
+                mCurrentRawState.buttonState, MotionClassification::NONE,
+                AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
                 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
-                mPointerSimple.downTime);
+                mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
     if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
         float vscroll = mCurrentRawState.rawVScroll;
         float hscroll = mCurrentRawState.rawHScroll;
-        mWheelYVelocityControl.move(when, NULL, &vscroll);
-        mWheelXVelocityControl.move(when, &hscroll, NULL);
+        mWheelYVelocityControl.move(when, nullptr, &vscroll);
+        mWheelXVelocityControl.move(when, &hscroll, nullptr);
 
         // Send scroll.
         PointerCoords pointerCoords;
@@ -6436,12 +6497,13 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
-                AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, 0,
-                mViewport.displayId, /* deviceTimestamp */ 0,
+        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+                mSource, displayId, policyFlags,
+                AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState,
+                MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
                 1, &mPointerSimple.currentProperties, &pointerCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
-                mPointerSimple.downTime);
+                mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -6498,11 +6560,14 @@
             ALOG_ASSERT(false);
         }
     }
-
-    NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
-            action, actionButton, flags, metaState, buttonState, edgeFlags,
-            mViewport.displayId, deviceTimestamp, pointerCount, pointerProperties, pointerCoords,
-            xPrecision, yPrecision, downTime);
+    const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE);
+    const int32_t deviceId = getDeviceId();
+    std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
+    NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId,
+            source, displayId, policyFlags,
+            action, actionButton, flags, metaState, buttonState, MotionClassification::NONE,
+            edgeFlags, deviceTimestamp, pointerCount, pointerProperties, pointerCoords,
+            xPrecision, yPrecision, downTime, std::move(frames));
     getListener()->notifyMotion(&args);
 }
 
@@ -6535,7 +6600,7 @@
 }
 
 void TouchInputMapper::fadePointer() {
-    if (mPointerController != NULL) {
+    if (mPointerController != nullptr) {
         mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
     }
 }
@@ -6574,7 +6639,7 @@
         }
     }
 
-    return NULL;
+    return nullptr;
 }
 
 void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) {
@@ -6812,6 +6877,16 @@
     return true;
 }
 
+std::optional<int32_t> TouchInputMapper::getAssociatedDisplay() {
+    if (mParameters.hasAssociatedDisplay) {
+        if (mDeviceMode == DEVICE_MODE_POINTER) {
+            return std::make_optional(mPointerController->getDisplayId());
+        } else {
+            return std::make_optional(mViewport.displayId);
+        }
+    }
+    return std::nullopt;
+}
 
 // --- SingleTouchInputMapper ---
 
@@ -6923,7 +6998,7 @@
 #if DEBUG_POINTERS
             ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
                     "ignoring the rest.",
-                    getDeviceName().string(), MAX_POINTERS);
+                    getDeviceName().c_str(), MAX_POINTERS);
 #endif
             break; // too many fingers!
         }
@@ -7014,7 +7089,7 @@
         if (slotCount > MAX_SLOTS) {
             ALOGW("MultiTouch Device %s reported %zu slots but the framework "
                     "only supports a maximum of %zu slots at this time.",
-                    getDeviceName().string(), slotCount, MAX_SLOTS);
+                    getDeviceName().c_str(), slotCount, MAX_SLOTS);
             slotCount = MAX_SLOTS;
         }
         mMultiTouchMotionAccumulator.configure(getDevice(),
@@ -7257,7 +7332,7 @@
         // Prefer to keep explicitly mapped axes.
         if (mAxes.size() > PointerCoords::MAX_AXES) {
             ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.",
-                    getDeviceName().string(), mAxes.size(), PointerCoords::MAX_AXES);
+                    getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES);
             pruneAxes(true);
             pruneAxes(false);
         }
@@ -7279,7 +7354,7 @@
                 } else {
                     ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
                             "have already been assigned to other axes.",
-                            getDeviceName().string(), mAxes.keyAt(i));
+                            getDeviceName().c_str(), mAxes.keyAt(i));
                     mAxes.removeItemsAt(i--);
                     numAxes -= 1;
                 }
@@ -7308,7 +7383,7 @@
             continue;
         }
         ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
-                getDeviceName().string(), mAxes.keyAt(i));
+                getDeviceName().c_str(), mAxes.keyAt(i));
         mAxes.removeItemsAt(i);
     }
 }
@@ -7423,10 +7498,11 @@
     // TODO: Use the input device configuration to control this behavior more finely.
     uint32_t policyFlags = 0;
 
-    NotifyMotionArgs args(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, policyFlags,
-            AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-            ADISPLAY_ID_NONE, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-            0, 0, 0);
+    NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+            AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags,
+            AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, MotionClassification::NONE,
+            AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1,
+            &pointerProperties, &pointerCoords, 0, 0, 0, /* videoFrames */ {});
     getListener()->notifyMotion(&args);
 }
 
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 2f98e69..fed55ac 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -20,6 +20,7 @@
 #include "EventHub.h"
 #include "PointerControllerInterface.h"
 #include "InputListener.h"
+#include "InputReaderBase.h"
 
 #include <input/DisplayViewport.h>
 #include <input/Input.h>
@@ -28,315 +29,20 @@
 #include <ui/DisplayInfo.h>
 #include <utils/KeyedVector.h>
 #include <utils/Condition.h>
-#include <utils/Thread.h>
 #include <utils/Mutex.h>
 #include <utils/Timers.h>
-#include <utils/RefBase.h>
 #include <utils/BitSet.h>
-#include <utils/SortedVector.h>
 
+#include <optional>
 #include <stddef.h>
 #include <unistd.h>
-
-// Maximum supported size of a vibration pattern.
-// Must be at least 2.
-#define MAX_VIBRATE_PATTERN_SIZE 100
-
-// Maximum allowable delay value in a vibration pattern before
-// which the delay will be truncated.
-#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
+#include <vector>
 
 namespace android {
 
 class InputDevice;
 class InputMapper;
 
-/*
- * Input reader configuration.
- *
- * Specifies various options that modify the behavior of the input reader.
- */
-struct InputReaderConfiguration {
-    // Describes changes that have occurred.
-    enum {
-        // The pointer speed changed.
-        CHANGE_POINTER_SPEED = 1 << 0,
-
-        // The pointer gesture control changed.
-        CHANGE_POINTER_GESTURE_ENABLEMENT = 1 << 1,
-
-        // The display size or orientation changed.
-        CHANGE_DISPLAY_INFO = 1 << 2,
-
-        // The visible touches option changed.
-        CHANGE_SHOW_TOUCHES = 1 << 3,
-
-        // The keyboard layouts must be reloaded.
-        CHANGE_KEYBOARD_LAYOUTS = 1 << 4,
-
-        // The device name alias supplied by the may have changed for some devices.
-        CHANGE_DEVICE_ALIAS = 1 << 5,
-
-        // The location calibration matrix changed.
-        CHANGE_TOUCH_AFFINE_TRANSFORMATION = 1 << 6,
-
-        // The presence of an external stylus has changed.
-        CHANGE_EXTERNAL_STYLUS_PRESENCE = 1 << 7,
-
-        // The pointer capture mode has changed.
-        CHANGE_POINTER_CAPTURE = 1 << 8,
-
-        // The set of disabled input devices (disabledDevices) has changed.
-        CHANGE_ENABLED_STATE = 1 << 9,
-
-        // All devices must be reopened.
-        CHANGE_MUST_REOPEN = 1 << 31,
-    };
-
-    // Gets the amount of time to disable virtual keys after the screen is touched
-    // in order to filter out accidental virtual key presses due to swiping gestures
-    // or taps near the edge of the display.  May be 0 to disable the feature.
-    nsecs_t virtualKeyQuietTime;
-
-    // The excluded device names for the platform.
-    // Devices with these names will be ignored.
-    Vector<String8> excludedDeviceNames;
-
-    // Velocity control parameters for mouse pointer movements.
-    VelocityControlParameters pointerVelocityControlParameters;
-
-    // Velocity control parameters for mouse wheel movements.
-    VelocityControlParameters wheelVelocityControlParameters;
-
-    // True if pointer gestures are enabled.
-    bool pointerGesturesEnabled;
-
-    // Quiet time between certain pointer gesture transitions.
-    // Time to allow for all fingers or buttons to settle into a stable state before
-    // starting a new gesture.
-    nsecs_t pointerGestureQuietInterval;
-
-    // The minimum speed that a pointer must travel for us to consider switching the active
-    // touch pointer to it during a drag.  This threshold is set to avoid switching due
-    // to noise from a finger resting on the touch pad (perhaps just pressing it down).
-    float pointerGestureDragMinSwitchSpeed; // in pixels per second
-
-    // Tap gesture delay time.
-    // The time between down and up must be less than this to be considered a tap.
-    nsecs_t pointerGestureTapInterval;
-
-    // Tap drag gesture delay time.
-    // The time between the previous tap's up and the next down must be less than
-    // this to be considered a drag.  Otherwise, the previous tap is finished and a
-    // new tap begins.
-    //
-    // Note that the previous tap will be held down for this entire duration so this
-    // interval must be shorter than the long press timeout.
-    nsecs_t pointerGestureTapDragInterval;
-
-    // The distance in pixels that the pointer is allowed to move from initial down
-    // to up and still be called a tap.
-    float pointerGestureTapSlop; // in pixels
-
-    // Time after the first touch points go down to settle on an initial centroid.
-    // This is intended to be enough time to handle cases where the user puts down two
-    // fingers at almost but not quite exactly the same time.
-    nsecs_t pointerGestureMultitouchSettleInterval;
-
-    // The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
-    // at least two pointers have moved at least this far from their starting place.
-    float pointerGestureMultitouchMinDistance; // in pixels
-
-    // The transition from PRESS to SWIPE gesture mode can only occur when the
-    // cosine of the angle between the two vectors is greater than or equal to than this value
-    // which indicates that the vectors are oriented in the same direction.
-    // When the vectors are oriented in the exactly same direction, the cosine is 1.0.
-    // (In exactly opposite directions, the cosine is -1.0.)
-    float pointerGestureSwipeTransitionAngleCosine;
-
-    // The transition from PRESS to SWIPE gesture mode can only occur when the
-    // fingers are no more than this far apart relative to the diagonal size of
-    // the touch pad.  For example, a ratio of 0.5 means that the fingers must be
-    // no more than half the diagonal size of the touch pad apart.
-    float pointerGestureSwipeMaxWidthRatio;
-
-    // The gesture movement speed factor relative to the size of the display.
-    // Movement speed applies when the fingers are moving in the same direction.
-    // Without acceleration, a full swipe of the touch pad diagonal in movement mode
-    // will cover this portion of the display diagonal.
-    float pointerGestureMovementSpeedRatio;
-
-    // The gesture zoom speed factor relative to the size of the display.
-    // Zoom speed applies when the fingers are mostly moving relative to each other
-    // to execute a scale gesture or similar.
-    // Without acceleration, a full swipe of the touch pad diagonal in zoom mode
-    // will cover this portion of the display diagonal.
-    float pointerGestureZoomSpeedRatio;
-
-    // True to show the location of touches on the touch screen as spots.
-    bool showTouches;
-
-    // True if pointer capture is enabled.
-    bool pointerCapture;
-
-    // The set of currently disabled input devices.
-    SortedVector<int32_t> disabledDevices;
-
-    InputReaderConfiguration() :
-            virtualKeyQuietTime(0),
-            pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
-            wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
-            pointerGesturesEnabled(true),
-            pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
-            pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
-            pointerGestureTapInterval(150 * 1000000LL), // 150 ms
-            pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
-            pointerGestureTapSlop(10.0f), // 10 pixels
-            pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
-            pointerGestureMultitouchMinDistance(15), // 15 pixels
-            pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
-            pointerGestureSwipeMaxWidthRatio(0.25f),
-            pointerGestureMovementSpeedRatio(0.8f),
-            pointerGestureZoomSpeedRatio(0.3f),
-            showTouches(false) { }
-
-    bool getDisplayViewport(ViewportType viewportType, const String8* displayId,
-            DisplayViewport* outViewport) const;
-    void setPhysicalDisplayViewport(ViewportType viewportType, const DisplayViewport& viewport);
-    void setVirtualDisplayViewports(const Vector<DisplayViewport>& viewports);
-
-
-    void dump(std::string& dump) const;
-    void dumpViewport(std::string& dump, const DisplayViewport& viewport) const;
-
-private:
-    DisplayViewport mInternalDisplay;
-    DisplayViewport mExternalDisplay;
-    Vector<DisplayViewport> mVirtualDisplays;
-};
-
-
-struct TouchAffineTransformation {
-    float x_scale;
-    float x_ymix;
-    float x_offset;
-    float y_xmix;
-    float y_scale;
-    float y_offset;
-
-    TouchAffineTransformation() :
-        x_scale(1.0f), x_ymix(0.0f), x_offset(0.0f),
-        y_xmix(0.0f), y_scale(1.0f), y_offset(0.0f) {
-    }
-
-    TouchAffineTransformation(float xscale, float xymix, float xoffset,
-            float yxmix, float yscale, float yoffset) :
-        x_scale(xscale), x_ymix(xymix), x_offset(xoffset),
-        y_xmix(yxmix), y_scale(yscale), y_offset(yoffset) {
-    }
-
-    void applyTo(float& x, float& y) const;
-};
-
-
-/*
- * Input reader policy interface.
- *
- * The input reader policy is used by the input reader to interact with the Window Manager
- * and other system components.
- *
- * The actual implementation is partially supported by callbacks into the DVM
- * via JNI.  This interface is also mocked in the unit tests.
- *
- * These methods must NOT re-enter the input reader since they may be called while
- * holding the input reader lock.
- */
-class InputReaderPolicyInterface : public virtual RefBase {
-protected:
-    InputReaderPolicyInterface() { }
-    virtual ~InputReaderPolicyInterface() { }
-
-public:
-    /* Gets the input reader configuration. */
-    virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) = 0;
-
-    /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
-    virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0;
-
-    /* Notifies the input reader policy that some input devices have changed
-     * and provides information about all current input devices.
-     */
-    virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) = 0;
-
-    /* Gets the keyboard layout for a particular input device. */
-    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(
-            const InputDeviceIdentifier& identifier) = 0;
-
-    /* Gets a user-supplied alias for a particular input device, or an empty string if none. */
-    virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
-
-    /* Gets the affine calibration associated with the specified device. */
-    virtual TouchAffineTransformation getTouchAffineTransformation(
-            const String8& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
-};
-
-
-/* Processes raw input events and sends cooked event data to an input listener. */
-class InputReaderInterface : public virtual RefBase {
-protected:
-    InputReaderInterface() { }
-    virtual ~InputReaderInterface() { }
-
-public:
-    /* Dumps the state of the input reader.
-     *
-     * This method may be called on any thread (usually by the input manager). */
-    virtual void dump(std::string& dump) = 0;
-
-    /* Called by the heatbeat to ensures that the reader has not deadlocked. */
-    virtual void monitor() = 0;
-
-    /* Returns true if the input device is enabled. */
-    virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
-
-    /* Runs a single iteration of the processing loop.
-     * Nominally reads and processes one incoming message from the EventHub.
-     *
-     * This method should be called on the input reader thread.
-     */
-    virtual void loopOnce() = 0;
-
-    /* Gets information about all input devices.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices) = 0;
-
-    /* Query current input state. */
-    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t scanCode) = 0;
-    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t keyCode) = 0;
-    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
-            int32_t sw) = 0;
-
-    /* Toggle Caps Lock */
-    virtual void toggleCapsLockState(int32_t deviceId) = 0;
-
-    /* Determine whether physical keys exist for the given framework-domain key codes. */
-    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
-            size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
-
-    /* Requests that a reconfiguration of all input devices.
-     * The changes flag is a bitfield that indicates what has changed and whether
-     * the input devices must all be reopened. */
-    virtual void requestRefreshConfiguration(uint32_t changes) = 0;
-
-    /* Controls the vibrator of a particular input device. */
-    virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
-            ssize_t repeat, int32_t token) = 0;
-    virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
-};
 
 struct StylusState {
     /* Time the stylus event was received. */
@@ -390,6 +96,8 @@
     virtual InputReaderPolicyInterface* getPolicy() = 0;
     virtual InputListenerInterface* getListener() = 0;
     virtual EventHubInterface* getEventHub() = 0;
+
+    virtual uint32_t getNextSequenceNum() = 0;
 };
 
 
@@ -438,6 +146,7 @@
             ssize_t repeat, int32_t token);
     virtual void cancelVibrate(int32_t deviceId, int32_t token);
 
+    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId);
 protected:
     // These members are protected so they can be instrumented by test cases.
     virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
@@ -462,6 +171,7 @@
         virtual InputReaderPolicyInterface* getPolicy();
         virtual InputListenerInterface* getListener();
         virtual EventHubInterface* getEventHub();
+        virtual uint32_t getNextSequenceNum();
     } mContext;
 
     friend class ContextImpl;
@@ -477,6 +187,9 @@
 
     InputReaderConfiguration mConfig;
 
+    // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers
+    uint32_t mNextSequenceNum;
+
     // The event queue.
     static const int EVENT_BUFFER_SIZE = 256;
     RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
@@ -528,19 +241,6 @@
 };
 
 
-/* Reads raw events from the event hub and processes them, endlessly. */
-class InputReaderThread : public Thread {
-public:
-    explicit InputReaderThread(const sp<InputReaderInterface>& reader);
-    virtual ~InputReaderThread();
-
-private:
-    sp<InputReaderInterface> mReader;
-
-    virtual bool threadLoop();
-};
-
-
 /* Represents the state of a single input device. */
 class InputDevice {
 public:
@@ -552,13 +252,16 @@
     inline int32_t getId() const { return mId; }
     inline int32_t getControllerNumber() const { return mControllerNumber; }
     inline int32_t getGeneration() const { return mGeneration; }
-    inline const String8& getName() const { return mIdentifier.name; }
-    inline const String8& getDescriptor() { return mIdentifier.descriptor; }
+    inline const std::string getName() const { return mIdentifier.name; }
+    inline const std::string getDescriptor() { return mIdentifier.descriptor; }
     inline uint32_t getClasses() const { return mClasses; }
     inline uint32_t getSources() const { return mSources; }
 
     inline bool isExternal() { return mIsExternal; }
     inline void setExternal(bool external) { mIsExternal = external; }
+    inline std::optional<uint8_t> getAssociatedDisplayPort() const {
+        return mAssociatedDisplayPort;
+    }
 
     inline void setMic(bool hasMic) { mHasMic = hasMic; }
     inline bool hasMic() const { return mHasMic; }
@@ -618,19 +321,21 @@
         return value;
     }
 
+    std::optional<int32_t> getAssociatedDisplay();
 private:
     InputReaderContext* mContext;
     int32_t mId;
     int32_t mGeneration;
     int32_t mControllerNumber;
     InputDeviceIdentifier mIdentifier;
-    String8 mAlias;
+    std::string mAlias;
     uint32_t mClasses;
 
     Vector<InputMapper*> mMappers;
 
     uint32_t mSources;
     bool mIsExternal;
+    std::optional<uint8_t> mAssociatedDisplayPort;
     bool mHasMic;
     bool mDropUntilNextSync;
 
@@ -773,6 +478,8 @@
     RawAbsoluteAxisInfo slot;
 
     RawPointerAxes();
+    inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
+    inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
     void clear();
 };
 
@@ -862,6 +569,69 @@
     }
 };
 
+/**
+ * Basic statistics information.
+ * Keep track of min, max, average, and standard deviation of the received samples.
+ * Used to report latency information about input events.
+ */
+struct LatencyStatistics {
+    float min;
+    float max;
+    // Sum of all samples
+    float sum;
+    // Sum of squares of all samples
+    float sum2;
+    // The number of samples
+    size_t count;
+    // The last time statistics were reported.
+    nsecs_t lastReportTime;
+
+    LatencyStatistics() {
+        reset(systemTime(SYSTEM_TIME_MONOTONIC));
+    }
+
+    inline void addValue(float x) {
+        if (x < min) {
+            min = x;
+        }
+        if (x > max) {
+            max = x;
+        }
+        sum += x;
+        sum2 += x * x;
+        count++;
+    }
+
+    // Get the average value. Should not be called if no samples have been added.
+    inline float mean() {
+        if (count == 0) {
+            return 0;
+        }
+        return sum / count;
+    }
+
+    // Get the standard deviation. Should not be called if no samples have been added.
+    inline float stdev() {
+        if (count == 0) {
+            return 0;
+        }
+        float average = mean();
+        return sqrt(sum2 / count - average * average);
+    }
+
+    /**
+     * Reset internal state. The variable 'when' is the time when the data collection started.
+     * Call this to start a new data collection window.
+     */
+    inline void reset(nsecs_t when) {
+        max = 0;
+        min = std::numeric_limits<float>::max();
+        sum = 0;
+        sum2 = 0;
+        count = 0;
+        lastReportTime = when;
+    }
+};
 
 /* Keeps track of the state of single-touch protocol. */
 class SingleTouchMotionAccumulator {
@@ -980,7 +750,7 @@
 
     inline InputDevice* getDevice() { return mDevice; }
     inline int32_t getDeviceId() { return mDevice->getId(); }
-    inline const String8 getDeviceName() { return mDevice->getName(); }
+    inline const std::string getDeviceName() { return mDevice->getName(); }
     inline InputReaderContext* getContext() { return mContext; }
     inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
     inline InputListenerInterface* getListener() { return mContext->getListener(); }
@@ -1010,7 +780,9 @@
     virtual void updateExternalStylusState(const StylusState& state);
 
     virtual void fadePointer();
-
+    virtual std::optional<int32_t> getAssociatedDisplay() {
+        return std::nullopt;
+    }
 protected:
     InputDevice* mDevice;
     InputReaderContext* mContext;
@@ -1094,6 +866,9 @@
     virtual void updateMetaState(int32_t keyCode);
 
 private:
+    // The current viewport.
+    std::optional<DisplayViewport> mViewport;
+
     struct KeyDown {
         int32_t keyCode;
         int32_t scanCode;
@@ -1102,8 +877,6 @@
     uint32_t mSource;
     int32_t mKeyboardType;
 
-    int32_t mOrientation; // orientation for dpad keys
-
     Vector<KeyDown> mKeyDowns; // keys that are down
     int32_t mMetaState;
     nsecs_t mDownTime; // time of most recent key down
@@ -1120,7 +893,6 @@
 
     // Immutable configuration parameters.
     struct Parameters {
-        bool hasAssociatedDisplay;
         bool orientationAware;
         bool handlesKeyRepeat;
     } mParameters;
@@ -1128,6 +900,9 @@
     void configureParameters();
     void dumpParameters(std::string& dump);
 
+    int32_t getOrientation();
+    int32_t getDisplayId();
+
     bool isKeyboardOrGamepadKey(int32_t scanCode);
     bool isMediaKey(int32_t keyCode);
 
@@ -1161,6 +936,7 @@
 
     virtual void fadePointer();
 
+    virtual std::optional<int32_t> getAssociatedDisplay();
 private:
     // Amount that trackball needs to move in order to generate a key event.
     static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
@@ -1254,7 +1030,7 @@
     virtual void cancelTouch(nsecs_t when);
     virtual void timeoutExpired(nsecs_t when);
     virtual void updateExternalStylusState(const StylusState& state);
-
+    virtual std::optional<int32_t> getAssociatedDisplay();
 protected:
     CursorButtonAccumulator mCursorButtonAccumulator;
     CursorScrollAccumulator mCursorScrollAccumulator;
@@ -1305,7 +1081,7 @@
         bool associatedDisplayIsExternal;
         bool orientationAware;
         bool hasButtonUnderPad;
-        String8 uniqueDisplayId;
+        std::string uniqueDisplayId;
 
         enum GestureMode {
             GESTURE_MODE_SINGLE_TOUCH,
@@ -1803,6 +1579,11 @@
     VelocityControl mWheelXVelocityControl;
     VelocityControl mWheelYVelocityControl;
 
+    // Latency statistics for touch events
+    struct LatencyStatistics mStatistics;
+
+    std::optional<DisplayViewport> findViewport();
+
     void resetExternalStylus();
     void clearStylusDataPendingFlags();
 
@@ -1870,6 +1651,8 @@
 
     static void assignPointerIds(const RawState* last, RawState* current);
 
+    void reportEventForStatistics(nsecs_t evdevTime);
+
     const char* modeToString(DeviceMode deviceMode);
 };
 
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
new file mode 100644
index 0000000..f48a645
--- /dev/null
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputReaderBase"
+
+//#define LOG_NDEBUG 0
+
+#include "InputReaderBase.h"
+
+#include <android/log.h>
+#include <android-base/stringprintf.h>
+
+#define INDENT "  "
+#define INDENT2 "    "
+#define INDENT3 "      "
+#define INDENT4 "        "
+#define INDENT5 "          "
+
+using android::base::StringPrintf;
+
+namespace android {
+
+// --- InputReaderThread ---
+
+InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
+        Thread(/*canCallJava*/ true), mReader(reader) {
+}
+
+InputReaderThread::~InputReaderThread() {
+}
+
+bool InputReaderThread::threadLoop() {
+    mReader->loopOnce();
+    return true;
+}
+
+// --- InputReaderConfiguration ---
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByUniqueId(
+        const std::string& uniqueDisplayId) const {
+    if (uniqueDisplayId.empty()) {
+        ALOGE("Empty string provided to %s", __func__);
+        return std::nullopt;
+    }
+    size_t count = 0;
+    std::optional<DisplayViewport> result = std::nullopt;
+    for (const DisplayViewport& currentViewport : mDisplays) {
+        if (uniqueDisplayId == currentViewport.uniqueId) {
+            result = std::make_optional(currentViewport);
+            count++;
+        }
+    }
+    if (count > 1) {
+        ALOGE("Found %zu viewports with uniqueId %s, but expected 1 at most",
+            count, uniqueDisplayId.c_str());
+    }
+    return result;
+}
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByType(ViewportType type)
+        const {
+    size_t count = 0;
+    std::optional<DisplayViewport> result = std::nullopt;
+    for (const DisplayViewport& currentViewport : mDisplays) {
+        // Return the first match
+        if (currentViewport.type == type && !result) {
+            result = std::make_optional(currentViewport);
+            count++;
+        }
+    }
+    if (count > 1) {
+        ALOGE("Found %zu viewports with type %s, but expected 1 at most",
+                count, viewportTypeToString(type));
+    }
+    return result;
+}
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByPort(
+        uint8_t displayPort) const {
+    for (const DisplayViewport& currentViewport : mDisplays) {
+        const std::optional<uint8_t>& physicalPort = currentViewport.physicalPort;
+        if (physicalPort && (*physicalPort == displayPort)) {
+            return std::make_optional(currentViewport);
+        }
+    }
+    return std::nullopt;
+}
+
+void InputReaderConfiguration::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
+    mDisplays = viewports;
+}
+
+void InputReaderConfiguration::dump(std::string& dump) const {
+    for (const DisplayViewport& viewport : mDisplays) {
+        dumpViewport(dump, viewport);
+    }
+}
+
+void InputReaderConfiguration::dumpViewport(std::string& dump, const DisplayViewport& viewport)
+        const {
+    dump += StringPrintf(INDENT4 "%s\n", viewport.toString().c_str());
+}
+
+
+// -- TouchAffineTransformation --
+void TouchAffineTransformation::applyTo(float& x, float& y) const {
+    float newX, newY;
+    newX = x * x_scale + y * x_ymix + x_offset;
+    newY = x * y_xmix + y * y_scale + y_offset;
+
+    x = newX;
+    y = newY;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/inputflinger/InputReaderFactory.cpp
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/inputflinger/InputReaderFactory.cpp
index e6ac6bf..3534f6b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/inputflinger/InputReaderFactory.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "InputReaderFactory.h"
+#include "InputReader.h"
 
 namespace android {
-namespace mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+sp<InputReaderInterface> createInputReader(
+        const sp<InputReaderPolicyInterface>& policy,
+        const sp<InputListenerInterface>& listener) {
+    return new InputReader(new EventHub(), policy, listener);
+}
 
-} // namespace mock
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/InputReporter.cpp b/services/inputflinger/InputReporter.cpp
new file mode 100644
index 0000000..8d3153c
--- /dev/null
+++ b/services/inputflinger/InputReporter.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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 "InputReporterInterface.h"
+
+namespace android {
+
+// --- InputReporter ---
+
+class InputReporter : public InputReporterInterface {
+public:
+    void reportUnhandledKey(uint32_t sequenceNum) override;
+    void reportDroppedKey(uint32_t sequenceNum) override;
+};
+
+void InputReporter::reportUnhandledKey(uint32_t sequenceNum) {
+  // do nothing
+}
+
+void InputReporter::reportDroppedKey(uint32_t sequenceNum) {
+  // do nothing
+}
+
+sp<InputReporterInterface> createInputReporter() {
+  return new InputReporter();
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputWindow.cpp b/services/inputflinger/InputWindow.cpp
deleted file mode 100644
index 3ae7972..0000000
--- a/services/inputflinger/InputWindow.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 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_TAG "InputWindow"
-#define LOG_NDEBUG 0
-
-#include "InputWindow.h"
-
-#include <log/log.h>
-
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
-namespace android {
-
-// --- InputWindowInfo ---
-void InputWindowInfo::addTouchableRegion(const Rect& region) {
-    touchableRegion.orSelf(region);
-}
-
-bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
-    return touchableRegion.contains(x,y);
-}
-
-bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
-    return x >= frameLeft && x < frameRight
-            && y >= frameTop && y < frameBottom;
-}
-
-bool InputWindowInfo::isTrustedOverlay() const {
-    return layoutParamsType == TYPE_INPUT_METHOD
-            || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
-            || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY
-            || layoutParamsType == TYPE_STATUS_BAR
-            || layoutParamsType == TYPE_NAVIGATION_BAR
-            || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL
-            || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY
-            || layoutParamsType == TYPE_DOCK_DIVIDER
-            || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY
-            || layoutParamsType == TYPE_INPUT_CONSUMER;
-}
-
-bool InputWindowInfo::supportsSplitTouch() const {
-    return layoutParamsFlags & FLAG_SPLIT_TOUCH;
-}
-
-bool InputWindowInfo::overlaps(const InputWindowInfo* other) const {
-    return frameLeft < other->frameRight && frameRight > other->frameLeft
-            && frameTop < other->frameBottom && frameBottom > other->frameTop;
-}
-
-
-// --- InputWindowHandle ---
-
-InputWindowHandle::InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) :
-    inputApplicationHandle(inputApplicationHandle), mInfo(NULL) {
-}
-
-InputWindowHandle::~InputWindowHandle() {
-    delete mInfo;
-}
-
-void InputWindowHandle::releaseInfo() {
-    if (mInfo) {
-        delete mInfo;
-        mInfo = NULL;
-    }
-}
-
-} // namespace android
diff --git a/services/inputflinger/TouchVideoDevice.cpp b/services/inputflinger/TouchVideoDevice.cpp
new file mode 100644
index 0000000..ad70ccc
--- /dev/null
+++ b/services/inputflinger/TouchVideoDevice.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TouchVideoDevice.h"
+
+#define LOG_TAG "TouchVideoDevice"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/videodev2.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <iostream>
+
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+
+TouchVideoDevice::TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath,
+        uint32_t width, uint32_t height,
+        const std::array<const int16_t*, NUM_BUFFERS>& readLocations) :
+        mFd(fd), mName(std::move(name)), mPath(std::move(devicePath)),
+        mWidth(width), mHeight(height),
+        mReadLocations(readLocations) {
+    mFrames.reserve(MAX_QUEUE_SIZE);
+};
+
+std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePath) {
+    unique_fd fd(open(devicePath.c_str(), O_RDWR | O_NONBLOCK));
+    if (fd.get() == INVALID_FD) {
+        ALOGE("Could not open video device %s: %s", devicePath.c_str(), strerror(errno));
+        return nullptr;
+    }
+
+    struct v4l2_capability cap;
+    int result = ioctl(fd.get(), VIDIOC_QUERYCAP, &cap);
+    if (result == -1) {
+        ALOGE("VIDIOC_QUERYCAP failed: %s", strerror(errno));
+        return nullptr;
+    }
+    if (!(cap.capabilities & V4L2_CAP_TOUCH)) {
+        ALOGE("Capability V4L2_CAP_TOUCH is not present, can't use device for heatmap data. "
+                "Make sure device specifies V4L2_CAP_TOUCH");
+        return nullptr;
+    }
+    ALOGI("Opening video device: driver = %s, card = %s, bus_info = %s, version = %i",
+            cap.driver, cap.card, cap.bus_info, cap.version);
+    std::string name = reinterpret_cast<const char*>(cap.card);
+
+    struct v4l2_input v4l2_input_struct;
+    result = ioctl(fd.get(), VIDIOC_ENUMINPUT, &v4l2_input_struct);
+    if (result == -1) {
+        ALOGE("VIDIOC_ENUMINPUT failed: %s", strerror(errno));
+        return nullptr;
+    }
+
+    if (v4l2_input_struct.type != V4L2_INPUT_TYPE_TOUCH) {
+        ALOGE("Video device does not provide touch data. "
+                "Make sure device specifies V4L2_INPUT_TYPE_TOUCH.");
+        return nullptr;
+    }
+
+    struct v4l2_format v4l2_fmt;
+    v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    result = ioctl(fd.get(), VIDIOC_G_FMT, &v4l2_fmt);
+    if (result == -1) {
+        ALOGE("VIDIOC_G_FMT failed: %s", strerror(errno));
+        return nullptr;
+    }
+    const uint32_t width = v4l2_fmt.fmt.pix.width;
+    const uint32_t height = v4l2_fmt.fmt.pix.height;
+    ALOGI("Frame dimensions: width = %" PRIu32 " height = %" PRIu32, width, height);
+
+    struct v4l2_requestbuffers req;
+    req.count = NUM_BUFFERS;
+    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    req.memory = V4L2_MEMORY_MMAP;
+    result = ioctl(fd.get(), VIDIOC_REQBUFS, &req);
+    if (result == -1) {
+        ALOGE("VIDIOC_REQBUFS failed: %s", strerror(errno));
+        return nullptr;
+    }
+    if (req.count != NUM_BUFFERS) {
+        ALOGE("Requested %zu buffers, but driver responded with count=%i", NUM_BUFFERS, req.count);
+        return nullptr;
+    }
+
+    struct v4l2_buffer buf = {};
+    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    buf.memory = V4L2_MEMORY_MMAP;
+    std::array<const int16_t*, NUM_BUFFERS> readLocations;
+    for (size_t i = 0; i < NUM_BUFFERS; i++) {
+        buf.index = i;
+        result = ioctl(fd.get(), VIDIOC_QUERYBUF, &buf);
+        if (result == -1) {
+            ALOGE("VIDIOC_QUERYBUF failed: %s", strerror(errno));
+            return nullptr;
+        }
+        if (buf.length != width * height * sizeof(int16_t)) {
+            ALOGE("Unexpected value of buf.length = %i (offset = %" PRIu32 ")",
+                    buf.length, buf.m.offset);
+            return nullptr;
+        }
+
+        readLocations[i] = static_cast<const int16_t*>(mmap(nullptr /* start anywhere */,
+                buf.length, PROT_READ /* required */, MAP_SHARED /* recommended */,
+                fd.get(), buf.m.offset));
+        if (readLocations[i] == MAP_FAILED) {
+            ALOGE("%s: map failed: %s", __func__, strerror(errno));
+            return nullptr;
+        }
+    }
+
+    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    result = ioctl(fd.get(), VIDIOC_STREAMON, &type);
+    if (result == -1) {
+        ALOGE("VIDIOC_STREAMON failed: %s", strerror(errno));
+        return nullptr;
+    }
+
+    for (size_t i = 0; i < NUM_BUFFERS; i++) {
+        buf.index = i;
+        result = ioctl(fd.get(), VIDIOC_QBUF, &buf);
+        if (result == -1) {
+            ALOGE("VIDIOC_QBUF failed for buffer %zu: %s", i, strerror(errno));
+            return nullptr;
+        }
+    }
+    // Using 'new' to access a non-public constructor.
+    return std::unique_ptr<TouchVideoDevice>(new TouchVideoDevice(
+            fd.release(), std::move(name), std::move(devicePath), width, height, readLocations));
+}
+
+size_t TouchVideoDevice::readAndQueueFrames() {
+    std::vector<TouchVideoFrame> frames = readFrames();
+    const size_t numFrames = frames.size();
+    if (numFrames == 0) {
+        // Likely an error occurred
+        return 0;
+    }
+    // Concatenate the vectors, then clip up to maximum size allowed
+    mFrames.insert(mFrames.end(), std::make_move_iterator(frames.begin()),
+            std::make_move_iterator(frames.end()));
+    if (mFrames.size() > MAX_QUEUE_SIZE) {
+        ALOGE("More than %zu frames have been accumulated. Dropping %zu frames", MAX_QUEUE_SIZE,
+                mFrames.size() - MAX_QUEUE_SIZE);
+        mFrames.erase(mFrames.begin(), mFrames.end() - MAX_QUEUE_SIZE);
+    }
+    return numFrames;
+}
+
+std::vector<TouchVideoFrame> TouchVideoDevice::consumeFrames() {
+    std::vector<TouchVideoFrame> frames = std::move(mFrames);
+    mFrames = {};
+    return frames;
+}
+
+std::optional<TouchVideoFrame> TouchVideoDevice::readFrame() {
+    struct v4l2_buffer buf = {};
+    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    buf.memory = V4L2_MEMORY_MMAP;
+    int result = ioctl(mFd.get(), VIDIOC_DQBUF, &buf);
+    if (result == -1) {
+        // EAGAIN means we've reached the end of the read buffer, so it's expected.
+        if (errno != EAGAIN) {
+            ALOGE("VIDIOC_DQBUF failed: %s", strerror(errno));
+        }
+        return std::nullopt;
+    }
+    if ((buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) {
+        // We use CLOCK_MONOTONIC for input events, so if the clocks don't match,
+        // we can't compare timestamps. Just log a warning, since this is a driver issue
+        ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC",
+                buf.timestamp.tv_sec, buf.timestamp.tv_usec);
+    }
+    std::vector<int16_t> data(mWidth * mHeight);
+    const int16_t* readFrom = mReadLocations[buf.index];
+    std::copy(readFrom, readFrom + mWidth * mHeight, data.begin());
+    TouchVideoFrame frame(mWidth, mHeight, std::move(data), buf.timestamp);
+
+    result = ioctl(mFd.get(), VIDIOC_QBUF, &buf);
+    if (result == -1) {
+        ALOGE("VIDIOC_QBUF failed: %s", strerror(errno));
+    }
+    return std::make_optional(std::move(frame));
+}
+
+/*
+ * This function should not be called unless buffer is ready! This must be checked with
+ * select, poll, epoll, or some other similar api first.
+ * The oldest frame will be at the beginning of the array.
+ */
+std::vector<TouchVideoFrame> TouchVideoDevice::readFrames() {
+    std::vector<TouchVideoFrame> frames;
+    while (true) {
+        std::optional<TouchVideoFrame> frame = readFrame();
+        if (!frame) {
+            break;
+        }
+        frames.push_back(std::move(*frame));
+    }
+    return frames;
+}
+
+TouchVideoDevice::~TouchVideoDevice() {
+    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    int result = ioctl(mFd.get(), VIDIOC_STREAMOFF, &type);
+    if (result == -1) {
+        ALOGE("VIDIOC_STREAMOFF failed: %s", strerror(errno));
+    }
+    for (const int16_t* buffer : mReadLocations) {
+        void* bufferAddress = static_cast<void*>(const_cast<int16_t*>(buffer));
+        result = munmap(bufferAddress, mWidth * mHeight * sizeof(int16_t));
+        if (result == -1) {
+            ALOGE("%s: Couldn't unmap: [%s]", __func__, strerror(errno));
+        }
+    }
+}
+
+std::string TouchVideoDevice::dump() const {
+    return StringPrintf("Video device %s (%s) : width=%" PRIu32 ", height=%" PRIu32
+            ", fd=%i, hasValidFd=%s",
+            mName.c_str(), mPath.c_str(), mWidth, mHeight, mFd.get(),
+            hasValidFd() ? "true" : "false");
+}
+
+} // namespace android
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index 775dbdc..cbe0190 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -30,6 +30,9 @@
         "libutils",
         "libhardware",
     ],
+    static_libs: [
+        "libarect",
+    ],
 
     cflags: [
         "-Wall",
@@ -54,8 +57,9 @@
     shared_libs: [
         "libbinder",
         "libinputflingerhost",
-        "libutils",
+        "libutils"
     ],
-
-    init_rc: ["inputflinger.rc"],
+    static_libs: [
+        "libarect",
+    ],
 }
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index bd11d56..2f046c3 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -217,18 +217,18 @@
     idi.product = id->productId;
     idi.version = id->version;
 
-    String8 configFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
+    std::string configFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
             idi, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
-    if (configFile.isEmpty()) {
+    if (configFile.empty()) {
         ALOGD("No input device configuration file found for device '%s'.",
-                idi.name.string());
+                idi.name.c_str());
     } else {
         auto propMap = new input_property_map_t();
-        status_t status = PropertyMap::load(configFile, &propMap->propertyMap);
+        status_t status = PropertyMap::load(String8(configFile.c_str()), &propMap->propertyMap);
         if (status) {
             ALOGE("Error loading input device configuration file for device '%s'. "
                     "Using default configuration.",
-                    idi.name.string());
+                    idi.name.c_str());
             delete propMap;
             return nullptr;
         }
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 39e69e5..9d0be95 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -39,6 +39,10 @@
     InputFlinger() ANDROID_API;
 
     virtual status_t dump(int fd, const Vector<String16>& args);
+    void setInputWindows(const Vector<InputWindowInfo>&) {}
+    void transferTouchFocus(const sp<IBinder>&, const sp<IBinder>&) {}
+    void registerInputChannel(const sp<InputChannel>&) {}
+    void unregisterInputChannel(const sp<InputChannel>&) {}
 
 private:
     virtual ~InputFlinger();
diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/include/EventHub.h
similarity index 88%
rename from services/inputflinger/EventHub.h
rename to services/inputflinger/include/EventHub.h
index 66bc294..295aca8 100644
--- a/services/inputflinger/EventHub.h
+++ b/services/inputflinger/include/EventHub.h
@@ -18,6 +18,8 @@
 #ifndef _RUNTIME_EVENT_HUB_H
 #define _RUNTIME_EVENT_HUB_H
 
+#include <vector>
+
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/Keyboard.h>
@@ -29,32 +31,19 @@
 #include <utils/List.h>
 #include <utils/Errors.h>
 #include <utils/PropertyMap.h>
-#include <utils/Vector.h>
 #include <utils/KeyedVector.h>
 #include <utils/BitSet.h>
 
 #include <linux/input.h>
 #include <sys/epoll.h>
 
+#include "TouchVideoDevice.h"
+
 /* Convenience constants. */
 
 #define BTN_FIRST 0x100  // first button code
 #define BTN_LAST 0x15f   // last button code
 
-/*
- * These constants are used privately in Android to pass raw timestamps
- * through evdev from uinput device drivers because there is currently no
- * other way to transfer this information.  The evdev driver automatically
- * timestamps all input events with the time they were posted and clobbers
- * whatever information was passed in.
- *
- * For the purposes of this hack, the timestamp is specified in the
- * CLOCK_MONOTONIC timebase and is split into two EV_MSC events specifying
- * seconds and microseconds.
- */
-#define MSC_ANDROID_TIME_SEC 0x6
-#define MSC_ANDROID_TIME_USEC 0x7
-
 namespace android {
 
 enum {
@@ -207,7 +196,7 @@
 
     // Sets devices that are excluded from opening.
     // This can be used to ignore input devices for sensors.
-    virtual void setExcludedDevices(const Vector<String8>& devices) = 0;
+    virtual void setExcludedDevices(const std::vector<std::string>& devices) = 0;
 
     /*
      * Wait for events to become available and returns them.
@@ -222,6 +211,7 @@
      * Returns the number of events obtained, or 0 if the timeout expired.
      */
     virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
+    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0;
 
     /*
      * Query current input state.
@@ -303,7 +293,7 @@
     virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
             AxisInfo* outAxisInfo) const;
 
-    virtual void setExcludedDevices(const Vector<String8>& devices);
+    virtual void setExcludedDevices(const std::vector<std::string>& devices);
 
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
     virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
@@ -314,6 +304,7 @@
             const int32_t* keyCodes, uint8_t* outFlags) const;
 
     virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize);
+    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId);
 
     virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const;
     virtual bool hasLed(int32_t deviceId, int32_t led) const;
@@ -344,9 +335,11 @@
 
         int fd; // may be -1 if device is closed
         const int32_t id;
-        const String8 path;
+        const std::string path;
         const InputDeviceIdentifier identifier;
 
+        std::unique_ptr<TouchVideoDevice> videoDevice;
+
         uint32_t classes;
 
         uint8_t keyBitmask[(KEY_MAX + 1) / 8];
@@ -357,7 +350,7 @@
         uint8_t ffBitmask[(FF_MAX + 1) / 8];
         uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
 
-        String8 configurationFile;
+        std::string configurationFile;
         PropertyMap* configuration;
         VirtualKeyMap* virtualKeyMap;
         KeyMap keyMap;
@@ -370,10 +363,8 @@
 
         int32_t controllerNumber;
 
-        int32_t timestampOverrideSec;
-        int32_t timestampOverrideUsec;
-
-        Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
+        Device(int fd, int32_t id, const std::string& path,
+                const InputDeviceIdentifier& identifier);
         ~Device();
 
         void close();
@@ -385,19 +376,21 @@
         const bool isVirtual; // set if fd < 0 is passed to constructor
 
         const sp<KeyCharacterMap>& getKeyCharacterMap() const {
-            if (combinedKeyMap != NULL) {
+            if (combinedKeyMap != nullptr) {
                 return combinedKeyMap;
             }
             return keyMap.keyCharacterMap;
         }
     };
 
-    status_t openDeviceLocked(const char *devicePath);
+    status_t openDeviceLocked(const char* devicePath);
+    void openVideoDeviceLocked(const std::string& devicePath);
     void createVirtualKeyboardLocked();
     void addDeviceLocked(Device* device);
     void assignDescriptorLocked(InputDeviceIdentifier& identifier);
 
-    status_t closeDeviceByPathLocked(const char *devicePath);
+    void closeDeviceByPathLocked(const char *devicePath);
+    void closeVideoDeviceByPathLocked(const std::string& devicePath);
     void closeDeviceLocked(Device* device);
     void closeAllDevicesLocked();
 
@@ -406,16 +399,26 @@
     bool isDeviceEnabled(int32_t deviceId);
     status_t enableDevice(int32_t deviceId);
     status_t disableDevice(int32_t deviceId);
+    status_t registerFdForEpoll(int fd);
+    status_t unregisterFdFromEpoll(int fd);
     status_t registerDeviceForEpollLocked(Device* device);
+    void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice);
     status_t unregisterDeviceFromEpollLocked(Device* device);
+    void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
 
     status_t scanDirLocked(const char *dirname);
+    status_t scanVideoDirLocked(const std::string& dirname);
     void scanDevicesLocked();
     status_t readNotifyLocked();
 
-    Device* getDeviceByDescriptorLocked(String8& descriptor) const;
+    Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
     Device* getDeviceLocked(int32_t deviceId) const;
     Device* getDeviceByPathLocked(const char* devicePath) const;
+    /**
+     * Look through all available fd's (both for input devices and for video devices),
+     * and return the device pointer.
+     */
+    Device* getDeviceByFdLocked(int fd) const;
 
     bool hasKeycodeLocked(Device* device, int keycode) const;
 
@@ -450,6 +453,14 @@
     BitSet32 mControllerNumbers;
 
     KeyedVector<int32_t, Device*> mDevices;
+    /**
+     * Video devices that report touchscreen heatmap, but have not (yet) been paired
+     * with a specific input device. Video device discovery is independent from input device
+     * discovery, so the two types of devices could be found in any order.
+     * Ideally, video devices in this queue do not have an open fd, or at least aren't
+     * actively streaming.
+     */
+    std::vector<std::unique_ptr<TouchVideoDevice>> mUnattachedVideoDevices;
 
     Device *mOpeningDevices;
     Device *mClosingDevices;
@@ -457,19 +468,15 @@
     bool mNeedToSendFinishedDeviceScan;
     bool mNeedToReopenDevices;
     bool mNeedToScanDevices;
-    Vector<String8> mExcludedDevices;
+    std::vector<std::string> mExcludedDevices;
 
     int mEpollFd;
     int mINotifyFd;
     int mWakeReadPipeFd;
     int mWakeWritePipeFd;
 
-    // Ids used for epoll notifications not associated with devices.
-    static const uint32_t EPOLL_ID_INOTIFY = 0x80000001;
-    static const uint32_t EPOLL_ID_WAKE = 0x80000002;
-
-    // Epoll FD list size hint.
-    static const int EPOLL_SIZE_HINT = 8;
+    int mInputWd;
+    int mVideoWd;
 
     // Maximum number of signalled FDs to handle at a time.
     static const int EPOLL_MAX_EVENTS = 16;
diff --git a/services/inputflinger/InputListener.h b/services/inputflinger/include/InputListener.h
similarity index 77%
rename from services/inputflinger/InputListener.h
rename to services/inputflinger/include/InputListener.h
index 77afb34..13ae7dd 100644
--- a/services/inputflinger/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -17,7 +17,10 @@
 #ifndef _UI_INPUT_LISTENER_H
 #define _UI_INPUT_LISTENER_H
 
+#include <vector>
+
 #include <input/Input.h>
+#include <input/TouchVideoFrame.h>
 #include <utils/RefBase.h>
 #include <utils/Vector.h>
 
@@ -28,6 +31,12 @@
 
 /* Superclass of all input event argument objects */
 struct NotifyArgs {
+    uint32_t sequenceNum;
+
+    inline NotifyArgs() : sequenceNum(0) { }
+
+    inline explicit NotifyArgs(uint32_t sequenceNum) : sequenceNum(sequenceNum) { }
+
     virtual ~NotifyArgs() { }
 
     virtual void notify(const sp<InputListenerInterface>& listener) const = 0;
@@ -40,7 +49,9 @@
 
     inline NotifyConfigurationChangedArgs() { }
 
-    explicit NotifyConfigurationChangedArgs(nsecs_t eventTime);
+    bool operator==(const NotifyConfigurationChangedArgs& rhs) const;
+
+    NotifyConfigurationChangedArgs(uint32_t sequenceNum, nsecs_t eventTime);
 
     NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other);
 
@@ -55,6 +66,7 @@
     nsecs_t eventTime;
     int32_t deviceId;
     uint32_t source;
+    int32_t displayId;
     uint32_t policyFlags;
     int32_t action;
     int32_t flags;
@@ -65,9 +77,11 @@
 
     inline NotifyKeyArgs() { }
 
-    NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
-            int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
-            int32_t metaState, nsecs_t downTime);
+    NotifyKeyArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+            int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
+            int32_t scanCode, int32_t metaState, nsecs_t downTime);
+
+    bool operator==(const NotifyKeyArgs& rhs) const;
 
     NotifyKeyArgs(const NotifyKeyArgs& other);
 
@@ -82,14 +96,18 @@
     nsecs_t eventTime;
     int32_t deviceId;
     uint32_t source;
+    int32_t displayId;
     uint32_t policyFlags;
     int32_t action;
     int32_t actionButton;
     int32_t flags;
     int32_t metaState;
     int32_t buttonState;
+    /**
+     * Classification of the current touch gesture
+     */
+    MotionClassification classification;
     int32_t edgeFlags;
-    int32_t displayId;
     /**
      * A timestamp in the input device's time base, not the platform's.
      * The units are microseconds since the last reset.
@@ -103,20 +121,25 @@
     float xPrecision;
     float yPrecision;
     nsecs_t downTime;
+    std::vector<TouchVideoFrame> videoFrames;
 
     inline NotifyMotionArgs() { }
 
-    NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
+    NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+            int32_t displayId, uint32_t policyFlags,
             int32_t action, int32_t actionButton, int32_t flags,
-            int32_t metaState, int32_t buttonState,
-            int32_t edgeFlags, int32_t displayId, uint32_t deviceTimestamp, uint32_t pointerCount,
+            int32_t metaState, int32_t buttonState, MotionClassification classification,
+            int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount,
             const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-            float xPrecision, float yPrecision, nsecs_t downTime);
+            float xPrecision, float yPrecision, nsecs_t downTime,
+            const std::vector<TouchVideoFrame>& videoFrames);
 
     NotifyMotionArgs(const NotifyMotionArgs& other);
 
     virtual ~NotifyMotionArgs() { }
 
+    bool operator==(const NotifyMotionArgs& rhs) const;
+
     virtual void notify(const sp<InputListenerInterface>& listener) const;
 };
 
@@ -130,11 +153,13 @@
 
     inline NotifySwitchArgs() { }
 
-    NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
+    NotifySwitchArgs(uint32_t sequenceNum, nsecs_t eventTime, uint32_t policyFlags,
             uint32_t switchValues, uint32_t switchMask);
 
     NotifySwitchArgs(const NotifySwitchArgs& other);
 
+    bool operator==(const NotifySwitchArgs rhs) const;
+
     virtual ~NotifySwitchArgs() { }
 
     virtual void notify(const sp<InputListenerInterface>& listener) const;
@@ -149,10 +174,12 @@
 
     inline NotifyDeviceResetArgs() { }
 
-    NotifyDeviceResetArgs(nsecs_t eventTime, int32_t deviceId);
+    NotifyDeviceResetArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId);
 
     NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other);
 
+    bool operator==(const NotifyDeviceResetArgs& rhs) const;
+
     virtual ~NotifyDeviceResetArgs() { }
 
     virtual void notify(const sp<InputListenerInterface>& listener) const;
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
new file mode 100644
index 0000000..fe1d4d0
--- /dev/null
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_READER_BASE_H
+#define _UI_INPUT_READER_BASE_H
+
+#include "PointerControllerInterface.h"
+
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/DisplayViewport.h>
+#include <input/VelocityControl.h>
+#include <input/VelocityTracker.h>
+#include <utils/KeyedVector.h>
+#include <utils/Thread.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+
+#include <optional>
+#include <stddef.h>
+#include <unistd.h>
+#include <unordered_map>
+#include <vector>
+
+// Maximum supported size of a vibration pattern.
+// Must be at least 2.
+#define MAX_VIBRATE_PATTERN_SIZE 100
+
+// Maximum allowable delay value in a vibration pattern before
+// which the delay will be truncated.
+#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
+
+namespace android {
+
+/* Processes raw input events and sends cooked event data to an input listener. */
+class InputReaderInterface : public virtual RefBase {
+protected:
+    InputReaderInterface() { }
+    virtual ~InputReaderInterface() { }
+
+public:
+    /* Dumps the state of the input reader.
+     *
+     * This method may be called on any thread (usually by the input manager). */
+    virtual void dump(std::string& dump) = 0;
+
+    /* Called by the heatbeat to ensures that the reader has not deadlocked. */
+    virtual void monitor() = 0;
+
+    /* Returns true if the input device is enabled. */
+    virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
+
+    /* Runs a single iteration of the processing loop.
+     * Nominally reads and processes one incoming message from the EventHub.
+     *
+     * This method should be called on the input reader thread.
+     */
+    virtual void loopOnce() = 0;
+
+    /* Gets information about all input devices.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices) = 0;
+
+    /* Query current input state. */
+    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+            int32_t scanCode) = 0;
+    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+            int32_t keyCode) = 0;
+    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+            int32_t sw) = 0;
+
+    /* Toggle Caps Lock */
+    virtual void toggleCapsLockState(int32_t deviceId) = 0;
+
+    /* Determine whether physical keys exist for the given framework-domain key codes. */
+    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+            size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
+
+    /* Requests that a reconfiguration of all input devices.
+     * The changes flag is a bitfield that indicates what has changed and whether
+     * the input devices must all be reopened. */
+    virtual void requestRefreshConfiguration(uint32_t changes) = 0;
+
+    /* Controls the vibrator of a particular input device. */
+    virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+            ssize_t repeat, int32_t token) = 0;
+    virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
+
+    /* Return true if the device can send input events to the specified display. */
+    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
+};
+
+/* Reads raw events from the event hub and processes them, endlessly. */
+class InputReaderThread : public Thread {
+public:
+    explicit InputReaderThread(const sp<InputReaderInterface>& reader);
+    virtual ~InputReaderThread();
+
+private:
+    sp<InputReaderInterface> mReader;
+
+    virtual bool threadLoop();
+};
+
+/*
+ * Input reader configuration.
+ *
+ * Specifies various options that modify the behavior of the input reader.
+ */
+struct InputReaderConfiguration {
+    // Describes changes that have occurred.
+    enum {
+        // The pointer speed changed.
+        CHANGE_POINTER_SPEED = 1 << 0,
+
+        // The pointer gesture control changed.
+        CHANGE_POINTER_GESTURE_ENABLEMENT = 1 << 1,
+
+        // The display size or orientation changed.
+        CHANGE_DISPLAY_INFO = 1 << 2,
+
+        // The visible touches option changed.
+        CHANGE_SHOW_TOUCHES = 1 << 3,
+
+        // The keyboard layouts must be reloaded.
+        CHANGE_KEYBOARD_LAYOUTS = 1 << 4,
+
+        // The device name alias supplied by the may have changed for some devices.
+        CHANGE_DEVICE_ALIAS = 1 << 5,
+
+        // The location calibration matrix changed.
+        CHANGE_TOUCH_AFFINE_TRANSFORMATION = 1 << 6,
+
+        // The presence of an external stylus has changed.
+        CHANGE_EXTERNAL_STYLUS_PRESENCE = 1 << 7,
+
+        // The pointer capture mode has changed.
+        CHANGE_POINTER_CAPTURE = 1 << 8,
+
+        // The set of disabled input devices (disabledDevices) has changed.
+        CHANGE_ENABLED_STATE = 1 << 9,
+
+        // All devices must be reopened.
+        CHANGE_MUST_REOPEN = 1 << 31,
+    };
+
+    // Gets the amount of time to disable virtual keys after the screen is touched
+    // in order to filter out accidental virtual key presses due to swiping gestures
+    // or taps near the edge of the display.  May be 0 to disable the feature.
+    nsecs_t virtualKeyQuietTime;
+
+    // The excluded device names for the platform.
+    // Devices with these names will be ignored.
+    std::vector<std::string> excludedDeviceNames;
+
+    // The associations between input ports and display ports.
+    // Used to determine which DisplayViewport should be tied to which InputDevice.
+    std::unordered_map<std::string, uint8_t> portAssociations;
+
+    // Velocity control parameters for mouse pointer movements.
+    VelocityControlParameters pointerVelocityControlParameters;
+
+    // Velocity control parameters for mouse wheel movements.
+    VelocityControlParameters wheelVelocityControlParameters;
+
+    // True if pointer gestures are enabled.
+    bool pointerGesturesEnabled;
+
+    // Quiet time between certain pointer gesture transitions.
+    // Time to allow for all fingers or buttons to settle into a stable state before
+    // starting a new gesture.
+    nsecs_t pointerGestureQuietInterval;
+
+    // The minimum speed that a pointer must travel for us to consider switching the active
+    // touch pointer to it during a drag.  This threshold is set to avoid switching due
+    // to noise from a finger resting on the touch pad (perhaps just pressing it down).
+    float pointerGestureDragMinSwitchSpeed; // in pixels per second
+
+    // Tap gesture delay time.
+    // The time between down and up must be less than this to be considered a tap.
+    nsecs_t pointerGestureTapInterval;
+
+    // Tap drag gesture delay time.
+    // The time between the previous tap's up and the next down must be less than
+    // this to be considered a drag.  Otherwise, the previous tap is finished and a
+    // new tap begins.
+    //
+    // Note that the previous tap will be held down for this entire duration so this
+    // interval must be shorter than the long press timeout.
+    nsecs_t pointerGestureTapDragInterval;
+
+    // The distance in pixels that the pointer is allowed to move from initial down
+    // to up and still be called a tap.
+    float pointerGestureTapSlop; // in pixels
+
+    // Time after the first touch points go down to settle on an initial centroid.
+    // This is intended to be enough time to handle cases where the user puts down two
+    // fingers at almost but not quite exactly the same time.
+    nsecs_t pointerGestureMultitouchSettleInterval;
+
+    // The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
+    // at least two pointers have moved at least this far from their starting place.
+    float pointerGestureMultitouchMinDistance; // in pixels
+
+    // The transition from PRESS to SWIPE gesture mode can only occur when the
+    // cosine of the angle between the two vectors is greater than or equal to than this value
+    // which indicates that the vectors are oriented in the same direction.
+    // When the vectors are oriented in the exactly same direction, the cosine is 1.0.
+    // (In exactly opposite directions, the cosine is -1.0.)
+    float pointerGestureSwipeTransitionAngleCosine;
+
+    // The transition from PRESS to SWIPE gesture mode can only occur when the
+    // fingers are no more than this far apart relative to the diagonal size of
+    // the touch pad.  For example, a ratio of 0.5 means that the fingers must be
+    // no more than half the diagonal size of the touch pad apart.
+    float pointerGestureSwipeMaxWidthRatio;
+
+    // The gesture movement speed factor relative to the size of the display.
+    // Movement speed applies when the fingers are moving in the same direction.
+    // Without acceleration, a full swipe of the touch pad diagonal in movement mode
+    // will cover this portion of the display diagonal.
+    float pointerGestureMovementSpeedRatio;
+
+    // The gesture zoom speed factor relative to the size of the display.
+    // Zoom speed applies when the fingers are mostly moving relative to each other
+    // to execute a scale gesture or similar.
+    // Without acceleration, a full swipe of the touch pad diagonal in zoom mode
+    // will cover this portion of the display diagonal.
+    float pointerGestureZoomSpeedRatio;
+
+    // True to show the location of touches on the touch screen as spots.
+    bool showTouches;
+
+    // True if pointer capture is enabled.
+    bool pointerCapture;
+
+    // The set of currently disabled input devices.
+    SortedVector<int32_t> disabledDevices;
+
+    InputReaderConfiguration() :
+            virtualKeyQuietTime(0),
+            pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
+            wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
+            pointerGesturesEnabled(true),
+            pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
+            pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
+            pointerGestureTapInterval(150 * 1000000LL), // 150 ms
+            pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
+            pointerGestureTapSlop(10.0f), // 10 pixels
+            pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
+            pointerGestureMultitouchMinDistance(15), // 15 pixels
+            pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
+            pointerGestureSwipeMaxWidthRatio(0.25f),
+            pointerGestureMovementSpeedRatio(0.8f),
+            pointerGestureZoomSpeedRatio(0.3f),
+            showTouches(false), pointerCapture(false) { }
+
+    std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
+    std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
+            const;
+    std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t physicalPort) const;
+    void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
+
+
+    void dump(std::string& dump) const;
+    void dumpViewport(std::string& dump, const DisplayViewport& viewport) const;
+
+private:
+    std::vector<DisplayViewport> mDisplays;
+};
+
+struct TouchAffineTransformation {
+    float x_scale;
+    float x_ymix;
+    float x_offset;
+    float y_xmix;
+    float y_scale;
+    float y_offset;
+
+    TouchAffineTransformation() :
+        x_scale(1.0f), x_ymix(0.0f), x_offset(0.0f),
+        y_xmix(0.0f), y_scale(1.0f), y_offset(0.0f) {
+    }
+
+    TouchAffineTransformation(float xscale, float xymix, float xoffset,
+            float yxmix, float yscale, float yoffset) :
+        x_scale(xscale), x_ymix(xymix), x_offset(xoffset),
+        y_xmix(yxmix), y_scale(yscale), y_offset(yoffset) {
+    }
+
+    void applyTo(float& x, float& y) const;
+};
+
+/*
+ * Input reader policy interface.
+ *
+ * The input reader policy is used by the input reader to interact with the Window Manager
+ * and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI.  This interface is also mocked in the unit tests.
+ *
+ * These methods must NOT re-enter the input reader since they may be called while
+ * holding the input reader lock.
+ */
+class InputReaderPolicyInterface : public virtual RefBase {
+protected:
+    InputReaderPolicyInterface() { }
+    virtual ~InputReaderPolicyInterface() { }
+
+public:
+    /* Gets the input reader configuration. */
+    virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) = 0;
+
+    /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
+    virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0;
+
+    /* Notifies the input reader policy that some input devices have changed
+     * and provides information about all current input devices.
+     */
+    virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) = 0;
+
+    /* Gets the keyboard layout for a particular input device. */
+    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(
+            const InputDeviceIdentifier& identifier) = 0;
+
+    /* Gets a user-supplied alias for a particular input device, or an empty string if none. */
+    virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
+
+    /* Gets the affine calibration associated with the specified device. */
+    virtual TouchAffineTransformation getTouchAffineTransformation(
+            const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_READER_COMMON_H
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/inputflinger/include/InputReaderFactory.h
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/inputflinger/include/InputReaderFactory.h
index e6ac6bf..9db6233 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/inputflinger/include/InputReaderFactory.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <utils/StrongPointer.h>
 
 namespace android {
-namespace mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+class InputReaderInterface;
+class InputReaderPolicyInterface;
+class InputListenerInterface;
 
-} // namespace mock
+sp<InputReaderInterface> createInputReader(
+        const sp<InputReaderPolicyInterface>& policy,
+        const sp<InputListenerInterface>& listener);
+
 } // namespace android
diff --git a/services/inputflinger/include/InputReporterInterface.h b/services/inputflinger/include/InputReporterInterface.h
new file mode 100644
index 0000000..906d7f2
--- /dev/null
+++ b/services/inputflinger/include/InputReporterInterface.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 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 _UI_INPUT_REPORTER_INTERFACE_H
+#define _UI_INPUT_REPORTER_INTERFACE_H
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+/*
+ * The interface used by the InputDispatcher to report information about input events after
+ * it is sent to the application, such as if a key is unhandled or dropped.
+ */
+class InputReporterInterface : public virtual RefBase {
+protected:
+    virtual ~InputReporterInterface() { }
+
+public:
+    // Report a key that was not handled by the system or apps.
+    // A key event is unhandled if:
+    //   - The event was not handled and there is no fallback key; or
+    //   - The event was not handled and it has a fallback key,
+    //       but the fallback key was not handled.
+    virtual void reportUnhandledKey(uint32_t sequenceNum) = 0;
+
+    // Report a key that was dropped by InputDispatcher.
+    // A key can be dropped for several reasons. See the enum
+    // InputDispatcher::DropReason for details.
+    virtual void reportDroppedKey(uint32_t sequenceNum) = 0;
+};
+
+/*
+ * Factory method for InputReporter.
+ */
+sp<InputReporterInterface> createInputReporter();
+
+} // namespace android
+
+#endif // _UI_INPUT_REPORTER_INTERFACE_H
diff --git a/services/inputflinger/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
similarity index 96%
rename from services/inputflinger/PointerControllerInterface.h
rename to services/inputflinger/include/PointerControllerInterface.h
index e94dd94..bc0f1f9 100644
--- a/services/inputflinger/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -98,6 +98,9 @@
 
     /* Removes all spots. */
     virtual void clearSpots() = 0;
+
+    /* Gets the id of the display where the pointer should be shown. */
+    virtual int32_t getDisplayId() const = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/include/TouchVideoDevice.h b/services/inputflinger/include/TouchVideoDevice.h
new file mode 100644
index 0000000..7ff2653
--- /dev/null
+++ b/services/inputflinger/include/TouchVideoDevice.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+#define _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+
+#include <array>
+#include <android-base/unique_fd.h>
+#include <input/TouchVideoFrame.h>
+#include <optional>
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace android {
+
+/**
+ * Represents a video device that uses v4l2 api to report touch heatmap data.
+ */
+class TouchVideoDevice {
+public:
+    /**
+     * Create a new TouchVideoDevice for the path provided.
+     * Return nullptr upon failure.
+     */
+    static std::unique_ptr<TouchVideoDevice> create(std::string devicePath);
+    ~TouchVideoDevice();
+
+    bool hasValidFd() const { return mFd.get() != INVALID_FD; }
+    /**
+     * Obtain the file descriptor associated with this video device.
+     * Could be used for adding to epoll.
+     */
+    int getFd() const { return mFd.get(); }
+    /**
+     * Get the name of this video device.
+     */
+    const std::string& getName() const { return mName; }
+    /**
+     * Get the file path of this video device.
+     */
+    const std::string& getPath() const { return mPath; }
+    /**
+     * Get the width of the heatmap frame
+     */
+    uint32_t getWidth() const { return mWidth; }
+    /**
+     * Get the height of the heatmap frame
+     */
+    uint32_t getHeight() const { return mHeight; }
+    /**
+     * Direct read of the frame. Stores the frame into internal buffer.
+     * Return the number of frames that were successfully read.
+     *
+     * This function should not be called unless buffer is ready!
+     * This must be checked with select, poll, epoll, or similar api first.
+     * If epoll indicates that there is data ready to read, but this function
+     * returns zero, then it is likely an error occurred.
+     */
+    size_t readAndQueueFrames();
+    /**
+     * Return all of the queued frames, and erase them from the local buffer.
+     */
+    std::vector<TouchVideoFrame> consumeFrames();
+    /**
+     * Get string representation of this video device.
+     */
+    std::string dump() const;
+
+private:
+    android::base::unique_fd mFd;
+    std::string mName;
+    std::string mPath;
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+
+    static constexpr int INVALID_FD = -1;
+    /**
+     * How many buffers to request for heatmap.
+     * The kernel driver will be allocating these buffers for us,
+     * and will provide memory locations to read these from.
+     */
+    static constexpr size_t NUM_BUFFERS = 3;
+    std::array<const int16_t*, NUM_BUFFERS> mReadLocations;
+    /**
+     * How many buffers to keep for the internal queue. When the internal buffer
+     * exceeds this capacity, oldest frames will be dropped.
+     */
+    static constexpr size_t MAX_QUEUE_SIZE = 10;
+    std::vector<TouchVideoFrame> mFrames;
+
+    /**
+     * The constructor is private because opening a v4l2 device requires many checks.
+     * To get a new TouchVideoDevice, use 'create' instead.
+     */
+    explicit TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath,
+            uint32_t width, uint32_t height,
+            const std::array<const int16_t*, NUM_BUFFERS>& readLocations);
+    /**
+     * Read all currently available frames.
+     */
+    std::vector<TouchVideoFrame> readFrames();
+    /**
+     * Read a single frame. May return nullopt if no data is currently available for reading.
+     */
+    std::optional<TouchVideoFrame> readFrame();
+};
+} // namespace android
+#endif //_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index afaf139..1835449 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -3,16 +3,22 @@
 cc_test {
     name: "inputflinger_tests",
     srcs: [
-        "InputReader_test.cpp",
+        "BlockingQueue_test.cpp",
+        "TestInputListener.cpp",
+        "InputClassifier_test.cpp",
         "InputDispatcher_test.cpp",
+        "InputReader_test.cpp",
     ],
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wextra",
         "-Wno-unused-parameter",
     ],
     shared_libs: [
+        "android.hardware.input.classifier@1.0",
         "libbase",
+        "libbinder",
         "libcutils",
         "liblog",
         "libutils",
@@ -21,6 +27,8 @@
         "libui",
         "libinput",
         "libinputflinger",
+        "libinputreader",
+        "libinputflinger_base",
         "libinputservice",
     ],
 }
diff --git a/services/inputflinger/tests/BlockingQueue_test.cpp b/services/inputflinger/tests/BlockingQueue_test.cpp
new file mode 100644
index 0000000..0dea8d7
--- /dev/null
+++ b/services/inputflinger/tests/BlockingQueue_test.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2019 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 "../BlockingQueue.h"
+
+
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+
+
+// --- BlockingQueueTest ---
+
+/**
+ * Sanity check of basic pop and push operation.
+ */
+TEST(BlockingQueueTest, Queue_AddAndRemove) {
+    constexpr size_t capacity = 10;
+    BlockingQueue<int> queue(capacity);
+
+    ASSERT_TRUE(queue.push(1));
+    ASSERT_EQ(queue.pop(), 1);
+}
+
+/**
+ * Make sure the queue has strict capacity limits.
+ */
+TEST(BlockingQueueTest, Queue_ReachesCapacity) {
+    constexpr size_t capacity = 3;
+    BlockingQueue<int> queue(capacity);
+
+    // First 3 elements should be added successfully
+    ASSERT_TRUE(queue.push(1));
+    ASSERT_TRUE(queue.push(2));
+    ASSERT_TRUE(queue.push(3));
+    ASSERT_FALSE(queue.push(4)) << "Queue should reach capacity at size " << capacity;
+}
+
+/**
+ * Make sure the queue maintains FIFO order.
+ * Add elements and remove them, and check the order.
+ */
+TEST(BlockingQueueTest, Queue_isFIFO) {
+    constexpr size_t capacity = 10;
+    BlockingQueue<int> queue(capacity);
+
+    for (size_t i = 0; i < capacity; i++) {
+        ASSERT_TRUE(queue.push(static_cast<int>(i)));
+    }
+    for (size_t i = 0; i < capacity; i++) {
+        ASSERT_EQ(queue.pop(), static_cast<int>(i));
+    }
+}
+
+TEST(BlockingQueueTest, Queue_Clears) {
+    constexpr size_t capacity = 2;
+    BlockingQueue<int> queue(capacity);
+
+    queue.push(1);
+    queue.push(2);
+    queue.clear();
+    queue.push(3);
+    // Should no longer receive elements 1 and 2
+    ASSERT_EQ(3, queue.pop());
+}
+
+TEST(BlockingQueueTest, Queue_Erases) {
+    constexpr size_t capacity = 4;
+    BlockingQueue<int> queue(capacity);
+
+    queue.push(1);
+    queue.push(2);
+    queue.push(3);
+    queue.push(4);
+    // Erase elements 2 and 4
+    queue.erase([](int element) { return element == 2 || element == 4; });
+    // Should no longer receive elements 2 and 4
+    ASSERT_EQ(1, queue.pop());
+    ASSERT_EQ(3, queue.pop());
+}
+
+// --- BlockingQueueTest - Multiple threads ---
+
+TEST(BlockingQueueTest, Queue_AllowsMultipleThreads) {
+    constexpr size_t capacity = 100; // large capacity to increase likelihood that threads overlap
+    BlockingQueue<int> queue(capacity);
+
+    // Fill queue from a different thread
+    std::thread fillQueue([&queue](){
+        for (size_t i = 0; i < capacity; i++) {
+            ASSERT_TRUE(queue.push(static_cast<int>(i)));
+        }
+    });
+
+    // Make sure all elements are received in correct order
+    for (size_t i = 0; i < capacity; i++) {
+        ASSERT_EQ(queue.pop(), static_cast<int>(i));
+    }
+
+    fillQueue.join();
+}
+
+/**
+ * When the queue has no elements, and pop is called, it should block
+ * the current thread until an element is added to the queue (from another thread).
+ * Here we create a separate thread and call pop on an empty queue. Next,
+ * we check that the thread is blocked.
+ */
+TEST(BlockingQueueTest, Queue_BlocksWhileWaitingForElements) {
+    constexpr size_t capacity = 1;
+    BlockingQueue<int> queue(capacity);
+
+    std::atomic_bool hasReceivedElement = false;
+
+    // fill queue from a different thread
+    std::thread waitUntilHasElements([&queue, &hasReceivedElement](){
+        queue.pop(); // This should block until an element has been added
+        hasReceivedElement = true;
+    });
+
+    ASSERT_FALSE(hasReceivedElement);
+    queue.push(1);
+    waitUntilHasElements.join();
+    ASSERT_TRUE(hasReceivedElement);
+}
+
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
new file mode 100644
index 0000000..20699c9
--- /dev/null
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2019 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 "../InputClassifier.h"
+#include <gtest/gtest.h>
+
+#include "TestInputListener.h"
+
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+
+using namespace android::hardware::input;
+
+namespace android {
+
+// --- InputClassifierTest ---
+
+static NotifyMotionArgs generateBasicMotionArgs() {
+    // Create a basic motion event for testing
+    constexpr size_t pointerCount = 1;
+    PointerProperties properties[pointerCount];
+    properties[0].id = 0;
+    properties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+    PointerCoords coords[pointerCount];
+    coords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 1);
+    coords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
+    static constexpr nsecs_t downTime = 2;
+    NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/,
+            AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN,
+            0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE,
+            AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/,
+            0/*pointerCount*/, properties, coords, 0/*xPrecision*/, 0/*yPrecision*/,
+            downTime, {}/*videoFrames*/);
+    return motionArgs;
+}
+
+class InputClassifierTest : public testing::Test {
+protected:
+    sp<InputClassifierInterface> mClassifier;
+    sp<TestInputListener> mTestListener;
+
+    virtual void SetUp() override {
+        mTestListener = new TestInputListener();
+        mClassifier = new InputClassifier(mTestListener);
+    }
+
+    virtual void TearDown() override {
+        mClassifier.clear();
+        mTestListener.clear();
+    }
+};
+
+/**
+ * Create a basic configuration change and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyConfigurationChangedArgs) {
+    // Create a basic configuration change and send to classifier
+    NotifyConfigurationChangedArgs args(1/*sequenceNum*/, 2/*eventTime*/);
+
+    mClassifier->notifyConfigurationChanged(&args);
+    NotifyConfigurationChangedArgs outArgs;
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled(&outArgs));
+    ASSERT_EQ(args, outArgs);
+}
+
+TEST_F(InputClassifierTest, SendToNextStage_NotifyKeyArgs) {
+    // Create a basic key event and send to classifier
+    NotifyKeyArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/, AINPUT_SOURCE_KEYBOARD,
+            ADISPLAY_ID_DEFAULT, 0/*policyFlags*/, AKEY_EVENT_ACTION_DOWN, 4/*flags*/,
+            AKEYCODE_HOME, 5/*scanCode*/, AMETA_NONE, 6/*downTime*/);
+
+    mClassifier->notifyKey(&args);
+    NotifyKeyArgs outArgs;
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&outArgs));
+    ASSERT_EQ(args, outArgs);
+}
+
+
+/**
+ * Create a basic motion event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyMotionArgs) {
+    NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+    mClassifier->notifyMotion(&motionArgs);
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(motionArgs, args);
+}
+
+/**
+ * Create a basic switch event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifySwitchArgs) {
+    NotifySwitchArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*policyFlags*/, 4/*switchValues*/,
+            5/*switchMask*/);
+
+    mClassifier->notifySwitch(&args);
+    NotifySwitchArgs outArgs;
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifySwitchWasCalled(&outArgs));
+    ASSERT_EQ(args, outArgs);
+}
+
+/**
+ * Create a basic device reset event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) {
+    NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/);
+
+    mClassifier->notifyDeviceReset(&args);
+    NotifyDeviceResetArgs outArgs;
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyDeviceResetWasCalled(&outArgs));
+    ASSERT_EQ(args, outArgs);
+}
+
+// --- MotionClassifierTest ---
+
+class MotionClassifierTest : public testing::Test {
+protected:
+    std::unique_ptr<MotionClassifierInterface> mMotionClassifier;
+
+    virtual void SetUp() override {
+        sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+                classifier::V1_0::IInputClassifier::getService();
+        if (service) {
+            mMotionClassifier = std::make_unique<MotionClassifier>(service);
+        }
+    }
+};
+
+/**
+ * Since MotionClassifier creates a new thread to communicate with HAL,
+ * it's not really expected to ever exit. However, for testing purposes,
+ * we need to ensure that it is able to exit cleanly.
+ * If the thread is not properly cleaned up, it will generate SIGABRT.
+ * The logic for exiting the thread and cleaning up the resources is inside
+ * the destructor. Here, we just make sure the destructor does not crash.
+ */
+TEST_F(MotionClassifierTest, Destructor_DoesNotCrash) {
+    mMotionClassifier = nullptr;
+}
+
+/**
+ * Make sure MotionClassifier can handle events that don't have any
+ * video frames.
+ */
+TEST_F(MotionClassifierTest, Classify_NoVideoFrames) {
+    NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+    // We are not checking the return value, because we can't be making assumptions
+    // about the HAL operation, since it will be highly hardware-dependent
+    if (mMotionClassifier) {
+        ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+    }
+}
+
+/**
+ * Make sure nothing crashes when a videoFrame is sent.
+ */
+TEST_F(MotionClassifierTest, Classify_OneVideoFrame) {
+    NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+    std::vector<int16_t> videoData = {1, 2, 3, 4};
+    timeval timestamp = { 1, 1};
+    TouchVideoFrame frame(2, 2, std::move(videoData), timestamp);
+    motionArgs.videoFrames = {frame};
+
+    // We are not checking the return value, because we can't be making assumptions
+    // about the HAL operation, since it will be highly hardware-dependent
+    if (mMotionClassifier) {
+        ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+    }
+}
+
+/**
+ * Make sure nothing crashes when 2 videoFrames are sent.
+ */
+TEST_F(MotionClassifierTest, Classify_TwoVideoFrames) {
+    NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+    std::vector<int16_t> videoData1 = {1, 2, 3, 4};
+    timeval timestamp1 = { 1, 1};
+    TouchVideoFrame frame1(2, 2, std::move(videoData1), timestamp1);
+
+    std::vector<int16_t> videoData2 = {6, 6, 6, 6};
+    timeval timestamp2 = { 1, 2};
+    TouchVideoFrame frame2(2, 2, std::move(videoData2), timestamp2);
+
+    motionArgs.videoFrames = {frame1, frame2};
+
+    // We are not checking the return value, because we can't be making assumptions
+    // about the HAL operation, since it will be highly hardware-dependent
+    if (mMotionClassifier) {
+        ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+    }
+}
+
+/**
+ * Make sure MotionClassifier does not crash when it is reset.
+ */
+TEST_F(MotionClassifierTest, Reset_DoesNotCrash) {
+    if (mMotionClassifier) {
+        ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset());
+    }
+}
+
+/**
+ * Make sure MotionClassifier does not crash when a device is reset.
+ */
+TEST_F(MotionClassifierTest, DeviceReset_DoesNotCrash) {
+    NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/);
+    if (mMotionClassifier) {
+        ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset(args));
+    }
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index aa6df24..0f987c3 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,6 +16,8 @@
 
 #include "../InputDispatcher.h"
 
+#include <binder/Binder.h>
+
 #include <gtest/gtest.h>
 #include <linux/input.h>
 
@@ -28,7 +30,7 @@
 static const int32_t DEVICE_ID = 1;
 
 // An arbitrary display id.
-static const int32_t DISPLAY_ID = 0;
+static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
 
 // An arbitrary injector pid / uid pair that has permission to inject events.
 static const int32_t INJECTOR_PID = 999;
@@ -46,26 +48,90 @@
 
 public:
     FakeInputDispatcherPolicy() {
+        mInputEventFiltered = false;
+        mTime = -1;
+        mAction = -1;
+        mDisplayId = -1;
+    }
+
+    void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
+        ASSERT_TRUE(mInputEventFiltered)
+                << "Expected filterInputEvent() to have been called.";
+
+        ASSERT_EQ(mTime, args->eventTime)
+                << "Expected time of filtered event was not matched";
+        ASSERT_EQ(mAction, args->action)
+                << "Expected action of filtered event was not matched";
+        ASSERT_EQ(mDisplayId, args->displayId)
+                << "Expected displayId of filtered event was not matched";
+
+        reset();
+    }
+
+    void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyKeyArgs* args) {
+        ASSERT_TRUE(mInputEventFiltered)
+                << "Expected filterInputEvent() to have been called.";
+
+        ASSERT_EQ(mTime, args->eventTime)
+                << "Expected time of filtered event was not matched";
+        ASSERT_EQ(mAction, args->action)
+                << "Expected action of filtered event was not matched";
+        ASSERT_EQ(mDisplayId, args->displayId)
+                << "Expected displayId of filtered event was not matched";
+
+        reset();
+    }
+
+    void assertFilterInputEventWasNotCalled() {
+        ASSERT_FALSE(mInputEventFiltered)
+                << "Expected filterInputEvent() to not have been called.";
     }
 
 private:
+    bool mInputEventFiltered;
+    nsecs_t mTime;
+    int32_t mAction;
+    int32_t mDisplayId;
+
     virtual void notifyConfigurationChanged(nsecs_t) {
     }
 
     virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&,
-            const sp<InputWindowHandle>&,
+            const sp<IBinder>&,
             const std::string&) {
         return 0;
     }
 
-    virtual void notifyInputChannelBroken(const sp<InputWindowHandle>&) {
+    virtual void notifyInputChannelBroken(const sp<IBinder>&) {
+    }
+
+    virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) {
     }
 
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
         *outConfig = mConfig;
     }
 
-    virtual bool filterInputEvent(const InputEvent*, uint32_t) {
+    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+        switch (inputEvent->getType()) {
+            case AINPUT_EVENT_TYPE_KEY: {
+                const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
+                mTime = keyEvent->getEventTime();
+                mAction = keyEvent->getAction();
+                mDisplayId = keyEvent->getDisplayId();
+                break;
+            }
+
+            case AINPUT_EVENT_TYPE_MOTION: {
+                const MotionEvent* motionEvent = static_cast<const MotionEvent*>(inputEvent);
+                mTime = motionEvent->getEventTime();
+                mAction = motionEvent->getAction();
+                mDisplayId = motionEvent->getDisplayId();
+                break;
+            }
+        }
+
+        mInputEventFiltered = true;
         return true;
     }
 
@@ -75,12 +141,12 @@
     virtual void interceptMotionBeforeQueueing(nsecs_t, uint32_t&) {
     }
 
-    virtual nsecs_t interceptKeyBeforeDispatching(const sp<InputWindowHandle>&,
+    virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&,
             const KeyEvent*, uint32_t) {
         return 0;
     }
 
-    virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>&,
+    virtual bool dispatchUnhandledKey(const sp<IBinder>&,
             const KeyEvent*, uint32_t, KeyEvent*) {
         return false;
     }
@@ -94,6 +160,13 @@
     virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) {
         return false;
     }
+
+    void reset() {
+        mInputEventFiltered = false;
+        mTime = -1;
+        mAction = -1;
+        mDisplayId = -1;
+    }
 };
 
 
@@ -103,13 +176,20 @@
 protected:
     sp<FakeInputDispatcherPolicy> mFakePolicy;
     sp<InputDispatcher> mDispatcher;
+    sp<InputDispatcherThread> mDispatcherThread;
 
     virtual void SetUp() {
         mFakePolicy = new FakeInputDispatcherPolicy();
         mDispatcher = new InputDispatcher(mFakePolicy);
+        mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+        //Start InputDispatcher thread
+        mDispatcherThread = new InputDispatcherThread(mDispatcher);
+        mDispatcherThread->run("InputDispatcherTest", PRIORITY_URGENT_DISPLAY);
     }
 
     virtual void TearDown() {
+        mDispatcherThread->requestExit();
+        mDispatcherThread.clear();
         mFakePolicy.clear();
         mDispatcher.clear();
     }
@@ -120,20 +200,20 @@
     KeyEvent event;
 
     // Rejects undefined key actions.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
             /*action*/ -1, 0,
             AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject key events with undefined action.";
 
     // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
             AKEY_EVENT_ACTION_MULTIPLE, 0,
             AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject key events with ACTION_MULTIPLE.";
 }
@@ -148,109 +228,686 @@
         pointerCoords[i].clear();
     }
 
+    // Some constants commonly used below
+    constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+    constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
+    constexpr int32_t metaState = AMETA_NONE;
+    constexpr MotionClassification classification = MotionClassification::NONE;
+
     // Rejects undefined motion actions.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
-            /*action*/ -1, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME,
-            /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID,
+            /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with undefined action.";
 
     // Rejects pointer down with invalid index.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID,
             AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME,
-            /*pointerCount*/ 1, pointerProperties, pointerCoords);
+            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer down index too large.";
 
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID,
             AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME,
-            /*pointerCount*/ 1, pointerProperties, pointerCoords);
+            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer down index too small.";
 
     // Rejects pointer up with invalid index.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID,
             AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME,
-            /*pointerCount*/ 1, pointerProperties, pointerCoords);
+            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer up index too large.";
 
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID,
             AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME,
-            /*pointerCount*/ 1, pointerProperties, pointerCoords);
+            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer up index too small.";
 
     // Rejects motion events with invalid number of pointers.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
-            AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME,
-            /*pointerCount*/ 0, pointerProperties, pointerCoords);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID,
+            AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with 0 pointers.";
 
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
-            AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID,
+            AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with more than MAX_POINTERS pointers.";
 
     // Rejects motion events with invalid pointer ids.
     pointerProperties[0].id = -1;
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
-            AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME,
-            /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID,
+            AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer ids less than 0.";
 
     pointerProperties[0].id = MAX_POINTER_ID + 1;
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
-            AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME,
-            /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID,
+            AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
 
     // Rejects motion events with duplicate pointer ids.
     pointerProperties[0].id = 1;
     pointerProperties[1].id = 1;
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
-            AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME,
-            /*pointerCount*/ 2, pointerProperties, pointerCoords);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID,
+            AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event, DISPLAY_ID,
+            &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with duplicate pointer ids.";
 }
 
+// --- InputDispatcherTest SetInputWindowTest ---
+static const int32_t INJECT_EVENT_TIMEOUT = 500;
+static const int32_t DISPATCHING_TIMEOUT = 100;
+
+class FakeApplicationHandle : public InputApplicationHandle {
+public:
+    FakeApplicationHandle() {}
+    virtual ~FakeApplicationHandle() {}
+
+    virtual bool updateInfo() {
+        if (!mInfo) {
+            mInfo = new InputApplicationInfo();
+        }
+        mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
+        return true;
+    }
+};
+
+class FakeInputReceiver {
+public:
+    void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId,
+            int32_t expectedFlags = 0) {
+        uint32_t consumeSeq;
+        InputEvent* event;
+        status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
+            &consumeSeq, &event);
+
+        ASSERT_EQ(OK, status)
+                << mName.c_str() << ": consumer consume should return OK.";
+        ASSERT_TRUE(event != nullptr)
+                << mName.c_str() << ": consumer should have returned non-NULL event.";
+        ASSERT_EQ(expectedEventType, event->getType())
+                << mName.c_str() << ": event type should match.";
+
+        ASSERT_EQ(expectedDisplayId, event->getDisplayId())
+                << mName.c_str() << ": event displayId should be the same as expected.";
+
+        int32_t flags;
+        switch (expectedEventType) {
+            case AINPUT_EVENT_TYPE_KEY: {
+                KeyEvent* typedEvent = static_cast<KeyEvent*>(event);
+                flags = typedEvent->getFlags();
+                break;
+            }
+            case AINPUT_EVENT_TYPE_MOTION: {
+                MotionEvent* typedEvent = static_cast<MotionEvent*>(event);
+                flags = typedEvent->getFlags();
+                break;
+            }
+            default: {
+                FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
+            }
+        }
+        ASSERT_EQ(expectedFlags, flags)
+                << mName.c_str() << ": event flags should be the same as expected.";
+
+        status = mConsumer->sendFinishedSignal(consumeSeq, handled());
+        ASSERT_EQ(OK, status)
+                << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
+    }
+
+    void assertNoEvents() {
+        uint32_t consumeSeq;
+        InputEvent* event;
+        status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
+            &consumeSeq, &event);
+        ASSERT_NE(OK, status)
+                << mName.c_str()
+                << ": should not have received any events, so consume(..) should not return OK.";
+    }
+
+protected:
+        explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher,
+            const std::string name, int32_t displayId) :
+                mDispatcher(dispatcher), mName(name), mDisplayId(displayId) {
+            InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+            mConsumer = new InputConsumer(mClientChannel);
+        }
+
+        virtual ~FakeInputReceiver() {
+        }
+
+        // return true if the event has been handled.
+        virtual bool handled() {
+            return false;
+        }
+
+        sp<InputDispatcher> mDispatcher;
+        sp<InputChannel> mServerChannel, mClientChannel;
+        sp<IBinder> mToken;
+        InputConsumer *mConsumer;
+        PreallocatedInputEventFactory mEventFactory;
+
+        std::string mName;
+        int32_t mDisplayId;
+};
+
+class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
+public:
+    static const int32_t WIDTH = 600;
+    static const int32_t HEIGHT = 800;
+
+    FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+        const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
+            FakeInputReceiver(dispatcher, name, displayId),
+            mFocused(false) {
+            mServerChannel->setToken(new BBinder());
+            mDispatcher->registerInputChannel(mServerChannel, displayId);
+ 
+            inputApplicationHandle->updateInfo();
+            mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+    }
+
+    virtual bool updateInfo() {
+        mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr;
+        mInfo.name = mName;
+        mInfo.layoutParamsFlags = 0;
+        mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+        mInfo.frameLeft = 0;
+        mInfo.frameTop = 0;
+        mInfo.frameRight = WIDTH;
+        mInfo.frameBottom = HEIGHT;
+        mInfo.globalScaleFactor = 1.0;
+        mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+        mInfo.visible = true;
+        mInfo.canReceiveKeys = true;
+        mInfo.hasFocus = mFocused;
+        mInfo.hasWallpaper = false;
+        mInfo.paused = false;
+        mInfo.layer = 0;
+        mInfo.ownerPid = INJECTOR_PID;
+        mInfo.ownerUid = INJECTOR_UID;
+        mInfo.inputFeatures = 0;
+        mInfo.displayId = mDisplayId;
+
+        return true;
+    }
+
+    void setFocus() {
+        mFocused = true;
+    }
+
+    void releaseChannel() {
+        mServerChannel.clear();
+        InputWindowHandle::releaseChannel();
+    }
+protected:
+    virtual bool handled() {
+        return true;
+    }
+
+    bool mFocused;
+};
+
+static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
+        int32_t displayId = ADISPLAY_ID_NONE) {
+    KeyEvent event;
+    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    // Define a valid key down event.
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
+            AKEY_EVENT_ACTION_DOWN, /* flags */ 0,
+            AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime);
+
+    // Inject event until dispatch out.
+    return dispatcher->injectInputEvent(
+            &event,
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+            INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+}
+
+static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
+        int32_t displayId) {
+    MotionEvent event;
+    PointerProperties pointerProperties[1];
+    PointerCoords pointerCoords[1];
+
+    pointerProperties[0].clear();
+    pointerProperties[0].id = 0;
+    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+    pointerCoords[0].clear();
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+
+    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    // Define a valid motion down event.
+    event.initialize(DEVICE_ID, source, displayId,
+            AMOTION_EVENT_ACTION_DOWN, /* actionButton */0, /* flags */ 0, /* edgeFlags */ 0,
+            AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+            /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
+            /* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties,
+            pointerCoords);
+
+    // Inject event until dispatch out.
+    return dispatcher->injectInputEvent(
+            &event,
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+            INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+}
+
+static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
+    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    // Define a valid key event.
+    NotifyKeyArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+            displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0,
+            AKEYCODE_A, KEY_A, AMETA_NONE, currentTime);
+
+    return args;
+}
+
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
+    PointerProperties pointerProperties[1];
+    PointerCoords pointerCoords[1];
+
+    pointerProperties[0].clear();
+    pointerProperties[0].id = 0;
+    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+    pointerCoords[0].clear();
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+
+    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    // Define a valid motion event.
+    NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId,
+            POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
+            AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+            AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, pointerProperties,
+            pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, currentTime,
+            /* videoFrames */ {});
+
+    return args;
+}
+
+TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
+            ADISPLAY_ID_DEFAULT);
+
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.add(window);
+
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+    // Window should receive motion event.
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+}
+
+// The foreground window should receive the first touch down event.
+TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+            ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+            ADISPLAY_ID_DEFAULT);
+
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.add(windowTop);
+    inputWindowHandles.add(windowSecond);
+
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+    // Top window should receive the touch down event. Second window should not receive anything.
+    windowTop->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowSecond->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+            ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+            ADISPLAY_ID_DEFAULT);
+
+    // Set focused application.
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    // Expect one focus window exist in display.
+    windowSecond->setFocus();
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.add(windowTop);
+    inputWindowHandles.add(windowSecond);
+
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+    // Focused window should receive event.
+    windowTop->assertNoEvents();
+    windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+}
+
+TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+            ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+            ADISPLAY_ID_DEFAULT);
+
+    // Set focused application.
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first)
+    windowTop->setFocus();
+    windowSecond->setFocus();
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.add(windowTop);
+    inputWindowHandles.add(windowSecond);
+
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+    // Top focused window should receive event.
+    windowTop->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowSecond->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+            ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+            ADISPLAY_ID_DEFAULT);
+
+    // Set focused application.
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    windowTop->setFocus();
+    windowSecond->setFocus();
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.add(windowTop);
+    inputWindowHandles.add(windowSecond);
+    // Release channel for window is no longer valid.
+    windowTop->releaseChannel();
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+
+    // Test inject a key down, should dispatch to a valid window.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+    // Top window is invalid, so it should not receive any input event.
+    windowTop->assertNoEvents();
+    windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+}
+
+/* Test InputDispatcher for MultiDisplay */
+class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
+public:
+    static constexpr int32_t SECOND_DISPLAY_ID = 1;
+    virtual void SetUp() {
+        InputDispatcherTest::SetUp();
+
+        application1 = new FakeApplicationHandle();
+        windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
+                ADISPLAY_ID_DEFAULT);
+        Vector<sp<InputWindowHandle>> inputWindowHandles;
+        inputWindowHandles.push(windowInPrimary);
+        // Set focus window for primary display, but focused display would be second one.
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
+        windowInPrimary->setFocus();
+        mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+
+        application2 = new FakeApplicationHandle();
+        windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
+                SECOND_DISPLAY_ID);
+        // Set focus to second display window.
+        Vector<sp<InputWindowHandle>> inputWindowHandles_Second;
+        inputWindowHandles_Second.push(windowInSecondary);
+        // Set focus display to second one.
+        mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
+        // Set focus window for second display.
+        mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
+        windowInSecondary->setFocus();
+        mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+    }
+
+    virtual void TearDown() {
+        InputDispatcherTest::TearDown();
+
+        application1.clear();
+        windowInPrimary.clear();
+        application2.clear();
+        windowInSecondary.clear();
+    }
+
+protected:
+    sp<FakeApplicationHandle> application1;
+    sp<FakeWindowHandle> windowInPrimary;
+    sp<FakeApplicationHandle> application2;
+    sp<FakeWindowHandle> windowInSecondary;
+};
+
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) {
+    // Test touch down on primary display.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowInSecondary->assertNoEvents();
+
+    // Test touch down on second display.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+}
+
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
+    // Test inject a key down with display id specified.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_DEFAULT);
+    windowInSecondary->assertNoEvents();
+
+    // Test inject a key down without display id specified.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+
+    // Remove secondary display.
+    Vector<sp<InputWindowHandle>> noWindows;
+    mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID);
+
+    // Expect old focus should receive a cancel event.
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE,
+            AKEY_EVENT_FLAG_CANCELED);
+
+    // Test inject a key down, should timeout because of no target window.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT";
+    windowInPrimary->assertNoEvents();
+    windowInSecondary->assertNoEvents();
+}
+
+class FakeMonitorReceiver : public FakeInputReceiver, public RefBase {
+public:
+    FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
+            int32_t displayId) : FakeInputReceiver(dispatcher, name, displayId) {
+        mDispatcher->registerInputChannel(mServerChannel, displayId);
+    }
+};
+
+// Test per-display input monitors for motion event.
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
+    sp<FakeMonitorReceiver> monitorInPrimary =
+            new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    sp<FakeMonitorReceiver> monitorInSecondary =
+            new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+    // Test touch down on primary display.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    monitorInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowInSecondary->assertNoEvents();
+    monitorInSecondary->assertNoEvents();
+
+    // Test touch down on second display.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    monitorInPrimary->assertNoEvents();
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+
+    // Test inject a non-pointer motion event.
+    // If specific a display, it will dispatch to the focused window of particular display,
+    // or it will dispatch to the focused window of focused display.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+        AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    monitorInPrimary->assertNoEvents();
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
+    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
+}
+
+// Test per-display input monitors for key event.
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
+    //Input monitor per display.
+    sp<FakeMonitorReceiver> monitorInPrimary =
+            new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    sp<FakeMonitorReceiver> monitorInSecondary =
+            new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+    // Test inject a key down.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    monitorInPrimary->assertNoEvents();
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+}
+
+class InputFilterTest : public InputDispatcherTest {
+protected:
+    static constexpr int32_t SECOND_DISPLAY_ID = 1;
+
+    void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
+        NotifyMotionArgs motionArgs;
+
+        motionArgs = generateMotionArgs(
+                AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+        mDispatcher->notifyMotion(&motionArgs);
+        motionArgs = generateMotionArgs(
+                AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+        mDispatcher->notifyMotion(&motionArgs);
+
+        if (expectToBeFiltered) {
+            mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&motionArgs);
+        } else {
+            mFakePolicy->assertFilterInputEventWasNotCalled();
+        }
+    }
+
+    void testNotifyKey(bool expectToBeFiltered) {
+        NotifyKeyArgs keyArgs;
+
+        keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
+        mDispatcher->notifyKey(&keyArgs);
+        keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
+        mDispatcher->notifyKey(&keyArgs);
+
+        if (expectToBeFiltered) {
+            mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&keyArgs);
+        } else {
+            mFakePolicy->assertFilterInputEventWasNotCalled();
+        }
+    }
+};
+
+// Test InputFilter for MotionEvent
+TEST_F(InputFilterTest, MotionEvent_InputFilter) {
+    // Since the InputFilter is disabled by default, check if touch events aren't filtered.
+    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
+    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+
+    // Enable InputFilter
+    mDispatcher->setInputFilterEnabled(true);
+    // Test touch on both primary and second display, and check if both events are filtered.
+    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true);
+    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true);
+
+    // Disable InputFilter
+    mDispatcher->setInputFilterEnabled(false);
+    // Test touch on both primary and second display, and check if both events aren't filtered.
+    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
+    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+}
+
+// Test InputFilter for KeyEvent
+TEST_F(InputFilterTest, KeyEvent_InputFilter) {
+    // Since the InputFilter is disabled by default, check if key event aren't filtered.
+    testNotifyKey(/*expectToBeFiltered*/ false);
+
+    // Enable InputFilter
+    mDispatcher->setInputFilterEnabled(true);
+    // Send a key event, and check if it is filtered.
+    testNotifyKey(/*expectToBeFiltered*/ true);
+
+    // Disable InputFilter
+    mDispatcher->setInputFilterEnabled(false);
+    // Send a key event, and check if it isn't filtered.
+    testNotifyKey(/*expectToBeFiltered*/ false);
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 22f15a0..84c5ad6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -15,12 +15,13 @@
  */
 
 #include "../InputReader.h"
+#include "TestInputListener.h"
 
-#include <inttypes.h>
-#include <utils/List.h>
 #include <gtest/gtest.h>
+#include <inttypes.h>
 #include <math.h>
 
+
 namespace android {
 
 // An arbitrary time value.
@@ -28,12 +29,14 @@
 
 // Arbitrary display properties.
 static const int32_t DISPLAY_ID = 0;
+static const int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
 static const int32_t DISPLAY_WIDTH = 480;
 static const int32_t DISPLAY_HEIGHT = 800;
 static const int32_t VIRTUAL_DISPLAY_ID = 1;
 static const int32_t VIRTUAL_DISPLAY_WIDTH = 400;
 static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
-static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "Vr-display-unique-ID";
+static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
+static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
 
 // Error tolerance for floating point assertions.
 static const float EPSILON = 0.001f;
@@ -55,6 +58,7 @@
     float mMinX, mMinY, mMaxX, mMaxY;
     float mX, mY;
     int32_t mButtonState;
+    int32_t mDisplayId;
 
 protected:
     virtual ~FakePointerController() { }
@@ -62,7 +66,7 @@
 public:
     FakePointerController() :
         mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0),
-        mButtonState(0) {
+        mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) {
     }
 
     void setBounds(float minX, float minY, float maxX, float maxY) {
@@ -73,6 +77,10 @@
         mMaxY = maxY;
     }
 
+    void setDisplayId(int32_t displayId) {
+        mDisplayId = displayId;
+    }
+
     virtual void setPosition(float x, float y) {
         mX = x;
         mY = y;
@@ -91,6 +99,10 @@
         *outY = mY;
     }
 
+    virtual int32_t getDisplayId() const {
+        return mDisplayId;
+    }
+
 private:
     virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
         *outMinX = mMinX;
@@ -132,6 +144,7 @@
     InputReaderConfiguration mConfig;
     KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
     Vector<InputDeviceInfo> mInputDevices;
+    std::vector<DisplayViewport> mViewports;
     TouchAffineTransformation transform;
 
 protected:
@@ -141,23 +154,37 @@
     FakeInputReaderPolicy() {
     }
 
-    void setDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
-            const String8& uniqueId) {
-        DisplayViewport v = createDisplayViewport(displayId, width, height, orientation, uniqueId);
-        // Set the size of both the internal and external display at the same time.
-        mConfig.setPhysicalDisplayViewport(ViewportType::VIEWPORT_INTERNAL, v);
-        mConfig.setPhysicalDisplayViewport(ViewportType::VIEWPORT_EXTERNAL, v);
+    virtual void clearViewports() {
+        mViewports.clear();
+        mConfig.setDisplayViewports(mViewports);
     }
 
-    void setVirtualDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
-            const String8& uniqueId) {
-        Vector<DisplayViewport> viewports;
-        viewports.push_back(createDisplayViewport(displayId, width, height, orientation, uniqueId));
-        mConfig.setVirtualDisplayViewports(viewports);
+    std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const {
+        return mConfig.getDisplayViewportByUniqueId(uniqueId);
+    }
+    std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const {
+        return mConfig.getDisplayViewportByType(type);
     }
 
-    void addExcludedDeviceName(const String8& deviceName) {
-        mConfig.excludedDeviceNames.push(deviceName);
+    std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const {
+        return mConfig.getDisplayViewportByPort(displayPort);
+    }
+
+    void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
+            const std::string& uniqueId, std::optional<uint8_t> physicalPort,
+            ViewportType viewportType) {
+        const DisplayViewport viewport = createDisplayViewport(displayId, width, height,
+                orientation, uniqueId, physicalPort, viewportType);
+        mViewports.push_back(viewport);
+        mConfig.setDisplayViewports(mViewports);
+    }
+
+    void addExcludedDeviceName(const std::string& deviceName) {
+        mConfig.excludedDeviceNames.push_back(deviceName);
+    }
+
+    void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort) {
+        mConfig.portAssociations.insert({inputPort, displayPort});
     }
 
     void addDisabledDevice(int32_t deviceId) {
@@ -188,7 +215,7 @@
         return mInputDevices;
     }
 
-    TouchAffineTransformation getTouchAffineTransformation(const String8& inputDeviceDescriptor,
+    TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
             int32_t surfaceRotation) {
         return transform;
     }
@@ -203,7 +230,8 @@
 
 private:
     DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
-            int32_t orientation, const String8& uniqueId) {
+            int32_t orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort,
+            ViewportType type) {
         bool isRotated = (orientation == DISPLAY_ORIENTATION_90
                 || orientation == DISPLAY_ORIENTATION_270);
         DisplayViewport v;
@@ -220,6 +248,8 @@
         v.deviceWidth = isRotated ? height : width;
         v.deviceHeight = isRotated ? width : height;
         v.uniqueId = uniqueId;
+        v.physicalPort = physicalPort;
+        v.type = type;
         return v;
     }
 
@@ -236,122 +266,14 @@
     }
 
     virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) {
-        return NULL;
+        return nullptr;
     }
 
-    virtual String8 getDeviceAlias(const InputDeviceIdentifier&) {
-        return String8::empty();
+    virtual std::string getDeviceAlias(const InputDeviceIdentifier&) {
+        return "";
     }
 };
 
-
-// --- FakeInputListener ---
-
-class FakeInputListener : public InputListenerInterface {
-private:
-    List<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
-    List<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
-    List<NotifyKeyArgs> mNotifyKeyArgsQueue;
-    List<NotifyMotionArgs> mNotifyMotionArgsQueue;
-    List<NotifySwitchArgs> mNotifySwitchArgsQueue;
-
-protected:
-    virtual ~FakeInputListener() { }
-
-public:
-    FakeInputListener() {
-    }
-
-    void assertNotifyConfigurationChangedWasCalled(
-            NotifyConfigurationChangedArgs* outEventArgs = NULL) {
-        ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
-                << "Expected notifyConfigurationChanged() to have been called.";
-        if (outEventArgs) {
-            *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
-        }
-        mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
-    }
-
-    void assertNotifyConfigurationChangedWasNotCalled() {
-        ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
-                << "Expected notifyConfigurationChanged() to not have been called.";
-    }
-
-    void assertNotifyDeviceResetWasCalled(
-            NotifyDeviceResetArgs* outEventArgs = NULL) {
-        ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
-                << "Expected notifyDeviceReset() to have been called.";
-        if (outEventArgs) {
-            *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
-        }
-        mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
-    }
-
-    void assertNotifyDeviceResetWasNotCalled() {
-        ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
-                << "Expected notifyDeviceReset() to not have been called.";
-    }
-
-    void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = NULL) {
-        ASSERT_FALSE(mNotifyKeyArgsQueue.empty())
-                << "Expected notifyKey() to have been called.";
-        if (outEventArgs) {
-            *outEventArgs = *mNotifyKeyArgsQueue.begin();
-        }
-        mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
-    }
-
-    void assertNotifyKeyWasNotCalled() {
-        ASSERT_TRUE(mNotifyKeyArgsQueue.empty())
-                << "Expected notifyKey() to not have been called.";
-    }
-
-    void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = NULL) {
-        ASSERT_FALSE(mNotifyMotionArgsQueue.empty())
-                << "Expected notifyMotion() to have been called.";
-        if (outEventArgs) {
-            *outEventArgs = *mNotifyMotionArgsQueue.begin();
-        }
-        mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
-    }
-
-    void assertNotifyMotionWasNotCalled() {
-        ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
-                << "Expected notifyMotion() to not have been called.";
-    }
-
-    void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = NULL) {
-        ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
-                << "Expected notifySwitch() to have been called.";
-        if (outEventArgs) {
-            *outEventArgs = *mNotifySwitchArgsQueue.begin();
-        }
-        mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
-    }
-
-private:
-    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
-        mNotifyConfigurationChangedArgsQueue.push_back(*args);
-    }
-
-    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-        mNotifyDeviceResetArgsQueue.push_back(*args);
-    }
-
-    virtual void notifyKey(const NotifyKeyArgs* args) {
-        mNotifyKeyArgsQueue.push_back(*args);
-    }
-
-    virtual void notifyMotion(const NotifyMotionArgs* args) {
-        mNotifyMotionArgsQueue.push_back(*args);
-    }
-
-    virtual void notifySwitch(const NotifySwitchArgs* args) {
-        mNotifySwitchArgsQueue.push_back(*args);
-    }
-};
-
-
 // --- FakeEventHub ---
 
 class FakeEventHub : public EventHubInterface {
@@ -392,7 +314,7 @@
     };
 
     KeyedVector<int32_t, Device*> mDevices;
-    Vector<String8> mExcludedDevices;
+    std::vector<std::string> mExcludedDevices;
     List<RawEvent> mEvents;
 
 protected:
@@ -405,7 +327,7 @@
 public:
     FakeEventHub() { }
 
-    void addDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+    void addDevice(int32_t deviceId, const std::string& name, uint32_t classes) {
         Device* device = new Device(classes);
         device->identifier.name = name;
         mDevices.add(deviceId, device);
@@ -422,7 +344,7 @@
 
     bool isDeviceEnabled(int32_t deviceId) {
         Device* device = getDevice(deviceId);
-        if (device == NULL) {
+        if (device == nullptr) {
             ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
             return false;
         }
@@ -432,7 +354,7 @@
     status_t enableDevice(int32_t deviceId) {
         status_t result;
         Device* device = getDevice(deviceId);
-        if (device == NULL) {
+        if (device == nullptr) {
             ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
             return BAD_VALUE;
         }
@@ -446,7 +368,7 @@
 
     status_t disableDevice(int32_t deviceId) {
         Device* device = getDevice(deviceId);
-        if (device == NULL) {
+        if (device == nullptr) {
             ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
             return BAD_VALUE;
         }
@@ -534,7 +456,7 @@
         return device->leds.valueFor(led);
     }
 
-    Vector<String8>& getExcludedDevices() {
+    std::vector<std::string>& getExcludedDevices() {
         return mExcludedDevices;
     }
 
@@ -566,7 +488,7 @@
 private:
     Device* getDevice(int32_t deviceId) const {
         ssize_t index = mDevices.indexOfKey(deviceId);
-        return index >= 0 ? mDevices.valueAt(index) : NULL;
+        return index >= 0 ? mDevices.valueAt(index) : nullptr;
     }
 
     virtual uint32_t getDeviceClasses(int32_t deviceId) const {
@@ -651,14 +573,14 @@
                 return &device->keysByScanCode.valueAt(index);
             }
         }
-        return NULL;
+        return nullptr;
     }
 
     virtual status_t mapAxis(int32_t, int32_t, AxisInfo*) const {
         return NAME_NOT_FOUND;
     }
 
-    virtual void setExcludedDevices(const Vector<String8>& devices) {
+    virtual void setExcludedDevices(const std::vector<std::string>& devices) {
         mExcludedDevices = devices;
     }
 
@@ -672,6 +594,10 @@
         return 1;
     }
 
+    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) {
+        return {};
+    }
+
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const {
         Device* device = getDevice(deviceId);
         if (device) {
@@ -781,7 +707,7 @@
     }
 
     virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t) const {
-        return NULL;
+        return nullptr;
     }
 
     virtual bool setKeyboardLayoutOverlay(int32_t, const sp<KeyCharacterMap>&) {
@@ -821,13 +747,14 @@
     int32_t mGlobalMetaState;
     bool mUpdateGlobalMetaStateWasCalled;
     int32_t mGeneration;
+    uint32_t mNextSequenceNum;
 
 public:
     FakeInputReaderContext(const sp<EventHubInterface>& eventHub,
             const sp<InputReaderPolicyInterface>& policy,
             const sp<InputListenerInterface>& listener) :
             mEventHub(eventHub), mPolicy(policy), mListener(listener),
-            mGlobalMetaState(0) {
+            mGlobalMetaState(0), mNextSequenceNum(1) {
     }
 
     virtual ~FakeInputReaderContext() { }
@@ -891,6 +818,10 @@
     virtual void dispatchExternalStylusState(const StylusState&) {
 
     }
+
+    virtual uint32_t getNextSequenceNum() {
+        return mNextSequenceNum++;
+    }
 };
 
 
@@ -910,6 +841,7 @@
     bool mResetWasCalled;
     bool mProcessWasCalled;
 
+    std::optional<DisplayViewport> mViewport;
 public:
     FakeInputMapper(InputDevice* device, uint32_t sources) :
             InputMapper(device),
@@ -940,7 +872,7 @@
         mResetWasCalled = false;
     }
 
-    void assertProcessWasCalled(RawEvent* outLastEvent = NULL) {
+    void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) {
         ASSERT_TRUE(mProcessWasCalled)
                 << "Expected process() to have been called.";
         if (outLastEvent) {
@@ -978,8 +910,14 @@
         }
     }
 
-    virtual void configure(nsecs_t, const InputReaderConfiguration*, uint32_t) {
+    virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
         mConfigureWasCalled = true;
+
+        // Find the associated viewport if exist.
+        const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+        if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+            mViewport = config->getDisplayViewportByPort(*displayPort);
+        }
     }
 
     virtual void reset(nsecs_t) {
@@ -1026,6 +964,13 @@
 
     virtual void fadePointer() {
     }
+
+    virtual std::optional<int32_t> getAssociatedDisplay() {
+        if (mViewport) {
+            return std::make_optional(mViewport->displayId);
+        }
+        return std::nullopt;
+    }
 };
 
 
@@ -1039,7 +984,7 @@
             const sp<InputReaderPolicyInterface>& policy,
             const sp<InputListenerInterface>& listener) :
             InputReader(eventHub, policy, listener),
-            mNextDevice(NULL) {
+            mNextDevice(nullptr) {
     }
 
     virtual ~InstrumentedInputReader() {
@@ -1052,10 +997,11 @@
         mNextDevice = device;
     }
 
-    InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const String8& name,
-            uint32_t classes) {
+    InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name,
+            uint32_t classes, const std::string& location = "") {
         InputDeviceIdentifier identifier;
         identifier.name = name;
+        identifier.location = location;
         int32_t generation = deviceId + 1;
         return new InputDevice(&mContext, deviceId, generation, controllerNumber, identifier,
                 classes);
@@ -1066,7 +1012,7 @@
             const InputDeviceIdentifier& identifier, uint32_t classes) {
         if (mNextDevice) {
             InputDevice* device = mNextDevice;
-            mNextDevice = NULL;
+            mNextDevice = nullptr;
             return device;
         }
         return InputReader::createDeviceLocked(deviceId, controllerNumber, identifier, classes);
@@ -1075,12 +1021,197 @@
     friend class InputReaderTest;
 };
 
+// --- InputReaderPolicyTest ---
+class InputReaderPolicyTest : public testing::Test {
+protected:
+    sp<FakeInputReaderPolicy> mFakePolicy;
+
+    virtual void SetUp() {
+        mFakePolicy = new FakeInputReaderPolicy();
+    }
+    virtual void TearDown() {
+        mFakePolicy.clear();
+    }
+};
+
+/**
+ * Check that empty set of viewports is an acceptable configuration.
+ * Also try to get internal viewport two different ways - by type and by uniqueId.
+ *
+ * There will be confusion if two viewports with empty uniqueId and identical type are present.
+ * Such configuration is not currently allowed.
+ */
+TEST_F(InputReaderPolicyTest, Viewports_GetCleared) {
+    static const std::string uniqueId = "local:0";
+
+    // We didn't add any viewports yet, so there shouldn't be any.
+    std::optional<DisplayViewport> internalViewport =
+            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    ASSERT_FALSE(internalViewport);
+
+    // Add an internal viewport, then clear it
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+
+    // Check matching by uniqueId
+    internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
+    ASSERT_TRUE(internalViewport);
+    ASSERT_EQ(ViewportType::VIEWPORT_INTERNAL, internalViewport->type);
+
+    // Check matching by viewport type
+    internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    ASSERT_TRUE(internalViewport);
+    ASSERT_EQ(uniqueId, internalViewport->uniqueId);
+
+    mFakePolicy->clearViewports();
+    // Make sure nothing is found after clear
+    internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
+    ASSERT_FALSE(internalViewport);
+    internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    ASSERT_FALSE(internalViewport);
+}
+
+TEST_F(InputReaderPolicyTest, Viewports_GetByType) {
+    const std::string internalUniqueId = "local:0";
+    const std::string externalUniqueId = "local:1";
+    const std::string virtualUniqueId1 = "virtual:2";
+    const std::string virtualUniqueId2 = "virtual:3";
+    constexpr int32_t virtualDisplayId1 = 2;
+    constexpr int32_t virtualDisplayId2 = 3;
+
+    // Add an internal viewport
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+    // Add an external viewport
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT, ViewportType::VIEWPORT_EXTERNAL);
+    // Add an virtual viewport
+    mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+    // Add another virtual viewport
+    mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+
+    // Check matching by type for internal
+    std::optional<DisplayViewport> internalViewport =
+            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    ASSERT_TRUE(internalViewport);
+    ASSERT_EQ(internalUniqueId, internalViewport->uniqueId);
+
+    // Check matching by type for external
+    std::optional<DisplayViewport> externalViewport =
+            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_EXTERNAL);
+    ASSERT_TRUE(externalViewport);
+    ASSERT_EQ(externalUniqueId, externalViewport->uniqueId);
+
+    // Check matching by uniqueId for virtual viewport #1
+    std::optional<DisplayViewport> virtualViewport1 =
+            mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1);
+    ASSERT_TRUE(virtualViewport1);
+    ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport1->type);
+    ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId);
+    ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId);
+
+    // Check matching by uniqueId for virtual viewport #2
+    std::optional<DisplayViewport> virtualViewport2 =
+            mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2);
+    ASSERT_TRUE(virtualViewport2);
+    ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport2->type);
+    ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId);
+    ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId);
+}
+
+
+/**
+ * We can have 2 viewports of the same kind. We can distinguish them by uniqueId, and confirm
+ * that lookup works by checking display id.
+ * Check that 2 viewports of each kind is possible, for all existing viewport types.
+ */
+TEST_F(InputReaderPolicyTest, Viewports_TwoOfSameType) {
+    const std::string uniqueId1 = "uniqueId1";
+    const std::string uniqueId2 = "uniqueId2";
+    constexpr int32_t displayId1 = 2;
+    constexpr int32_t displayId2 = 3;
+
+    std::vector<ViewportType> types = {ViewportType::VIEWPORT_INTERNAL,
+            ViewportType::VIEWPORT_EXTERNAL, ViewportType::VIEWPORT_VIRTUAL};
+    for (const ViewportType& type : types) {
+        mFakePolicy->clearViewports();
+        // Add a viewport
+        mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT, type);
+        // Add another viewport
+        mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT, type);
+
+        // Check that correct display viewport was returned by comparing the display IDs.
+        std::optional<DisplayViewport> viewport1 =
+                mFakePolicy->getDisplayViewportByUniqueId(uniqueId1);
+        ASSERT_TRUE(viewport1);
+        ASSERT_EQ(displayId1, viewport1->displayId);
+        ASSERT_EQ(type, viewport1->type);
+
+        std::optional<DisplayViewport> viewport2 =
+                mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
+        ASSERT_TRUE(viewport2);
+        ASSERT_EQ(displayId2, viewport2->displayId);
+        ASSERT_EQ(type, viewport2->type);
+
+        // When there are multiple viewports of the same kind, and uniqueId is not specified
+        // in the call to getDisplayViewport, then that situation is not supported.
+        // The viewports can be stored in any order, so we cannot rely on the order, since that
+        // is just implementation detail.
+        // However, we can check that it still returns *a* viewport, we just cannot assert
+        // which one specifically is returned.
+        std::optional<DisplayViewport> someViewport = mFakePolicy->getDisplayViewportByType(type);
+        ASSERT_TRUE(someViewport);
+    }
+}
+
+/**
+ * Check getDisplayViewportByPort
+ */
+TEST_F(InputReaderPolicyTest, Viewports_GetByPort) {
+    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+    const std::string uniqueId1 = "uniqueId1";
+    const std::string uniqueId2 = "uniqueId2";
+    constexpr int32_t displayId1 = 1;
+    constexpr int32_t displayId2 = 2;
+    const uint8_t hdmi1 = 0;
+    const uint8_t hdmi2 = 1;
+    const uint8_t hdmi3 = 2;
+
+    mFakePolicy->clearViewports();
+    // Add a viewport that's associated with some display port that's not of interest.
+    mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId1, hdmi3, type);
+    // Add another viewport, connected to HDMI1 port
+    mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId2, hdmi1, type);
+
+    // Check that correct display viewport was returned by comparing the display ports.
+    std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1);
+    ASSERT_TRUE(hdmi1Viewport);
+    ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
+    ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
+
+    // Check that we can still get the same viewport using the uniqueId
+    hdmi1Viewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
+    ASSERT_TRUE(hdmi1Viewport);
+    ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
+    ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
+    ASSERT_EQ(type, hdmi1Viewport->type);
+
+    // Check that we cannot find a port with "HDMI2", because we never added one
+    std::optional<DisplayViewport> hdmi2Viewport = mFakePolicy->getDisplayViewportByPort(hdmi2);
+    ASSERT_FALSE(hdmi2Viewport);
+}
 
 // --- InputReaderTest ---
 
 class InputReaderTest : public testing::Test {
 protected:
-    sp<FakeInputListener> mFakeListener;
+    sp<TestInputListener> mFakeListener;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<FakeEventHub> mFakeEventHub;
     sp<InstrumentedInputReader> mReader;
@@ -1088,7 +1219,7 @@
     virtual void SetUp() {
         mFakeEventHub = new FakeEventHub();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeListener = new FakeInputListener();
+        mFakeListener = new TestInputListener();
 
         mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
     }
@@ -1101,7 +1232,7 @@
         mFakeEventHub.clear();
     }
 
-    void addDevice(int32_t deviceId, const String8& name, uint32_t classes,
+    void addDevice(int32_t deviceId, const std::string& name, uint32_t classes,
             const PropertyMap* configuration) {
         mFakeEventHub->addDevice(deviceId, name, classes);
 
@@ -1129,7 +1260,7 @@
     }
 
     FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
-            const String8& name, uint32_t classes, uint32_t sources,
+            const std::string& name, uint32_t classes, uint32_t sources,
             const PropertyMap* configuration) {
         InputDevice* device = mReader->newDevice(deviceId, controllerNumber, name, classes);
         FakeInputMapper* mapper = new FakeInputMapper(device, sources);
@@ -1141,17 +1272,18 @@
 };
 
 TEST_F(InputReaderTest, GetInputDevices) {
-    ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"),
-            INPUT_DEVICE_CLASS_KEYBOARD, NULL));
-    ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("ignored"),
-            0, NULL)); // no classes so device will be ignored
+    ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard",
+            INPUT_DEVICE_CLASS_KEYBOARD, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored",
+            0, nullptr)); // no classes so device will be ignored
+
 
     Vector<InputDeviceInfo> inputDevices;
     mReader->getInputDevices(inputDevices);
 
     ASSERT_EQ(1U, inputDevices.size());
     ASSERT_EQ(1, inputDevices[0].getId());
-    ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.string());
+    ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
     ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
@@ -1160,7 +1292,7 @@
     inputDevices = mFakePolicy->getInputDevices();
     ASSERT_EQ(1U, inputDevices.size());
     ASSERT_EQ(1, inputDevices[0].getId());
-    ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.string());
+    ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
     ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
@@ -1169,14 +1301,14 @@
 TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
     constexpr int32_t deviceId = 1;
     constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
-    InputDevice* device = mReader->newDevice(deviceId, 0, String8("fake"), deviceClass);
+    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
     // Must add at least one mapper or the device will be ignored!
     FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
     device->addMapper(mapper);
     mReader->setNextDevice(device);
-    addDevice(deviceId, String8("fake"), deviceClass, NULL);
+    addDevice(deviceId, "fake", deviceClass, nullptr);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(NULL));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -1207,9 +1339,9 @@
 }
 
 TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = NULL;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
+    FakeInputMapper* mapper = nullptr;
+    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
+            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
     mapper->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(0,
@@ -1234,9 +1366,9 @@
 }
 
 TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = NULL;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
+    FakeInputMapper* mapper = nullptr;
+    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
+            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
     mapper->setScanCodeState(KEY_A, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(0,
@@ -1261,9 +1393,9 @@
 }
 
 TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = NULL;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
+    FakeInputMapper* mapper = nullptr;
+    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
+            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
     mapper->setSwitchState(SW_LID, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(0,
@@ -1288,9 +1420,10 @@
 }
 
 TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = NULL;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
+    FakeInputMapper* mapper = nullptr;
+    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
+            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
+
     mapper->addSupportedKeyCode(AKEYCODE_A);
     mapper->addSupportedKeyCode(AKEYCODE_B);
 
@@ -1323,7 +1456,7 @@
 }
 
 TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
-    addDevice(1, String8("ignored"), INPUT_DEVICE_CLASS_KEYBOARD, NULL);
+    addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
 
     NotifyConfigurationChangedArgs args;
 
@@ -1332,9 +1465,9 @@
 }
 
 TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
-    FakeInputMapper* mapper = NULL;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
+    FakeInputMapper* mapper = nullptr;
+    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
+            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
 
     mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1);
     mReader->loopOnce();
@@ -1349,6 +1482,69 @@
     ASSERT_EQ(1, event.value);
 }
 
+TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) {
+    constexpr int32_t deviceId = 1;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
+    // Must add at least one mapper or the device will be ignored!
+    FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
+    device->addMapper(mapper);
+    mReader->setNextDevice(device);
+    addDevice(deviceId, "fake", deviceClass, nullptr);
+
+    NotifyDeviceResetArgs resetArgs;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    uint32_t prevSequenceNum = resetArgs.sequenceNum;
+
+    disableDevice(deviceId, device);
+    mReader->loopOnce();
+    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+    ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
+    prevSequenceNum = resetArgs.sequenceNum;
+
+    enableDevice(deviceId, device);
+    mReader->loopOnce();
+    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+    ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
+    prevSequenceNum = resetArgs.sequenceNum;
+
+    disableDevice(deviceId, device);
+    mReader->loopOnce();
+    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+    ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
+    prevSequenceNum = resetArgs.sequenceNum;
+}
+
+TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
+    constexpr int32_t deviceId = 1;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    const char* DEVICE_LOCATION = "USB1";
+    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass,
+            DEVICE_LOCATION);
+    FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
+    device->addMapper(mapper);
+    mReader->setNextDevice(device);
+    addDevice(deviceId, "fake", deviceClass, nullptr);
+
+    const uint8_t hdmi1 = 1;
+
+    // Associated touch screen with second display.
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+
+    // Add default and second display.
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+    mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
+    mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    mReader->loopOnce();
+
+    // Check device.
+    ASSERT_EQ(deviceId, device->getId());
+    ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, DISPLAY_ID));
+    ASSERT_TRUE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
+}
+
 
 // --- InputDeviceTest ---
 
@@ -1362,7 +1558,7 @@
 
     sp<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<FakeInputListener> mFakeListener;
+    sp<TestInputListener> mFakeListener;
     FakeInputReaderContext* mFakeContext;
 
     InputDevice* mDevice;
@@ -1370,10 +1566,10 @@
     virtual void SetUp() {
         mFakeEventHub = new FakeEventHub();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeListener = new FakeInputListener();
+        mFakeListener = new TestInputListener();
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
 
-        mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
+        mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
         mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
@@ -1399,7 +1595,7 @@
 
 TEST_F(InputDeviceTest, ImmutableProperties) {
     ASSERT_EQ(DEVICE_ID, mDevice->getId());
-    ASSERT_STREQ(DEVICE_NAME, mDevice->getName());
+    ASSERT_STREQ(DEVICE_NAME, mDevice->getName().c_str());
     ASSERT_EQ(DEVICE_CLASSES, mDevice->getClasses());
 }
 
@@ -1427,7 +1623,7 @@
     InputDeviceInfo info;
     mDevice->getDeviceInfo(&info);
     ASSERT_EQ(DEVICE_ID, info.getId());
-    ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.string());
+    ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.c_str());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NONE, info.getKeyboardType());
     ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, info.getSources());
 
@@ -1497,7 +1693,7 @@
     InputDeviceInfo info;
     mDevice->getDeviceInfo(&info);
     ASSERT_EQ(DEVICE_ID, info.getId());
-    ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.string());
+    ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.c_str());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_ALPHABETIC, info.getKeyboardType());
     ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), info.getSources());
 
@@ -1549,6 +1745,7 @@
 class InputMapperTest : public testing::Test {
 protected:
     static const char* DEVICE_NAME;
+    static const char* DEVICE_LOCATION;
     static const int32_t DEVICE_ID;
     static const int32_t DEVICE_GENERATION;
     static const int32_t DEVICE_CONTROLLER_NUMBER;
@@ -1556,21 +1753,22 @@
 
     sp<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<FakeInputListener> mFakeListener;
+    sp<TestInputListener> mFakeListener;
     FakeInputReaderContext* mFakeContext;
     InputDevice* mDevice;
 
     virtual void SetUp() {
         mFakeEventHub = new FakeEventHub();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeListener = new FakeInputListener();
+        mFakeListener = new TestInputListener();
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
+        identifier.location = DEVICE_LOCATION;
         mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
                 DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
 
-        mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
+        mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
     }
 
     virtual void TearDown() {
@@ -1582,7 +1780,7 @@
     }
 
     void addConfigurationProperty(const char* key, const char* value) {
-        mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8(key), String8(value));
+        mFakeEventHub->addConfigurationProperty(mDevice->getId(), String8(key), String8(value));
     }
 
     void configureDevice(uint32_t changes) {
@@ -1596,22 +1794,22 @@
     }
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
-            int32_t orientation) {
-        mFakePolicy->setDisplayViewport(displayId, width, height, orientation, String8::empty());
+            int32_t orientation, const std::string& uniqueId,
+            std::optional<uint8_t> physicalPort, ViewportType viewportType) {
+        mFakePolicy->addDisplayViewport(
+                displayId, width, height, orientation, uniqueId, physicalPort, viewportType);
         configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     }
 
-    void setVirtualDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
-            int32_t orientation, const String8& uniqueId) {
-        mFakePolicy->setVirtualDisplayViewport(displayId, width, height, orientation, uniqueId);
-        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    void clearViewports() {
+        mFakePolicy->clearViewports();
     }
 
-    static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type,
+    static void process(InputMapper* mapper, nsecs_t when, int32_t type,
             int32_t code, int32_t value) {
         RawEvent event;
         event.when = when;
-        event.deviceId = deviceId;
+        event.deviceId = mapper->getDeviceId();
         event.type = type;
         event.code = code;
         event.value = value;
@@ -1621,7 +1819,7 @@
     static void assertMotionRange(const InputDeviceInfo& info,
             int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) {
         const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
-        ASSERT_TRUE(range != NULL) << "Axis: " << axis << " Source: " << source;
+        ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
         ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
         ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
         ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
@@ -1655,6 +1853,7 @@
 };
 
 const char* InputMapperTest::DEVICE_NAME = "device";
+const char* InputMapperTest::DEVICE_LOCATION = "USB1";
 const int32_t InputMapperTest::DEVICE_ID = 1;
 const int32_t InputMapperTest::DEVICE_GENERATION = 2;
 const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
@@ -1689,10 +1888,10 @@
     SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
     addMapperAndConfigure(mapper);
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_LID, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_HEADPHONE_INSERT, 0);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_SW, SW_LID, 1);
+    process(mapper, ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
+    process(mapper, ARBITRARY_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 
     NotifySwitchArgs args;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
@@ -1708,21 +1907,33 @@
 
 class KeyboardInputMapperTest : public InputMapperTest {
 protected:
+    const std::string UNIQUE_ID = "local:0";
+
+    void prepareDisplay(int32_t orientation);
+
     void testDPadKeyRotation(KeyboardInputMapper* mapper,
             int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode);
 };
 
+/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
+ * orientation.
+ */
+void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+}
+
 void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
-        int32_t originalScanCode, int32_t, int32_t rotatedKeyCode) {
+        int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) {
     NotifyKeyArgs args;
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
     ASSERT_EQ(originalScanCode, args.scanCode);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
     ASSERT_EQ(originalScanCode, args.scanCode);
@@ -1749,8 +1960,7 @@
     addMapperAndConfigure(mapper);
 
     // Key down by scan code.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_HOME, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
     NotifyKeyArgs args;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -1765,8 +1975,7 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key up by scan code.
-    process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
-            EV_KEY, KEY_HOME, 0);
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -1780,10 +1989,8 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key down by usage code.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_MSC, MSC_SCAN, USAGE_A);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, 0, 1);
+    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+    process(mapper, ARBITRARY_TIME, EV_KEY, 0, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -1797,10 +2004,8 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key up by usage code.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_MSC, MSC_SCAN, USAGE_A);
-    process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
-            EV_KEY, 0, 0);
+    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, 0, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -1814,10 +2019,8 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key down with unknown scan code or usage code.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_UNKNOWN, 1);
+    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -1831,10 +2034,8 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key up with unknown scan code or usage code.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
-    process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
-            EV_KEY, KEY_UNKNOWN, 0);
+    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -1860,8 +2061,7 @@
     ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
 
     // Metakey down.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_LEFTSHIFT, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
     NotifyKeyArgs args;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
@@ -1869,22 +2069,19 @@
     ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
 
     // Key down.
-    process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
-            EV_KEY, KEY_A, 1);
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
 
     // Key up.
-    process(mapper, ARBITRARY_TIME + 2, DEVICE_ID,
-            EV_KEY, KEY_A, 0);
+    process(mapper, ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
 
     // Metakey up.
-    process(mapper, ARBITRARY_TIME + 3, DEVICE_ID,
-            EV_KEY, KEY_LEFTSHIFT, 0);
+    process(mapper, ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_NONE, args.metaState);
     ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
@@ -1901,9 +2098,7 @@
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addMapperAndConfigure(mapper);
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_90);
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1925,9 +2120,7 @@
     addConfigurationProperty("keyboard.orientationAware", "1");
     addMapperAndConfigure(mapper);
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1937,9 +2130,8 @@
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_90);
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1949,9 +2141,8 @@
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_180);
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1961,9 +2152,8 @@
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_270);
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_270);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1976,26 +2166,81 @@
     // Special case: if orientation changes while key is down, we still emit the same keycode
     // in the key up as we did in the key down.
     NotifyKeyArgs args;
-
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_270);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1);
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_270);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
     ASSERT_EQ(KEY_UP, args.scanCode);
     ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_180);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0);
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_180);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
     ASSERT_EQ(KEY_UP, args.scanCode);
     ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
 }
 
+TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware) {
+    // If the keyboard is not orientation aware,
+    // key events should not be associated with a specific display id
+    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
+            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    addMapperAndConfigure(mapper);
+    NotifyKeyArgs args;
+
+    // Display id should be ADISPLAY_ID_NONE without any display configuration.
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
+
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
+}
+
+TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) {
+    // If the keyboard is orientation aware,
+    // key events should be associated with the internal viewport
+    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
+            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    addConfigurationProperty("keyboard.orientationAware", "1");
+    addMapperAndConfigure(mapper);
+    NotifyKeyArgs args;
+
+    // Display id should be ADISPLAY_ID_NONE without any display configuration.
+    // ^--- already checked by the previous test
+
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+            UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(DISPLAY_ID, args.displayId);
+
+    constexpr int32_t newDisplayId = 2;
+    clearViewports();
+    setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+            UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(newDisplayId, args.displayId);
+}
+
 TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
     KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
@@ -2052,60 +2297,48 @@
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
 
     // Toggle caps lock on.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_CAPSLOCK, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_CAPSLOCK, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper->getMetaState());
 
     // Toggle num lock on.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_NUMLOCK, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_NUMLOCK, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper->getMetaState());
 
     // Toggle caps lock off.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_CAPSLOCK, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_CAPSLOCK, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper->getMetaState());
 
     // Toggle scroll lock on.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_SCROLLLOCK, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_SCROLLLOCK, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
 
     // Toggle num lock off.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_NUMLOCK, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_NUMLOCK, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
 
     // Toggle scroll lock off.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_SCROLLLOCK, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID,
-            EV_KEY, KEY_SCROLLLOCK, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
@@ -2125,11 +2358,18 @@
         InputMapperTest::SetUp();
 
         mFakePointerController = new FakePointerController();
-        mFakePolicy->setPointerController(DEVICE_ID, mFakePointerController);
+        mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
     }
 
     void testMotionRotation(CursorInputMapper* mapper,
             int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY);
+
+    void prepareDisplay(int32_t orientation) {
+        const std::string uniqueId = "local:0";
+        const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
+        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                orientation, uniqueId, NO_PORT, viewportType);
+    }
 };
 
 const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
@@ -2138,9 +2378,9 @@
         int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) {
     NotifyMotionArgs args;
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, originalX);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, originalY);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, originalX);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, originalY);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2174,8 +2414,8 @@
     mapper->populateDeviceInfo(&info);
 
     // Initially there may not be a valid motion range.
-    ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
-    ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
+    ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
+    ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
             AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
 
@@ -2226,8 +2466,8 @@
 
     // Button press.
     // Mostly testing non x/y behavior here so we don't need to check again elsewhere.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -2267,8 +2507,8 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Button release.  Should have same down time.
-    process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
-    process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0);
+    process(mapper, ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -2316,16 +2556,16 @@
     NotifyMotionArgs args;
 
     // Motion in X but not Y.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Motion in Y but not X.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, -2);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, -2);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2340,8 +2580,8 @@
     NotifyMotionArgs args;
 
     // Button press.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2353,8 +2593,8 @@
             0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Button release.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2374,10 +2614,10 @@
     NotifyMotionArgs args;
 
     // Combined X, Y and Button.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, -2);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 1);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, -2);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2391,9 +2631,9 @@
             1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Move X, Y a bit while pressed.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 2);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 2);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2401,8 +2641,8 @@
             1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Release Button.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2419,9 +2659,7 @@
     addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_90);
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -2438,8 +2676,7 @@
     addConfigurationProperty("cursor.orientationAware", "1");
     addMapperAndConfigure(mapper);
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -2449,8 +2686,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_90);
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0, -1));
@@ -2460,8 +2696,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1,  1));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_180);
+    prepareDisplay(DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0, -1,  0));
@@ -2471,8 +2706,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_270);
+    prepareDisplay(DISPLAY_ORIENTATION_270);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0,  1));
@@ -2496,8 +2730,8 @@
     NotifyKeyArgs keyArgs;
 
     // press BTN_LEFT, release BTN_LEFT
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_LEFT, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
@@ -2512,8 +2746,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_LEFT, 0);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -2536,9 +2770,9 @@
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_RIGHT, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
@@ -2565,8 +2799,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_RIGHT, 0);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
@@ -2581,16 +2815,16 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
     ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -2607,8 +2841,8 @@
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // press BTN_BACK, release BTN_BACK
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_BACK, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_BACK, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -2627,8 +2861,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_BACK, 0);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_BACK, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -2648,8 +2882,8 @@
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
 
     // press BTN_SIDE, release BTN_SIDE
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_SIDE, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_SIDE, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -2668,8 +2902,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_SIDE, 0);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_SIDE, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -2689,8 +2923,8 @@
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
 
     // press BTN_FORWARD, release BTN_FORWARD
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_FORWARD, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_FORWARD, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -2709,8 +2943,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_FORWARD, 0);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_FORWARD, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -2730,8 +2964,8 @@
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
 
     // press BTN_EXTRA, release BTN_EXTRA
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_EXTRA, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_EXTRA, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -2750,8 +2984,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_EXTRA, 0);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_EXTRA, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -2782,9 +3016,9 @@
 
     NotifyMotionArgs args;
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 10);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 20);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -2811,9 +3045,9 @@
     NotifyMotionArgs args;
 
     // Move.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 10);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 20);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
@@ -2822,8 +3056,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f));
 
     // Button press.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
@@ -2836,8 +3070,8 @@
             0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Button release.
-    process(mapper, ARBITRARY_TIME + 2, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
-    process(mapper, ARBITRARY_TIME + 2, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME + 2, EV_KEY, BTN_MOUSE, 0);
+    process(mapper, ARBITRARY_TIME + 2, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
@@ -2850,9 +3084,9 @@
             0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Another move.
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 30);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 40);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 30);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 40);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
@@ -2871,9 +3105,9 @@
     ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
 
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 10);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 20);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -2882,6 +3116,30 @@
     ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
 }
 
+TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addMapperAndConfigure(mapper);
+
+    // Setup PointerController for second display.
+    constexpr int32_t SECOND_DISPLAY_ID = 1;
+    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    mFakePointerController->setPosition(100, 200);
+    mFakePointerController->setButtonState(0);
+    mFakePointerController->setDisplayId(SECOND_DISPLAY_ID);
+
+    NotifyMotionArgs args;
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
+    ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
+}
+
 
 // --- TouchInputMapperTest ---
 
@@ -2917,6 +3175,9 @@
 
     static const VirtualKeyDefinition VIRTUAL_KEYS[2];
 
+    const std::string UNIQUE_ID = "local:0";
+    const std::string SECONDARY_UNIQUE_ID = "local:1";
+
     enum Axes {
         POSITION = 1 << 0,
         TOUCH = 1 << 1,
@@ -2931,7 +3192,8 @@
         TOOL_TYPE = 1 << 10,
     };
 
-    void prepareDisplay(int32_t orientation);
+    void prepareDisplay(int32_t orientation, std::optional<uint8_t> port = NO_PORT);
+    void prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port = NO_PORT);
     void prepareVirtualDisplay(int32_t orientation);
     void prepareVirtualKeys();
     void prepareLocationCalibration();
@@ -2984,13 +3246,20 @@
         { KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 },
 };
 
-void TouchInputMapperTest::prepareDisplay(int32_t orientation) {
-    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation);
+void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
+            UNIQUE_ID, port, ViewportType::VIEWPORT_INTERNAL);
+}
+
+void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
+    setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, SECONDARY_UNIQUE_ID, port, type);
 }
 
 void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
-    setVirtualDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
-        VIRTUAL_DISPLAY_HEIGHT, orientation, String8(VIRTUAL_DISPLAY_UNIQUE_ID));
+    setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
+        VIRTUAL_DISPLAY_HEIGHT, orientation,
+        VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
 }
 
 void TouchInputMapperTest::prepareVirtualKeys() {
@@ -3089,48 +3358,48 @@
 }
 
 void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 1);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, x);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, y);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
 }
 
 void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, x);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, y);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
 }
 
 void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper* mapper) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 0);
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 0);
 }
 
 void SingleTouchInputMapperTest::processPressure(
         SingleTouchInputMapper* mapper, int32_t pressure) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_PRESSURE, pressure);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_PRESSURE, pressure);
 }
 
 void SingleTouchInputMapperTest::processToolMajor(
         SingleTouchInputMapper* mapper, int32_t toolMajor) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
 }
 
 void SingleTouchInputMapperTest::processDistance(
         SingleTouchInputMapper* mapper, int32_t distance) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_DISTANCE, distance);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_DISTANCE, distance);
 }
 
 void SingleTouchInputMapperTest::processTilt(
         SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TILT_X, tiltX);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TILT_Y, tiltY);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_X, tiltX);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_Y, tiltY);
 }
 
 void SingleTouchInputMapperTest::processKey(
         SingleTouchInputMapper* mapper, int32_t code, int32_t value) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, code, value);
+    process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
 }
 
 void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 }
 
 
@@ -3719,6 +3988,7 @@
     NotifyMotionArgs args;
 
     // Rotation 0.
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_0);
     processDown(mapper, toRawX(50), toRawY(75));
     processSync(mapper);
@@ -3732,6 +4002,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 
     // Rotation 90.
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_90);
     processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
     processSync(mapper);
@@ -3745,6 +4016,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 
     // Rotation 180.
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_180);
     processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
     processSync(mapper);
@@ -3758,6 +4030,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 
     // Rotation 270.
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
     processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
     processSync(mapper);
@@ -4441,75 +4714,75 @@
 
 void MultiTouchInputMapperTest::processPosition(
         MultiTouchInputMapper* mapper, int32_t x, int32_t y) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_X, x);
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_Y, y);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, x);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
 }
 
 void MultiTouchInputMapperTest::processTouchMajor(
         MultiTouchInputMapper* mapper, int32_t touchMajor) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
 }
 
 void MultiTouchInputMapperTest::processTouchMinor(
         MultiTouchInputMapper* mapper, int32_t touchMinor) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
 }
 
 void MultiTouchInputMapperTest::processToolMajor(
         MultiTouchInputMapper* mapper, int32_t toolMajor) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
 }
 
 void MultiTouchInputMapperTest::processToolMinor(
         MultiTouchInputMapper* mapper, int32_t toolMinor) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
 }
 
 void MultiTouchInputMapperTest::processOrientation(
         MultiTouchInputMapper* mapper, int32_t orientation) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_ORIENTATION, orientation);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation);
 }
 
 void MultiTouchInputMapperTest::processPressure(
         MultiTouchInputMapper* mapper, int32_t pressure) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_PRESSURE, pressure);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_PRESSURE, pressure);
 }
 
 void MultiTouchInputMapperTest::processDistance(
         MultiTouchInputMapper* mapper, int32_t distance) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_DISTANCE, distance);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_DISTANCE, distance);
 }
 
 void MultiTouchInputMapperTest::processId(
         MultiTouchInputMapper* mapper, int32_t id) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TRACKING_ID, id);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, id);
 }
 
 void MultiTouchInputMapperTest::processSlot(
         MultiTouchInputMapper* mapper, int32_t slot) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_SLOT, slot);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, slot);
 }
 
 void MultiTouchInputMapperTest::processToolType(
         MultiTouchInputMapper* mapper, int32_t toolType) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
 }
 
 void MultiTouchInputMapperTest::processKey(
         MultiTouchInputMapper* mapper, int32_t code, int32_t value) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, code, value);
+    process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
 }
 
 void MultiTouchInputMapperTest::processTimestamp(MultiTouchInputMapper* mapper, uint32_t value) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_MSC, MSC_TIMESTAMP, value);
+    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, value);
 }
 
 void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_MT_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_MT_REPORT, 0);
 }
 
 void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) {
-    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 }
 
 
@@ -5944,5 +6217,102 @@
     ASSERT_EQ(0U, args.deviceTimestamp);
 }
 
+/**
+ * Set the input device port <--> display port associations, and check that the
+ * events are routed to the display that matches the display port.
+ * This can be checked by looking at the displayId of the resulting NotifyMotionArgs.
+ */
+TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    const std::string usb2 = "USB2";
+    const uint8_t hdmi1 = 0;
+    const uint8_t hdmi2 = 1;
+    const std::string secondaryUniqueId = "uniqueId2";
+    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+    mFakePolicy->addInputPortAssociation(usb2, hdmi2);
+
+    // We are intentionally not adding the viewport for display 1 yet. Since the port association
+    // for this input device is specified, and the matching viewport is not present,
+    // the input device should be disabled (at the mapper level).
+
+    // Add viewport for display 2 on hdmi2
+    prepareSecondaryDisplay(type, hdmi2);
+    // Send a touch event
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // Add viewport for display 1 on hdmi1
+    prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+    // Send a touch event again
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(DISPLAY_ID, args.displayId);
+}
+
+/**
+ * Expect fallback to internal viewport if device is external and external viewport is not present.
+ */
+TEST_F(MultiTouchInputMapperTest, Viewports_Fallback) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    mDevice->setExternal(true);
+    addMapperAndConfigure(mapper);
+
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
+
+    NotifyMotionArgs motionArgs;
+
+    // Expect the event to be sent to the internal viewport,
+    // because an external viewport is not present.
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
+
+    // Expect the event to be sent to the external viewport if it is present.
+    prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
+    // Setup PointerController for second display.
+    sp<FakePointerController> fakePointerController = new FakePointerController();
+    fakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    fakePointerController->setPosition(100, 200);
+    fakePointerController->setButtonState(0);
+    fakePointerController->setDisplayId(SECONDARY_DISPLAY_ID);
+    mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+
+    // Check source is mouse that would obtain the PointerController.
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+
+    NotifyMotionArgs motionArgs;
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
+}
 
 } // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
new file mode 100644
index 0000000..3ee33f1
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <gtest/gtest.h>
+
+#include "TestInputListener.h"
+
+namespace android {
+
+// --- TestInputListener ---
+
+TestInputListener::TestInputListener() { }
+
+TestInputListener::~TestInputListener() { }
+
+void TestInputListener::assertNotifyConfigurationChangedWasCalled(
+        NotifyConfigurationChangedArgs* outEventArgs) {
+    ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
+            << "Expected notifyConfigurationChanged() to have been called.";
+    if (outEventArgs) {
+        *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
+    }
+    mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() {
+    ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
+            << "Expected notifyConfigurationChanged() to not have been called.";
+}
+
+void TestInputListener::assertNotifyDeviceResetWasCalled(
+        NotifyDeviceResetArgs* outEventArgs) {
+    ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
+            << "Expected notifyDeviceReset() to have been called.";
+    if (outEventArgs) {
+        *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
+    }
+    mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyDeviceResetWasNotCalled() {
+    ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
+            << "Expected notifyDeviceReset() to not have been called.";
+}
+
+void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) {
+    ASSERT_FALSE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to have been called.";
+    if (outEventArgs) {
+        *outEventArgs = *mNotifyKeyArgsQueue.begin();
+    }
+    mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyKeyWasNotCalled() {
+    ASSERT_TRUE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to not have been called.";
+}
+
+void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) {
+    ASSERT_FALSE(mNotifyMotionArgsQueue.empty()) << "Expected notifyMotion() to have been called.";
+    if (outEventArgs) {
+        *outEventArgs = *mNotifyMotionArgsQueue.begin();
+    }
+    mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyMotionWasNotCalled() {
+    ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
+            << "Expected notifyMotion() to not have been called.";
+}
+
+void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) {
+    ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
+            << "Expected notifySwitch() to have been called.";
+    if (outEventArgs) {
+        *outEventArgs = *mNotifySwitchArgsQueue.begin();
+    }
+    mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
+}
+
+void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+    mNotifyConfigurationChangedArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+    mNotifyDeviceResetArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
+    mNotifyKeyArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
+    mNotifyMotionArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
+        mNotifySwitchArgsQueue.push_back(*args);
+    }
+
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
new file mode 100644
index 0000000..085d343
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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 _UI_TEST_INPUT_LISTENER_H
+#define _UI_TEST_INPUT_LISTENER_H
+
+#include <gtest/gtest.h>
+#include "InputListener.h"
+
+namespace android {
+
+// --- TestInputListener ---
+
+class TestInputListener : public InputListenerInterface {
+private:
+    std::vector<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
+    std::vector<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
+    std::vector<NotifyKeyArgs> mNotifyKeyArgsQueue;
+    std::vector<NotifyMotionArgs> mNotifyMotionArgsQueue;
+    std::vector<NotifySwitchArgs> mNotifySwitchArgsQueue;
+
+protected:
+    virtual ~TestInputListener();
+
+public:
+    TestInputListener();
+
+    void assertNotifyConfigurationChangedWasCalled(
+            NotifyConfigurationChangedArgs* outEventArgs = nullptr);
+
+    void assertNotifyConfigurationChangedWasNotCalled();
+
+    void assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* outEventArgs = nullptr);
+
+    void assertNotifyDeviceResetWasNotCalled();
+
+    void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr);
+
+    void assertNotifyKeyWasNotCalled();
+
+    void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
+
+    void assertNotifyMotionWasNotCalled();
+
+    void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
+
+private:
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
+
+    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+
+    virtual void notifyKey(const NotifyKeyArgs* args);
+
+    virtual void notifyMotion(const NotifyMotionArgs* args);
+
+    virtual void notifySwitch(const NotifySwitchArgs* args);
+};
+
+} // namespace android
+#endif
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index a7f3a52..33a2747 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -41,18 +41,21 @@
         "liblog",
         "libbinder",
         "libsensor",
+        "libsensorprivacy",
         "libcrypto",
         "libbase",
         "libhidlbase",
         "libhidltransport",
         "libhwbinder",
+        "libfmq",
         "android.hardware.sensors@1.0",
+        "android.hardware.sensors@2.0",
     ],
 
     static_libs: ["android.hardware.sensors@1.0-convert"],
 
-    // our public headers depend on libsensor
-    export_shared_lib_headers: ["libsensor"],
+    // our public headers depend on libsensor and libsensorprivacy
+    export_shared_lib_headers: ["libsensor", "libsensorprivacy"],
 }
 
 cc_binary {
@@ -62,6 +65,7 @@
 
     shared_libs: [
         "libsensorservice",
+        "libsensorprivacy",
         "libbinder",
         "libutils",
     ],
diff --git a/services/sensorservice/BatteryService.cpp b/services/sensorservice/BatteryService.cpp
index d8e5b29..14f9a12 100644
--- a/services/sensorservice/BatteryService.cpp
+++ b/services/sensorservice/BatteryService.cpp
@@ -94,7 +94,7 @@
 bool BatteryService::checkService() {
     if (mBatteryStatService == nullptr) {
         const sp<IServiceManager> sm(defaultServiceManager());
-        if (sm != NULL) {
+        if (sm != nullptr) {
             const String16 name("batterystats");
             mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name));
         }
diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp
index 1025a88..207b097 100644
--- a/services/sensorservice/RecentEventLogger.cpp
+++ b/services/sensorservice/RecentEventLogger.cpp
@@ -26,24 +26,32 @@
 
 namespace {
     constexpr size_t LOG_SIZE = 10;
+    constexpr size_t LOG_SIZE_MED = 30;  // debugging for slower sensors
     constexpr size_t LOG_SIZE_LARGE = 50;  // larger samples for debugging
 }// unnamed namespace
 
 RecentEventLogger::RecentEventLogger(int sensorType) :
         mSensorType(sensorType), mEventSize(eventSizeBySensorType(mSensorType)),
-        mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false) {
+        mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false),
+        mIsLastEventCurrent(false) {
     // blank
 }
 
 void RecentEventLogger::addEvent(const sensors_event_t& event) {
     std::lock_guard<std::mutex> lk(mLock);
     mRecentEvents.emplace(event);
+    mIsLastEventCurrent = true;
 }
 
 bool RecentEventLogger::isEmpty() const {
     return mRecentEvents.size() == 0;
 }
 
+void RecentEventLogger::setLastEventStale() {
+    std::lock_guard<std::mutex> lk(mLock);
+    mIsLastEventCurrent = false;
+}
+
 std::string RecentEventLogger::dump() const {
     std::lock_guard<std::mutex> lk(mLock);
 
@@ -84,10 +92,10 @@
     }
 }
 
-bool RecentEventLogger::populateLastEvent(sensors_event_t *event) const {
+bool RecentEventLogger::populateLastEventIfCurrent(sensors_event_t *event) const {
     std::lock_guard<std::mutex> lk(mLock);
 
-    if (mRecentEvents.size()) {
+    if (mIsLastEventCurrent && mRecentEvents.size()) {
         // Index 0 contains the latest event emplace()'ed
         *event = mRecentEvents[0].mEvent;
         return true;
@@ -98,10 +106,16 @@
 
 
 size_t RecentEventLogger::logSizeBySensorType(int sensorType) {
-    return (sensorType == SENSOR_TYPE_STEP_COUNTER ||
-            sensorType == SENSOR_TYPE_SIGNIFICANT_MOTION ||
-            sensorType == SENSOR_TYPE_ACCELEROMETER ||
-            sensorType == SENSOR_TYPE_LIGHT) ? LOG_SIZE_LARGE : LOG_SIZE;
+    if (sensorType == SENSOR_TYPE_STEP_COUNTER ||
+        sensorType == SENSOR_TYPE_SIGNIFICANT_MOTION ||
+        sensorType == SENSOR_TYPE_ACCELEROMETER ||
+        sensorType == SENSOR_TYPE_LIGHT) {
+        return LOG_SIZE_LARGE;
+    }
+    if (sensorType == SENSOR_TYPE_PROXIMITY) {
+        return LOG_SIZE_MED;
+    }
+    return LOG_SIZE;
 }
 
 RecentEventLogger::SensorEventLog::SensorEventLog(const sensors_event_t& e) : mEvent(e) {
diff --git a/services/sensorservice/RecentEventLogger.h b/services/sensorservice/RecentEventLogger.h
index bf1f655..67378b7 100644
--- a/services/sensorservice/RecentEventLogger.h
+++ b/services/sensorservice/RecentEventLogger.h
@@ -37,8 +37,13 @@
 public:
     explicit RecentEventLogger(int sensorType);
     void addEvent(const sensors_event_t& event);
-    bool populateLastEvent(sensors_event_t *event) const;
+
+    // Populate event with the last recorded sensor event if it is not stale. An event is
+    // considered stale if the sensor has become deactivated since the event was recorded.
+    // returns true on success, false if no recent event is available or the last event is stale
+    bool populateLastEventIfCurrent(sensors_event_t *event) const;
     bool isEmpty() const;
+    void setLastEventStale();
     virtual ~RecentEventLogger() {}
 
     // Dumpable interface
@@ -59,6 +64,7 @@
     RingBuffer<SensorEventLog> mRecentEvents;
 
     bool mMaskData;
+    bool mIsLastEventCurrent;
 
 private:
     static size_t logSizeBySensorType(int sensorType);
diff --git a/services/sensorservice/RotationVectorSensor.cpp b/services/sensorservice/RotationVectorSensor.cpp
index 7b00f4d..f2ea02e 100644
--- a/services/sensorservice/RotationVectorSensor.cpp
+++ b/services/sensorservice/RotationVectorSensor.cpp
@@ -94,7 +94,7 @@
             return "GeoMag Rotation Vector Sensor";
         default:
             assert(0);
-            return NULL;
+            return nullptr;
     }
 }
 
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 115a983..189ae36 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -13,7 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include "SensorDevice.h"
+
+#include "android/hardware/sensors/2.0/ISensorsCallback.h"
+#include "android/hardware/sensors/2.0/types.h"
 #include "SensorService.h"
 
 #include <android-base/logging.h>
@@ -26,9 +30,14 @@
 #include <cinttypes>
 #include <thread>
 
+using namespace android::hardware::sensors;
 using namespace android::hardware::sensors::V1_0;
 using namespace android::hardware::sensors::V1_0::implementation;
+using android::hardware::sensors::V2_0::ISensorsCallback;
+using android::hardware::sensors::V2_0::EventQueueFlagBits;
+using android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
 using android::hardware::hidl_vec;
+using android::hardware::Return;
 using android::SensorDeviceUtils::HidlServiceRegistrationWaiter;
 
 namespace android {
@@ -51,12 +60,55 @@
     }
 }
 
+template<typename EnumType>
+constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
+    return static_cast<typename std::underlying_type<EnumType>::type>(value);
+}
+
+// Used internally by the framework to wake the Event FMQ. These values must start after
+// the last value of EventQueueFlagBits
+enum EventQueueFlagBitsInternal : uint32_t {
+    INTERNAL_WAKE =  1 << 16,
+};
+
+void SensorsHalDeathReceivier::serviceDied(
+        uint64_t /* cookie */,
+        const wp<::android::hidl::base::V1_0::IBase>& /* service */) {
+    ALOGW("Sensors HAL died, attempting to reconnect.");
+    SensorDevice::getInstance().prepareForReconnect();
+}
+
+struct SensorsCallback : public ISensorsCallback {
+    using Result = ::android::hardware::sensors::V1_0::Result;
+    Return<void> onDynamicSensorsConnected(
+            const hidl_vec<SensorInfo> &dynamicSensorsAdded) override {
+        return SensorDevice::getInstance().onDynamicSensorsConnected(dynamicSensorsAdded);
+    }
+
+    Return<void> onDynamicSensorsDisconnected(
+            const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) override {
+        return SensorDevice::getInstance().onDynamicSensorsDisconnected(
+                dynamicSensorHandlesRemoved);
+    }
+};
+
 SensorDevice::SensorDevice()
-        : mHidlTransportErrors(20), mRestartWaiter(new HidlServiceRegistrationWaiter()) {
+        : mHidlTransportErrors(20),
+          mRestartWaiter(new HidlServiceRegistrationWaiter()),
+          mEventQueueFlag(nullptr),
+          mWakeLockQueueFlag(nullptr),
+          mReconnecting(false) {
     if (!connectHidlService()) {
         return;
     }
 
+    initializeSensorList();
+
+    mIsDirectReportSupported =
+           (checkReturn(mSensors->unregisterDirectChannel(-1)) != Result::INVALID_OPERATION);
+}
+
+void SensorDevice::initializeSensorList() {
     float minPowerMa = 0.001; // 1 microAmp
 
     checkReturn(mSensors->getSensorsList(
@@ -81,37 +133,208 @@
                     checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */));
                 }
             }));
+}
 
-    mIsDirectReportSupported =
-           (checkReturn(mSensors->unregisterDirectChannel(-1)) != Result::INVALID_OPERATION);
+SensorDevice::~SensorDevice() {
+    if (mEventQueueFlag != nullptr) {
+        hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
+        mEventQueueFlag = nullptr;
+    }
+
+    if (mWakeLockQueueFlag != nullptr) {
+        hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
+        mWakeLockQueueFlag = nullptr;
+    }
 }
 
 bool SensorDevice::connectHidlService() {
+    HalConnectionStatus status = connectHidlServiceV2_0();
+    if (status == HalConnectionStatus::DOES_NOT_EXIST) {
+        status = connectHidlServiceV1_0();
+    }
+    return (status == HalConnectionStatus::CONNECTED);
+}
+
+SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV1_0() {
     // SensorDevice will wait for HAL service to start if HAL is declared in device manifest.
     size_t retry = 10;
+    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
 
     while (retry-- > 0) {
-        mSensors = ISensors::getService();
-        if (mSensors == nullptr) {
+        sp<V1_0::ISensors> sensors = V1_0::ISensors::getService();
+        if (sensors == nullptr) {
             // no sensor hidl service found
+            connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
             break;
         }
 
+        mSensors = new SensorServiceUtil::SensorsWrapperV1_0(sensors);
         mRestartWaiter->reset();
         // Poke ISensor service. If it has lingering connection from previous generation of
         // system server, it will kill itself. There is no intention to handle the poll result,
         // which will be done since the size is 0.
         if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
             // ok to continue
+            connectionStatus = HalConnectionStatus::CONNECTED;
             break;
         }
 
         // hidl service is restarting, pointer is invalid.
         mSensors = nullptr;
+        connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
         ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry);
         mRestartWaiter->wait();
     }
-    return (mSensors != nullptr);
+
+    return connectionStatus;
+}
+
+SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_0() {
+    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
+    sp<V2_0::ISensors> sensors = V2_0::ISensors::getService();
+
+    if (sensors == nullptr) {
+        connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
+    } else {
+        mSensors = new SensorServiceUtil::SensorsWrapperV2_0(sensors);
+
+        mEventQueue = std::make_unique<EventMessageQueue>(
+                SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
+                true /* configureEventFlagWord */);
+
+        mWakeLockQueue = std::make_unique<WakeLockQueue>(
+                SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
+                true /* configureEventFlagWord */);
+
+        hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
+        hardware::EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag);
+
+        hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
+        hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(),
+                                             &mWakeLockQueueFlag);
+
+        CHECK(mSensors != nullptr && mEventQueue != nullptr &&
+                mWakeLockQueue != nullptr && mEventQueueFlag != nullptr &&
+                mWakeLockQueueFlag != nullptr);
+
+        status_t status = StatusFromResult(checkReturn(mSensors->initialize(
+                *mEventQueue->getDesc(),
+                *mWakeLockQueue->getDesc(),
+                new SensorsCallback())));
+
+        if (status != NO_ERROR) {
+            connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
+            ALOGE("Failed to initialize Sensors HAL (%s)", strerror(-status));
+        } else {
+            connectionStatus = HalConnectionStatus::CONNECTED;
+            mSensorsHalDeathReceiver = new SensorsHalDeathReceivier();
+            sensors->linkToDeath(mSensorsHalDeathReceiver, 0 /* cookie */);
+        }
+    }
+
+    return connectionStatus;
+}
+
+void SensorDevice::prepareForReconnect() {
+    mReconnecting = true;
+
+    // Wake up the polling thread so it returns and allows the SensorService to initiate
+    // a reconnect.
+    mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE));
+}
+
+void SensorDevice::reconnect() {
+    Mutex::Autolock _l(mLock);
+    mSensors = nullptr;
+
+    auto previousActivations = mActivationCount;
+    auto previousSensorList = mSensorList;
+
+    mActivationCount.clear();
+    mSensorList.clear();
+
+    if (connectHidlServiceV2_0() == HalConnectionStatus::CONNECTED) {
+        initializeSensorList();
+
+        if (sensorHandlesChanged(previousSensorList, mSensorList)) {
+            LOG_ALWAYS_FATAL("Sensor handles changed, cannot re-enable sensors.");
+        } else {
+            reactivateSensors(previousActivations);
+        }
+    }
+    mReconnecting = false;
+}
+
+bool SensorDevice::sensorHandlesChanged(const Vector<sensor_t>& oldSensorList,
+                                        const Vector<sensor_t>& newSensorList) {
+    bool didChange = false;
+
+    if (oldSensorList.size() != newSensorList.size()) {
+        didChange = true;
+    }
+
+    for (size_t i = 0; i < newSensorList.size() && !didChange; i++) {
+        bool found = false;
+        const sensor_t& newSensor = newSensorList[i];
+        for (size_t j = 0; j < oldSensorList.size() && !found; j++) {
+            const sensor_t& prevSensor = oldSensorList[j];
+            if (prevSensor.handle == newSensor.handle) {
+                found = true;
+                if (!sensorIsEquivalent(prevSensor, newSensor)) {
+                    didChange = true;
+                }
+            }
+        }
+
+        if (!found) {
+            // Could not find the new sensor in the old list of sensors, the lists must
+            // have changed.
+            didChange = true;
+        }
+    }
+    return didChange;
+}
+
+bool SensorDevice::sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor) {
+    bool equivalent = true;
+    if (prevSensor.handle != newSensor.handle ||
+            (strcmp(prevSensor.vendor, newSensor.vendor) != 0) ||
+            (strcmp(prevSensor.stringType, newSensor.stringType) != 0) ||
+            (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) ||
+            (prevSensor.version != newSensor.version) ||
+            (prevSensor.type != newSensor.type) ||
+            (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) ||
+            (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) ||
+            (std::abs(prevSensor.power - newSensor.power) > 0.001f) ||
+            (prevSensor.minDelay != newSensor.minDelay) ||
+            (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) ||
+            (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) ||
+            (prevSensor.maxDelay != newSensor.maxDelay) ||
+            (prevSensor.flags != newSensor.flags)) {
+        equivalent = false;
+    }
+    return equivalent;
+}
+
+void SensorDevice::reactivateSensors(const DefaultKeyedVector<int, Info>& previousActivations) {
+    for (size_t i = 0; i < mSensorList.size(); i++) {
+        int handle = mSensorList[i].handle;
+        ssize_t activationIndex = previousActivations.indexOfKey(handle);
+        if (activationIndex < 0 || previousActivations[activationIndex].numActiveClients() <= 0) {
+            continue;
+        }
+
+        const Info& info = previousActivations[activationIndex];
+        for (size_t j = 0; j < info.batchParams.size(); j++) {
+            const BatchParams& batchParams = info.batchParams[j];
+            status_t res = batchLocked(info.batchParams.keyAt(j), handle, 0 /* flags */,
+                    batchParams.mTSample, batchParams.mTBatch);
+
+            if (res == NO_ERROR) {
+                activateLocked(info.batchParams.keyAt(j), handle, true /* enabled */);
+            }
+        }
+    }
 }
 
 void SensorDevice::handleDynamicSensorConnection(int handle, bool connected) {
@@ -173,6 +396,19 @@
 ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
     if (mSensors == nullptr) return NO_INIT;
 
+    ssize_t eventsRead = 0;
+    if (mSensors->supportsMessageQueues()) {
+        eventsRead = pollFmq(buffer, count);
+    } else if (mSensors->supportsPolling()) {
+        eventsRead = pollHal(buffer, count);
+    } else {
+        ALOGE("Must support polling or FMQ");
+        eventsRead = -1;
+    }
+    return eventsRead;
+}
+
+ssize_t SensorDevice::pollHal(sensors_event_t* buffer, size_t count) {
     ssize_t err;
     int numHidlTransportErrors = 0;
     bool hidlTransportError = false;
@@ -208,7 +444,7 @@
 
     if(numHidlTransportErrors > 0) {
         ALOGE("Saw %d Hidl transport failures", numHidlTransportErrors);
-        HidlTransportErrorLog errLog(time(NULL), numHidlTransportErrors);
+        HidlTransportErrorLog errLog(time(nullptr), numHidlTransportErrors);
         mHidlTransportErrors.add(errLog);
         mTotalHidlTransportErrors++;
     }
@@ -216,6 +452,89 @@
     return err;
 }
 
+ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead) {
+    ssize_t eventsRead = 0;
+    size_t availableEvents = mEventQueue->availableToRead();
+
+    if (availableEvents == 0) {
+        uint32_t eventFlagState = 0;
+
+        // Wait for events to become available. This is necessary so that the Event FMQ's read() is
+        // able to be called with the correct number of events to read. If the specified number of
+        // events is not available, then read() would return no events, possibly introducing
+        // additional latency in delivering events to applications.
+        mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS) |
+                              asBaseType(INTERNAL_WAKE), &eventFlagState);
+        availableEvents = mEventQueue->availableToRead();
+
+        if ((eventFlagState & asBaseType(EventQueueFlagBits::READ_AND_PROCESS)) &&
+                availableEvents == 0) {
+            ALOGW("Event FMQ wake without any events");
+        }
+
+        if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) {
+            ALOGD("Event FMQ internal wake, returning from poll with no events");
+            return DEAD_OBJECT;
+        }
+    }
+
+    size_t eventsToRead = std::min({availableEvents, maxNumEventsToRead, mEventBuffer.size()});
+    if (eventsToRead > 0) {
+        if (mEventQueue->read(mEventBuffer.data(), eventsToRead)) {
+            // Notify the Sensors HAL that sensor events have been read. This is required to support
+            // the use of writeBlocking by the Sensors HAL.
+            mEventQueueFlag->wake(asBaseType(EventQueueFlagBits::EVENTS_READ));
+
+            for (size_t i = 0; i < eventsToRead; i++) {
+                convertToSensorEvent(mEventBuffer[i], &buffer[i]);
+            }
+            eventsRead = eventsToRead;
+        } else {
+            ALOGW("Failed to read %zu events, currently %zu events available",
+                    eventsToRead, availableEvents);
+        }
+    }
+
+    return eventsRead;
+}
+
+Return<void> SensorDevice::onDynamicSensorsConnected(
+        const hidl_vec<SensorInfo> &dynamicSensorsAdded) {
+    // Allocate a sensor_t structure for each dynamic sensor added and insert
+    // it into the dictionary of connected dynamic sensors keyed by handle.
+    for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
+        const SensorInfo &info = dynamicSensorsAdded[i];
+
+        auto it = mConnectedDynamicSensors.find(info.sensorHandle);
+        CHECK(it == mConnectedDynamicSensors.end());
+
+        sensor_t *sensor = new sensor_t();
+        convertToSensor(info, sensor);
+
+        mConnectedDynamicSensors.insert(
+                std::make_pair(sensor->handle, sensor));
+    }
+
+    return Return<void>();
+}
+
+Return<void> SensorDevice::onDynamicSensorsDisconnected(
+        const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) {
+    (void) dynamicSensorHandlesRemoved;
+    // TODO: Currently dynamic sensors do not seem to be removed
+    return Return<void>();
+}
+
+void SensorDevice::writeWakeLockHandled(uint32_t count) {
+    if (mSensors != nullptr && mSensors->supportsMessageQueues()) {
+        if (mWakeLockQueue->write(&count)) {
+            mWakeLockQueueFlag->wake(asBaseType(WakeLockQueueFlagBits::DATA_WRITTEN));
+        } else {
+            ALOGW("Failed to write wake lock handled");
+        }
+    }
+}
+
 void SensorDevice::autoDisable(void *ident, int handle) {
     Mutex::Autolock _l(mLock);
     ssize_t activationIndex = mActivationCount.indexOfKey(handle);
@@ -230,10 +549,15 @@
 status_t SensorDevice::activate(void* ident, int handle, int enabled) {
     if (mSensors == nullptr) return NO_INIT;
 
-    status_t err(NO_ERROR);
+    Mutex::Autolock _l(mLock);
+    return activateLocked(ident, handle, enabled);
+}
+
+status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) {
     bool actuateHardware = false;
 
-    Mutex::Autolock _l(mLock);
+    status_t err(NO_ERROR);
+
     ssize_t activationIndex = mActivationCount.indexOfKey(handle);
     if (activationIndex < 0) {
         ALOGW("Handle %d cannot be found in activation record", handle);
@@ -333,6 +657,11 @@
              ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
 
     Mutex::Autolock _l(mLock);
+    return batchLocked(ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
+}
+
+status_t SensorDevice::batchLocked(void* ident, int handle, int flags, int64_t samplingPeriodNs,
+                                   int64_t maxBatchReportLatencyNs) {
     ssize_t activationIndex = mActivationCount.indexOfKey(handle);
     if (activationIndex < 0) {
         ALOGW("Handle %d cannot be found in activation record", handle);
@@ -563,7 +892,7 @@
 
 // ---------------------------------------------------------------------------
 
-int SensorDevice::Info::numActiveClients() {
+int SensorDevice::Info::numActiveClients() const {
     SensorDevice& device(SensorDevice::getInstance());
     int num = 0;
     for (size_t i = 0; i < batchParams.size(); ++i) {
@@ -651,19 +980,9 @@
         const hidl_vec<Event> &src,
         const hidl_vec<SensorInfo> &dynamicSensorsAdded,
         sensors_event_t *dst) {
-    // Allocate a sensor_t structure for each dynamic sensor added and insert
-    // it into the dictionary of connected dynamic sensors keyed by handle.
-    for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
-        const SensorInfo &info = dynamicSensorsAdded[i];
 
-        auto it = mConnectedDynamicSensors.find(info.sensorHandle);
-        CHECK(it == mConnectedDynamicSensors.end());
-
-        sensor_t *sensor = new sensor_t;
-        convertToSensor(info, sensor);
-
-        mConnectedDynamicSensors.insert(
-                std::make_pair(sensor->handle, sensor));
+    if (dynamicSensorsAdded.size() > 0) {
+        onDynamicSensorsConnected(dynamicSensorsAdded);
     }
 
     for (size_t i = 0; i < src.size(); ++i) {
@@ -672,8 +991,12 @@
 }
 
 void SensorDevice::handleHidlDeath(const std::string & detail) {
-    // restart is the only option at present.
-    LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str());
+    if (!SensorDevice::getInstance().mSensors->supportsMessageQueues()) {
+        // restart is the only option at present.
+        LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str());
+    } else {
+        ALOGD("ISensors HAL died, death recipient will attempt reconnect");
+    }
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 6d75051..2a69654 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -19,20 +19,22 @@
 
 #include "SensorDeviceUtils.h"
 #include "SensorServiceUtils.h"
+#include "SensorsWrapper.h"
 
+#include <fmq/MessageQueue.h>
+#include <sensor/SensorEventQueue.h>
 #include <sensor/Sensor.h>
 #include <stdint.h>
 #include <sys/types.h>
 #include <utils/KeyedVector.h>
 #include <utils/Singleton.h>
 #include <utils/String8.h>
+#include <utils/Timers.h>
 
 #include <string>
 #include <unordered_map>
 #include <algorithm> //std::max std::min
 
-#include "android/hardware/sensors/1.0/ISensors.h"
-
 #include "RingBuffer.h"
 
 // ---------------------------------------------------------------------------
@@ -40,8 +42,13 @@
 namespace android {
 
 // ---------------------------------------------------------------------------
+class SensorsHalDeathReceivier : public android::hardware::hidl_death_recipient {
+    virtual void serviceDied(uint64_t cookie,
+                             const wp<::android::hidl::base::V1_0::IBase>& service) override;
+};
 
-class SensorDevice : public Singleton<SensorDevice>, public SensorServiceUtil::Dumpable {
+class SensorDevice : public Singleton<SensorDevice>,
+                     public SensorServiceUtil::Dumpable {
 public:
     class HidlTransportErrorLog {
      public:
@@ -69,6 +76,10 @@
         int mCount;   // number of transport errors observed
     };
 
+    ~SensorDevice();
+    void prepareForReconnect();
+    void reconnect();
+
     ssize_t getSensorList(sensor_t const** list);
 
     void handleDynamicSensorConnection(int handle, bool connected);
@@ -76,6 +87,7 @@
     int getHalDeviceVersion() const;
 
     ssize_t poll(sensors_event_t* buffer, size_t count);
+    void writeWakeLockHandled(uint32_t count);
 
     status_t activate(void* ident, int handle, int enabled);
     status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
@@ -98,12 +110,22 @@
     status_t injectSensorData(const sensors_event_t *event);
     void notifyConnectionDestroyed(void *ident);
 
+    using Result = ::android::hardware::sensors::V1_0::Result;
+    hardware::Return<void> onDynamicSensorsConnected(
+            const hardware::hidl_vec<hardware::sensors::V1_0::SensorInfo> &dynamicSensorsAdded);
+    hardware::Return<void> onDynamicSensorsDisconnected(
+            const hardware::hidl_vec<int32_t> &dynamicSensorHandlesRemoved);
+
+    bool isReconnecting() const {
+        return mReconnecting;
+    }
+
     // Dumpable
     virtual std::string dump() const;
 private:
     friend class Singleton<SensorDevice>;
 
-    sp<hardware::sensors::V1_0::ISensors> mSensors;
+    sp<SensorServiceUtil::ISensorsWrapper> mSensors;
     Vector<sensor_t> mSensorList;
     std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors;
 
@@ -151,7 +173,7 @@
         // the removed ident. If index >=0, ident is present and successfully removed.
         ssize_t removeBatchParamsForIdent(void* ident);
 
-        int numActiveClients();
+        int numActiveClients() const;
     };
     DefaultKeyedVector<int, Info> mActivationCount;
 
@@ -163,6 +185,26 @@
     SortedVector<void *> mDisabledClients;
     SensorDevice();
     bool connectHidlService();
+    void initializeSensorList();
+    void reactivateSensors(const DefaultKeyedVector<int, Info>& previousActivations);
+    static bool sensorHandlesChanged(const Vector<sensor_t>& oldSensorList,
+                                     const Vector<sensor_t>& newSensorList);
+    static bool sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor);
+
+    enum HalConnectionStatus {
+        CONNECTED, // Successfully connected to the HAL
+        DOES_NOT_EXIST, // Could not find the HAL
+        FAILED_TO_CONNECT, // Found the HAL but failed to connect/initialize
+        UNKNOWN,
+    };
+    HalConnectionStatus connectHidlServiceV1_0();
+    HalConnectionStatus connectHidlServiceV2_0();
+
+    ssize_t pollHal(sensors_event_t* buffer, size_t count);
+    ssize_t pollFmq(sensors_event_t* buffer, size_t count);
+    status_t activateLocked(void* ident, int handle, int enabled);
+    status_t batchLocked(void* ident, int handle, int flags, int64_t samplingPeriodNs,
+                         int64_t maxBatchReportLatencyNs);
 
     static void handleHidlDeath(const std::string &detail);
     template<typename T>
@@ -189,6 +231,19 @@
             sensors_event_t *dst);
 
     bool mIsDirectReportSupported;
+
+    typedef hardware::MessageQueue<Event, hardware::kSynchronizedReadWrite> EventMessageQueue;
+    typedef hardware::MessageQueue<uint32_t, hardware::kSynchronizedReadWrite> WakeLockQueue;
+    std::unique_ptr<EventMessageQueue> mEventQueue;
+    std::unique_ptr<WakeLockQueue> mWakeLockQueue;
+
+    hardware::EventFlag* mEventQueueFlag;
+    hardware::EventFlag* mWakeLockQueueFlag;
+
+    std::array<Event, SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT> mEventBuffer;
+
+    sp<SensorsHalDeathReceivier> mSensorsHalDeathReceiver;
+    std::atomic_bool mReconnecting;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 956844f..b66cbcf 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -31,9 +31,10 @@
         const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode,
         const String16& opPackageName, bool hasSensorAccess)
     : mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false),
-      mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(NULL),
-      mCacheSize(0), mMaxCacheSize(0), mPackageName(packageName), mOpPackageName(opPackageName),
-      mDestroyed(false), mHasSensorAccess(hasSensorAccess) {
+      mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr),
+      mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
+      mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false),
+      mHasSensorAccess(hasSensorAccess) {
     mChannel = new BitTube(mService->mSocketBufferSize);
 #if DEBUG_CONNECTIONS
     mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
@@ -55,8 +56,8 @@
     }
 
     mService->cleanupConnection(this);
-    if (mEventCache != NULL) {
-        delete mEventCache;
+    if (mEventCache != nullptr) {
+        delete[] mEventCache;
     }
     mDestroyed = true;
 }
@@ -200,7 +201,7 @@
 
     // Add the file descriptor to the Looper for receiving acknowledegments if the app has
     // registered for wake-up sensors OR for sending events in the cache.
-    int ret = looper->addFd(mChannel->getSendFd(), 0, looper_flags, this, NULL);
+    int ret = looper->addFd(mChannel->getSendFd(), 0, looper_flags, this, nullptr);
     if (ret == 1) {
         ALOGD_IF(DEBUG_CONNECTIONS, "%p addFd fd=%d", this, mChannel->getSendFd());
         mHasLooperCallbacks = true;
@@ -224,7 +225,7 @@
         wp<const SensorEventConnection> const * mapFlushEventsToConnections) {
     // filter out events not for this connection
 
-    sensors_event_t* sanitizedBuffer = nullptr;
+    std::unique_ptr<sensors_event_t[]> sanitizedBuffer;
 
     int count = 0;
     Mutex::Autolock _l(mConnectionLock);
@@ -278,7 +279,7 @@
                     }
                 } else {
                     // Regular sensor event, just copy it to the scratch buffer.
-                    if (mHasSensorAccess) {
+                    if (hasSensorAccess()) {
                         scratch[count++] = buffer[i];
                     }
                 }
@@ -289,11 +290,12 @@
                                         buffer[i].meta_data.sensor == sensor_handle)));
         }
     } else {
-        if (mHasSensorAccess) {
+        if (hasSensorAccess()) {
             scratch = const_cast<sensors_event_t *>(buffer);
             count = numEvents;
         } else {
-            scratch = sanitizedBuffer = new sensors_event_t[numEvents];
+            sanitizedBuffer.reset(new sensors_event_t[numEvents]);
+            scratch = sanitizedBuffer.get();
             for (size_t i = 0; i < numEvents; i++) {
                 if (buffer[i].type == SENSOR_TYPE_META_DATA) {
                     scratch[count++] = buffer[i++];
@@ -305,7 +307,6 @@
     sendPendingFlushEventsLocked();
     // Early return if there are no events for this connection.
     if (count == 0) {
-        delete sanitizedBuffer;
         return status_t(NO_ERROR);
     }
 
@@ -315,39 +316,12 @@
     if (mCacheSize != 0) {
         // There are some events in the cache which need to be sent first. Copy this buffer to
         // the end of cache.
-        if (mCacheSize + count <= mMaxCacheSize) {
-            memcpy(&mEventCache[mCacheSize], scratch, count * sizeof(sensors_event_t));
-            mCacheSize += count;
-        } else {
-            // Check if any new sensors have registered on this connection which may have increased
-            // the max cache size that is desired.
-            if (mCacheSize + count < computeMaxCacheSizeLocked()) {
-                reAllocateCacheLocked(scratch, count);
-                delete sanitizedBuffer;
-                return status_t(NO_ERROR);
-            }
-            // Some events need to be dropped.
-            int remaningCacheSize = mMaxCacheSize - mCacheSize;
-            if (remaningCacheSize != 0) {
-                memcpy(&mEventCache[mCacheSize], scratch,
-                                                remaningCacheSize * sizeof(sensors_event_t));
-            }
-            int numEventsDropped = count - remaningCacheSize;
-            countFlushCompleteEventsLocked(mEventCache, numEventsDropped);
-            // Drop the first "numEventsDropped" in the cache.
-            memmove(mEventCache, &mEventCache[numEventsDropped],
-                    (mCacheSize - numEventsDropped) * sizeof(sensors_event_t));
-
-            // Copy the remainingEvents in scratch buffer to the end of cache.
-            memcpy(&mEventCache[mCacheSize - numEventsDropped], scratch + remaningCacheSize,
-                                            numEventsDropped * sizeof(sensors_event_t));
-        }
-        delete sanitizedBuffer;
+        appendEventsToCacheLocked(scratch, count);
         return status_t(NO_ERROR);
     }
 
     int index_wake_up_event = -1;
-    if (mHasSensorAccess) {
+    if (hasSensorAccess()) {
         index_wake_up_event = findWakeUpSensorEventLocked(scratch, count);
         if (index_wake_up_event >= 0) {
             scratch[index_wake_up_event].flags |= WAKE_UP_SENSOR_EVENT_NEEDS_ACK;
@@ -373,18 +347,17 @@
             --mTotalAcksNeeded;
 #endif
         }
-        if (mEventCache == NULL) {
+        if (mEventCache == nullptr) {
             mMaxCacheSize = computeMaxCacheSizeLocked();
             mEventCache = new sensors_event_t[mMaxCacheSize];
             mCacheSize = 0;
         }
-        memcpy(&mEventCache[mCacheSize], scratch, count * sizeof(sensors_event_t));
-        mCacheSize += count;
+        // Save the events so that they can be written later
+        appendEventsToCacheLocked(scratch, count);
 
         // Add this file descriptor to the looper to get a callback when this fd is available for
         // writing.
         updateLooperRegistrationLocked(mService->getLooper());
-        delete sanitizedBuffer;
         return size;
     }
 
@@ -394,7 +367,6 @@
     }
 #endif
 
-    delete sanitizedBuffer;
     return size < 0 ? status_t(size) : status_t(NO_ERROR);
 }
 
@@ -403,6 +375,10 @@
     mHasSensorAccess = hasAccess;
 }
 
+bool SensorService::SensorEventConnection::hasSensorAccess() {
+    return mHasSensorAccess && !mService->mSensorPrivacyPolicy->isSensorPrivacyEnabled();
+}
+
 void SensorService::SensorEventConnection::reAllocateCacheLocked(sensors_event_t const* scratch,
                                                                  int count) {
     sensors_event_t *eventCache_new;
@@ -415,12 +391,66 @@
     ALOGD_IF(DEBUG_CONNECTIONS, "reAllocateCacheLocked maxCacheSize=%d %d", mMaxCacheSize,
             new_cache_size);
 
-    delete mEventCache;
+    delete[] mEventCache;
     mEventCache = eventCache_new;
     mCacheSize += count;
     mMaxCacheSize = new_cache_size;
 }
 
+void SensorService::SensorEventConnection::appendEventsToCacheLocked(sensors_event_t const* events,
+                                                                     int count) {
+    if (count <= 0) {
+        return;
+    } else if (mCacheSize + count <= mMaxCacheSize) {
+        // The events fit within the current cache: add them
+        memcpy(&mEventCache[mCacheSize], events, count * sizeof(sensors_event_t));
+        mCacheSize += count;
+    } else if (mCacheSize + count <= computeMaxCacheSizeLocked()) {
+        // The events fit within a resized cache: resize the cache and add the events
+        reAllocateCacheLocked(events, count);
+    } else {
+        // The events do not fit within the cache: drop the oldest events.
+        int freeSpace = mMaxCacheSize - mCacheSize;
+
+        // Drop up to the currently cached number of events to make room for new events
+        int cachedEventsToDrop = std::min(mCacheSize, count - freeSpace);
+
+        // New events need to be dropped if there are more new events than the size of the cache
+        int newEventsToDrop = std::max(0, count - mMaxCacheSize);
+
+        // Determine the number of new events to copy into the cache
+        int eventsToCopy = std::min(mMaxCacheSize, count);
+
+        constexpr nsecs_t kMinimumTimeBetweenDropLogNs = 2 * 1000 * 1000 * 1000; // 2 sec
+        if (events[0].timestamp - mTimeOfLastEventDrop > kMinimumTimeBetweenDropLogNs) {
+            ALOGW("Dropping %d cached events (%d/%d) to save %d/%d new events. %d events previously"
+                    " dropped", cachedEventsToDrop, mCacheSize, mMaxCacheSize, eventsToCopy,
+                    count, mEventsDropped);
+            mEventsDropped = 0;
+            mTimeOfLastEventDrop = events[0].timestamp;
+        } else {
+            // Record the number dropped
+            mEventsDropped += cachedEventsToDrop + newEventsToDrop;
+        }
+
+        // Check for any flush complete events in the events that will be dropped
+        countFlushCompleteEventsLocked(mEventCache, cachedEventsToDrop);
+        countFlushCompleteEventsLocked(events, newEventsToDrop);
+
+        // Only shift the events if they will not all be overwritten
+        if (eventsToCopy != mMaxCacheSize) {
+            memmove(mEventCache, &mEventCache[cachedEventsToDrop],
+                    (mCacheSize - cachedEventsToDrop) * sizeof(sensors_event_t));
+        }
+        mCacheSize -= cachedEventsToDrop;
+
+        // Copy the events into the cache
+        memcpy(&mEventCache[mCacheSize], &events[newEventsToDrop],
+                eventsToCopy * sizeof(sensors_event_t));
+        mCacheSize += eventsToCopy;
+    }
+}
+
 void SensorService::SensorEventConnection::sendPendingFlushEventsLocked() {
     ASensorEvent flushCompleteEvent;
     memset(&flushCompleteEvent, 0, sizeof(flushCompleteEvent));
@@ -465,7 +495,7 @@
     for (int numEventsSent = 0; numEventsSent < mCacheSize;) {
         const int numEventsToWrite = helpers::min(mCacheSize - numEventsSent, maxWriteSize);
         int index_wake_up_event = -1;
-        if (mHasSensorAccess) {
+        if (hasSensorAccess()) {
             index_wake_up_event =
                       findWakeUpSensorEventLocked(mEventCache + numEventsSent, numEventsToWrite);
             if (index_wake_up_event >= 0) {
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 032721e..7077880 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -53,7 +53,7 @@
                           bool hasSensorAccess);
 
     status_t sendEvents(sensors_event_t const* buffer, size_t count, sensors_event_t* scratch,
-                        wp<const SensorEventConnection> const * mapFlushEventsToConnections = NULL);
+                        wp<const SensorEventConnection> const * mapFlushEventsToConnections = nullptr);
     bool hasSensor(int32_t handle) const;
     bool hasAnySensor() const;
     bool hasOneShotSensors() const;
@@ -108,6 +108,10 @@
     // size, reallocate memory and copy over events from the older cache.
     void reAllocateCacheLocked(sensors_event_t const* scratch, int count);
 
+    // Add the events to the cache. If the cache would be exceeded, drop events at the beginning of
+    // the cache.
+    void appendEventsToCacheLocked(sensors_event_t const* events, int count);
+
     // LooperCallback method. If there is data to read on this fd, it is an ack from the app that it
     // has read events from a wake up sensor, decrement mWakeLockRefCount.  If this fd is available
     // for writing send the data from the cache.
@@ -126,6 +130,10 @@
     void updateLooperRegistration(const sp<Looper>& looper); void
             updateLooperRegistrationLocked(const sp<Looper>& looper);
 
+    // Returns whether sensor access is available based on both the uid being active and sensor
+    // privacy not being enabled.
+    bool hasSensorAccess();
+
     sp<SensorService> const mService;
     sp<BitTube> mChannel;
     uid_t mUid;
@@ -161,6 +169,8 @@
 
     sensors_event_t *mEventCache;
     int mCacheSize, mMaxCacheSize;
+    int64_t mTimeOfLastEventDrop;
+    int mEventsDropped;
     String8 mPackageName;
     const String16 mOpPackageName;
 #if DEBUG_CONNECTIONS
diff --git a/services/sensorservice/SensorRecord.cpp b/services/sensorservice/SensorRecord.cpp
index 53fb9de..7c4c6a2 100644
--- a/services/sensorservice/SensorRecord.cpp
+++ b/services/sensorservice/SensorRecord.cpp
@@ -71,7 +71,7 @@
     if (mPendingFlushConnections.size() > 0) {
         return mPendingFlushConnections[0];
     }
-    return NULL;
+    return nullptr;
 }
 
 void SensorService::SensorRecord::clearAllPendingFlushConnections() {
diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h
index bba8372..5411515 100644
--- a/services/sensorservice/SensorRegistrationInfo.h
+++ b/services/sensorservice/SensorRegistrationInfo.h
@@ -47,7 +47,7 @@
         mPid = (thread != nullptr) ? thread->getCallingPid() : -1;
         mUid = (thread != nullptr) ? thread->getCallingUid() : -1;
 
-        time_t rawtime = time(NULL);
+        time_t rawtime = time(nullptr);
         struct tm * timeinfo = localtime(&rawtime);
         mHour = static_cast<int8_t>(timeinfo->tm_hour);
         mMin = static_cast<int8_t>(timeinfo->tm_min);
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 8e9e7fd..9a37ff1 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -29,6 +29,7 @@
 #include <openssl/hmac.h>
 #include <openssl/rand.h>
 #include <sensor/SensorEventQueue.h>
+#include <sensorprivacy/SensorPrivacyManager.h>
 #include <utils/SystemClock.h>
 
 #include "BatteryService.h"
@@ -47,6 +48,7 @@
 #include "SensorRecord.h"
 #include "SensorRegistrationInfo.h"
 
+#include <ctime>
 #include <inttypes.h>
 #include <math.h>
 #include <sched.h>
@@ -87,6 +89,7 @@
     : mInitCheck(NO_INIT), mSocketBufferSize(SOCKET_BUFFER_SIZE_NON_BATCHED),
       mWakeLockAcquired(false) {
     mUidPolicy = new UidPolicy(this);
+    mSensorPrivacyPolicy = new SensorPrivacyPolicy(this);
 }
 
 bool SensorService::initializeHmacKey() {
@@ -250,7 +253,7 @@
             // it to maxSystemSocketBufferSize if necessary.
             FILE *fp = fopen("/proc/sys/net/core/wmem_max", "r");
             char line[128];
-            if (fp != NULL && fgets(line, sizeof(line), fp) != NULL) {
+            if (fp != nullptr && fgets(line, sizeof(line), fp) != nullptr) {
                 line[sizeof(line) - 1] = '\0';
                 size_t maxSystemSocketBufferSize;
                 sscanf(line, "%zu", &maxSystemSocketBufferSize);
@@ -285,6 +288,9 @@
 
             // Start watching UID changes to apply policy.
             mUidPolicy->registerSelf();
+
+            // Start watching sensor privacy changes
+            mSensorPrivacyPolicy->registerSelf();
         }
     }
 }
@@ -295,7 +301,7 @@
     {
         Mutex::Autolock _l(mLock);
         for (size_t i = 0 ; i < activeConnections.size(); i++) {
-            if (activeConnections[i] != 0 && activeConnections[i]->getUid() == uid) {
+            if (activeConnections[i] != nullptr && activeConnections[i]->getUid() == uid) {
                 activeConnections[i]->setSensorAccess(hasAccess);
             }
         }
@@ -306,7 +312,7 @@
     int handle = s->getSensor().getHandle();
     int type = s->getSensor().getType();
     if (mSensors.add(handle, s, isDebug, isVirtual)){
-        mRecentEvent.emplace(handle, new RecentEventLogger(type));
+        mRecentEvent.emplace(handle, new SensorServiceUtil::RecentEventLogger(type));
         return s->getSensor();
     } else {
         return mSensors.getNonSensor();
@@ -337,6 +343,7 @@
         delete entry.second;
     }
     mUidPolicy->unregisterSelf();
+    mSensorPrivacyPolicy->unregisterSelf();
 }
 
 status_t SensorService::dump(int fd, const Vector<String16>& args) {
@@ -363,35 +370,16 @@
             }
 
             mCurrentOperatingMode = RESTRICTED;
-            // temporarily stop all sensor direct report
-            for (auto &i : mDirectConnections) {
-                sp<SensorDirectConnection> connection(i.promote());
-                if (connection != nullptr) {
-                    connection->stopAll(true /* backupRecord */);
-                }
-            }
-
-            dev.disableAllSensors();
-            // Clear all pending flush connections for all active sensors. If one of the active
-            // connections has called flush() and the underlying sensor has been disabled before a
-            // flush complete event is returned, we need to remove the connection from this queue.
-            for (size_t i=0 ; i< mActiveSensors.size(); ++i) {
-                mActiveSensors.valueAt(i)->clearAllPendingFlushConnections();
-            }
+            // temporarily stop all sensor direct report and disable sensors
+            disableAllSensorsLocked();
             mWhiteListedPackage.setTo(String8(args[1]));
             return status_t(NO_ERROR);
         } else if (args.size() == 1 && args[0] == String16("enable")) {
             // If currently in restricted mode, reset back to NORMAL mode else ignore.
             if (mCurrentOperatingMode == RESTRICTED) {
                 mCurrentOperatingMode = NORMAL;
-                dev.enableAllSensors();
-                // recover all sensor direct report
-                for (auto &i : mDirectConnections) {
-                    sp<SensorDirectConnection> connection(i.promote());
-                    if (connection != nullptr) {
-                        connection->recoverAll();
-                    }
-                }
+                // enable sensors and recover all sensor direct report
+                enableAllSensorsLocked();
             }
             if (mCurrentOperatingMode == DATA_INJECTION) {
                resetToNormalModeLocked();
@@ -423,6 +411,11 @@
         } else {
             // Default dump the sensor list and debugging information.
             //
+            timespec curTime;
+            clock_gettime(CLOCK_REALTIME, &curTime);
+            struct tm* timeinfo = localtime(&(curTime.tv_sec));
+            result.appendFormat("Captured at: %02d:%02d:%02d.%03d\n", timeinfo->tm_hour,
+                                timeinfo->tm_min, timeinfo->tm_sec, (int)ns2ms(curTime.tv_nsec));
             result.append("Sensor Device:\n");
             result.append(SensorDevice::getInstance().dump().c_str());
 
@@ -471,11 +464,13 @@
                case DATA_INJECTION:
                    result.appendFormat(" DATA_INJECTION : %s\n", mWhiteListedPackage.string());
             }
+            result.appendFormat("Sensor Privacy: %s\n",
+                    mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled");
 
             result.appendFormat("%zd active connections\n", mActiveConnections.size());
             for (size_t i=0 ; i < mActiveConnections.size() ; i++) {
                 sp<SensorEventConnection> connection(mActiveConnections[i].promote());
-                if (connection != 0) {
+                if (connection != nullptr) {
                     result.appendFormat("Connection Number: %zu \n", i);
                     connection->dump(result);
                 }
@@ -513,6 +508,52 @@
     return NO_ERROR;
 }
 
+void SensorService::disableAllSensors() {
+    Mutex::Autolock _l(mLock);
+    disableAllSensorsLocked();
+}
+
+void SensorService::disableAllSensorsLocked() {
+    SensorDevice& dev(SensorDevice::getInstance());
+    for (auto &i : mDirectConnections) {
+        sp<SensorDirectConnection> connection(i.promote());
+        if (connection != nullptr) {
+            connection->stopAll(true /* backupRecord */);
+        }
+    }
+    dev.disableAllSensors();
+    // Clear all pending flush connections for all active sensors. If one of the active
+    // connections has called flush() and the underlying sensor has been disabled before a
+    // flush complete event is returned, we need to remove the connection from this queue.
+    for (size_t i=0 ; i< mActiveSensors.size(); ++i) {
+        mActiveSensors.valueAt(i)->clearAllPendingFlushConnections();
+    }
+}
+
+void SensorService::enableAllSensors() {
+    Mutex::Autolock _l(mLock);
+    enableAllSensorsLocked();
+}
+
+void SensorService::enableAllSensorsLocked() {
+    // sensors should only be enabled if the operating state is not restricted and sensor
+    // privacy is not enabled.
+    if (mCurrentOperatingMode == RESTRICTED || mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
+        ALOGW("Sensors cannot be enabled: mCurrentOperatingMode = %d, sensor privacy = %s",
+              mCurrentOperatingMode,
+              mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled");
+        return;
+    }
+    SensorDevice& dev(SensorDevice::getInstance());
+    dev.enableAllSensors();
+    for (auto &i : mDirectConnections) {
+        sp<SensorDirectConnection> connection(i.promote());
+        if (connection != nullptr) {
+            connection->recoverAll();
+        }
+    }
+}
+
 // NOTE: This is a remote API - make sure all args are validated
 status_t SensorService::shellCommand(int in, int out, int err, Vector<String16>& args) {
     if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) {
@@ -627,8 +668,13 @@
     do {
         ssize_t count = device.poll(mSensorEventBuffer, numEventMax);
         if (count < 0) {
-            ALOGE("sensor poll failed (%s)", strerror(-count));
-            break;
+            if(count == DEAD_OBJECT && device.isReconnecting()) {
+                device.reconnect();
+                continue;
+            } else {
+                ALOGE("sensor poll failed (%s)", strerror(-count));
+                break;
+            }
         }
 
         // Reset sensors_event_t.flags to zero for all events in the buffer.
@@ -651,16 +697,18 @@
         // sending events to clients (incrementing SensorEventConnection::mWakeLockRefCount) should
         // not be interleaved with decrementing SensorEventConnection::mWakeLockRefCount and
         // releasing the wakelock.
-        bool bufferHasWakeUpEvent = false;
+        uint32_t wakeEvents = 0;
         for (int i = 0; i < count; i++) {
             if (isWakeUpSensorEvent(mSensorEventBuffer[i])) {
-                bufferHasWakeUpEvent = true;
-                break;
+                wakeEvents++;
             }
         }
 
-        if (bufferHasWakeUpEvent && !mWakeLockAcquired) {
-            setWakeLockAcquiredLocked(true);
+        if (wakeEvents > 0) {
+            if (!mWakeLockAcquired) {
+                setWakeLockAcquiredLocked(true);
+            }
+            device.writeWakeLockHandled(wakeEvents);
         }
         recordLastValueLocked(mSensorEventBuffer, count);
 
@@ -722,11 +770,11 @@
             // on the hardware sensor. mapFlushEventsToConnections[i] will be the
             // SensorEventConnection mapped to the corresponding flush_complete_event in
             // mSensorEventBuffer[i] if such a mapping exists (NULL otherwise).
-            mMapFlushEventsToConnections[i] = NULL;
+            mMapFlushEventsToConnections[i] = nullptr;
             if (mSensorEventBuffer[i].type == SENSOR_TYPE_META_DATA) {
                 const int sensor_handle = mSensorEventBuffer[i].meta_data.sensor;
                 SensorRecord* rec = mActiveSensors.valueFor(sensor_handle);
-                if (rec != NULL) {
+                if (rec != nullptr) {
                     mMapFlushEventsToConnections[i] = rec->getFirstPendingFlushConnection();
                     rec->removeFirstPendingFlushConnection();
                 }
@@ -770,7 +818,7 @@
 
                     size_t numConnections = activeConnections.size();
                     for (size_t i=0 ; i < numConnections; ++i) {
-                        if (activeConnections[i] != NULL) {
+                        if (activeConnections[i] != nullptr) {
                             activeConnections[i]->removeSensor(handle);
                         }
                     }
@@ -783,7 +831,7 @@
         bool needsWakeLock = false;
         size_t numConnections = activeConnections.size();
         for (size_t i=0 ; i < numConnections; ++i) {
-            if (activeConnections[i] != 0) {
+            if (activeConnections[i] != nullptr) {
                 activeConnections[i]->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
                         mMapFlushEventsToConnections);
                 needsWakeLock |= activeConnections[i]->needsWakeLock();
@@ -816,7 +864,7 @@
     {
         Mutex::Autolock _l(mLock);
         for (size_t i=0 ; i < activeConnections.size(); ++i) {
-            if (activeConnections[i] != 0) {
+            if (activeConnections[i] != nullptr) {
                 activeConnections[i]->resetWakeLockRefCount();
             }
         }
@@ -1021,15 +1069,15 @@
         int requestedMode, const String16& opPackageName) {
     // Only 2 modes supported for a SensorEventConnection ... NORMAL and DATA_INJECTION.
     if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) {
-        return NULL;
+        return nullptr;
     }
 
     Mutex::Autolock _l(mLock);
     // To create a client in DATA_INJECTION mode to inject data, SensorService should already be
     // operating in DI mode.
     if (requestedMode == DATA_INJECTION) {
-        if (mCurrentOperatingMode != DATA_INJECTION) return NULL;
-        if (!isWhiteListedPackage(packageName)) return NULL;
+        if (mCurrentOperatingMode != DATA_INJECTION) return nullptr;
+        if (!isWhiteListedPackage(packageName)) return nullptr;
     }
 
     uid_t uid = IPCThreadState::self()->getCallingUid();
@@ -1063,6 +1111,12 @@
         const native_handle *resource) {
     Mutex::Autolock _l(mLock);
 
+    // No new direct connections are allowed when sensor privacy is enabled
+    if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
+        ALOGE("Cannot create new direct connections when sensor privacy is enabled");
+        return nullptr;
+    }
+
     struct sensors_direct_mem_t mem = {
         .type = type,
         .format = format,
@@ -1277,6 +1331,15 @@
             ALOGD_IF(DEBUG_CONNECTIONS, "... and it was the last connection");
             mActiveSensors.removeItemsAt(i, 1);
             mActiveVirtualSensors.erase(handle);
+
+            // If this is the last connection, then mark the RecentEventLogger as stale. This is
+            // critical for on-change events since the previous event is sent to a client if the
+            // sensor is already active. If two clients request the sensor at the same time, one
+            // of the clients would receive a stale event.
+            auto logger = mRecentEvent.find(handle);
+            if (logger != mRecentEvent.end()) {
+                logger->second->setLastEventStale();
+            }
             delete rec;
             size--;
         } else {
@@ -1325,7 +1388,7 @@
     }
 
     SensorRecord* rec = mActiveSensors.valueFor(handle);
-    if (rec == 0) {
+    if (rec == nullptr) {
         rec = new SensorRecord(connection);
         mActiveSensors.add(handle, rec);
         if (sensor->isVirtual()) {
@@ -1343,16 +1406,17 @@
                 auto logger = mRecentEvent.find(handle);
                 if (logger != mRecentEvent.end()) {
                     sensors_event_t event;
-                    // It is unlikely that this buffer is empty as the sensor is already active.
-                    // One possible corner case may be two applications activating an on-change
-                    // sensor at the same time.
-                    if(logger->second->populateLastEvent(&event)) {
+                    // Verify that the last sensor event was generated from the current activation
+                    // of the sensor. If not, it is possible for an on-change sensor to receive a
+                    // sensor event that is stale if two clients re-activate the sensor
+                    // simultaneously.
+                    if(logger->second->populateLastEventIfCurrent(&event)) {
                         event.sensor = handle;
                         if (event.version == sizeof(sensors_event_t)) {
                             if (isWakeUpSensorEvent(event) && !mWakeLockAcquired) {
                                 setWakeLockAcquiredLocked(true);
                             }
-                            connection->sendEvents(&event, 1, NULL);
+                            connection->sendEvents(&event, 1, nullptr);
                             if (!connection->needsWakeLock() && mWakeLockAcquired) {
                                 checkWakeLockStateLocked();
                             }
@@ -1534,7 +1598,7 @@
             status_t err_flush = sensor->flush(connection.get(), handle);
             if (err_flush == NO_ERROR) {
                 SensorRecord* rec = mActiveSensors.valueFor(handle);
-                if (rec != NULL) rec->addPendingFlushConnection(connection);
+                if (rec != nullptr) rec->addPendingFlushConnection(connection);
             }
             err = (err_flush != NO_ERROR) ? err_flush : err;
         }
@@ -1592,7 +1656,7 @@
     bool releaseLock = true;
     for (size_t i=0 ; i<mActiveConnections.size() ; i++) {
         sp<SensorEventConnection> connection(mActiveConnections[i].promote());
-        if (connection != 0) {
+        if (connection != nullptr) {
             if (connection->needsWakeLock()) {
                 releaseLock = false;
                 break;
@@ -1617,7 +1681,7 @@
     Mutex::Autolock _l(mLock);
     for (size_t i=0 ; i < mActiveConnections.size(); ++i) {
         sp<SensorEventConnection> connection(mActiveConnections[i].promote());
-        if (connection != 0) {
+        if (connection != nullptr) {
             activeConnections->add(connection);
         }
     }
@@ -1730,4 +1794,31 @@
     return mActiveUids.find(uid) != mActiveUids.end();
 }
 
+void SensorService::SensorPrivacyPolicy::registerSelf() {
+    SensorPrivacyManager spm;
+    mSensorPrivacyEnabled = spm.isSensorPrivacyEnabled();
+    spm.addSensorPrivacyListener(this);
+}
+
+void SensorService::SensorPrivacyPolicy::unregisterSelf() {
+    SensorPrivacyManager spm;
+    spm.removeSensorPrivacyListener(this);
+}
+
+bool SensorService::SensorPrivacyPolicy::isSensorPrivacyEnabled() {
+    return mSensorPrivacyEnabled;
+}
+
+binder::Status SensorService::SensorPrivacyPolicy::onSensorPrivacyChanged(bool enabled) {
+    mSensorPrivacyEnabled = enabled;
+    sp<SensorService> service = mService.promote();
+    if (service != nullptr) {
+        if (enabled) {
+            service->disableAllSensors();
+        } else {
+            service->enableAllSensors();
+        }
+    }
+    return binder::Status::ok();
+}
 }; // namespace android
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index f71723d..136ee27 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -26,6 +26,7 @@
 #include <sensor/ISensorServer.h>
 #include <sensor/ISensorEventConnection.h>
 #include <sensor/Sensor.h>
+#include "android/hardware/BnSensorPrivacyListener.h"
 
 #include <utils/AndroidThreads.h>
 #include <utils/KeyedVector.h>
@@ -59,7 +60,6 @@
 namespace android {
 // ---------------------------------------------------------------------------
 class SensorInterface;
-using namespace SensorServiceUtil;
 
 class SensorService :
         public BinderService<SensorService>,
@@ -118,6 +118,8 @@
             void onUidGone(uid_t uid, bool disabled);
             void onUidActive(uid_t uid);
             void onUidIdle(uid_t uid, bool disabled);
+            void onUidStateChanged(uid_t uid __unused, int32_t procState __unused,
+                                   int64_t procStateSeq __unused) {}
 
             void addOverrideUid(uid_t uid, bool active);
             void removeOverrideUid(uid_t uid);
@@ -131,6 +133,30 @@
             std::unordered_map<uid_t, bool> mOverrideUids;
     };
 
+    // Sensor privacy allows a user to disable access to all sensors on the device. When
+    // enabled sensor privacy will prevent all apps, including active apps, from accessing
+    // sensors, they will not receive trigger nor on-change events, flush event behavior
+    // does not change, and recurring events are the same as the first one delivered when
+    // sensor privacy was enabled. All sensor direct connections will be stopped as well
+    // and new direct connections will not be allowed while sensor privacy is enabled.
+    // Once sensor privacy is disabled access to sensors will be restored for active
+    // apps, previously stopped direct connections will be restarted, and new direct
+    // connections will be allowed again.
+    class SensorPrivacyPolicy : public hardware::BnSensorPrivacyListener {
+        public:
+            explicit SensorPrivacyPolicy(wp<SensorService> service) : mService(service) {}
+            void registerSelf();
+            void unregisterSelf();
+
+            bool isSensorPrivacyEnabled();
+
+            binder::Status onSensorPrivacyChanged(bool enabled);
+
+        private:
+            wp<SensorService> mService;
+            std::atomic_bool mSensorPrivacyEnabled;
+    };
+
     enum Mode {
        // The regular operating mode where any application can register/unregister/call flush on
        // sensors.
@@ -274,10 +300,17 @@
     // Prints the shell command help
     status_t printHelp(int out);
 
+    // temporarily stops all active direct connections and disables all sensors
+    void disableAllSensors();
+    void disableAllSensorsLocked();
+    // restarts the previously stopped direct connections and enables all sensors
+    void enableAllSensors();
+    void enableAllSensorsLocked();
+
     static uint8_t sHmacGlobalKey[128];
     static bool sHmacGlobalKeyIsValid;
 
-    SensorList mSensors;
+    SensorServiceUtil::SensorList mSensors;
     status_t mInitCheck;
 
     // Socket buffersize used to initialize BitTube. This size depends on whether batching is
@@ -294,7 +327,7 @@
     bool mWakeLockAcquired;
     sensors_event_t *mSensorEventBuffer, *mSensorEventScratch;
     wp<const SensorEventConnection> * mMapFlushEventsToConnections;
-    std::unordered_map<int, RecentEventLogger*> mRecentEvent;
+    std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent;
     SortedVector< wp<SensorDirectConnection> > mDirectConnections;
     Mode mCurrentOperatingMode;
 
@@ -308,6 +341,7 @@
     Vector<SensorRegistrationInfo> mLastNSensorRegistrations;
 
     sp<UidPolicy> mUidPolicy;
+    sp<SensorPrivacyPolicy> mSensorPrivacyPolicy;
 };
 
 } // namespace android
diff --git a/services/sensorservice/SensorsWrapper.h b/services/sensorservice/SensorsWrapper.h
new file mode 100644
index 0000000..d1a7234
--- /dev/null
+++ b/services/sensorservice/SensorsWrapper.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SENSORS_WRAPPER_H
+#define ANDROID_SENSORS_WRAPPER_H
+
+#include "android/hardware/sensors/1.0/ISensors.h"
+#include "android/hardware/sensors/2.0/ISensors.h"
+#include "android/hardware/sensors/2.0/ISensorsCallback.h"
+
+#include <utils/LightRefBase.h>
+
+namespace android {
+namespace SensorServiceUtil {
+
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+using ::android::hardware::sensors::V1_0::Event;
+using ::android::hardware::sensors::V1_0::ISensors;
+using ::android::hardware::sensors::V1_0::OperationMode;
+using ::android::hardware::sensors::V1_0::RateLevel;
+using ::android::hardware::sensors::V1_0::Result;
+using ::android::hardware::sensors::V1_0::SharedMemInfo;
+using ::android::hardware::sensors::V2_0::ISensorsCallback;
+
+/*
+ * The ISensorsWrapper interface includes all function from supported Sensors HAL versions. This
+ * allows for the SensorDevice to use the ISensorsWrapper interface to interact with the Sensors
+ * HAL regardless of the current version of the Sensors HAL that is loaded. Each concrete
+ * instantiation of ISensorsWrapper must correspond to a specific Sensors HAL version. This design
+ * is beneficial because only the functions that change between Sensors HAL versions must be newly
+ * newly implemented, any previously implemented function that does not change may remain the same.
+ *
+ * Functions that exist across all versions of the Sensors HAL should be implemented as pure
+ * virtual functions which forces the concrete instantiations to implement the functions.
+ *
+ * Functions that do not exist across all versions of the Sensors HAL should include a default
+ * implementation that generates an error if called. The default implementation should never
+ * be called and must be overridden by Sensors HAL versions that support the function.
+ */
+class ISensorsWrapper : public VirtualLightRefBase {
+public:
+    virtual bool supportsPolling() const = 0;
+
+    virtual bool supportsMessageQueues() const = 0;
+
+    virtual Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) = 0;
+
+    virtual Return<Result> setOperationMode(OperationMode mode) = 0;
+
+    virtual Return<Result> activate(int32_t sensorHandle, bool enabled) = 0;
+
+    virtual Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+                                 int64_t maxReportLatencyNs) = 0;
+
+    virtual Return<Result> flush(int32_t sensorHandle) = 0;
+
+    virtual Return<Result> injectSensorData(const Event& event) = 0;
+
+    virtual Return<void> registerDirectChannel(const SharedMemInfo& mem,
+                                               ISensors::registerDirectChannel_cb _hidl_cb) = 0;
+
+    virtual Return<Result> unregisterDirectChannel(int32_t channelHandle) = 0;
+
+    virtual Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle,
+                                            RateLevel rate,
+                                            ISensors::configDirectReport_cb _hidl_cb) = 0;
+
+    virtual Return<void> poll(int32_t maxCount, ISensors::poll_cb _hidl_cb) {
+        (void)maxCount;
+        (void)_hidl_cb;
+        // TODO (b/111070257): Generate an assert-level error since this should never be called
+        // directly
+        return Return<void>();
+    }
+
+    virtual Return<Result> initialize(const MQDescriptorSync<Event>& eventQueueDesc,
+                                      const MQDescriptorSync<uint32_t>& wakeLockDesc,
+                                      const ::android::sp<ISensorsCallback>& callback) {
+        (void)eventQueueDesc;
+        (void)wakeLockDesc;
+        (void)callback;
+        // TODO (b/111070257): Generate an assert-level error since this should never be called
+        // directly
+        return Result::INVALID_OPERATION;
+    }
+};
+
+template<typename T>
+class SensorsWrapperBase : public ISensorsWrapper {
+public:
+    SensorsWrapperBase(sp<T> sensors) :
+        mSensors(sensors) { };
+
+    Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) override {
+        return mSensors->getSensorsList(_hidl_cb);
+    }
+
+    Return<Result> setOperationMode(OperationMode mode) override {
+        return mSensors->setOperationMode(mode);
+    }
+
+    Return<Result> activate(int32_t sensorHandle, bool enabled) override {
+        return mSensors->activate(sensorHandle, enabled);
+    }
+
+    Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+                         int64_t maxReportLatencyNs) override {
+        return mSensors->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
+    }
+
+    Return<Result> flush(int32_t sensorHandle) override {
+        return mSensors->flush(sensorHandle);
+    }
+
+    Return<Result> injectSensorData(const Event& event) override {
+        return mSensors->injectSensorData(event);
+    }
+
+    Return<void> registerDirectChannel(const SharedMemInfo& mem,
+                                       ISensors::registerDirectChannel_cb _hidl_cb) override {
+        return mSensors->registerDirectChannel(mem, _hidl_cb);
+    }
+
+    Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
+        return mSensors->unregisterDirectChannel(channelHandle);
+    }
+
+    Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle,
+                                    RateLevel rate,
+                                    ISensors::configDirectReport_cb _hidl_cb) override {
+        return mSensors->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
+    }
+
+protected:
+    sp<T> mSensors;
+};
+
+class SensorsWrapperV1_0 : public SensorsWrapperBase<hardware::sensors::V1_0::ISensors> {
+public:
+    SensorsWrapperV1_0(sp<hardware::sensors::V1_0::ISensors> sensors) :
+        SensorsWrapperBase(sensors) { };
+
+    bool supportsPolling() const override {
+        return true;
+    }
+
+    bool supportsMessageQueues() const override {
+        return false;
+    }
+
+    Return<void> poll(int32_t maxCount,
+                      hardware::sensors::V1_0::ISensors::poll_cb _hidl_cb) override {
+        return mSensors->poll(maxCount, _hidl_cb);
+    }
+};
+
+class SensorsWrapperV2_0 : public SensorsWrapperBase<hardware::sensors::V2_0::ISensors> {
+public:
+    SensorsWrapperV2_0(sp<hardware::sensors::V2_0::ISensors> sensors)
+        : SensorsWrapperBase(sensors) { };
+
+    bool supportsPolling() const override {
+        return false;
+    }
+
+    bool supportsMessageQueues() const override {
+        return true;
+    }
+
+    Return<Result> initialize(const MQDescriptorSync<Event>& eventQueueDesc,
+                              const MQDescriptorSync<uint32_t>& wakeLockDesc,
+                              const ::android::sp<ISensorsCallback>& callback) override {
+        return mSensors->initialize(eventQueueDesc, wakeLockDesc, callback);
+    }
+};
+
+}; // namespace SensorServiceUtil
+}; // namespace android
+
+#endif // ANDROID_SENSORS_WRAPPER_H
diff --git a/services/sensorservice/hidl/EventQueue.cpp b/services/sensorservice/hidl/EventQueue.cpp
index ff20066..b781744 100644
--- a/services/sensorservice/hidl/EventQueue.cpp
+++ b/services/sensorservice/hidl/EventQueue.cpp
@@ -64,7 +64,7 @@
               mInternalQueue(internalQueue) {
 
     mLooper->addFd(internalQueue->getFd(), ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT,
-            new EventQueueLooperCallback(internalQueue, callback), NULL /* data */);
+            new EventQueueLooperCallback(internalQueue, callback), nullptr /* data */);
 }
 
 void EventQueue::onLastStrongRef(const void *id) {
diff --git a/services/sensorservice/hidl/SensorManager.cpp b/services/sensorservice/hidl/SensorManager.cpp
index fee6da1..9380600 100644
--- a/services/sensorservice/hidl/SensorManager.cpp
+++ b/services/sensorservice/hidl/SensorManager.cpp
@@ -157,7 +157,7 @@
             JavaVMAttachArgs args{
                 .version = JNI_VERSION_1_2,
                 .name = POLL_THREAD_NAME,
-                .group = NULL
+                .group = nullptr
             };
             JNIEnv* env;
             if (javaVm->AttachCurrentThread(&env, &args) != JNI_OK) {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 22e7761..f514fe3 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -1,9 +1,9 @@
 cc_defaults {
     name: "surfaceflinger_defaults",
     cflags: [
-        "-DLOG_TAG=\"SurfaceFlinger\"",
         "-Wall",
         "-Werror",
+        "-Wformat",
         "-Wthread-safety",
         "-Wunused",
         "-Wunreachable-code",
@@ -14,6 +14,7 @@
     name: "libsurfaceflinger_defaults",
     defaults: ["surfaceflinger_defaults"],
     cflags: [
+        "-DLOG_TAG=\"SurfaceFlinger\"",
         "-DGL_GLEXT_PROTOTYPES",
         "-DEGL_EGLEXT_PROTOTYPES",
     ],
@@ -22,9 +23,13 @@
         "android.hardware.configstore-utils",
         "android.hardware.configstore@1.0",
         "android.hardware.configstore@1.1",
+        "android.hardware.configstore@1.2",
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
+        "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.3",
         "android.hardware.power@1.0",
         "android.hardware.power@1.3",
         "libbase",
@@ -43,15 +48,21 @@
         "libhwbinder",
         "liblayers_proto",
         "liblog",
+	"libnativewindow",
         "libpdx_default_transport",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
         "libsync",
         "libtimestats_proto",
         "libui",
+        "libinput",
         "libutils",
+        "libutilscallstack",
+        "libSurfaceFlingerProperties",
     ],
     static_libs: [
+        "libcompositionengine",
+        "librenderengine",
         "libserviceutils",
         "libtrace_proto",
         "libvr_manager",
@@ -60,14 +71,20 @@
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
         "android.hardware.graphics.composer@2.2-command-buffer",
+        "android.hardware.graphics.composer@2.3-command-buffer",
     ],
     export_static_lib_headers: [
+        "libcompositionengine",
+        "librenderengine",
         "libserviceutils",
     ],
     export_shared_lib_headers: [
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
+        "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.3",
         "android.hardware.power@1.3",
         "libhidlbase",
         "libhidltransport",
@@ -75,6 +92,18 @@
     ],
 }
 
+cc_defaults {
+    name: "libsurfaceflinger_production_defaults",
+    defaults: ["libsurfaceflinger_defaults"],
+    cflags: [
+        "-fvisibility=hidden",
+        "-fwhole-program-vtables", // requires ThinLTO
+    ],
+    lto: {
+        thin: true,
+    },
+}
+
 cc_library_headers {
     name: "libsurfaceflinger_headers",
     export_include_dirs: ["."],
@@ -87,78 +116,73 @@
     srcs: [
         "BufferLayer.cpp",
         "BufferLayerConsumer.cpp",
+        "BufferQueueLayer.cpp",
+        "BufferStateLayer.cpp",
+        "BufferStateLayerCache.cpp",
         "Client.cpp",
         "ColorLayer.cpp",
         "ContainerLayer.cpp",
         "DisplayDevice.cpp",
         "DisplayHardware/ComposerHal.cpp",
+        "DisplayHardware/DisplayIdentification.cpp",
         "DisplayHardware/FramebufferSurface.cpp",
         "DisplayHardware/HWC2.cpp",
         "DisplayHardware/HWComposer.cpp",
         "DisplayHardware/HWComposerBufferCache.cpp",
         "DisplayHardware/PowerAdvisor.cpp",
         "DisplayHardware/VirtualDisplaySurface.cpp",
-        "DispSync.cpp",
         "Effects/Daltonizer.cpp",
-        "EventControlThread.cpp",
         "EventLog/EventLog.cpp",
-        "EventThread.cpp",
         "FrameTracker.cpp",
         "Layer.cpp",
+        "LayerBE.cpp",
         "LayerProtoHelper.cpp",
         "LayerRejecter.cpp",
         "LayerStats.cpp",
         "LayerVector.cpp",
-        "MessageQueue.cpp",
         "MonitoredProducer.cpp",
+        "NativeWindowSurface.cpp",
         "RenderArea.cpp",
-        "RenderEngine/Description.cpp",
-        "RenderEngine/GLES20RenderEngine.cpp",
-        "RenderEngine/GLExtensions.cpp",
-        "RenderEngine/Image.cpp",
-        "RenderEngine/Mesh.cpp",
-        "RenderEngine/Program.cpp",
-        "RenderEngine/ProgramCache.cpp",
-        "RenderEngine/RenderEngine.cpp",
-        "RenderEngine/Surface.cpp",
-        "RenderEngine/Texture.cpp",
+        "Scheduler/DispSync.cpp",
+        "Scheduler/DispSyncSource.cpp",
+        "Scheduler/EventControlThread.cpp",
+        "Scheduler/EventThread.cpp",
+        "Scheduler/IdleTimer.cpp",
+        "Scheduler/LayerHistory.cpp",
+        "Scheduler/MessageQueue.cpp",
+        "Scheduler/Scheduler.cpp",
+        "Scheduler/SchedulerUtils.cpp",
+        "Scheduler/PhaseOffsets.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceInterceptor.cpp",
         "SurfaceTracing.cpp",
         "TimeStats/TimeStats.cpp",
-        "Transform.cpp",
+        "TransactionCompletedThread.cpp",
     ],
 }
 
 cc_library_shared {
+    // Please use libsurfaceflinger_defaults to configure how the sources are
+    // built, so the same settings can be used elsewhere.
     name: "libsurfaceflinger",
-    defaults: ["libsurfaceflinger_defaults"],
-    cflags: [
-        "-fvisibility=hidden",
-        "-Werror=format",
-    ],
+    defaults: ["libsurfaceflinger_production_defaults"],
     srcs: [
         ":libsurfaceflinger_sources",
+
+        // Note: SurfaceFlingerFactory is not in the default sources so that it
+        // can be easily replaced.
+        "SurfaceFlingerFactory.cpp",
     ],
     logtags: ["EventLog/EventLogTags.logtags"],
-    include_dirs: [
-        "frameworks/native/vulkan/vkjson",
-        "frameworks/native/vulkan/include",
-    ],
-    cppflags: [
-        "-fwhole-program-vtables", // requires ThinLTO
-    ],
-    lto: {
-        thin: true,
-    },
 }
 
-cc_binary {
-    name: "surfaceflinger",
+cc_defaults {
+    name: "libsurfaceflinger_binary",
     defaults: ["surfaceflinger_defaults"],
-    init_rc: ["surfaceflinger.rc"],
-    srcs: ["main_surfaceflinger.cpp"],
+    cflags: [
+        "-DLOG_TAG=\"SurfaceFlinger\"",
+    ],
     whole_static_libs: [
         "libsigchain",
     ],
@@ -166,16 +190,19 @@
         "android.frameworks.displayservice@1.0",
         "android.hardware.configstore-utils",
         "android.hardware.configstore@1.0",
+        "android.hardware.configstore@1.2",
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
         "libbinder",
         "libcutils",
         "libdisplayservicehidl",
         "libhidlbase",
         "libhidltransport",
+        "libinput",
         "liblayers_proto",
         "liblog",
         "libprocessgroup",
-        "libsurfaceflinger",
+        "libsync",
         "libtimestats_proto",
         "libutils",
     ],
@@ -184,19 +211,22 @@
         "libtrace_proto",
     ],
     ldflags: ["-Wl,--export-dynamic"],
+}
 
-    // TODO(b/71715793): These version-scripts are required due to the use of
-    // whole_static_libs to pull in libsigchain. To work, the files had to be
-    // locally duplicated from their original location
-    // $ANDROID_ROOT/art/sigchainlib/
-    multilib: {
-        lib32: {
-            version_script: "version-script32.txt",
-        },
-        lib64: {
-            version_script: "version-script64.txt",
-        },
-    },
+filegroup {
+    name: "surfaceflinger_binary_sources",
+    srcs: ["main_surfaceflinger.cpp"],
+}
+
+cc_binary {
+    name: "surfaceflinger",
+    defaults: ["libsurfaceflinger_binary"],
+    init_rc: ["surfaceflinger.rc"],
+    srcs: [":surfaceflinger_binary_sources"],
+    shared_libs: [
+        "libsurfaceflinger",
+        "libSurfaceFlingerProperties",
+    ],
 }
 
 cc_library_shared {
@@ -222,3 +252,26 @@
     "TimeStats/timestatsproto",
     "tests",
 ]
+
+cc_library_shared {
+    name: "libSurfaceFlingerProperties",
+    srcs: [
+        "SurfaceFlingerProperties.cpp",
+        "sysprop/*.sysprop",
+    ],
+    shared_libs: [
+        "android.hardware.configstore-utils",
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore@1.1",
+        "android.hardware.configstore@1.2",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "libutils",
+    ],
+    export_shared_lib_headers: [
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+    ],
+}
diff --git a/services/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h
index 3e9d443..97028a8 100644
--- a/services/surfaceflinger/Barrier.h
+++ b/services/surfaceflinger/Barrier.h
@@ -18,46 +18,40 @@
 #define ANDROID_BARRIER_H
 
 #include <stdint.h>
-#include <sys/types.h>
-#include <utils/threads.h>
+#include <condition_variable>
+#include <mutex>
 
 namespace android {
 
 class Barrier
 {
 public:
-    inline Barrier() : state(CLOSED) { }
-    inline ~Barrier() { }
-
     // Release any threads waiting at the Barrier.
     // Provides release semantics: preceding loads and stores will be visible
     // to other threads before they wake up.
     void open() {
-        Mutex::Autolock _l(lock);
-        state = OPENED;
-        cv.broadcast();
+        std::lock_guard<std::mutex> lock(mMutex);
+        mIsOpen = true;
+        mCondition.notify_all();
     }
 
     // Reset the Barrier, so wait() will block until open() has been called.
     void close() {
-        Mutex::Autolock _l(lock);
-        state = CLOSED;
+        std::lock_guard<std::mutex> lock(mMutex);
+        mIsOpen = false;
     }
 
     // Wait until the Barrier is OPEN.
     // Provides acquire semantics: no subsequent loads or stores will occur
     // until wait() returns.
     void wait() const {
-        Mutex::Autolock _l(lock);
-        while (state == CLOSED) {
-            cv.wait(lock);
-        }
+        std::unique_lock<std::mutex> lock(mMutex);
+        mCondition.wait(lock, [this]() NO_THREAD_SAFETY_ANALYSIS { return mIsOpen; });
     }
 private:
-    enum { OPENED, CLOSED };
-    mutable     Mutex       lock;
-    mutable     Condition   cv;
-    volatile    int         state;
+    mutable std::mutex mMutex;
+    mutable std::condition_variable mCondition;
+    int mIsOpen GUARDED_BY(mMutex){false};
 };
 
 }; // namespace android
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index f5b5eda..9662fcd 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -23,9 +23,10 @@
 #include "Colorizer.h"
 #include "DisplayDevice.h"
 #include "LayerRejecter.h"
-#include "clz.h"
 
-#include "RenderEngine/RenderEngine.h"
+#include "TimeStats/TimeStats.h"
+
+#include <renderengine/RenderEngine.h>
 
 #include <gui/BufferItem.h>
 #include <gui/BufferQueue.h>
@@ -50,28 +51,14 @@
 
 namespace android {
 
-BufferLayer::BufferLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name,
-                         uint32_t w, uint32_t h, uint32_t flags)
-      : Layer(flinger, client, name, w, h, flags),
-        mConsumer(nullptr),
-        mTextureName(UINT32_MAX),
-        mFormat(PIXEL_FORMAT_NONE),
-        mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
-        mBufferLatched(false),
-        mPreviousFrameNumber(0),
-        mUpdateTexImageFailed(false),
-        mRefreshPending(false) {
-    ALOGV("Creating Layer %s", name.string());
+BufferLayer::BufferLayer(const LayerCreationArgs& args)
+      : Layer(args), mTextureName(args.flinger->getNewTexture()) {
+    ALOGV("Creating Layer %s", args.name.string());
 
-    mTextureName = mFlinger->getNewTexture();
-    mTexture.init(Texture::TEXTURE_EXTERNAL, mTextureName);
+    mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
 
-    if (flags & ISurfaceComposerClient::eNonPremultiplied) mPremultipliedAlpha = false;
-
-    mCurrentState.requested = mCurrentState.active;
-
-    // drawing state & current state are identical
-    mDrawingState = mCurrentState;
+    mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
+    mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
 }
 
 BufferLayer::~BufferLayer() {
@@ -81,15 +68,17 @@
         ALOGE("Found stale hardware composer layers when destroying "
               "surface flinger layer %s",
               mName.string());
-        destroyAllHwcLayers();
+        destroyAllHwcLayersPlusChildren();
     }
+
+    mFlinger->mTimeStats->onDestroy(getSequence());
 }
 
 void BufferLayer::useSurfaceDamage() {
     if (mFlinger->mForceFullDamage) {
         surfaceDamageRegion = Region::INVALID_REGION;
     } else {
-        surfaceDamageRegion = mConsumer->getSurfaceDamage();
+        surfaceDamageRegion = getDrawingSurfaceDamage();
     }
 }
 
@@ -97,46 +86,27 @@
     surfaceDamageRegion.clear();
 }
 
-bool BufferLayer::isProtected() const {
-    const sp<GraphicBuffer>& buffer(getBE().compositionInfo.mBuffer);
-    return (buffer != 0) &&
-            (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+bool BufferLayer::isOpaque(const Layer::State& s) const {
+    // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
+    // layer's opaque flag.
+    if ((getBE().compositionInfo.hwc.sidebandStream == nullptr) && (mActiveBuffer == nullptr)) {
+        return false;
+    }
+
+    // if the layer has the opaque flag, then we're always opaque,
+    // otherwise we use the current buffer's format.
+    return ((s.flags & layer_state_t::eLayerOpaque) != 0) || getOpacityForFormat(getPixelFormat());
 }
 
 bool BufferLayer::isVisible() const {
     return !(isHiddenByPolicy()) && getAlpha() > 0.0f &&
-            (getBE().compositionInfo.mBuffer != nullptr ||
-             getBE().compositionInfo.hwc.sidebandStream != nullptr);
+            (mActiveBuffer != nullptr || getBE().compositionInfo.hwc.sidebandStream != nullptr);
 }
 
 bool BufferLayer::isFixedSize() const {
     return getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE;
 }
 
-status_t BufferLayer::setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) {
-    uint32_t const maxSurfaceDims =
-            min(mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims());
-
-    // never allow a surface larger than what our underlying GL implementation
-    // can handle.
-    if ((uint32_t(w) > maxSurfaceDims) || (uint32_t(h) > maxSurfaceDims)) {
-        ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h));
-        return BAD_VALUE;
-    }
-
-    mFormat = format;
-
-    mPotentialCursor = (flags & ISurfaceComposerClient::eCursorWindow) ? true : false;
-    mProtectedByApp = (flags & ISurfaceComposerClient::eProtectedByApp) ? true : false;
-    mCurrentOpacity = getOpacityForFormat(format);
-
-    mConsumer->setDefaultBufferSize(w, h);
-    mConsumer->setDefaultBufferFormat(format);
-    mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
-
-    return NO_ERROR;
-}
-
 static constexpr mat4 inverseOrientation(uint32_t transform) {
     const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
     const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
@@ -155,14 +125,12 @@
     return inverse(tr);
 }
 
-/*
- * onDraw will draw the current layer onto the presentable buffer
- */
-void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip,
-                         bool useIdentityTransform) const {
+bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                                     bool useIdentityTransform, Region& clearRegion,
+                                     renderengine::LayerSettings& layer) {
     ATRACE_CALL();
-
-    if (CC_UNLIKELY(getBE().compositionInfo.mBuffer == 0)) {
+    Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, layer);
+    if (CC_UNLIKELY(mActiveBuffer == 0)) {
         // the texture has not been created yet, this Layer has
         // in fact never been drawn into. This happens frequently with
         // SurfaceView because the WindowManager can't know when the client
@@ -179,37 +147,34 @@
                 finished = true;
                 return;
             }
-            under.orSelf(renderArea.getTransform().transform(layer->visibleRegion));
+            under.orSelf(layer->visibleRegion);
         });
         // if not everything below us is covered, we plug the holes!
         Region holes(clip.subtract(under));
         if (!holes.isEmpty()) {
-            clearWithOpenGL(renderArea, 0, 0, 0, 1);
+            clearRegion.orSelf(holes);
         }
-        return;
+        return false;
     }
-
-    // Bind the current buffer to the GL texture, and wait for it to be
-    // ready for us to draw into.
-    status_t err = mConsumer->bindTextureImage();
-    if (err != NO_ERROR) {
-        ALOGW("onDraw: bindTextureImage failed (err=%d)", err);
-        // Go ahead and draw the buffer anyway; no matter what we do the screen
-        // is probably going to have something visibly wrong.
-    }
-
     bool blackOutLayer = isProtected() || (isSecure() && !renderArea.isSecure());
-
-    auto& engine(mFlinger->getRenderEngine());
-
+    const State& s(getDrawingState());
     if (!blackOutLayer) {
+        layer.source.buffer.buffer = mActiveBuffer;
+        layer.source.buffer.isOpaque = isOpaque(s);
+        layer.source.buffer.fence = mActiveBufferFence;
+        layer.source.buffer.cacheHint = useCachedBufferForClientComposition()
+                ? renderengine::Buffer::CachingHint::USE_CACHE
+                : renderengine::Buffer::CachingHint::NO_CACHE;
+        layer.source.buffer.textureName = mTextureName;
+        layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
+        layer.source.buffer.isY410BT2020 = isHdrY410();
         // TODO: we could be more subtle with isFixedSize()
-        const bool useFiltering = getFiltering() || needsFiltering(renderArea) || isFixedSize();
+        const bool useFiltering = needsFiltering() || renderArea.needsFiltering() || isFixedSize();
 
         // Query the texture matrix given our current filtering mode.
         float textureMatrix[16];
-        mConsumer->setFilteringEnabled(useFiltering);
-        mConsumer->getTransformMatrix(textureMatrix);
+        setFilteringEnabled(useFiltering);
+        getDrawingTransformMatrix(textureMatrix);
 
         if (getTransformToDisplayInverse()) {
             /*
@@ -239,66 +204,127 @@
             memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
         }
 
-        // Set things up for texturing.
-        mTexture.setDimensions(getBE().compositionInfo.mBuffer->getWidth(),
-                               getBE().compositionInfo.mBuffer->getHeight());
-        mTexture.setFiltering(useFiltering);
-        mTexture.setMatrix(textureMatrix);
+        const Rect win{computeBounds()};
+        const float bufferWidth = getBufferSize(s).getWidth();
+        const float bufferHeight = getBufferSize(s).getHeight();
 
-        engine.setupLayerTexturing(mTexture);
+        const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
+        const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
+        const float translateY = float(win.top) / bufferHeight;
+        const float translateX = float(win.left) / bufferWidth;
+
+        // Flip y-coordinates because GLConsumer expects OpenGL convention.
+        mat4 tr = mat4::translate(vec4(.5, .5, 0, 1)) * mat4::scale(vec4(1, -1, 1, 1)) *
+                mat4::translate(vec4(-.5, -.5, 0, 1)) *
+                mat4::translate(vec4(translateX, translateY, 0, 1)) *
+                mat4::scale(vec4(scaleWidth, scaleHeight, 1.0, 1.0));
+
+        layer.source.buffer.useTextureFiltering = useFiltering;
+        layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr;
     } else {
-        engine.setupLayerBlackedOut();
-    }
-    drawWithOpenGL(renderArea, useIdentityTransform);
-    engine.disableTexturing();
-}
-
-void BufferLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
-    mConsumer->setReleaseFence(releaseFence);
-}
-
-void BufferLayer::abandon() {
-    mConsumer->abandon();
-}
-
-bool BufferLayer::shouldPresentNow(const DispSync& dispSync) const {
-    if (mSidebandStreamChanged || mAutoRefresh) {
-        return true;
+        // If layer is blacked out, force alpha to 1 so that we draw a black color
+        // layer.
+        layer.source.buffer.buffer = nullptr;
+        layer.alpha = 1.0;
     }
 
-    Mutex::Autolock lock(mQueueItemLock);
-    if (mQueueItems.empty()) {
-        return false;
-    }
-    auto timestamp = mQueueItems[0].mTimestamp;
-    nsecs_t expectedPresent = mConsumer->computeExpectedPresent(dispSync);
-
-    // Ignore timestamps more than a second in the future
-    bool isPlausible = timestamp < (expectedPresent + s2ns(1));
-    ALOGW_IF(!isPlausible,
-             "[%s] Timestamp %" PRId64 " seems implausible "
-             "relative to expectedPresent %" PRId64,
-             mName.string(), timestamp, expectedPresent);
-
-    bool isDue = timestamp < expectedPresent;
-    return isDue || !isPlausible;
+    return true;
 }
 
-void BufferLayer::setTransformHint(uint32_t orientation) const {
-    mConsumer->setTransformHint(orientation);
+bool BufferLayer::isHdrY410() const {
+    // pixel format is HDR Y410 masquerading as RGBA_1010102
+    return (mCurrentDataSpace == ui::Dataspace::BT2020_ITU_PQ &&
+            getDrawingApi() == NATIVE_WINDOW_API_MEDIA &&
+            getBE().compositionInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
+}
+
+void BufferLayer::setPerFrameData(DisplayId displayId, const ui::Transform& transform,
+                                  const Rect& viewport, int32_t supportedPerFrameMetadata) {
+    RETURN_IF_NO_HWC_LAYER(displayId);
+
+    // Apply this display's projection's viewport to the visible region
+    // before giving it to the HWC HAL.
+    Region visible = transform.transform(visibleRegion.intersect(viewport));
+
+    auto& hwcInfo = getBE().mHwcLayers[displayId];
+    auto& hwcLayer = hwcInfo.layer;
+    auto error = hwcLayer->setVisibleRegion(visible);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        visible.dump(LOG_TAG);
+    }
+    getBE().compositionInfo.hwc.visibleRegion = visible;
+
+    error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        surfaceDamageRegion.dump(LOG_TAG);
+    }
+    getBE().compositionInfo.hwc.surfaceDamage = surfaceDamageRegion;
+
+    // Sideband layers
+    if (getBE().compositionInfo.hwc.sidebandStream.get()) {
+        setCompositionType(displayId, HWC2::Composition::Sideband);
+        ALOGV("[%s] Requesting Sideband composition", mName.string());
+        error = hwcLayer->setSidebandStream(getBE().compositionInfo.hwc.sidebandStream->handle());
+        if (error != HWC2::Error::None) {
+            ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(),
+                  getBE().compositionInfo.hwc.sidebandStream->handle(), to_string(error).c_str(),
+                  static_cast<int32_t>(error));
+        }
+        getBE().compositionInfo.compositionType = HWC2::Composition::Sideband;
+        return;
+    }
+
+    // Device or Cursor layers
+    if (mPotentialCursor) {
+        ALOGV("[%s] Requesting Cursor composition", mName.string());
+        setCompositionType(displayId, HWC2::Composition::Cursor);
+    } else {
+        ALOGV("[%s] Requesting Device composition", mName.string());
+        setCompositionType(displayId, HWC2::Composition::Device);
+    }
+
+    ALOGV("setPerFrameData: dataspace = %d", mCurrentDataSpace);
+    error = hwcLayer->setDataspace(mCurrentDataSpace);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    const HdrMetadata& metadata = getDrawingHdrMetadata();
+    error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata, metadata);
+    if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) {
+        ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    error = hwcLayer->setColorTransform(getColorTransform());
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
+                to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+    getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace;
+    getBE().compositionInfo.hwc.hdrMetadata = getDrawingHdrMetadata();
+    getBE().compositionInfo.hwc.supportedPerFrameMetadata = supportedPerFrameMetadata;
+    getBE().compositionInfo.hwc.colorTransform = getColorTransform();
+
+    setHwcLayerBuffer(displayId);
 }
 
 bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) {
     if (mBufferLatched) {
         Mutex::Autolock lock(mFrameEventHistoryMutex);
-        mFrameEventHistory.addPreComposition(mCurrentFrameNumber,
-                                             refreshStartTime);
+        mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime);
     }
     mRefreshPending = false;
-    return mQueuedFrames > 0 || mSidebandStreamChanged ||
-            mAutoRefresh;
+    return hasReadyFrame();
 }
-bool BufferLayer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence,
+
+bool BufferLayer::onPostComposition(const std::optional<DisplayId>& displayId,
+                                    const std::shared_ptr<FenceTime>& glDoneFence,
                                     const std::shared_ptr<FenceTime>& presentFence,
                                     const CompositorTiming& compositorTiming) {
     // mFrameLatencyNeeded is true when a new frame was latched for the
@@ -308,18 +334,18 @@
     // Update mFrameEventHistory.
     {
         Mutex::Autolock lock(mFrameEventHistoryMutex);
-        mFrameEventHistory.addPostComposition(mCurrentFrameNumber, glDoneFence,
-                                              presentFence, compositorTiming);
+        mFrameEventHistory.addPostComposition(mCurrentFrameNumber, glDoneFence, presentFence,
+                                              compositorTiming);
     }
 
     // Update mFrameTracker.
-    nsecs_t desiredPresentTime = mConsumer->getTimestamp();
+    nsecs_t desiredPresentTime = getDesiredPresentTime();
     mFrameTracker.setDesiredPresentTime(desiredPresentTime);
 
-    const std::string layerName(getName().c_str());
-    mTimeStats.setDesiredTime(layerName, mCurrentFrameNumber, desiredPresentTime);
+    const int32_t layerID = getSequence();
+    mFlinger->mTimeStats->setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
 
-    std::shared_ptr<FenceTime> frameReadyFence = mConsumer->getCurrentFenceTime();
+    std::shared_ptr<FenceTime> frameReadyFence = getCurrentFenceTime();
     if (frameReadyFence->isValid()) {
         mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
     } else {
@@ -329,14 +355,13 @@
     }
 
     if (presentFence->isValid()) {
-        mTimeStats.setPresentFence(layerName, mCurrentFrameNumber, presentFence);
+        mFlinger->mTimeStats->setPresentFence(layerID, mCurrentFrameNumber, presentFence);
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
-    } else {
+    } else if (displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
         // The HWC doesn't support present fences, so use the refresh
         // timestamp instead.
-        const nsecs_t actualPresentTime =
-                mFlinger->getHwComposer().getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
-        mTimeStats.setPresentTime(layerName, mCurrentFrameNumber, actualPresentTime);
+        const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId);
+        mFlinger->mTimeStats->setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
         mFrameTracker.setActualPresentTime(actualPresentTime);
     }
 
@@ -345,58 +370,20 @@
     return true;
 }
 
-std::vector<OccupancyTracker::Segment> BufferLayer::getOccupancyHistory(bool forceFlush) {
-    std::vector<OccupancyTracker::Segment> history;
-    status_t result = mConsumer->getOccupancyHistory(forceFlush, &history);
-    if (result != NO_ERROR) {
-        ALOGW("[%s] Failed to obtain occupancy history (%d)", mName.string(), result);
-        return {};
-    }
-    return history;
-}
-
-bool BufferLayer::getTransformToDisplayInverse() const {
-    return mConsumer->getTransformToDisplayInverse();
-}
-
-void BufferLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
-    if (!mConsumer->releasePendingBuffer()) {
-        return;
-    }
-
-    auto releaseFenceTime =
-            std::make_shared<FenceTime>(mConsumer->getPrevFinalReleaseFence());
-    mReleaseTimeline.updateSignalTimes();
-    mReleaseTimeline.push(releaseFenceTime);
-
-    Mutex::Autolock lock(mFrameEventHistoryMutex);
-    if (mPreviousFrameNumber != 0) {
-        mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime,
-                                      std::move(releaseFenceTime));
-    }
-}
-
-Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                                const sp<Fence>& releaseFence) {
     ATRACE_CALL();
 
-    if (android_atomic_acquire_cas(true, false, &mSidebandStreamChanged) == 0) {
-        // mSidebandStreamChanged was true
-        mSidebandStream = mConsumer->getSidebandStream();
-        // replicated in LayerBE until FE/BE is ready to be synchronized
-        getBE().compositionInfo.hwc.sidebandStream = mSidebandStream;
-        if (getBE().compositionInfo.hwc.sidebandStream != nullptr) {
-            setTransactionFlags(eTransactionNeeded);
-            mFlinger->setTransactionFlags(eTraversalNeeded);
-        }
-        recomputeVisibleRegions = true;
+    std::optional<Region> sidebandStreamDirtyRegion = latchSidebandStream(recomputeVisibleRegions);
 
-        const State& s(getDrawingState());
-        return getTransform().transform(Region(Rect(s.active.w, s.active.h)));
+    if (sidebandStreamDirtyRegion) {
+        return *sidebandStreamDirtyRegion;
     }
 
-    Region outDirtyRegion;
-    if (mQueuedFrames <= 0 && !mAutoRefresh) {
-        return outDirtyRegion;
+    Region dirtyRegion;
+
+    if (!hasReadyFrame()) {
+        return dirtyRegion;
     }
 
     // if we've already called updateTexImage() without going through
@@ -405,119 +392,41 @@
     // compositionComplete() call.
     // we'll trigger an update in onPreComposition().
     if (mRefreshPending) {
-        return outDirtyRegion;
+        return dirtyRegion;
     }
 
     // If the head buffer's acquire fence hasn't signaled yet, return and
     // try again later
-    if (!headFenceHasSignaled()) {
+    if (!fenceHasSignaled()) {
         mFlinger->signalLayerUpdate();
-        return outDirtyRegion;
+        return dirtyRegion;
     }
 
     // Capture the old state of the layer for comparisons later
     const State& s(getDrawingState());
     const bool oldOpacity = isOpaque(s);
-    sp<GraphicBuffer> oldBuffer = getBE().compositionInfo.mBuffer;
+    sp<GraphicBuffer> oldBuffer = mActiveBuffer;
 
     if (!allTransactionsSignaled()) {
         mFlinger->signalLayerUpdate();
-        return outDirtyRegion;
+        return dirtyRegion;
     }
 
-    // This boolean is used to make sure that SurfaceFlinger's shadow copy
-    // of the buffer queue isn't modified when the buffer queue is returning
-    // BufferItem's that weren't actually queued. This can happen in shared
-    // buffer mode.
-    bool queuedBuffer = false;
-    LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
-                    getProducerStickyTransform() != 0, mName.string(),
-                    mOverrideScalingMode, mFreezeGeometryUpdates);
-    status_t updateResult =
-            mConsumer->updateTexImage(&r, mFlinger->mPrimaryDispSync,
-                                                    &mAutoRefresh, &queuedBuffer,
-                                                    mLastFrameNumberReceived);
-    if (updateResult == BufferQueue::PRESENT_LATER) {
-        // Producer doesn't want buffer to be displayed yet.  Signal a
-        // layer update so we check again at the next opportunity.
-        mFlinger->signalLayerUpdate();
-        return outDirtyRegion;
-    } else if (updateResult == BufferLayerConsumer::BUFFER_REJECTED) {
-        // If the buffer has been rejected, remove it from the shadow queue
-        // and return early
-        if (queuedBuffer) {
-            Mutex::Autolock lock(mQueueItemLock);
-            mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber);
-            mQueueItems.removeAt(0);
-            android_atomic_dec(&mQueuedFrames);
-        }
-        return outDirtyRegion;
-    } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
-        // This can occur if something goes wrong when trying to create the
-        // EGLImage for this buffer. If this happens, the buffer has already
-        // been released, so we need to clean up the queue and bug out
-        // early.
-        if (queuedBuffer) {
-            Mutex::Autolock lock(mQueueItemLock);
-            mQueueItems.clear();
-            android_atomic_and(0, &mQueuedFrames);
-            mTimeStats.clearLayerRecord(getName().c_str());
-        }
-
-        // Once we have hit this state, the shadow queue may no longer
-        // correctly reflect the incoming BufferQueue's contents, so even if
-        // updateTexImage starts working, the only safe course of action is
-        // to continue to ignore updates.
-        mUpdateTexImageFailed = true;
-
-        return outDirtyRegion;
+    status_t err = updateTexImage(recomputeVisibleRegions, latchTime, releaseFence);
+    if (err != NO_ERROR) {
+        return dirtyRegion;
     }
 
-    if (queuedBuffer) {
-        // Autolock scope
-        auto currentFrameNumber = mConsumer->getFrameNumber();
-
-        Mutex::Autolock lock(mQueueItemLock);
-
-        // Remove any stale buffers that have been dropped during
-        // updateTexImage
-        while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
-            mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber);
-            mQueueItems.removeAt(0);
-            android_atomic_dec(&mQueuedFrames);
-        }
-
-        const std::string layerName(getName().c_str());
-        mTimeStats.setAcquireFence(layerName, currentFrameNumber, mQueueItems[0].mFenceTime);
-        mTimeStats.setLatchTime(layerName, currentFrameNumber, latchTime);
-
-        mQueueItems.removeAt(0);
-    }
-
-    // Decrement the queued-frames count.  Signal another event if we
-    // have more frames pending.
-    if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1) ||
-        mAutoRefresh) {
-        mFlinger->signalLayerUpdate();
-    }
-
-    // update the active buffer
-    getBE().compositionInfo.mBuffer =
-            mConsumer->getCurrentBuffer(&getBE().compositionInfo.mBufferSlot);
-    // replicated in LayerBE until FE/BE is ready to be synchronized
-    mActiveBuffer = getBE().compositionInfo.mBuffer;
-    if (getBE().compositionInfo.mBuffer == nullptr) {
-        // this can only happen if the very first buffer was rejected.
-        return outDirtyRegion;
+    err = updateActiveBuffer();
+    if (err != NO_ERROR) {
+        return dirtyRegion;
     }
 
     mBufferLatched = true;
-    mPreviousFrameNumber = mCurrentFrameNumber;
-    mCurrentFrameNumber = mConsumer->getFrameNumber();
 
-    {
-        Mutex::Autolock lock(mFrameEventHistoryMutex);
-        mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+    err = updateFrameNumber(latchTime);
+    if (err != NO_ERROR) {
+        return dirtyRegion;
     }
 
     mRefreshPending = true;
@@ -528,38 +437,36 @@
         recomputeVisibleRegions = true;
     }
 
-    ui::Dataspace dataSpace = mConsumer->getCurrentDataSpace();
-    // treat modern dataspaces as legacy dataspaces whenever possible, until
-    // we can trust the buffer producers
+    ui::Dataspace dataSpace = getDrawingDataSpace();
+    // translate legacy dataspaces to modern dataspaces
     switch (dataSpace) {
-        case ui::Dataspace::V0_SRGB:
-            dataSpace = ui::Dataspace::SRGB;
+        case ui::Dataspace::SRGB:
+            dataSpace = ui::Dataspace::V0_SRGB;
             break;
-        case ui::Dataspace::V0_SRGB_LINEAR:
-            dataSpace = ui::Dataspace::SRGB_LINEAR;
+        case ui::Dataspace::SRGB_LINEAR:
+            dataSpace = ui::Dataspace::V0_SRGB_LINEAR;
             break;
-        case ui::Dataspace::V0_JFIF:
-            dataSpace = ui::Dataspace::JFIF;
+        case ui::Dataspace::JFIF:
+            dataSpace = ui::Dataspace::V0_JFIF;
             break;
-        case ui::Dataspace::V0_BT601_625:
-            dataSpace = ui::Dataspace::BT601_625;
+        case ui::Dataspace::BT601_625:
+            dataSpace = ui::Dataspace::V0_BT601_625;
             break;
-        case ui::Dataspace::V0_BT601_525:
-            dataSpace = ui::Dataspace::BT601_525;
+        case ui::Dataspace::BT601_525:
+            dataSpace = ui::Dataspace::V0_BT601_525;
             break;
-        case ui::Dataspace::V0_BT709:
-            dataSpace = ui::Dataspace::BT709;
+        case ui::Dataspace::BT709:
+            dataSpace = ui::Dataspace::V0_BT709;
             break;
         default:
             break;
     }
     mCurrentDataSpace = dataSpace;
 
-    Rect crop(mConsumer->getCurrentCrop());
-    const uint32_t transform(mConsumer->getCurrentTransform());
-    const uint32_t scalingMode(mConsumer->getCurrentScalingMode());
-    if ((crop != mCurrentCrop) ||
-        (transform != mCurrentTransform) ||
+    Rect crop(getDrawingCrop());
+    const uint32_t transform(getDrawingTransform());
+    const uint32_t scalingMode(getDrawingScalingMode());
+    if ((crop != mCurrentCrop) || (transform != mCurrentTransform) ||
         (scalingMode != mCurrentScalingMode)) {
         mCurrentCrop = crop;
         mCurrentTransform = transform;
@@ -568,15 +475,13 @@
     }
 
     if (oldBuffer != nullptr) {
-        uint32_t bufWidth = getBE().compositionInfo.mBuffer->getWidth();
-        uint32_t bufHeight = getBE().compositionInfo.mBuffer->getHeight();
-        if (bufWidth != uint32_t(oldBuffer->width) ||
-            bufHeight != uint32_t(oldBuffer->height)) {
+        uint32_t bufWidth = mActiveBuffer->getWidth();
+        uint32_t bufHeight = mActiveBuffer->getHeight();
+        if (bufWidth != uint32_t(oldBuffer->width) || bufHeight != uint32_t(oldBuffer->height)) {
             recomputeVisibleRegions = true;
         }
     }
 
-    mCurrentOpacity = getOpacityForFormat(getBE().compositionInfo.mBuffer->format);
     if (oldOpacity != isOpaque(s)) {
         recomputeVisibleRegions = true;
     }
@@ -603,306 +508,37 @@
     }
 
     // FIXME: postedRegion should be dirty & bounds
-    Region dirtyRegion(Rect(s.active.w, s.active.h));
-
     // transform the dirty region to window-manager space
-    outDirtyRegion = (getTransform().transform(dirtyRegion));
-
-    return outDirtyRegion;
+    return getTransform().transform(Region(getBufferSize(s)));
 }
 
-void BufferLayer::setDefaultBufferSize(uint32_t w, uint32_t h) {
-    mConsumer->setDefaultBufferSize(w, h);
-}
-
-void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) {
-    // Apply this display's projection's viewport to the visible region
-    // before giving it to the HWC HAL.
-    const Transform& tr = displayDevice->getTransform();
-    const auto& viewport = displayDevice->getViewport();
-    Region visible = tr.transform(visibleRegion.intersect(viewport));
-    auto hwcId = displayDevice->getHwcDisplayId();
-    if (!hasHwcLayer(hwcId)) {
-        return;
-    }
-    auto& hwcInfo = getBE().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(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-        visible.dump(LOG_TAG);
-    }
-
-    error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-        surfaceDamageRegion.dump(LOG_TAG);
-    }
-
-    // Sideband layers
-    if (getBE().compositionInfo.hwc.sidebandStream.get()) {
-        setCompositionType(hwcId, HWC2::Composition::Sideband);
-        ALOGV("[%s] Requesting Sideband composition", mName.string());
-        error = hwcLayer->setSidebandStream(getBE().compositionInfo.hwc.sidebandStream->handle());
-        if (error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(),
-                  getBE().compositionInfo.hwc.sidebandStream->handle(), to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-        return;
-    }
-
-    // Device or Cursor layers
-    if (mPotentialCursor) {
-        ALOGV("[%s] Requesting Cursor composition", mName.string());
-        setCompositionType(hwcId, HWC2::Composition::Cursor);
-    } else {
-        ALOGV("[%s] Requesting Device composition", mName.string());
-        setCompositionType(hwcId, HWC2::Composition::Device);
-    }
-
-    ALOGV("setPerFrameData: dataspace = %d", mCurrentDataSpace);
-    error = hwcLayer->setDataspace(mCurrentDataSpace);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
-              to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    const HdrMetadata& metadata = mConsumer->getCurrentHdrMetadata();
-    error = hwcLayer->setPerFrameMetadata(displayDevice->getSupportedPerFrameMetadata(), metadata);
-    if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) {
-        ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    uint32_t hwcSlot = 0;
-    sp<GraphicBuffer> hwcBuffer;
-    hwcInfo.bufferCache.getHwcBuffer(getBE().compositionInfo.mBufferSlot,
-                                     getBE().compositionInfo.mBuffer, &hwcSlot, &hwcBuffer);
-
-    auto acquireFence = mConsumer->getCurrentFence();
-    error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
-              getBE().compositionInfo.mBuffer->handle, to_string(error).c_str(),
-              static_cast<int32_t>(error));
-    }
-}
-
-bool BufferLayer::isOpaque(const Layer::State& s) const {
-    // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
-    // layer's opaque flag.
-    if ((getBE().compositionInfo.hwc.sidebandStream == nullptr) && (getBE().compositionInfo.mBuffer == nullptr)) {
-        return false;
-    }
-
-    // if the layer has the opaque flag, then we're always opaque,
-    // otherwise we use the current buffer's format.
-    return ((s.flags & layer_state_t::eLayerOpaque) != 0) || mCurrentOpacity;
-}
-
-void BufferLayer::onFirstRef() {
-    Layer::onFirstRef();
-
-    // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
-    sp<IGraphicBufferProducer> producer;
-    sp<IGraphicBufferConsumer> consumer;
-    BufferQueue::createBufferQueue(&producer, &consumer, true);
-    mProducer = new MonitoredProducer(producer, mFlinger, this);
-    {
-        // Grab the SF state lock during this since it's the only safe way to access RenderEngine
-        Mutex::Autolock lock(mFlinger->mStateLock);
-        mConsumer = new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName,
-                                            this);
-    }
-    mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
-    mConsumer->setContentsChangedListener(this);
-    mConsumer->setName(mName);
-
-    if (mFlinger->isLayerTripleBufferingDisabled()) {
-        mProducer->setMaxDequeuedBufferCount(2);
-    }
-
-    const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
-    updateTransformHint(hw);
-}
-
-// ---------------------------------------------------------------------------
-// Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener
-// ---------------------------------------------------------------------------
-
-void BufferLayer::onFrameAvailable(const BufferItem& item) {
-    // Add this buffer from our internal queue tracker
-    { // Autolock scope
-        Mutex::Autolock lock(mQueueItemLock);
-        mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
-                                                 item.mGraphicBuffer->getHeight(),
-                                                 item.mFrameNumber);
-        // Reset the frame number tracker when we receive the first buffer after
-        // a frame number reset
-        if (item.mFrameNumber == 1) {
-            mLastFrameNumberReceived = 0;
-        }
-
-        // Ensure that callbacks are handled in order
-        while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
-            status_t result = mQueueItemCondition.waitRelative(mQueueItemLock,
-                                                               ms2ns(500));
-            if (result != NO_ERROR) {
-                ALOGE("[%s] Timed out waiting on callback", mName.string());
-            }
-        }
-
-        mQueueItems.push_back(item);
-        android_atomic_inc(&mQueuedFrames);
-
-        // Wake up any pending callbacks
-        mLastFrameNumberReceived = item.mFrameNumber;
-        mQueueItemCondition.broadcast();
-    }
-
-    mFlinger->signalLayerUpdate();
-}
-
-void BufferLayer::onFrameReplaced(const BufferItem& item) {
-    { // Autolock scope
-        Mutex::Autolock lock(mQueueItemLock);
-
-        // Ensure that callbacks are handled in order
-        while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
-            status_t result = mQueueItemCondition.waitRelative(mQueueItemLock,
-                                                               ms2ns(500));
-            if (result != NO_ERROR) {
-                ALOGE("[%s] Timed out waiting on callback", mName.string());
-            }
-        }
-
-        if (mQueueItems.empty()) {
-            ALOGE("Can't replace a frame on an empty queue");
-            return;
-        }
-        mQueueItems.editItemAt(mQueueItems.size() - 1) = item;
-
-        // Wake up any pending callbacks
-        mLastFrameNumberReceived = item.mFrameNumber;
-        mQueueItemCondition.broadcast();
-    }
-}
-
-void BufferLayer::onSidebandStreamChanged() {
-    if (android_atomic_release_cas(false, true, &mSidebandStreamChanged) == 0) {
-        // mSidebandStreamChanged was false
-        mFlinger->signalLayerUpdate();
-    }
-}
-
-bool BufferLayer::needsFiltering(const RenderArea& renderArea) const {
-    return mNeedsFiltering || renderArea.needsFiltering();
-}
-
-// As documented in libhardware header, formats in the range
-// 0x100 - 0x1FF are specific to the HAL implementation, and
-// are known to have no alpha channel
-// TODO: move definition for device-specific range into
-// hardware.h, instead of using hard-coded values here.
-#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
-
-bool BufferLayer::getOpacityForFormat(uint32_t format) {
-    if (HARDWARE_IS_DEVICE_FORMAT(format)) {
-        return true;
-    }
-    switch (format) {
-        case HAL_PIXEL_FORMAT_RGBA_8888:
-        case HAL_PIXEL_FORMAT_BGRA_8888:
-        case HAL_PIXEL_FORMAT_RGBA_FP16:
-        case HAL_PIXEL_FORMAT_RGBA_1010102:
-            return false;
-    }
-    // in all other case, we have no blending (also for unknown formats)
-    return true;
-}
-
-bool BufferLayer::isHdrY410() const {
-    // pixel format is HDR Y410 masquerading as RGBA_1010102
-    return (mCurrentDataSpace == ui::Dataspace::BT2020_ITU_PQ &&
-            mConsumer->getCurrentApi() == NATIVE_WINDOW_API_MEDIA &&
-            getBE().compositionInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
-}
-
-void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const {
-    ATRACE_CALL();
-    const State& s(getDrawingState());
-
-    computeGeometry(renderArea, getBE().mMesh, useIdentityTransform);
-
-    /*
-     * NOTE: the way we compute the texture coordinates here produces
-     * different results than when we take the HWC path -- in the later case
-     * the "source crop" is rounded to texel boundaries.
-     * This can produce significantly different results when the texture
-     * is scaled by a large amount.
-     *
-     * The GL code below is more logical (imho), and the difference with
-     * HWC is due to a limitation of the HWC API to integers -- a question
-     * is suspend is whether we should ignore this problem or revert to
-     * GL composition when a buffer scaling is applied (maybe with some
-     * minimal value)? Or, we could make GL behave like HWC -- but this feel
-     * like more of a hack.
-     */
-    const Rect bounds{computeBounds()}; // Rounds from FloatRect
-
-    Transform t = getTransform();
-    Rect win = bounds;
-    if (!s.finalCrop.isEmpty()) {
-        win = t.transform(win);
-        if (!win.intersect(s.finalCrop, &win)) {
-            win.clear();
-        }
-        win = t.inverse().transform(win);
-        if (!win.intersect(bounds, &win)) {
-            win.clear();
+// transaction
+void BufferLayer::notifyAvailableFrames() {
+    auto headFrameNumber = getHeadFrameNumber();
+    bool headFenceSignaled = fenceHasSignaled();
+    Mutex::Autolock lock(mLocalSyncPointMutex);
+    for (auto& point : mLocalSyncPoints) {
+        if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled) {
+            point->setFrameAvailable();
         }
     }
-
-    float left = float(win.left) / float(s.active.w);
-    float top = float(win.top) / float(s.active.h);
-    float right = float(win.right) / float(s.active.w);
-    float bottom = float(win.bottom) / float(s.active.h);
-
-    // TODO: we probably want to generate the texture coords with the mesh
-    // here we assume that we only have 4 vertices
-    Mesh::VertexArray<vec2> texCoords(getBE().mMesh.getTexCoordArray<vec2>());
-    texCoords[0] = vec2(left, 1.0f - top);
-    texCoords[1] = vec2(left, 1.0f - bottom);
-    texCoords[2] = vec2(right, 1.0f - bottom);
-    texCoords[3] = vec2(right, 1.0f - top);
-
-    auto& engine(mFlinger->getRenderEngine());
-    engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), false /* disableTexture */,
-                              getColor());
-    engine.setSourceDataSpace(mCurrentDataSpace);
-
-    if (isHdrY410()) {
-        engine.setSourceY410BT2020(true);
-    }
-
-    engine.drawMesh(getBE().mMesh);
-    engine.disableBlending();
-
-    engine.setSourceY410BT2020(false);
 }
 
-uint32_t BufferLayer::getProducerStickyTransform() const {
-    int producerStickyTransform = 0;
-    int ret = mProducer->query(NATIVE_WINDOW_STICKY_TRANSFORM, &producerStickyTransform);
-    if (ret != OK) {
-        ALOGW("%s: Error %s (%d) while querying window sticky transform.", __FUNCTION__,
-              strerror(-ret), ret);
-        return 0;
+bool BufferLayer::hasReadyFrame() const {
+    return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
+}
+
+uint32_t BufferLayer::getEffectiveScalingMode() const {
+    if (mOverrideScalingMode >= 0) {
+        return mOverrideScalingMode;
     }
-    return static_cast<uint32_t>(producerStickyTransform);
+
+    return mCurrentScalingMode;
+}
+
+bool BufferLayer::isProtected() const {
+    const sp<GraphicBuffer>& buffer(mActiveBuffer);
+    return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
 }
 
 bool BufferLayer::latchUnsignaledBuffers() {
@@ -919,65 +555,7 @@
     return latch;
 }
 
-uint64_t BufferLayer::getHeadFrameNumber() const {
-    Mutex::Autolock lock(mQueueItemLock);
-    if (!mQueueItems.empty()) {
-        return mQueueItems[0].mFrameNumber;
-    } else {
-        return mCurrentFrameNumber;
-    }
-}
-
-bool BufferLayer::headFenceHasSignaled() const {
-    if (latchUnsignaledBuffers()) {
-        return true;
-    }
-
-    Mutex::Autolock lock(mQueueItemLock);
-    if (mQueueItems.empty()) {
-        return true;
-    }
-    if (mQueueItems[0].mIsDroppable) {
-        // Even though this buffer's fence may not have signaled yet, it could
-        // be replaced by another buffer before it has a chance to, which means
-        // that it's possible to get into a situation where a buffer is never
-        // able to be latched. To avoid this, grab this buffer anyway.
-        return true;
-    }
-    return mQueueItems[0].mFenceTime->getSignalTime() !=
-            Fence::SIGNAL_TIME_PENDING;
-}
-
-uint32_t BufferLayer::getEffectiveScalingMode() const {
-    if (mOverrideScalingMode >= 0) {
-        return mOverrideScalingMode;
-    }
-    return mCurrentScalingMode;
-}
-
-// ----------------------------------------------------------------------------
-// transaction
-// ----------------------------------------------------------------------------
-
-void BufferLayer::notifyAvailableFrames() {
-    auto headFrameNumber = getHeadFrameNumber();
-    bool headFenceSignaled = headFenceHasSignaled();
-    Mutex::Autolock lock(mLocalSyncPointMutex);
-    for (auto& point : mLocalSyncPoints) {
-        if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled) {
-            point->setFrameAvailable();
-        }
-    }
-}
-
-sp<IGraphicBufferProducer> BufferLayer::getProducer() const {
-    return mProducer;
-}
-
-// ---------------------------------------------------------------------------
 // h/w composer set-up
-// ---------------------------------------------------------------------------
-
 bool BufferLayer::allTransactionsSignaled() {
     auto headFrameNumber = getHeadFrameNumber();
     bool matchingFramesFound = false;
@@ -1004,6 +582,73 @@
     return !matchingFramesFound || allTransactionsApplied;
 }
 
+// As documented in libhardware header, formats in the range
+// 0x100 - 0x1FF are specific to the HAL implementation, and
+// are known to have no alpha channel
+// TODO: move definition for device-specific range into
+// hardware.h, instead of using hard-coded values here.
+#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
+
+bool BufferLayer::getOpacityForFormat(uint32_t format) {
+    if (HARDWARE_IS_DEVICE_FORMAT(format)) {
+        return true;
+    }
+    switch (format) {
+        case HAL_PIXEL_FORMAT_RGBA_8888:
+        case HAL_PIXEL_FORMAT_BGRA_8888:
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
+        case HAL_PIXEL_FORMAT_RGBA_1010102:
+            return false;
+    }
+    // in all other case, we have no blending (also for unknown formats)
+    return true;
+}
+
+bool BufferLayer::needsFiltering() const {
+    const auto displayFrame = getBE().compositionInfo.hwc.displayFrame;
+    const auto sourceCrop = getBE().compositionInfo.hwc.sourceCrop;
+    return mNeedsFiltering || sourceCrop.getHeight() != displayFrame.getHeight() ||
+            sourceCrop.getWidth() != displayFrame.getWidth();
+}
+
+uint64_t BufferLayer::getHeadFrameNumber() const {
+    if (hasFrameUpdate()) {
+        return getFrameNumber();
+    } else {
+        return mCurrentFrameNumber;
+    }
+}
+
+Rect BufferLayer::getBufferSize(const State& s) const {
+    // If we have a sideband stream, or we are scaling the buffer then return the layer size since
+    // we cannot determine the buffer size.
+    if ((s.sidebandStream != nullptr) ||
+        (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
+        return Rect(getActiveWidth(s), getActiveHeight(s));
+    }
+
+    if (mActiveBuffer == nullptr) {
+        return Rect::INVALID_RECT;
+    }
+
+    uint32_t bufWidth = mActiveBuffer->getWidth();
+    uint32_t bufHeight = mActiveBuffer->getHeight();
+
+    // Undo any transformations on the buffer and return the result.
+    if (mCurrentTransform & ui::Transform::ROT_90) {
+        std::swap(bufWidth, bufHeight);
+    }
+
+    if (getTransformToDisplayInverse()) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufWidth, bufHeight);
+        }
+    }
+
+    return Rect(bufWidth, bufHeight);
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index bf0ca69..3fe8ed0 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -24,14 +24,13 @@
 #include "FrameTracker.h"
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
-#include "RenderEngine/Mesh.h"
-#include "RenderEngine/Texture.h"
 #include "SurfaceFlinger.h"
-#include "Transform.h"
 
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerState.h>
-
+#include <renderengine/Image.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/Texture.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
@@ -41,158 +40,153 @@
 #include <utils/String8.h>
 #include <utils/Timers.h>
 
+#include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE
+
 #include <stdint.h>
 #include <sys/types.h>
 #include <list>
 
 namespace android {
 
-/*
- * A new BufferQueue and a new BufferLayerConsumer are created when the
- * BufferLayer is first referenced.
- *
- * This also implements onFrameAvailable(), which notifies SurfaceFlinger
- * that new data has arrived.
- */
-class BufferLayer : public Layer, public BufferLayerConsumer::ContentsChangedListener {
+class BufferLayer : public Layer {
 public:
-    BufferLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w,
-                uint32_t h, uint32_t flags);
-
+    explicit BufferLayer(const LayerCreationArgs& args);
     ~BufferLayer() override;
 
-    // If we have received a new buffer this frame, we will pass its surface
-    // damage down to hardware composer. Otherwise, we must send a region with
-    // one empty rect.
-    void useSurfaceDamage();
-    void useEmptyDamage();
-
     // -----------------------------------------------------------------------
     // Overriden from Layer
     // -----------------------------------------------------------------------
-
-    /*
-     * getTypeId - Provide unique string for each class type in the Layer
-     * hierarchy
-     */
-    const char* getTypeId() const override { return "BufferLayer"; }
-
-    /*
-     * isProtected - true if the layer may contain protected content in the
-     * GRALLOC_USAGE_PROTECTED sense.
-     */
-    bool isProtected() const;
-
-    /*
-     * isVisible - true if this layer is visible, false otherwise
-     */
-    bool isVisible() const override;
-
-    /*
-     * isFixedSize - true if content has a fixed size
-     */
-    bool isFixedSize() const override;
-
-    // the this layer's size and format
-    status_t setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags);
-
-    /*
-     * onDraw - draws the surface.
-     */
-    void onDraw(const RenderArea& renderArea, const Region& clip,
-                bool useIdentityTransform) const override;
-
-    void onLayerDisplayed(const sp<Fence>& releaseFence) override;
-
-    void abandon() override;
-    bool shouldPresentNow(const DispSync& dispSync) const override;
-    void setTransformHint(uint32_t orientation) const override;
-    bool onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence,
-                           const std::shared_ptr<FenceTime>& presentFence,
-                           const CompositorTiming& compositorTiming) override;
-    std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
-    bool getTransformToDisplayInverse() const override;
-
 public:
-    bool onPreComposition(nsecs_t refreshStartTime) override;
+    // If we have received a new buffer this frame, we will pass its surface
+    // damage down to hardware composer. Otherwise, we must send a region with
+    // one empty rect.
+    void useSurfaceDamage() override;
+    void useEmptyDamage() override;
 
-    // If a buffer was replaced this frame, release the former buffer
-    void releasePendingBuffer(nsecs_t dequeueReadyTime);
-
-    /*
-     * latchBuffer - called each time the screen is redrawn and returns whether
-     * the visible regions need to be recomputed (this is a fairly heavy
-     * operation, so this should be set only if needed). Typically this is used
-     * to figure out if the content or size of a surface has changed.
-     */
-    Region latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
-    bool isBufferLatched() const override { return mRefreshPending; }
-    void setDefaultBufferSize(uint32_t w, uint32_t h) override;
-
-    bool isHdrY410() const override;
-
-    void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override;
+    // getTypeId - Provide unique string for each class type in the Layer
+    // hierarchy
+    const char* getTypeId() const override { return "BufferLayer"; }
 
     bool isOpaque(const Layer::State& s) const override;
 
-private:
-    void onFirstRef() override;
+    // isVisible - true if this layer is visible, false otherwise
+    bool isVisible() const override;
 
-    // Interface implementation for
-    // BufferLayerConsumer::ContentsChangedListener
-    void onFrameAvailable(const BufferItem& item) override;
-    void onFrameReplaced(const BufferItem& item) override;
-    void onSidebandStreamChanged() override;
+    // isProtected - true if the layer may contain protected content in the
+    // GRALLOC_USAGE_PROTECTED sense.
+    bool isProtected() const override;
 
-    // needsLinearFiltering - true if this surface's state requires filtering
-    bool needsFiltering(const RenderArea& renderArea) const;
+    // isFixedSize - true if content has a fixed size
+    bool isFixedSize() const override;
 
-    static bool getOpacityForFormat(uint32_t format);
+    bool isHdrY410() const override;
 
-    // drawing
-    void drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const;
+    void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport,
+                         int32_t supportedPerFrameMetadata) override;
 
-    // Temporary - Used only for LEGACY camera mode.
-    uint32_t getProducerStickyTransform() const;
+    bool onPreComposition(nsecs_t refreshStartTime) override;
+    bool onPostComposition(const std::optional<DisplayId>& displayId,
+                           const std::shared_ptr<FenceTime>& glDoneFence,
+                           const std::shared_ptr<FenceTime>& presentFence,
+                           const CompositorTiming& compositorTiming) override;
 
-    // Loads the corresponding system property once per process
-    static bool latchUnsignaledBuffers();
+    // latchBuffer - called each time the screen is redrawn and returns whether
+    // the visible regions need to be recomputed (this is a fairly heavy
+    // operation, so this should be set only if needed). Typically this is used
+    // to figure out if the content or size of a surface has changed.
+    // If there was a GL composition step rendering the previous frame, then
+    // releaseFence will be populated with a native fence that fires when
+    // composition has completed.
+    Region latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                       const sp<Fence>& releaseFence) override;
 
-    uint64_t getHeadFrameNumber() const;
-    bool headFenceHasSignaled() const;
+    bool isBufferLatched() const override { return mRefreshPending; }
+
+    void notifyAvailableFrames() override;
+
+    bool hasReadyFrame() const override;
 
     // Returns the current scaling mode, unless mOverrideScalingMode
     // is set, in which case, it returns mOverrideScalingMode
     uint32_t getEffectiveScalingMode() const override;
+    // -----------------------------------------------------------------------
 
-public:
-    void notifyAvailableFrames() override;
-
-    PixelFormat getPixelFormat() const override { return mFormat; }
-    sp<IGraphicBufferProducer> getProducer() const;
-
+    // -----------------------------------------------------------------------
+    // Functions that must be implemented by derived classes
+    // -----------------------------------------------------------------------
 private:
-    sp<BufferLayerConsumer> mConsumer;
+    virtual bool fenceHasSignaled() const = 0;
+
+    virtual nsecs_t getDesiredPresentTime() = 0;
+    virtual std::shared_ptr<FenceTime> getCurrentFenceTime() const = 0;
+
+    virtual void getDrawingTransformMatrix(float *matrix) = 0;
+    virtual uint32_t getDrawingTransform() const = 0;
+    virtual ui::Dataspace getDrawingDataSpace() const = 0;
+    virtual Rect getDrawingCrop() const = 0;
+    virtual uint32_t getDrawingScalingMode() const = 0;
+    virtual Region getDrawingSurfaceDamage() const = 0;
+    virtual const HdrMetadata& getDrawingHdrMetadata() const = 0;
+    virtual int getDrawingApi() const = 0;
+    virtual PixelFormat getPixelFormat() const = 0;
+
+    virtual uint64_t getFrameNumber() const = 0;
+
+    virtual bool getAutoRefresh() const = 0;
+    virtual bool getSidebandStreamChanged() const = 0;
+
+    virtual std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) = 0;
+
+    virtual bool hasFrameUpdate() const = 0;
+
+    virtual void setFilteringEnabled(bool enabled) = 0;
+
+    virtual status_t bindTextureImage() = 0;
+    virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                                    const sp<Fence>& flushFence) = 0;
+
+    virtual status_t updateActiveBuffer() = 0;
+    virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
+
+    virtual void setHwcLayerBuffer(DisplayId displayId) = 0;
+
+protected:
+    // Loads the corresponding system property once per process
+    static bool latchUnsignaledBuffers();
 
     // Check all of the local sync points to ensure that all transactions
     // which need to have been applied prior to the frame which is about to
     // be latched have signaled
     bool allTransactionsSignaled();
-    sp<IGraphicBufferProducer> mProducer;
 
-    // constants
-    uint32_t mTextureName; // from GLES
-    PixelFormat mFormat;
+    static bool getOpacityForFormat(uint32_t format);
 
-    // main thread
-    uint32_t mCurrentScalingMode;
-    bool mBufferLatched = false;   // TODO: Use mActiveBuffer?
-    uint64_t mPreviousFrameNumber; // Only accessed on the main thread.
-    // The texture used to draw the layer in GLES composition mode
-    mutable Texture mTexture;
+    // from GLES
+    const uint32_t mTextureName;
 
-    bool mUpdateTexImageFailed; // This is only accessed on the main thread.
-    bool mRefreshPending;
+    bool mRefreshPending{false};
+
+    // Returns true if, when drawing the active buffer during gpu compositon, we
+    // should use a cached buffer or not.
+    virtual bool useCachedBufferForClientComposition() const = 0;
+
+    // prepareClientLayer - constructs a RenderEngine layer for GPU composition.
+    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                            bool useIdentityTransform, Region& clearRegion,
+                            renderengine::LayerSettings& layer);
+
+private:
+    // Returns true if this layer requires filtering
+    bool needsFiltering() const;
+
+    uint64_t getHeadFrameNumber() const;
+
+    uint32_t mCurrentScalingMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
+
+    // main thread.
+    bool mBufferLatched{false}; // TODO: Use mActiveBuffer?
+
+    Rect getBufferSize(const State& s) const override;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 87333d0..7ed8184 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -20,11 +20,8 @@
 //#define LOG_NDEBUG 0
 
 #include "BufferLayerConsumer.h"
-
-#include "DispSync.h"
 #include "Layer.h"
-#include "RenderEngine/Image.h"
-#include "RenderEngine/RenderEngine.h"
+#include "Scheduler/DispSync.h"
 
 #include <inttypes.h>
 
@@ -38,10 +35,9 @@
 #include <gui/GLConsumer.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
-
 #include <private/gui/ComposerService.h>
-#include <private/gui/SyncFeatures.h>
-
+#include <renderengine/Image.h>
+#include <renderengine/RenderEngine.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
 #include <utils/Trace.h>
@@ -58,7 +54,8 @@
 static const mat4 mtxIdentity;
 
 BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq,
-                                         RE::RenderEngine& engine, uint32_t tex, Layer* layer)
+                                         renderengine::RenderEngine& engine, uint32_t tex,
+                                         Layer* layer)
       : ConsumerBase(bq, false),
         mCurrentCrop(Rect::EMPTY_RECT),
         mCurrentTransform(0),
@@ -101,56 +98,10 @@
     mContentsChangedListener = listener;
 }
 
-// We need to determine the time when a buffer acquired now will be
-// displayed.  This can be calculated:
-//   time when previous buffer's actual-present fence was signaled
-//    + current display refresh rate * HWC latency
-//    + a little extra padding
-//
-// Buffer producers are expected to set their desired presentation time
-// based on choreographer time stamps, which (coming from vsync events)
-// will be slightly later then the actual-present timing.  If we get a
-// desired-present time that is unintentionally a hair after the next
-// vsync, we'll hold the frame when we really want to display it.  We
-// need to take the offset between actual-present and reported-vsync
-// into account.
-//
-// If the system is configured without a DispSync phase offset for the app,
-// we also want to throw in a bit of padding to avoid edge cases where we
-// just barely miss.  We want to do it here, not in every app.  A major
-// source of trouble is the app's use of the display's ideal refresh time
-// (via Display.getRefreshRate()), which could be off of the actual refresh
-// by a few percent, with the error multiplied by the number of frames
-// between now and when the buffer should be displayed.
-//
-// If the refresh reported to the app has a phase offset, we shouldn't need
-// to tweak anything here.
-nsecs_t BufferLayerConsumer::computeExpectedPresent(const DispSync& dispSync) {
-    // The HWC doesn't currently have a way to report additional latency.
-    // Assume that whatever we submit now will appear right after the flip.
-    // For a smart panel this might be 1.  This is expressed in frames,
-    // rather than time, because we expect to have a constant frame delay
-    // regardless of the refresh rate.
-    const uint32_t hwcLatency = 0;
-
-    // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
-    const nsecs_t nextRefresh = dispSync.computeNextRefresh(hwcLatency);
-
-    // The DispSync time is already adjusted for the difference between
-    // vsync and reported-vsync (SurfaceFlinger::dispSyncPresentTimeOffset), so
-    // 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 (SurfaceFlinger::vsyncPhaseOffsetNs == 0) {
-        extraPadding = 1000000; // 1ms (6% of 60Hz)
-    }
-
-    return nextRefresh + extraPadding;
-}
-
-status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
+status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
                                              bool* autoRefresh, bool* queuedBuffer,
-                                             uint64_t maxFrameNumber) {
+                                             uint64_t maxFrameNumber,
+                                             const sp<Fence>& releaseFence) {
     ATRACE_CALL();
     BLC_LOGV("updateTexImage");
     Mutex::Autolock lock(mMutex);
@@ -160,18 +111,12 @@
         return NO_INIT;
     }
 
-    // Make sure RenderEngine is current
-    if (!mRE.isCurrent()) {
-        BLC_LOGE("updateTexImage: RenderEngine is not current");
-        return INVALID_OPERATION;
-    }
-
     BufferItem item;
 
     // Acquire the next buffer.
     // In asynchronous mode the list is guaranteed to be one buffer
     // deep, while in synchronous mode we use the oldest buffer.
-    status_t err = acquireBufferLocked(&item, computeExpectedPresent(dispSync), maxFrameNumber);
+    status_t err = acquireBufferLocked(&item, expectedPresentTime, maxFrameNumber);
     if (err != NO_ERROR) {
         if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
             err = NO_ERROR;
@@ -201,12 +146,12 @@
     }
 
     // Release the previous buffer.
-    err = updateAndReleaseLocked(item, &mPendingRelease);
+    err = updateAndReleaseLocked(item, &mPendingRelease, releaseFence);
     if (err != NO_ERROR) {
         return err;
     }
 
-    if (!SyncFeatures::getInstance().useNativeFenceSync()) {
+    if (!mRE.useNativeFenceSync()) {
         // Bind the new buffer to the GL texture.
         //
         // Older devices require the "implicit" synchronization provided
@@ -234,8 +179,7 @@
         return;
     }
 
-    auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
-                                            : mCurrentTextureImage->graphicBuffer();
+    auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer : mCurrentTextureBuffer;
     auto err = addReleaseFence(slot, buffer, fence);
     if (err != OK) {
         BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
@@ -271,32 +215,24 @@
     }
 
     // If item->mGraphicBuffer is not null, this buffer has not been acquired
-    // before, so any prior EglImage created is using a stale buffer. This
-    // replaces any old EglImage with a new one (using the new buffer).
+    // before.
     if (item->mGraphicBuffer != nullptr) {
-        mImages[item->mSlot] = new Image(item->mGraphicBuffer, mRE);
+        mImages[item->mSlot] = nullptr;
     }
 
     return NO_ERROR;
 }
 
-bool BufferLayerConsumer::canUseImageCrop(const Rect& crop) const {
-    // If the crop rect is not at the origin, we can't set the crop on the
-    // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
-    // extension.  In the future we can add a layered extension that
-    // removes this restriction if there is hardware that can support it.
-    return mRE.supportsImageCrop() && crop.left == 0 && crop.top == 0;
-}
-
 status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item,
-                                                     PendingRelease* pendingRelease) {
+                                                     PendingRelease* pendingRelease,
+                                                     const sp<Fence>& releaseFence) {
     status_t err = NO_ERROR;
 
     int slot = item.mSlot;
 
     // Do whatever sync ops we need to do before releasing the old slot.
     if (slot != mCurrentTexture) {
-        err = syncForReleaseLocked();
+        err = syncForReleaseLocked(releaseFence);
         if (err != NO_ERROR) {
             // Release the buffer we just acquired.  It's not safe to
             // release the old buffer, so instead we just drop the new frame.
@@ -308,19 +244,18 @@
     }
 
     BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
-             mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : 0,
-             slot, mSlots[slot].mGraphicBuffer->handle);
+             mCurrentTextureBuffer != nullptr ? mCurrentTextureBuffer->handle : 0, slot,
+             mSlots[slot].mGraphicBuffer->handle);
 
     // Hang onto the pointer so that it isn't freed in the call to
     // releaseBufferLocked() if we're in shared buffer mode and both buffers are
     // the same.
-    sp<Image> nextTextureImage = mImages[slot];
+    sp<GraphicBuffer> nextTextureBuffer = mSlots[slot].mGraphicBuffer;
 
     // release old buffer
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         if (pendingRelease == nullptr) {
-            status_t status =
-                    releaseBufferLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer());
+            status_t status = releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer);
             if (status < NO_ERROR) {
                 BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
                          status);
@@ -329,14 +264,16 @@
             }
         } else {
             pendingRelease->currentTexture = mCurrentTexture;
-            pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer();
+            pendingRelease->graphicBuffer = mCurrentTextureBuffer;
             pendingRelease->isPending = true;
         }
     }
 
     // Update the BufferLayerConsumer state.
     mCurrentTexture = slot;
-    mCurrentTextureImage = nextTextureImage;
+    mCurrentTextureBuffer = nextTextureBuffer;
+    mCurrentTextureBufferStaleForGpu = false;
+    mCurrentTextureImageFreed = nullptr;
     mCurrentCrop = item.mCrop;
     mCurrentTransform = item.mTransform;
     mCurrentScalingMode = item.mScalingMode;
@@ -357,41 +294,22 @@
 
 status_t BufferLayerConsumer::bindTextureImageLocked() {
     ATRACE_CALL();
-    mRE.checkErrors();
 
-    if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
-        BLC_LOGE("bindTextureImage: no currently-bound texture");
-        mRE.bindExternalTextureImage(mTexName, *mRE.createImage());
-        return NO_INIT;
-    }
-
-    const Rect& imageCrop = canUseImageCrop(mCurrentCrop) ? mCurrentCrop : Rect::EMPTY_RECT;
-    status_t err = mCurrentTextureImage->createIfNeeded(imageCrop);
-    if (err != NO_ERROR) {
-        BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture);
-        mRE.bindExternalTextureImage(mTexName, *mRE.createImage());
-        return UNKNOWN_ERROR;
-    }
-
-    mRE.bindExternalTextureImage(mTexName, mCurrentTextureImage->image());
-
-    // Wait for the new buffer to be ready.
-    return doFenceWaitLocked();
+    return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer, mCurrentFence, false);
 }
 
-status_t BufferLayerConsumer::syncForReleaseLocked() {
+status_t BufferLayerConsumer::syncForReleaseLocked(const sp<Fence>& releaseFence) {
     BLC_LOGV("syncForReleaseLocked");
 
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
-        if (SyncFeatures::getInstance().useNativeFenceSync()) {
-            base::unique_fd fenceFd = mRE.flush();
-            if (fenceFd == -1) {
+        if (mRE.useNativeFenceSync() && releaseFence != Fence::NO_FENCE) {
+            // TODO(alecmouri): fail further upstream if the fence is invalid
+            if (!releaseFence->isValid()) {
                 BLC_LOGE("syncForReleaseLocked: failed to flush RenderEngine");
                 return UNKNOWN_ERROR;
             }
-            sp<Fence> fence(new Fence(std::move(fenceFd)));
-            status_t err = addReleaseFenceLocked(mCurrentTexture,
-                                                 mCurrentTextureImage->graphicBuffer(), fence);
+            status_t err =
+                    addReleaseFenceLocked(mCurrentTexture, mCurrentTextureBuffer, releaseFence);
             if (err != OK) {
                 BLC_LOGE("syncForReleaseLocked: error adding release fence: "
                          "%s (%d)",
@@ -418,26 +336,23 @@
     bool needsRecompute = mFilteringEnabled != enabled;
     mFilteringEnabled = enabled;
 
-    if (needsRecompute && mCurrentTextureImage == nullptr) {
-        BLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == nullptr");
+    if (needsRecompute && mCurrentTextureBuffer == nullptr) {
+        BLC_LOGD("setFilteringEnabled called with mCurrentTextureBuffer == nullptr");
     }
 
-    if (needsRecompute && mCurrentTextureImage != nullptr) {
+    if (needsRecompute && mCurrentTextureBuffer != nullptr) {
         computeCurrentTransformMatrixLocked();
     }
 }
 
 void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
     BLC_LOGV("computeCurrentTransformMatrixLocked");
-    sp<GraphicBuffer> buf =
-            (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer();
-    if (buf == nullptr) {
+    if (mCurrentTextureBuffer == nullptr) {
         BLC_LOGD("computeCurrentTransformMatrixLocked: "
-                 "mCurrentTextureImage is nullptr");
+                 "mCurrentTextureBuffer is nullptr");
     }
-    const Rect& cropRect = canUseImageCrop(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop;
-    GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, buf, cropRect, mCurrentTransform,
-                                       mFilteringEnabled);
+    GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, mCurrentTextureBuffer, mCurrentCrop,
+                                       mCurrentTransform, mFilteringEnabled);
 }
 
 nsecs_t BufferLayerConsumer::getTimestamp() {
@@ -478,14 +393,27 @@
     return mCurrentApi;
 }
 
-sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot) const {
+sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot, sp<Fence>* outFence) const {
     Mutex::Autolock lock(mMutex);
 
     if (outSlot != nullptr) {
         *outSlot = mCurrentTexture;
     }
 
-    return (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer();
+    if (outFence != nullptr) {
+        *outFence = mCurrentFence;
+    }
+
+    return mCurrentTextureBuffer;
+}
+
+bool BufferLayerConsumer::getAndSetCurrentBufferCacheHint() {
+    Mutex::Autolock lock(mMutex);
+    bool useCache = mCurrentTextureBufferStaleForGpu;
+    // Set the staleness bit here, as this function is only called during a
+    // client composition path.
+    mCurrentTextureBufferStaleForGpu = true;
+    return useCache;
 }
 
 Rect BufferLayerConsumer::getCurrentCrop() const {
@@ -516,13 +444,8 @@
 }
 
 status_t BufferLayerConsumer::doFenceWaitLocked() const {
-    if (!mRE.isCurrent()) {
-        BLC_LOGE("doFenceWait: RenderEngine is not current");
-        return INVALID_OPERATION;
-    }
-
     if (mCurrentFence->isValid()) {
-        if (SyncFeatures::getInstance().useWaitSync()) {
+        if (mRE.useWaitSync()) {
             base::unique_fd fenceFd(mCurrentFence->dup());
             if (fenceFd == -1) {
                 BLC_LOGE("doFenceWait: error dup'ing fence fd: %d", errno);
@@ -548,8 +471,10 @@
     BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
     if (slotIndex == mCurrentTexture) {
         mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+        mCurrentTextureImageFreed = std::move(mImages[slotIndex]);
+    } else {
+        mImages[slotIndex] = nullptr;
     }
-    mImages[slotIndex].clear();
     ConsumerBase::freeBufferLocked(slotIndex);
 }
 
@@ -588,7 +513,7 @@
 
 void BufferLayerConsumer::abandonLocked() {
     BLC_LOGV("abandonLocked");
-    mCurrentTextureImage.clear();
+    mCurrentTextureBuffer.clear();
     ConsumerBase::abandonLocked();
 }
 
@@ -606,39 +531,4 @@
     ConsumerBase::dumpLocked(result, prefix);
 }
 
-BufferLayerConsumer::Image::Image(sp<GraphicBuffer> graphicBuffer, RE::RenderEngine& engine)
-      : mGraphicBuffer(graphicBuffer),
-        mImage{engine.createImage()},
-        mCreated(false),
-        mCropWidth(0),
-        mCropHeight(0) {}
-
-BufferLayerConsumer::Image::~Image() = default;
-
-status_t BufferLayerConsumer::Image::createIfNeeded(const Rect& imageCrop) {
-    const int32_t cropWidth = imageCrop.width();
-    const int32_t cropHeight = imageCrop.height();
-    if (mCreated && mCropWidth == cropWidth && mCropHeight == cropHeight) {
-        return OK;
-    }
-
-    mCreated = mImage->setNativeWindowBuffer(mGraphicBuffer->getNativeBuffer(),
-                                             mGraphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED,
-                                             cropWidth, cropHeight);
-    if (mCreated) {
-        mCropWidth = cropWidth;
-        mCropHeight = cropHeight;
-    } else {
-        mCropWidth = 0;
-        mCropHeight = 0;
-
-        const sp<GraphicBuffer>& buffer = mGraphicBuffer;
-        ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
-              buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
-              buffer->getPixelFormat());
-    }
-
-    return mCreated ? OK : UNKNOWN_ERROR;
-}
-
 }; // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index f81cdb1..e2ef399 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -37,10 +37,10 @@
 class Layer;
 class String8;
 
-namespace RE {
+namespace renderengine {
 class RenderEngine;
 class Image;
-} // namespace RE
+} // namespace renderengine
 
 /*
  * BufferLayerConsumer consumes buffers of graphics data from a BufferQueue,
@@ -73,15 +73,13 @@
     // BufferLayerConsumer constructs a new BufferLayerConsumer object.  The
     // tex parameter indicates the name of the RenderEngine texture to which
     // images are to be streamed.
-    BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, RE::RenderEngine& engine,
+    BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, renderengine::RenderEngine& engine,
                         uint32_t tex, Layer* layer);
 
     // Sets the contents changed listener. This should be used instead of
     // ConsumerBase::setFrameAvailableListener().
     void setContentsChangedListener(const wp<ContentsChangedListener>& listener);
 
-    nsecs_t computeExpectedPresent(const DispSync& dispSync);
-
     // updateTexImage acquires the most recently queued buffer, and sets the
     // image contents of the target texture to it.
     //
@@ -93,8 +91,9 @@
     // Unlike the GLConsumer version, this version takes a functor that may be
     // used to reject the newly acquired buffer.  It also does not bind the
     // RenderEngine texture until bindTextureImage is called.
-    status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync, bool* autoRefresh,
-                            bool* queuedBuffer, uint64_t maxFrameNumber);
+    status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
+                            bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber,
+                            const sp<Fence>& releaseFence);
 
     // See BufferLayerConsumer::bindTextureImageLocked().
     status_t bindTextureImage();
@@ -151,10 +150,16 @@
     // for use with bilinear filtering.
     void setFilteringEnabled(bool enabled);
 
+    // Sets mCurrentTextureBufferStaleForGpu to true to indicate that the
+    // buffer is now "stale" for GPU composition, and returns the old staleness
+    // bit as a caching hint.
+    bool getAndSetCurrentBufferCacheHint();
+
     // getCurrentBuffer returns the buffer associated with the current image.
     // When outSlot is not nullptr, the current buffer slot index is also
-    // returned.
-    sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr) const;
+    // returned. Simiarly, when outFence is not nullptr, the current output
+    // fence is returned.
+    sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
 
     // getCurrentCrop returns the cropping rectangle of the current buffer.
     Rect getCurrentCrop() const;
@@ -186,8 +191,7 @@
     // specific info in addition to the ConsumerBase behavior.
     virtual void dumpLocked(String8& result, const char* prefix) const;
 
-    // acquireBufferLocked overrides the ConsumerBase method to update the
-    // mImages array in addition to the ConsumerBase behavior.
+    // See ConsumerBase::acquireBufferLocked
     virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
                                          uint64_t maxFrameNumber = 0) override;
 
@@ -208,56 +212,17 @@
     // completion of the method will instead be returned to the caller, so that
     // it may call releaseBufferLocked itself later.
     status_t updateAndReleaseLocked(const BufferItem& item,
-                                    PendingRelease* pendingRelease = nullptr);
+                                    PendingRelease* pendingRelease = nullptr,
+                                    const sp<Fence>& releaseFence = Fence::NO_FENCE);
 
-    // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.  Uses
-    // mCurrentTexture if it's set, mCurrentTextureImage if not.  If the
-    // bind succeeds, this calls doFenceWait.
+    // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.
+    // If the bind succeeds, this calls doFenceWait.
     status_t bindTextureImageLocked();
 
 private:
-    // Image is a utility class for tracking and creating RE::Images. There
-    // is primarily just one image per slot, but there is also special cases:
-    //  - After freeBuffer, we must still keep the current image/buffer
-    // Reference counting RE::Images lets us handle all these cases easily while
-    // also only creating new RE::Images from buffers when required.
-    class Image : public LightRefBase<Image> {
-    public:
-        Image(sp<GraphicBuffer> graphicBuffer, RE::RenderEngine& engine);
-
-        Image(const Image& rhs) = delete;
-        Image& operator=(const Image& rhs) = delete;
-
-        // createIfNeeded creates an RE::Image if required (we haven't created
-        // one yet, or the crop-rect has changed).
-        status_t createIfNeeded(const Rect& imageCrop);
-
-        const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
-        const native_handle* graphicBufferHandle() {
-            return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
-        }
-
-        const RE::Image& image() const { return *mImage; }
-
-    private:
-        // Only allow instantiation using ref counting.
-        friend class LightRefBase<Image>;
-        virtual ~Image();
-
-        // mGraphicBuffer is the buffer that was used to create this image.
-        sp<GraphicBuffer> mGraphicBuffer;
-
-        // mImage is the image created from mGraphicBuffer.
-        std::unique_ptr<RE::Image> mImage;
-        bool mCreated;
-        int32_t mCropWidth;
-        int32_t mCropHeight;
-    };
-
     // freeBufferLocked frees up the given buffer slot. If the slot has been
     // initialized this will release the reference to the GraphicBuffer in
-    // that slot and destroy the RE::Image in that slot.  Otherwise it has no
-    // effect.
+    // that slot.  Otherwise it has no effect.
     //
     // This method must be called with mMutex locked.
     virtual void freeBufferLocked(int slotIndex);
@@ -283,7 +248,7 @@
     // current slot from RenderEngine.  If needed it will set the current
     // slot's fence to guard against a producer accessing the buffer before
     // the outstanding accesses have completed.
-    status_t syncForReleaseLocked();
+    status_t syncForReleaseLocked(const sp<Fence>& releaseFence);
 
     // The default consumer usage flags that BufferLayerConsumer always sets on its
     // BufferQueue instance; these will be OR:d with any additional flags passed
@@ -291,10 +256,14 @@
     // consume buffers as hardware textures.
     static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
 
-    // mCurrentTextureImage is the Image/buffer of the current texture. It's
+    // mCurrentTextureImage is the buffer containing the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
     // must track it separately in order to support the getCurrentBuffer method.
-    sp<Image> mCurrentTextureImage;
+    sp<GraphicBuffer> mCurrentTextureBuffer;
+
+    // True if the buffer was used for the previous client composition frame,
+    // and false otherwise.
+    bool mCurrentTextureBufferStaleForGpu;
 
     // mCurrentCrop is the crop rectangle that applies to the current texture.
     // It gets set each time updateTexImage is called.
@@ -352,7 +321,7 @@
     // setFilteringEnabled().
     bool mFilteringEnabled;
 
-    RE::RenderEngine& mRE;
+    renderengine::RenderEngine& mRE;
 
     // mTexName is the name of the RenderEngine texture to which streamed
     // images will be bound when bindTexImage is called. It is set at
@@ -364,15 +333,6 @@
 
     wp<ContentsChangedListener> mContentsChangedListener;
 
-    // mImages stores the buffers that have been allocated by the BufferQueue
-    // for each buffer slot.  It is initialized to null pointers, and gets
-    // filled in with the result of BufferQueue::acquire when the
-    // client dequeues a buffer from a
-    // slot that has not yet been used. The buffer allocated to a slot will also
-    // be replaced if the requested buffer usage or geometry differs from that
-    // of the buffer allocated to a slot.
-    sp<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS];
-
     // mCurrentTexture is the buffer slot index of the buffer that is currently
     // bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT,
     // indicating that no buffer slot is currently bound to the texture. Note,
@@ -381,6 +341,17 @@
     // reset mCurrentTexture to INVALID_BUFFER_SLOT.
     int mCurrentTexture;
 
+    // Cached image used for rendering the current texture through GPU
+    // composition, which contains the cached image after freeBufferLocked is
+    // called on the current buffer. Whenever latchBuffer is called, this is
+    // expected to be cleared. Then, if bindTexImage is called before the next
+    // buffer is acquired, then this image is bound.
+    std::unique_ptr<renderengine::Image> mCurrentTextureImageFreed;
+
+    // Cached images used for rendering the current texture through GPU
+    // composition.
+    std::unique_ptr<renderengine::Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
     // A release that is pending on the receipt of a new release fence from
     // presentDisplay
     PendingRelease mPendingRelease;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
new file mode 100644
index 0000000..edfeb0b
--- /dev/null
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BufferQueueLayer.h"
+#include "LayerRejecter.h"
+
+#include "TimeStats/TimeStats.h"
+
+#include <system/window.h>
+
+namespace android {
+
+BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
+
+BufferQueueLayer::~BufferQueueLayer() {
+    mConsumer->abandon();
+}
+
+// -----------------------------------------------------------------------
+// Interface implementation for Layer
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
+    mConsumer->setReleaseFence(releaseFence);
+}
+
+void BufferQueueLayer::setTransformHint(uint32_t orientation) const {
+    mConsumer->setTransformHint(orientation);
+}
+
+std::vector<OccupancyTracker::Segment> BufferQueueLayer::getOccupancyHistory(bool forceFlush) {
+    std::vector<OccupancyTracker::Segment> history;
+    status_t result = mConsumer->getOccupancyHistory(forceFlush, &history);
+    if (result != NO_ERROR) {
+        ALOGW("[%s] Failed to obtain occupancy history (%d)", mName.string(), result);
+        return {};
+    }
+    return history;
+}
+
+bool BufferQueueLayer::getTransformToDisplayInverse() const {
+    return mConsumer->getTransformToDisplayInverse();
+}
+
+void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
+    if (!mConsumer->releasePendingBuffer()) {
+        return;
+    }
+
+    auto releaseFenceTime = std::make_shared<FenceTime>(mConsumer->getPrevFinalReleaseFence());
+    mReleaseTimeline.updateSignalTimes();
+    mReleaseTimeline.push(releaseFenceTime);
+
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    if (mPreviousFrameNumber != 0) {
+        mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime,
+                                      std::move(releaseFenceTime));
+    }
+}
+
+void BufferQueueLayer::setDefaultBufferSize(uint32_t w, uint32_t h) {
+    mConsumer->setDefaultBufferSize(w, h);
+}
+
+int32_t BufferQueueLayer::getQueuedFrameCount() const {
+    return mQueuedFrames;
+}
+
+bool BufferQueueLayer::shouldPresentNow(nsecs_t expectedPresentTime) const {
+    if (getSidebandStreamChanged() || getAutoRefresh()) {
+        return true;
+    }
+
+    if (!hasFrameUpdate()) {
+        return false;
+    }
+
+    Mutex::Autolock lock(mQueueItemLock);
+
+    const int64_t addedTime = mQueueItems[0].mTimestamp;
+
+    // Ignore timestamps more than a second in the future
+    const bool isPlausible = addedTime < (expectedPresentTime + s2ns(1));
+    ALOGW_IF(!isPlausible,
+             "[%s] Timestamp %" PRId64 " seems implausible "
+             "relative to expectedPresent %" PRId64,
+             mName.string(), addedTime, expectedPresentTime);
+
+    const bool isDue = addedTime < expectedPresentTime;
+    return isDue || !isPlausible;
+}
+
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayer
+// -----------------------------------------------------------------------
+
+bool BufferQueueLayer::fenceHasSignaled() const {
+    if (latchUnsignaledBuffers()) {
+        return true;
+    }
+
+    if (!hasFrameUpdate()) {
+        return true;
+    }
+
+    Mutex::Autolock lock(mQueueItemLock);
+    if (mQueueItems[0].mIsDroppable) {
+        // Even though this buffer's fence may not have signaled yet, it could
+        // be replaced by another buffer before it has a chance to, which means
+        // that it's possible to get into a situation where a buffer is never
+        // able to be latched. To avoid this, grab this buffer anyway.
+        return true;
+    }
+    return mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+}
+
+nsecs_t BufferQueueLayer::getDesiredPresentTime() {
+    return mConsumer->getTimestamp();
+}
+
+std::shared_ptr<FenceTime> BufferQueueLayer::getCurrentFenceTime() const {
+    return mConsumer->getCurrentFenceTime();
+}
+
+void BufferQueueLayer::getDrawingTransformMatrix(float *matrix) {
+    return mConsumer->getTransformMatrix(matrix);
+}
+
+// NOTE: SurfaceFlinger's definitions of "Current" and "Drawing" do not neatly map to BufferQueue's
+// These functions get the fields for the frame that is currently in SurfaceFlinger's Drawing state
+// so the functions start with "getDrawing". The data is retrieved from the BufferQueueConsumer's
+// current buffer so the consumer functions start with "getCurrent".
+//
+// This results in the rather confusing functions below.
+uint32_t BufferQueueLayer::getDrawingTransform() const {
+    return mConsumer->getCurrentTransform();
+}
+
+ui::Dataspace BufferQueueLayer::getDrawingDataSpace() const {
+    return mConsumer->getCurrentDataSpace();
+}
+
+Rect BufferQueueLayer::getDrawingCrop() const {
+    return mConsumer->getCurrentCrop();
+}
+
+uint32_t BufferQueueLayer::getDrawingScalingMode() const {
+    return mConsumer->getCurrentScalingMode();
+}
+
+Region BufferQueueLayer::getDrawingSurfaceDamage() const {
+    return mConsumer->getSurfaceDamage();
+}
+
+const HdrMetadata& BufferQueueLayer::getDrawingHdrMetadata() const {
+    return mConsumer->getCurrentHdrMetadata();
+}
+
+int BufferQueueLayer::getDrawingApi() const {
+    return mConsumer->getCurrentApi();
+}
+
+PixelFormat BufferQueueLayer::getPixelFormat() const {
+    return mFormat;
+}
+
+uint64_t BufferQueueLayer::getFrameNumber() const {
+    Mutex::Autolock lock(mQueueItemLock);
+    return mQueueItems[0].mFrameNumber;
+}
+
+bool BufferQueueLayer::getAutoRefresh() const {
+    return mAutoRefresh;
+}
+
+bool BufferQueueLayer::getSidebandStreamChanged() const {
+    return mSidebandStreamChanged;
+}
+
+std::optional<Region> BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
+    bool sidebandStreamChanged = true;
+    if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
+        // mSidebandStreamChanged was changed to false
+        // replicated in LayerBE until FE/BE is ready to be synchronized
+        getBE().compositionInfo.hwc.sidebandStream = mConsumer->getSidebandStream();
+        if (getBE().compositionInfo.hwc.sidebandStream != nullptr) {
+            setTransactionFlags(eTransactionNeeded);
+            mFlinger->setTransactionFlags(eTraversalNeeded);
+        }
+        recomputeVisibleRegions = true;
+
+        const State& s(getDrawingState());
+        return getTransform().transform(Region(Rect(s.active_legacy.w, s.active_legacy.h)));
+    }
+    return {};
+}
+
+bool BufferQueueLayer::hasFrameUpdate() const {
+    return mQueuedFrames > 0;
+}
+
+void BufferQueueLayer::setFilteringEnabled(bool enabled) {
+    return mConsumer->setFilteringEnabled(enabled);
+}
+
+status_t BufferQueueLayer::bindTextureImage() {
+    return mConsumer->bindTextureImage();
+}
+
+status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                                          const sp<Fence>& releaseFence) {
+    // This boolean is used to make sure that SurfaceFlinger's shadow copy
+    // of the buffer queue isn't modified when the buffer queue is returning
+    // BufferItem's that weren't actually queued. This can happen in shared
+    // buffer mode.
+    bool queuedBuffer = false;
+    const int32_t layerID = getSequence();
+    LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
+                    getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode,
+                    getTransformToDisplayInverse(), mFreezeGeometryUpdates);
+
+    const nsecs_t expectedPresentTime = mFlinger->mUseScheduler
+            ? mFlinger->mScheduler->mPrimaryDispSync->expectedPresentTime()
+            : mFlinger->mPrimaryDispSync->expectedPresentTime();
+    status_t updateResult =
+            mConsumer->updateTexImage(&r, expectedPresentTime, &mAutoRefresh, &queuedBuffer,
+                                      mLastFrameNumberReceived, releaseFence);
+    if (updateResult == BufferQueue::PRESENT_LATER) {
+        // Producer doesn't want buffer to be displayed yet.  Signal a
+        // layer update so we check again at the next opportunity.
+        mFlinger->signalLayerUpdate();
+        return BAD_VALUE;
+    } else if (updateResult == BufferLayerConsumer::BUFFER_REJECTED) {
+        // If the buffer has been rejected, remove it from the shadow queue
+        // and return early
+        if (queuedBuffer) {
+            Mutex::Autolock lock(mQueueItemLock);
+            mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+            mQueueItems.removeAt(0);
+            mQueuedFrames--;
+        }
+        return BAD_VALUE;
+    } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
+        // This can occur if something goes wrong when trying to create the
+        // EGLImage for this buffer. If this happens, the buffer has already
+        // been released, so we need to clean up the queue and bug out
+        // early.
+        if (queuedBuffer) {
+            Mutex::Autolock lock(mQueueItemLock);
+            mQueueItems.clear();
+            mQueuedFrames = 0;
+            mFlinger->mTimeStats->onDestroy(layerID);
+        }
+
+        // Once we have hit this state, the shadow queue may no longer
+        // correctly reflect the incoming BufferQueue's contents, so even if
+        // updateTexImage starts working, the only safe course of action is
+        // to continue to ignore updates.
+        mUpdateTexImageFailed = true;
+
+        return BAD_VALUE;
+    }
+
+    if (queuedBuffer) {
+        // Autolock scope
+        auto currentFrameNumber = mConsumer->getFrameNumber();
+
+        Mutex::Autolock lock(mQueueItemLock);
+
+        // Remove any stale buffers that have been dropped during
+        // updateTexImage
+        while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
+            mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+            mQueueItems.removeAt(0);
+            mQueuedFrames--;
+        }
+
+        mFlinger->mTimeStats->setAcquireFence(layerID, currentFrameNumber,
+                                              mQueueItems[0].mFenceTime);
+        mFlinger->mTimeStats->setLatchTime(layerID, currentFrameNumber, latchTime);
+
+        mQueueItems.removeAt(0);
+    }
+
+    // Decrement the queued-frames count.  Signal another event if we
+    // have more frames pending.
+    if ((queuedBuffer && mQueuedFrames.fetch_sub(1) > 1) || mAutoRefresh) {
+        mFlinger->signalLayerUpdate();
+    }
+
+    return NO_ERROR;
+}
+
+status_t BufferQueueLayer::updateActiveBuffer() {
+    // update the active buffer
+    mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot, &mActiveBufferFence);
+    getBE().compositionInfo.mBuffer = mActiveBuffer;
+    getBE().compositionInfo.mBufferSlot = mActiveBufferSlot;
+
+    if (mActiveBuffer == nullptr) {
+        // this can only happen if the very first buffer was rejected.
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
+bool BufferQueueLayer::useCachedBufferForClientComposition() const {
+    return mConsumer->getAndSetCurrentBufferCacheHint();
+}
+
+status_t BufferQueueLayer::updateFrameNumber(nsecs_t latchTime) {
+    mPreviousFrameNumber = mCurrentFrameNumber;
+    mCurrentFrameNumber = mConsumer->getFrameNumber();
+
+    {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+    }
+    return NO_ERROR;
+}
+
+void BufferQueueLayer::setHwcLayerBuffer(DisplayId displayId) {
+    auto& hwcInfo = getBE().mHwcLayers[displayId];
+    auto& hwcLayer = hwcInfo.layer;
+
+    uint32_t hwcSlot = 0;
+    sp<GraphicBuffer> hwcBuffer;
+    hwcInfo.bufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer, &hwcSlot, &hwcBuffer);
+
+    auto acquireFence = mConsumer->getCurrentFence();
+    auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
+              getBE().compositionInfo.mBuffer->handle, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+    getBE().compositionInfo.mBufferSlot = mActiveBufferSlot;
+    getBE().compositionInfo.mBuffer = mActiveBuffer;
+    getBE().compositionInfo.hwc.fence = acquireFence;
+}
+
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayerConsumer::ContentsChangedListener
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::fakeVsync() {
+    mRefreshPending = false;
+    bool ignored = false;
+    latchBuffer(ignored, systemTime(), Fence::NO_FENCE);
+    usleep(16000);
+    releasePendingBuffer(systemTime());
+}
+
+void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
+    // Add this buffer from our internal queue tracker
+    { // Autolock scope
+        // Report the requested present time to the Scheduler.
+        if (mFlinger->mUseScheduler) {
+            mFlinger->mScheduler->addFramePresentTimeForLayer(item.mTimestamp,
+                                                              item.mIsAutoTimestamp, mName.c_str());
+        }
+
+        Mutex::Autolock lock(mQueueItemLock);
+        // Reset the frame number tracker when we receive the first buffer after
+        // a frame number reset
+        if (item.mFrameNumber == 1) {
+            mLastFrameNumberReceived = 0;
+        }
+
+        // Ensure that callbacks are handled in order
+        while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
+            status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
+            if (result != NO_ERROR) {
+                ALOGE("[%s] Timed out waiting on callback", mName.string());
+            }
+        }
+
+        mQueueItems.push_back(item);
+        mQueuedFrames++;
+
+        // Wake up any pending callbacks
+        mLastFrameNumberReceived = item.mFrameNumber;
+        mQueueItemCondition.broadcast();
+    }
+
+    mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
+                                             item.mGraphicBuffer->getHeight(), item.mFrameNumber);
+
+    // If this layer is orphaned, then we run a fake vsync pulse so that
+    // dequeueBuffer doesn't block indefinitely.
+    if (isRemovedFromCurrentState()) {
+        fakeVsync();
+    } else {
+        mFlinger->signalLayerUpdate();
+    }
+}
+
+void BufferQueueLayer::onFrameReplaced(const BufferItem& item) {
+    { // Autolock scope
+        Mutex::Autolock lock(mQueueItemLock);
+
+        // Ensure that callbacks are handled in order
+        while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
+            status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
+            if (result != NO_ERROR) {
+                ALOGE("[%s] Timed out waiting on callback", mName.string());
+            }
+        }
+
+        if (!hasFrameUpdate()) {
+            ALOGE("Can't replace a frame on an empty queue");
+            return;
+        }
+        mQueueItems.editItemAt(mQueueItems.size() - 1) = item;
+
+        // Wake up any pending callbacks
+        mLastFrameNumberReceived = item.mFrameNumber;
+        mQueueItemCondition.broadcast();
+    }
+}
+
+void BufferQueueLayer::onSidebandStreamChanged() {
+    bool sidebandStreamChanged = false;
+    if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, true)) {
+        // mSidebandStreamChanged was changed to true
+        mFlinger->signalLayerUpdate();
+    }
+}
+
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::onFirstRef() {
+    BufferLayer::onFirstRef();
+
+    // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer, true);
+    mProducer = new MonitoredProducer(producer, mFlinger, this);
+    {
+        // Grab the SF state lock during this since it's the only safe way to access RenderEngine
+        Mutex::Autolock lock(mFlinger->mStateLock);
+        mConsumer =
+                new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this);
+    }
+    mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
+    mConsumer->setContentsChangedListener(this);
+    mConsumer->setName(mName);
+
+    // BufferQueueCore::mMaxDequeuedBufferCount is default to 1
+    if (!mFlinger->isLayerTripleBufferingDisabled()) {
+        mProducer->setMaxDequeuedBufferCount(2);
+    }
+
+    if (const auto display = mFlinger->getDefaultDisplayDevice()) {
+        updateTransformHint(display);
+    }
+}
+
+status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) {
+    uint32_t const maxSurfaceDims =
+          std::min(mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims());
+
+    // never allow a surface larger than what our underlying GL implementation
+    // can handle.
+    if ((uint32_t(w) > maxSurfaceDims) || (uint32_t(h) > maxSurfaceDims)) {
+        ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h));
+        return BAD_VALUE;
+    }
+
+    mFormat = format;
+
+    setDefaultBufferSize(w, h);
+    mConsumer->setDefaultBufferFormat(format);
+    mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
+
+    return NO_ERROR;
+}
+
+sp<IGraphicBufferProducer> BufferQueueLayer::getProducer() const {
+    return mProducer;
+}
+
+uint32_t BufferQueueLayer::getProducerStickyTransform() const {
+    int producerStickyTransform = 0;
+    int ret = mProducer->query(NATIVE_WINDOW_STICKY_TRANSFORM, &producerStickyTransform);
+    if (ret != OK) {
+        ALOGW("%s: Error %s (%d) while querying window sticky transform.", __FUNCTION__,
+              strerror(-ret), ret);
+        return 0;
+    }
+    return static_cast<uint32_t>(producerStickyTransform);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
new file mode 100644
index 0000000..20992a4
--- /dev/null
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BufferLayer.h"
+
+#include <utils/String8.h>
+
+namespace android {
+
+/*
+ * A new BufferQueue and a new BufferLayerConsumer are created when the
+ * BufferLayer is first referenced.
+ *
+ * This also implements onFrameAvailable(), which notifies SurfaceFlinger
+ * that new data has arrived.
+ */
+class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener {
+public:
+    explicit BufferQueueLayer(const LayerCreationArgs&);
+    ~BufferQueueLayer() override;
+
+    // -----------------------------------------------------------------------
+    // Interface implementation for Layer
+    // -----------------------------------------------------------------------
+public:
+    void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+
+    void setTransformHint(uint32_t orientation) const override;
+
+    std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
+
+    bool getTransformToDisplayInverse() const override;
+
+    // If a buffer was replaced this frame, release the former buffer
+    void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
+
+    void setDefaultBufferSize(uint32_t w, uint32_t h) override;
+
+    int32_t getQueuedFrameCount() const override;
+
+    bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+    // -----------------------------------------------------------------------
+
+    // -----------------------------------------------------------------------
+    // Interface implementation for BufferLayer
+    // -----------------------------------------------------------------------
+public:
+    bool fenceHasSignaled() const override;
+
+protected:
+    bool useCachedBufferForClientComposition() const override;
+
+private:
+    nsecs_t getDesiredPresentTime() override;
+    std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
+
+    void getDrawingTransformMatrix(float *matrix) override;
+    uint32_t getDrawingTransform() const override;
+    ui::Dataspace getDrawingDataSpace() const override;
+    Rect getDrawingCrop() const override;
+    uint32_t getDrawingScalingMode() const override;
+    Region getDrawingSurfaceDamage() const override;
+    const HdrMetadata& getDrawingHdrMetadata() const override;
+    int getDrawingApi() const override;
+    PixelFormat getPixelFormat() const override;
+
+    uint64_t getFrameNumber() const override;
+
+    bool getAutoRefresh() const override;
+    bool getSidebandStreamChanged() const override;
+
+    std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override;
+
+    bool hasFrameUpdate() const override;
+
+    void setFilteringEnabled(bool enabled) override;
+
+    status_t bindTextureImage() override;
+    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                            const sp<Fence>& releaseFence) override;
+
+    status_t updateActiveBuffer() override;
+    status_t updateFrameNumber(nsecs_t latchTime) override;
+
+    void setHwcLayerBuffer(DisplayId displayId) override;
+
+    // -----------------------------------------------------------------------
+    // Interface implementation for BufferLayerConsumer::ContentsChangedListener
+    // -----------------------------------------------------------------------
+protected:
+    void onFrameAvailable(const BufferItem& item) override;
+    void onFrameReplaced(const BufferItem& item) override;
+    void onSidebandStreamChanged() override;
+    // -----------------------------------------------------------------------
+
+public:
+    status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
+
+    sp<IGraphicBufferProducer> getProducer() const;
+
+private:
+    // Temporary - Used only for LEGACY camera mode.
+    uint32_t getProducerStickyTransform() const;
+
+    void onFirstRef() override;
+
+    sp<BufferLayerConsumer> mConsumer;
+    sp<IGraphicBufferProducer> mProducer;
+
+    PixelFormat mFormat{PIXEL_FORMAT_NONE};
+
+    // Only accessed on the main thread.
+    uint64_t mPreviousFrameNumber{0};
+    bool mUpdateTexImageFailed{false};
+
+    // Local copy of the queued contents of the incoming BufferQueue
+    mutable Mutex mQueueItemLock;
+    Condition mQueueItemCondition;
+    Vector<BufferItem> mQueueItems;
+    std::atomic<uint64_t> mLastFrameNumberReceived{0};
+
+    bool mAutoRefresh{false};
+    int mActiveBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
+
+    // thread-safe
+    std::atomic<int32_t> mQueuedFrames{0};
+    std::atomic<bool> mSidebandStreamChanged{false};
+
+    void fakeVsync();
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
new file mode 100644
index 0000000..96a66cb
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -0,0 +1,663 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "BufferStateLayer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BufferStateLayer.h"
+#include "ColorLayer.h"
+
+#include "TimeStats/TimeStats.h"
+
+#include <private/gui/SyncFeatures.h>
+#include <renderengine/Image.h>
+
+#include <limits>
+
+namespace android {
+
+// clang-format off
+const std::array<float, 16> BufferStateLayer::IDENTITY_MATRIX{
+        1, 0, 0, 0,
+        0, 1, 0, 0,
+        0, 0, 1, 0,
+        0, 0, 0, 1
+};
+// clang-format on
+
+BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args) {
+    mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+}
+BufferStateLayer::~BufferStateLayer() = default;
+
+// -----------------------------------------------------------------------
+// Interface implementation for Layer
+// -----------------------------------------------------------------------
+void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
+    // The previous release fence notifies the client that SurfaceFlinger is done with the previous
+    // buffer that was presented on this layer. The first transaction that came in this frame that
+    // replaced the previous buffer on this layer needs this release fence, because the fence will
+    // let the client know when that previous buffer is removed from the screen.
+    //
+    // Every other transaction on this layer does not need a release fence because no other
+    // Transactions that were set on this layer this frame are going to have their preceeding buffer
+    // removed from the display this frame.
+    //
+    // For example, if we have 3 transactions this frame. The first transaction doesn't contain a
+    // buffer so it doesn't need a previous release fence because the layer still needs the previous
+    // buffer. The second transaction contains a buffer so it needs a previous release fence because
+    // the previous buffer will be released this frame. The third transaction also contains a
+    // buffer. It replaces the buffer in the second transaction. The buffer in the second
+    // transaction will now no longer be presented so it is released immediately and the third
+    // transaction doesn't need a previous release fence.
+    for (auto& handle : mDrawingState.callbackHandles) {
+        if (handle->releasePreviousBuffer) {
+            handle->previousReleaseFence = releaseFence;
+            break;
+        }
+    }
+}
+
+void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const {
+    // TODO(marissaw): send the transform hint to buffer owner
+    return;
+}
+
+void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) {
+    mFlinger->getTransactionCompletedThread().addPresentedCallbackHandles(
+            mDrawingState.callbackHandles);
+
+    mDrawingState.callbackHandles = {};
+}
+
+bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const {
+    if (getSidebandStreamChanged() || getAutoRefresh()) {
+        return true;
+    }
+
+    return hasFrameUpdate();
+}
+
+bool BufferStateLayer::willPresentCurrentTransaction() const {
+    // Returns true if the most recent Transaction applied to CurrentState will be presented.
+    return getSidebandStreamChanged() || getAutoRefresh() ||
+            (mCurrentState.modified && mCurrentState.buffer != nullptr);
+}
+
+bool BufferStateLayer::getTransformToDisplayInverse() const {
+    return mCurrentState.transformToDisplayInverse;
+}
+
+void BufferStateLayer::pushPendingState() {
+    if (!mCurrentState.modified) {
+        return;
+    }
+    mPendingStates.push_back(mCurrentState);
+    ATRACE_INT(mTransactionName.string(), mPendingStates.size());
+}
+
+bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) {
+    const bool stateUpdateAvailable = !mPendingStates.empty();
+    while (!mPendingStates.empty()) {
+        popPendingState(stateToCommit);
+    }
+    mCurrentStateModified = stateUpdateAvailable && mCurrentState.modified;
+    mCurrentState.modified = false;
+    return stateUpdateAvailable;
+}
+
+// Crop that applies to the window
+Rect BufferStateLayer::getCrop(const Layer::State& /*s*/) const {
+    return Rect::INVALID_RECT;
+}
+
+bool BufferStateLayer::setTransform(uint32_t transform) {
+    if (mCurrentState.transform == transform) return false;
+    mCurrentState.sequence++;
+    mCurrentState.transform = transform;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setTransformToDisplayInverse(bool transformToDisplayInverse) {
+    if (mCurrentState.transformToDisplayInverse == transformToDisplayInverse) return false;
+    mCurrentState.sequence++;
+    mCurrentState.transformToDisplayInverse = transformToDisplayInverse;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setCrop(const Rect& crop) {
+    if (mCurrentState.crop == crop) return false;
+    mCurrentState.sequence++;
+    mCurrentState.crop = crop;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setFrame(const Rect& frame) {
+    int x = frame.left;
+    int y = frame.top;
+    int w = frame.getWidth();
+    int h = frame.getHeight();
+
+    if (x < 0) {
+        x = 0;
+        w = frame.right;
+    }
+
+    if (y < 0) {
+        y = 0;
+        h = frame.bottom;
+    }
+
+    if (mCurrentState.active.transform.tx() == x && mCurrentState.active.transform.ty() == y &&
+        mCurrentState.active.w == w && mCurrentState.active.h == h) {
+        return false;
+    }
+
+    if (!frame.isValid()) {
+        x = y = w = h = 0;
+    }
+    mCurrentState.active.transform.set(x, y);
+    mCurrentState.active.w = w;
+    mCurrentState.active.h = h;
+
+    mCurrentState.sequence++;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer) {
+    if (mCurrentState.buffer) {
+        mReleasePreviousBuffer = true;
+    }
+
+    mCurrentState.sequence++;
+    mCurrentState.buffer = buffer;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) {
+    // The acquire fences of BufferStateLayers have already signaled before they are set
+    mCallbackHandleAcquireTime = fence->getSignalTime();
+
+    mCurrentState.acquireFence = fence;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setDataspace(ui::Dataspace dataspace) {
+    if (mCurrentState.dataspace == dataspace) return false;
+    mCurrentState.sequence++;
+    mCurrentState.dataspace = dataspace;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
+    if (mCurrentState.hdrMetadata == hdrMetadata) return false;
+    mCurrentState.sequence++;
+    mCurrentState.hdrMetadata = hdrMetadata;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setSurfaceDamageRegion(const Region& surfaceDamage) {
+    mCurrentState.sequence++;
+    mCurrentState.surfaceDamageRegion = surfaceDamage;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setApi(int32_t api) {
+    if (mCurrentState.api == api) return false;
+    mCurrentState.sequence++;
+    mCurrentState.api = api;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setSidebandStream(const sp<NativeHandle>& sidebandStream) {
+    if (mCurrentState.sidebandStream == sidebandStream) return false;
+    mCurrentState.sequence++;
+    mCurrentState.sidebandStream = sidebandStream;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+
+    if (!mSidebandStreamChanged.exchange(true)) {
+        // mSidebandStreamChanged was false
+        mFlinger->signalLayerUpdate();
+    }
+    return true;
+}
+
+bool BufferStateLayer::setTransactionCompletedListeners(
+        const std::vector<sp<CallbackHandle>>& handles) {
+    // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
+    if (handles.empty()) {
+        mReleasePreviousBuffer = false;
+        return false;
+    }
+
+    const bool willPresent = willPresentCurrentTransaction();
+
+    for (const auto& handle : handles) {
+        // If this transaction set a buffer on this layer, release its previous buffer
+        handle->releasePreviousBuffer = mReleasePreviousBuffer;
+
+        // If this layer will be presented in this frame
+        if (willPresent) {
+            // If this transaction set an acquire fence on this layer, set its acquire time
+            handle->acquireTime = mCallbackHandleAcquireTime;
+
+            // Notify the transaction completed thread that there is a pending latched callback
+            // handle
+            mFlinger->getTransactionCompletedThread().registerPendingCallbackHandle(handle);
+
+            // Store so latched time and release fence can be set
+            mCurrentState.callbackHandles.push_back(handle);
+
+        } else { // If this layer will NOT need to be relatched and presented this frame
+            // Notify the transaction completed thread this handle is done
+            mFlinger->getTransactionCompletedThread().addUnpresentedCallbackHandle(handle);
+        }
+    }
+
+    mReleasePreviousBuffer = false;
+    mCallbackHandleAcquireTime = -1;
+
+    return willPresent;
+}
+
+bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) {
+    mCurrentState.transparentRegionHint = transparent;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setColor(const half3& color) {
+    // create color layer if one does not yet exist
+    if (!mCurrentState.bgColorLayer) {
+        uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor;
+        const String8& name = mName + "BackgroundColorLayer";
+        mCurrentState.bgColorLayer =
+                new ColorLayer(LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags));
+
+        // add to child list
+        addChild(mCurrentState.bgColorLayer);
+        mFlinger->mLayersAdded = true;
+        // set up SF to handle added color layer
+        if (isRemovedFromCurrentState()) {
+            mCurrentState.bgColorLayer->onRemovedFromCurrentState();
+        }
+        mFlinger->setTransactionFlags(eTransactionNeeded);
+    }
+
+    mCurrentState.bgColorLayer->setColor(color);
+    mCurrentState.bgColorLayer->setLayer(std::numeric_limits<int32_t>::min());
+
+    return true;
+}
+
+bool BufferStateLayer::setColorAlpha(float alpha) {
+    if (!mCurrentState.bgColorLayer) {
+        ALOGE("Attempting to set color alpha on a buffer state layer with no background color");
+        return false;
+    }
+    mCurrentState.bgColorLayer->setAlpha(alpha);
+    return true;
+}
+
+bool BufferStateLayer::setColorDataspace(ui::Dataspace dataspace) {
+    if (!mCurrentState.bgColorLayer) {
+        ALOGE("Attempting to set color dataspace on a buffer state layer with no background color");
+        return false;
+    }
+    mCurrentState.bgColorLayer->setDataspace(dataspace);
+    return true;
+}
+
+Rect BufferStateLayer::getBufferSize(const State& s) const {
+    // for buffer state layers we use the display frame size as the buffer size.
+    if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
+        return Rect(getActiveWidth(s), getActiveHeight(s));
+    }
+
+    // if the display frame is not defined, use the parent bounds as the buffer size.
+    const auto& p = mDrawingParent.promote();
+    if (p != nullptr) {
+        Rect parentBounds = Rect(p->computeBounds(Region()));
+        if (!parentBounds.isEmpty()) {
+            return parentBounds;
+        }
+    }
+
+    // if there is no parent layer, use the buffer's bounds as the buffer size
+    if (s.buffer) {
+        return s.buffer->getBounds();
+    }
+    return Rect::INVALID_RECT;
+}
+// -----------------------------------------------------------------------
+
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayer
+// -----------------------------------------------------------------------
+bool BufferStateLayer::fenceHasSignaled() const {
+    if (latchUnsignaledBuffers()) {
+        return true;
+    }
+
+    return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+}
+
+nsecs_t BufferStateLayer::getDesiredPresentTime() {
+    // TODO(marissaw): support an equivalent to desiredPresentTime for timestats metrics
+    return 0;
+}
+
+std::shared_ptr<FenceTime> BufferStateLayer::getCurrentFenceTime() const {
+    return std::make_shared<FenceTime>(getDrawingState().acquireFence);
+}
+
+void BufferStateLayer::getDrawingTransformMatrix(float *matrix) {
+    std::copy(std::begin(mTransformMatrix), std::end(mTransformMatrix), matrix);
+}
+
+uint32_t BufferStateLayer::getDrawingTransform() const {
+    return getDrawingState().transform;
+}
+
+ui::Dataspace BufferStateLayer::getDrawingDataSpace() const {
+    return getDrawingState().dataspace;
+}
+
+// Crop that applies to the buffer
+Rect BufferStateLayer::getDrawingCrop() const {
+    const State& s(getDrawingState());
+
+    if (s.crop.isEmpty() && s.buffer) {
+        return s.buffer->getBounds();
+    } else if (s.buffer) {
+        Rect crop = s.crop;
+        crop.left = std::max(crop.left, 0);
+        crop.top = std::max(crop.top, 0);
+        uint32_t bufferWidth = s.buffer->getWidth();
+        uint32_t bufferHeight = s.buffer->getHeight();
+        if (bufferHeight <= std::numeric_limits<int32_t>::max() &&
+            bufferWidth <= std::numeric_limits<int32_t>::max()) {
+            crop.right = std::min(crop.right, static_cast<int32_t>(bufferWidth));
+            crop.bottom = std::min(crop.bottom, static_cast<int32_t>(bufferHeight));
+        }
+        if (!crop.isValid()) {
+            // Crop rect is out of bounds, return whole buffer
+            return s.buffer->getBounds();
+        }
+        return crop;
+    }
+    return s.crop;
+}
+
+uint32_t BufferStateLayer::getDrawingScalingMode() const {
+    return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+}
+
+Region BufferStateLayer::getDrawingSurfaceDamage() const {
+    return getDrawingState().surfaceDamageRegion;
+}
+
+const HdrMetadata& BufferStateLayer::getDrawingHdrMetadata() const {
+    return getDrawingState().hdrMetadata;
+}
+
+int BufferStateLayer::getDrawingApi() const {
+    return getDrawingState().api;
+}
+
+PixelFormat BufferStateLayer::getPixelFormat() const {
+    if (!mActiveBuffer) {
+        return PIXEL_FORMAT_NONE;
+    }
+    return mActiveBuffer->format;
+}
+
+uint64_t BufferStateLayer::getFrameNumber() const {
+    return mFrameNumber;
+}
+
+bool BufferStateLayer::getAutoRefresh() const {
+    // TODO(marissaw): support shared buffer mode
+    return false;
+}
+
+bool BufferStateLayer::getSidebandStreamChanged() const {
+    return mSidebandStreamChanged.load();
+}
+
+std::optional<Region> BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
+    if (mSidebandStreamChanged.exchange(false)) {
+        const State& s(getDrawingState());
+        // mSidebandStreamChanged was true
+        // replicated in LayerBE until FE/BE is ready to be synchronized
+        getBE().compositionInfo.hwc.sidebandStream = s.sidebandStream;
+        if (getBE().compositionInfo.hwc.sidebandStream != nullptr) {
+            setTransactionFlags(eTransactionNeeded);
+            mFlinger->setTransactionFlags(eTraversalNeeded);
+        }
+        recomputeVisibleRegions = true;
+
+        return getTransform().transform(Region(Rect(s.active.w, s.active.h)));
+    }
+    return {};
+}
+
+bool BufferStateLayer::hasFrameUpdate() const {
+    return mCurrentStateModified && getCurrentState().buffer != nullptr;
+}
+
+void BufferStateLayer::setFilteringEnabled(bool enabled) {
+    GLConsumer::computeTransformMatrix(mTransformMatrix.data(), mActiveBuffer, mCurrentCrop,
+                                       mCurrentTransform, enabled);
+}
+
+status_t BufferStateLayer::bindTextureImage() {
+    const State& s(getDrawingState());
+    auto& engine(mFlinger->getRenderEngine());
+
+    return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence, false);
+}
+
+status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
+                                          const sp<Fence>& releaseFence) {
+    const State& s(getDrawingState());
+
+    if (!s.buffer) {
+        return NO_ERROR;
+    }
+
+    const int32_t layerID = getSequence();
+
+    // Reject if the layer is invalid
+    uint32_t bufferWidth = s.buffer->width;
+    uint32_t bufferHeight = s.buffer->height;
+
+    if (s.transform & ui::Transform::ROT_90) {
+        std::swap(bufferWidth, bufferHeight);
+    }
+
+    if (s.transformToDisplayInverse) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufferWidth, bufferHeight);
+        }
+    }
+
+    if (getEffectiveScalingMode() == NATIVE_WINDOW_SCALING_MODE_FREEZE &&
+        (s.active.w != bufferWidth || s.active.h != bufferHeight)) {
+        ALOGE("[%s] rejecting buffer: "
+              "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
+              mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h);
+        mFlinger->mTimeStats->removeTimeRecord(layerID, getFrameNumber());
+        return BAD_VALUE;
+    }
+
+    for (auto& handle : mDrawingState.callbackHandles) {
+        handle->latchTime = latchTime;
+    }
+
+    // Handle sync fences
+    if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) {
+        // TODO(alecmouri): Fail somewhere upstream if the fence is invalid.
+        if (!releaseFence->isValid()) {
+            mFlinger->mTimeStats->onDestroy(layerID);
+            return UNKNOWN_ERROR;
+        }
+
+        // Check status of fences first because merging is expensive.
+        // Merging an invalid fence with any other fence results in an
+        // invalid fence.
+        auto currentStatus = s.acquireFence->getStatus();
+        if (currentStatus == Fence::Status::Invalid) {
+            ALOGE("Existing fence has invalid state");
+            mFlinger->mTimeStats->onDestroy(layerID);
+            return BAD_VALUE;
+        }
+
+        auto incomingStatus = releaseFence->getStatus();
+        if (incomingStatus == Fence::Status::Invalid) {
+            ALOGE("New fence has invalid state");
+            mDrawingState.acquireFence = releaseFence;
+            mFlinger->mTimeStats->onDestroy(layerID);
+            return BAD_VALUE;
+        }
+
+        // If both fences are signaled or both are unsignaled, we need to merge
+        // them to get an accurate timestamp.
+        if (currentStatus == incomingStatus) {
+            char fenceName[32] = {};
+            snprintf(fenceName, 32, "%.28s:%d", mName.string(), mFrameNumber);
+            sp<Fence> mergedFence =
+                    Fence::merge(fenceName, mDrawingState.acquireFence, releaseFence);
+            if (!mergedFence.get()) {
+                ALOGE("failed to merge release fences");
+                // synchronization is broken, the best we can do is hope fences
+                // signal in order so the new fence will act like a union
+                mDrawingState.acquireFence = releaseFence;
+                mFlinger->mTimeStats->onDestroy(layerID);
+                return BAD_VALUE;
+            }
+            mDrawingState.acquireFence = mergedFence;
+        } else if (incomingStatus == Fence::Status::Unsignaled) {
+            // If one fence has signaled and the other hasn't, the unsignaled
+            // fence will approximately correspond with the correct timestamp.
+            // There's a small race if both fences signal at about the same time
+            // and their statuses are retrieved with unfortunate timing. However,
+            // by this point, they will have both signaled and only the timestamp
+            // will be slightly off; any dependencies after this point will
+            // already have been met.
+            mDrawingState.acquireFence = releaseFence;
+        }
+    } else {
+        // Bind the new buffer to the GL texture.
+        //
+        // Older devices require the "implicit" synchronization provided
+        // by glEGLImageTargetTexture2DOES, which this method calls.  Newer
+        // devices will either call this in Layer::onDraw, or (if it's not
+        // a GL-composited layer) not at all.
+        status_t err = bindTextureImage();
+        if (err != NO_ERROR) {
+            mFlinger->mTimeStats->onDestroy(layerID);
+            return BAD_VALUE;
+        }
+    }
+
+    // TODO(marissaw): properly support mTimeStats
+    mFlinger->mTimeStats->setPostTime(layerID, getFrameNumber(), getName().c_str(), latchTime);
+    mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
+    mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime);
+
+    return NO_ERROR;
+}
+
+status_t BufferStateLayer::updateActiveBuffer() {
+    const State& s(getDrawingState());
+
+    if (s.buffer == nullptr) {
+        return BAD_VALUE;
+    }
+
+    mActiveBuffer = s.buffer;
+    mActiveBufferFence = s.acquireFence;
+    getBE().compositionInfo.mBuffer = mActiveBuffer;
+    getBE().compositionInfo.mBufferSlot = 0;
+
+    return NO_ERROR;
+}
+
+bool BufferStateLayer::useCachedBufferForClientComposition() const {
+    // TODO: Store a proper staleness bit to support EGLImage caching.
+    return false;
+}
+
+status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
+    // TODO(marissaw): support frame history events
+    mCurrentFrameNumber = mFrameNumber;
+    return NO_ERROR;
+}
+
+void BufferStateLayer::setHwcLayerBuffer(DisplayId displayId) {
+    auto& hwcInfo = getBE().mHwcLayers[displayId];
+    auto& hwcLayer = hwcInfo.layer;
+
+    const State& s(getDrawingState());
+
+    // TODO(marissaw): support more than one slot
+    uint32_t hwcSlot = 0;
+
+    auto error = hwcLayer->setBuffer(hwcSlot, s.buffer, s.acquireFence);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
+              s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    mCurrentStateModified = false;
+    mFrameNumber++;
+}
+
+void BufferStateLayer::onFirstRef() {
+    BufferLayer::onFirstRef();
+
+    if (const auto display = mFlinger->getDefaultDisplayDevice()) {
+        updateTransformHint(display);
+    }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
new file mode 100644
index 0000000..bef9f19
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BufferLayer.h"
+#include "Layer.h"
+
+#include <gui/GLConsumer.h>
+#include <renderengine/Image.h>
+#include <renderengine/RenderEngine.h>
+#include <system/window.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class BufferStateLayer : public BufferLayer {
+public:
+    explicit BufferStateLayer(const LayerCreationArgs&);
+    ~BufferStateLayer() override;
+
+    // -----------------------------------------------------------------------
+    // Interface implementation for Layer
+    // -----------------------------------------------------------------------
+    void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+    void setTransformHint(uint32_t orientation) const override;
+    void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
+
+    bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+
+    bool getTransformToDisplayInverse() const override;
+
+    uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override {
+        return flags;
+    }
+    void pushPendingState() override;
+    bool applyPendingStates(Layer::State* stateToCommit) override;
+
+    uint32_t getActiveWidth(const Layer::State& s) const override { return s.active.w; }
+    uint32_t getActiveHeight(const Layer::State& s) const override { return s.active.h; }
+    ui::Transform getActiveTransform(const Layer::State& s) const override {
+        return s.active.transform;
+    }
+    Region getActiveTransparentRegion(const Layer::State& s) const override {
+        return s.transparentRegionHint;
+    }
+    Rect getCrop(const Layer::State& s) const;
+
+    bool setTransform(uint32_t transform) override;
+    bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
+    bool setCrop(const Rect& crop) override;
+    bool setFrame(const Rect& frame) override;
+    bool setBuffer(const sp<GraphicBuffer>& buffer) override;
+    bool setAcquireFence(const sp<Fence>& fence) override;
+    bool setDataspace(ui::Dataspace dataspace) override;
+    bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
+    bool setSurfaceDamageRegion(const Region& surfaceDamage) override;
+    bool setApi(int32_t api) override;
+    bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
+    bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
+
+    // Override to ignore legacy layer state properties that are not used by BufferStateLayer
+    bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; }
+    bool setPosition(float /*x*/, float /*y*/, bool /*immediate*/) override { return false; }
+    bool setTransparentRegionHint(const Region& transparent) override;
+    bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/,
+                   bool /*allowNonRectPreservingTransforms*/) override {
+        return false;
+    }
+    bool setCrop_legacy(const Rect& /*crop*/, bool /*immediate*/) override { return false; }
+    bool setOverrideScalingMode(int32_t /*overrideScalingMode*/) override { return false; }
+    bool setColor(const half3& color) override;
+    bool setColorAlpha(float alpha) override;
+    bool setColorDataspace(ui::Dataspace dataspace) override;
+    void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
+                                      uint64_t /*frameNumber*/) override {}
+    void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
+                                      uint64_t /*frameNumber*/) override {}
+
+    Rect getBufferSize(const State& s) const override;
+    // -----------------------------------------------------------------------
+
+    // -----------------------------------------------------------------------
+    // Interface implementation for BufferLayer
+    // -----------------------------------------------------------------------
+    bool fenceHasSignaled() const override;
+
+protected:
+    bool useCachedBufferForClientComposition() const override;
+
+private:
+    nsecs_t getDesiredPresentTime() override;
+    std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
+
+    void getDrawingTransformMatrix(float *matrix) override;
+    uint32_t getDrawingTransform() const override;
+    ui::Dataspace getDrawingDataSpace() const override;
+    Rect getDrawingCrop() const override;
+    uint32_t getDrawingScalingMode() const override;
+    Region getDrawingSurfaceDamage() const override;
+    const HdrMetadata& getDrawingHdrMetadata() const override;
+    int getDrawingApi() const override;
+    PixelFormat getPixelFormat() const override;
+
+    uint64_t getFrameNumber() const override;
+
+    bool getAutoRefresh() const override;
+    bool getSidebandStreamChanged() const override;
+
+    std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override;
+
+    bool hasFrameUpdate() const override;
+
+    void setFilteringEnabled(bool enabled) override;
+
+    status_t bindTextureImage() override;
+    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                            const sp<Fence>& releaseFence) override;
+
+    status_t updateActiveBuffer() override;
+    status_t updateFrameNumber(nsecs_t latchTime) override;
+
+    void setHwcLayerBuffer(DisplayId displayId) override;
+
+private:
+    void onFirstRef() override;
+    bool willPresentCurrentTransaction() const;
+
+    static const std::array<float, 16> IDENTITY_MATRIX;
+
+    std::unique_ptr<renderengine::Image> mTextureImage;
+
+    std::array<float, 16> mTransformMatrix{IDENTITY_MATRIX};
+
+    std::atomic<bool> mSidebandStreamChanged{false};
+
+    uint32_t mFrameNumber{0};
+
+    sp<Fence> mPreviousReleaseFence;
+
+    bool mCurrentStateModified = false;
+    bool mReleasePreviousBuffer = false;
+    nsecs_t mCallbackHandleAcquireTime = -1;
+
+    // TODO(marissaw): support sticky transform for LEGACY camera mode
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayerCache.cpp b/services/surfaceflinger/BufferStateLayerCache.cpp
new file mode 100644
index 0000000..c82ad7b
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayerCache.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2019 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
+#undef LOG_TAG
+#define LOG_TAG "BufferStateLayerCache"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BufferStateLayerCache.h"
+
+#define MAX_CACHE_SIZE 64
+
+namespace android {
+
+int32_t BufferStateLayerCache::add(const sp<IBinder>& processToken,
+                                   const sp<GraphicBuffer>& buffer) {
+    std::lock_guard lock(mMutex);
+
+    auto& processCache = getProccessCache(processToken);
+
+    int32_t slot = findSlot(processCache);
+    if (slot < 0) {
+        return slot;
+    }
+
+    processCache[slot] = buffer;
+
+    return slot;
+}
+
+void BufferStateLayerCache::release(const sp<IBinder>& processToken, int32_t id) {
+    if (id < 0) {
+        ALOGE("invalid buffer id");
+        return;
+    }
+
+    std::lock_guard lock(mMutex);
+    auto& processCache = getProccessCache(processToken);
+
+    if (id >= processCache.size()) {
+        ALOGE("invalid buffer id");
+        return;
+    }
+    processCache[id] = nullptr;
+}
+
+sp<GraphicBuffer> BufferStateLayerCache::get(const sp<IBinder>& processToken, int32_t id) {
+    if (id < 0) {
+        ALOGE("invalid buffer id");
+        return nullptr;
+    }
+
+    std::lock_guard lock(mMutex);
+    auto& processCache = getProccessCache(processToken);
+
+    if (id >= processCache.size()) {
+        ALOGE("invalid buffer id");
+        return nullptr;
+    }
+    return processCache[id];
+}
+
+std::vector<sp<GraphicBuffer>>& BufferStateLayerCache::getProccessCache(
+        const sp<IBinder>& processToken) {
+    return mBuffers[processToken];
+}
+
+int32_t BufferStateLayerCache::findSlot(std::vector<sp<GraphicBuffer>>& processCache) {
+    int32_t slot = 0;
+
+    for (const sp<GraphicBuffer> buffer : processCache) {
+        if (!buffer) {
+            return slot;
+        }
+        slot++;
+    }
+
+    if (processCache.size() < MAX_CACHE_SIZE) {
+        processCache.push_back(nullptr);
+        return slot;
+    }
+
+    return -1;
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/BufferStateLayerCache.h b/services/surfaceflinger/BufferStateLayerCache.h
new file mode 100644
index 0000000..623f0c6
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayerCache.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 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 <android-base/thread_annotations.h>
+#include <binder/IBinder.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
+
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+class BufferStateLayerCache {
+public:
+    int32_t add(const sp<IBinder>& processToken, const sp<GraphicBuffer>& buffer);
+    void release(const sp<IBinder>& processToken, int32_t id);
+
+    sp<GraphicBuffer> get(const sp<IBinder>& processToken, int32_t id);
+
+private:
+    std::mutex mMutex;
+
+    std::vector<sp<GraphicBuffer>>& getProccessCache(const sp<IBinder>& processToken)
+            REQUIRES(mMutex);
+
+    int32_t findSlot(std::vector<sp<GraphicBuffer>>& proccessCache) REQUIRES(mMutex);
+
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& strongPointer) const {
+            return std::hash<IBinder*>{}(strongPointer.get());
+        }
+    };
+
+    std::unordered_map<sp<IBinder> /*caching process*/, std::vector<sp<GraphicBuffer>>, IBinderHash>
+            mBuffers GUARDED_BY(mMutex);
+};
+
+}; // namespace android
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 0b59147..0ca3759 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -35,55 +35,10 @@
 // ---------------------------------------------------------------------------
 
 Client::Client(const sp<SurfaceFlinger>& flinger)
-    : Client(flinger, nullptr)
+    : mFlinger(flinger)
 {
 }
 
-Client::Client(const sp<SurfaceFlinger>& flinger, const sp<Layer>& parentLayer)
-    : mFlinger(flinger),
-      mParentLayer(parentLayer)
-{
-}
-
-Client::~Client()
-{
-    // We need to post a message to remove our remaining layers rather than
-    // do so directly by acquiring the SurfaceFlinger lock. If we were to
-    // attempt to directly call the lock it becomes effectively impossible
-    // to use sp<Client> while holding the SF lock as descoping it could
-    // then trigger a dead-lock.
-
-    const size_t count = mLayers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        sp<Layer> l = mLayers.valueAt(i).promote();
-        if (l == nullptr) {
-            continue;
-        }
-        mFlinger->postMessageAsync(new LambdaMessage([flinger = mFlinger, l]() {
-            flinger->removeLayer(l);
-        }));
-    }
-}
-
-void Client::updateParent(const sp<Layer>& parentLayer) {
-    Mutex::Autolock _l(mLock);
-
-    // If we didn't ever have a parent, then we must instead be
-    // relying on permissions and we never need a parent.
-    if (mParentLayer != nullptr) {
-        mParentLayer = parentLayer;
-    }
-}
-
-sp<Layer> Client::getParentLayer(bool* outParentDied) const {
-    Mutex::Autolock _l(mLock);
-    sp<Layer> parent = mParentLayer.promote();
-    if (outParentDied != nullptr) {
-        *outParentDied = (mParentLayer != nullptr && parent == nullptr);
-    }
-    return parent;
-}
-
 status_t Client::initCheck() const {
     return NO_ERROR;
 }
@@ -118,40 +73,10 @@
     return lbc;
 }
 
-
-status_t Client::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    // these must be checked
-     IPCThreadState* ipc = IPCThreadState::self();
-     const int pid = ipc->getCallingPid();
-     const int uid = ipc->getCallingUid();
-     const int self_pid = getpid();
-     // If we are called from another non root process without the GRAPHICS, SYSTEM, or ROOT
-     // uid we require the sAccessSurfaceFlinger permission.
-     // We grant an exception in the case that the Client has a "parent layer", as its
-     // effects will be scoped to that layer.
-     if (CC_UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != AID_SYSTEM && uid != 0)
-             && (getParentLayer() == nullptr)) {
-         // we're called from a different process, do the real check
-         if (!PermissionCache::checkCallingPermission(sAccessSurfaceFlinger))
-         {
-             ALOGE("Permission Denial: "
-                     "can't openGlobalTransaction pid=%d, uid<=%d", pid, uid);
-             return PERMISSION_DENIED;
-         }
-     }
-     return BnSurfaceComposerClient::onTransact(code, data, reply, flags);
-}
-
-
-status_t Client::createSurface(
-        const String8& name,
-        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-        const sp<IBinder>& parentHandle, int32_t windowType, int32_t ownerUid,
-        sp<IBinder>* handle,
-        sp<IGraphicBufferProducer>* gbp)
-{
+status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
+                               uint32_t flags, const sp<IBinder>& parentHandle,
+                               LayerMetadata metadata, sp<IBinder>* handle,
+                               sp<IGraphicBufferProducer>* gbp) {
     sp<Layer> parent = nullptr;
     if (parentHandle != nullptr) {
         auto layerHandle = reinterpret_cast<Layer::Handle*>(parentHandle.get());
@@ -160,22 +85,29 @@
             return NAME_NOT_FOUND;
         }
     }
-    if (parent == nullptr) {
-        bool parentDied;
-        parent = getParentLayer(&parentDied);
-        // If we had a parent, but it died, we've lost all
-        // our capabilities.
-        if (parentDied) {
-            return NAME_NOT_FOUND;
-        }
-    }
 
-    return mFlinger->createLayer(name, this, w, h, format, flags, windowType,
-                                 ownerUid, handle, gbp, &parent);
+    // We rely on createLayer to check permissions.
+    return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
+                                 &parent);
 }
 
-status_t Client::destroySurface(const sp<IBinder>& handle) {
-    return mFlinger->onLayerRemoved(this, handle);
+status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
+                                         PixelFormat format, uint32_t flags,
+                                         const sp<IGraphicBufferProducer>& parent,
+                                         LayerMetadata metadata, sp<IBinder>* handle,
+                                         sp<IGraphicBufferProducer>* gbp) {
+    if (mFlinger->authenticateSurfaceTexture(parent) == false) {
+        return BAD_VALUE;
+    }
+
+    const auto& layer = (static_cast<MonitoredProducer*>(parent.get()))->getLayer();
+    if (layer == nullptr) {
+        return BAD_VALUE;
+    }
+
+    sp<IBinder> parentHandle = layer->getHandle();
+
+    return createSurface(name, w, h, format, flags, parentHandle, std::move(metadata), handle, gbp);
 }
 
 status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 49437ed..74e4818 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -39,46 +39,38 @@
 {
 public:
     explicit Client(const sp<SurfaceFlinger>& flinger);
-    Client(const sp<SurfaceFlinger>& flinger, const sp<Layer>& parentLayer);
-    ~Client();
+    ~Client() = default;
 
     status_t initCheck() const;
 
     // protected by SurfaceFlinger::mStateLock
     void attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer);
-
     void detachLayer(const Layer* layer);
 
     sp<Layer> getLayerUser(const sp<IBinder>& handle) const;
 
-    void updateParent(const sp<Layer>& parentLayer);
-
 private:
     // ISurfaceComposerClient interface
-    virtual status_t createSurface(
-            const String8& name,
-            uint32_t w, uint32_t h,PixelFormat format, uint32_t flags,
-            const sp<IBinder>& parent, int32_t windowType, int32_t ownerUid,
-            sp<IBinder>* handle,
-            sp<IGraphicBufferProducer>* gbp);
+    virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
+                                   uint32_t flags, const sp<IBinder>& parent,
+                                   LayerMetadata metadata, sp<IBinder>* handle,
+                                   sp<IGraphicBufferProducer>* gbp);
 
-    virtual status_t destroySurface(const sp<IBinder>& handle);
+    virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
+                                             PixelFormat format, uint32_t flags,
+                                             const sp<IGraphicBufferProducer>& parent,
+                                             LayerMetadata metadata, sp<IBinder>* handle,
+                                             sp<IGraphicBufferProducer>* gbp);
 
     virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
 
     virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const;
 
-    virtual status_t onTransact(
-        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
-
-    sp<Layer> getParentLayer(bool* outParentDied = nullptr) const;
-
     // constant
     sp<SurfaceFlinger> mFlinger;
 
     // protected by mLock
     DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayers;
-    wp<Layer> mParentLayer;
 
     // thread-safe
     mutable Mutex mLock;
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index ff957c0..34082c3 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -22,54 +22,43 @@
 #include <stdlib.h>
 #include <sys/types.h>
 
+#include <renderengine/RenderEngine.h>
+#include <ui/GraphicBuffer.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
-#include <ui/GraphicBuffer.h>
-
 #include "ColorLayer.h"
 #include "DisplayDevice.h"
-#include "RenderEngine/RenderEngine.h"
 #include "SurfaceFlinger.h"
 
 namespace android {
 // ---------------------------------------------------------------------------
 
-ColorLayer::ColorLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name,
-                       uint32_t w, uint32_t h, uint32_t flags)
-      : Layer(flinger, client, name, w, h, flags) {
-    // drawing state & current state are identical
-    mDrawingState = mCurrentState;
-}
+ColorLayer::ColorLayer(const LayerCreationArgs& args) : Layer(args) {}
 
-void ColorLayer::onDraw(const RenderArea& renderArea, const Region& /* clip */,
-                        bool useIdentityTransform) const {
-    half4 color = getColor();
-    if (color.a > 0) {
-        Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2);
-        computeGeometry(renderArea, mesh, useIdentityTransform);
-        auto& engine(mFlinger->getRenderEngine());
-        engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */,
-                                  true /* disableTexture */, color);
-        engine.drawMesh(mesh);
-        engine.disableBlending();
-    }
+ColorLayer::~ColorLayer() = default;
+
+bool ColorLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                                    bool useIdentityTransform, Region& clearRegion,
+                                    renderengine::LayerSettings& layer) {
+    Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, layer);
+    half4 color(getColor());
+    half3 solidColor(color.r, color.g, color.b);
+    layer.source.solidColor = solidColor;
+    return true;
 }
 
 bool ColorLayer::isVisible() const {
-    const Layer::State& s(getDrawingState());
-    return !isHiddenByPolicy() && s.color.a;
+    return !isHiddenByPolicy() && getAlpha() > 0.0f;
 }
 
-void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) {
-    const Transform& tr = displayDevice->getTransform();
-    const auto& viewport = displayDevice->getViewport();
-    Region visible = tr.transform(visibleRegion.intersect(viewport));
-    auto hwcId = displayDevice->getHwcDisplayId();
-    if (!hasHwcLayer(hwcId)) {
-        return;
-    }
-    auto& hwcInfo = getBE().mHwcLayers[hwcId];
+void ColorLayer::setPerFrameData(DisplayId displayId, const ui::Transform& transform,
+                                 const Rect& viewport, int32_t /* supportedPerFrameMetadata */) {
+    RETURN_IF_NO_HWC_LAYER(displayId);
+
+    Region visible = transform.transform(visibleRegion.intersect(viewport));
+
+    auto& hwcInfo = getBE().mHwcLayers[displayId];
     auto& hwcLayer = hwcInfo.layer;
     auto error = hwcLayer->setVisibleRegion(visible);
     if (error != HWC2::Error::None) {
@@ -77,14 +66,16 @@
               to_string(error).c_str(), static_cast<int32_t>(error));
         visible.dump(LOG_TAG);
     }
+    getBE().compositionInfo.hwc.visibleRegion = visible;
 
-    setCompositionType(hwcId, HWC2::Composition::SolidColor);
+    setCompositionType(displayId, HWC2::Composition::SolidColor);
 
     error = hwcLayer->setDataspace(mCurrentDataSpace);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
               to_string(error).c_str(), static_cast<int32_t>(error));
     }
+    getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace;
 
     half4 color = getColor();
     error = hwcLayer->setColor({static_cast<uint8_t>(std::round(255.0f * color.r)),
@@ -94,6 +85,9 @@
         ALOGE("[%s] Failed to set color: %s (%d)", mName.string(), to_string(error).c_str(),
               static_cast<int32_t>(error));
     }
+    getBE().compositionInfo.hwc.color = { static_cast<uint8_t>(std::round(255.0f * color.r)),
+                                      static_cast<uint8_t>(std::round(255.0f * color.g)),
+                                      static_cast<uint8_t>(std::round(255.0f * color.b)), 255 };
 
     // Clear out the transform, because it doesn't make sense absent a source buffer
     error = hwcLayer->setTransform(HWC2::Transform::None);
@@ -101,6 +95,22 @@
         ALOGE("[%s] Failed to clear transform: %s (%d)", mName.string(), to_string(error).c_str(),
               static_cast<int32_t>(error));
     }
+    getBE().compositionInfo.hwc.transform = HWC2::Transform::None;
+
+    error = hwcLayer->setColorTransform(getColorTransform());
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
+                to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+    getBE().compositionInfo.hwc.colorTransform = getColorTransform();
+
+    error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        surfaceDamageRegion.dump(LOG_TAG);
+    }
+    getBE().compositionInfo.hwc.surfaceDamage = surfaceDamageRegion;
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
index 0cde398..f43fc3c 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -25,16 +25,22 @@
 
 class ColorLayer : public Layer {
 public:
-    ColorLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w,
-               uint32_t h, uint32_t flags);
-    virtual ~ColorLayer() = default;
+    explicit ColorLayer(const LayerCreationArgs&);
+    ~ColorLayer() override;
 
     virtual const char* getTypeId() const { return "ColorLayer"; }
-    virtual void onDraw(const RenderArea& renderArea, const Region& clip,
-                        bool useIdentityTransform) const;
     bool isVisible() const override;
 
-    void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override;
+    void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport,
+                         int32_t supportedPerFrameMetadata) override;
+
+    bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
+
+protected:
+    FloatRect computeCrop(const sp<const DisplayDevice>& /*display*/) const override { return {}; }
+    virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                                    bool useIdentityTransform, Region& clearRegion,
+                                    renderengine::LayerSettings& layer);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Colorizer.h b/services/surfaceflinger/Colorizer.h
index d56b1c8..b7d61ce 100644
--- a/services/surfaceflinger/Colorizer.h
+++ b/services/surfaceflinger/Colorizer.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_SURFACE_FLINGER_COLORIZER_H
 #define ANDROID_SURFACE_FLINGER_COLORIZER_H
 
-#include <utils/String8.h>
+#include <android-base/stringprintf.h>
 
 namespace android {
 
@@ -40,19 +40,19 @@
         : mEnabled(enabled) {
     }
 
-    void colorize(String8& out, color c) {
+    void colorize(std::string& out, color c) {
         if (mEnabled) {
-            out.appendFormat("\e[%dm", c);
+            base::StringAppendF(&out, "\e[%dm", c);
         }
     }
 
-    void bold(String8& out) {
+    void bold(std::string& out) {
         if (mEnabled) {
             out.append("\e[1m");
         }
     }
 
-    void reset(String8& out) {
+    void reset(std::string& out) {
         if (mEnabled) {
             out.append("\e[0m");
         }
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
new file mode 100644
index 0000000..49fa84a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -0,0 +1,106 @@
+cc_defaults {
+    name: "libcompositionengine_defaults",
+    defaults: ["surfaceflinger_defaults"],
+    cflags: [
+        "-DLOG_TAG=\"CompositionEngine\"",
+    ],
+    shared_libs: [
+        "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.3",
+        "android.hardware.power@1.0",
+        "android.hardware.power@1.3",
+        "libbase",
+        "libcutils",
+        "libgui",
+        "liblayers_proto",
+        "liblog",
+        "libnativewindow",
+        "libsync",
+        "libtimestats_proto",
+        "libui",
+        "libutils",
+    ],
+    static_libs: [
+        "libmath",
+        "librenderengine",
+        "libtrace_proto",
+    ],
+    header_libs: [
+        "libsurfaceflinger_headers",
+    ],
+}
+
+cc_library {
+    name: "libcompositionengine",
+    defaults: ["libcompositionengine_defaults"],
+    srcs: [
+        "src/CompositionEngine.cpp",
+        "src/Display.cpp",
+        "src/DisplayColorProfile.cpp",
+        "src/DisplaySurface.cpp",
+        "src/DumpHelpers.cpp",
+        "src/Output.cpp",
+        "src/OutputCompositionState.cpp",
+        "src/RenderSurface.cpp",
+    ],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libcompositionengine_mocks",
+    defaults: ["libcompositionengine_defaults"],
+    srcs: [
+        "mock/CompositionEngine.cpp",
+        "mock/Display.cpp",
+        "mock/DisplayColorProfile.cpp",
+        "mock/DisplaySurface.cpp",
+        "mock/Output.cpp",
+        "mock/RenderSurface.cpp",
+    ],
+    static_libs: [
+        "libgtest",
+        "libgmock",
+        "libcompositionengine",
+    ],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+}
+
+cc_test {
+    name: "libcompositionengine_test",
+    test_suites: ["device-tests"],
+    defaults: ["libcompositionengine_defaults"],
+    srcs: [
+        "tests/CompositionEngineTest.cpp",
+        "tests/DisplayColorProfileTest.cpp",
+        "tests/DisplayTest.cpp",
+        "tests/MockHWComposer.cpp",
+        "tests/OutputTest.cpp",
+        "tests/RenderSurfaceTest.cpp",
+    ],
+    static_libs: [
+        "libcompositionengine",
+        "libcompositionengine_mocks",
+        "librenderengine_mocks",
+        "libgmock",
+        "libgtest",
+    ],
+    sanitize: {
+        // By using the address sanitizer, we not only uncover any issues
+        // with the test, but also any issues with the code under test.
+        //
+        // Note: If you get an runtime link error like:
+        //
+        //   CANNOT LINK EXECUTABLE "/data/local/tmp/libcompositionengine_test": library "libclang_rt.asan-aarch64-android.so" not found
+        //
+        // it is because the address sanitizer shared objects are not installed
+        // by default in the system image.
+        //
+        // You can either "make dist tests" before flashing, or set this
+        // option to false temporarily.
+        address: true,
+    },
+}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
new file mode 100644
index 0000000..30cdb49
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+namespace android {
+
+class HWComposer;
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine {
+
+class Display;
+
+struct DisplayCreationArgs;
+
+/**
+ * Encapsulates all the interfaces and implementation details for performing
+ * display output composition.
+ */
+class CompositionEngine {
+public:
+    virtual ~CompositionEngine();
+
+    // Create a composition Display
+    virtual std::shared_ptr<Display> createDisplay(DisplayCreationArgs&&) = 0;
+
+    virtual HWComposer& getHwComposer() const = 0;
+    virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
+
+    virtual renderengine::RenderEngine& getRenderEngine() const = 0;
+    virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
new file mode 100644
index 0000000..dbcd3bd
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 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 <cstdint>
+#include <optional>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+#include <compositionengine/Output.h>
+
+namespace android::compositionengine {
+
+struct RenderSurfaceCreationArgs;
+struct DisplayColorProfileCreationArgs;
+
+/**
+ * A display is a composition target which may be backed by a hardware composer
+ * display device
+ */
+class Display : public virtual Output {
+public:
+    // Gets the HWC DisplayId for the display if there is one
+    virtual const std::optional<DisplayId>& getId() const = 0;
+
+    // True if the display is secure
+    virtual bool isSecure() const = 0;
+
+    // True if the display is virtual
+    virtual bool isVirtual() const = 0;
+
+    // Releases the use of the HWC display, if any
+    virtual void disconnect() = 0;
+
+    // Creates a render color mode for the display
+    virtual void createDisplayColorProfile(DisplayColorProfileCreationArgs&&) = 0;
+
+    // Creates a render surface for the display
+    virtual void createRenderSurface(RenderSurfaceCreationArgs&&) = 0;
+
+protected:
+    ~Display() = default;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
new file mode 100644
index 0000000..e2a0d42
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 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 <cstdint>
+
+#include <ui/GraphicTypes.h>
+
+namespace android {
+
+class HdrCapabilities;
+
+namespace compositionengine {
+
+/**
+ * Encapsulates all the state and functionality for how colors should be
+ * transformed for a display
+ */
+class DisplayColorProfile {
+public:
+    constexpr static float sDefaultMinLumiance = 0.0;
+    constexpr static float sDefaultMaxLumiance = 500.0;
+
+    virtual ~DisplayColorProfile();
+
+    // Returns true if the profile is valid. This is meant to be checked post-
+    // construction and prior to use, as not everything is set up by the
+    // constructor.
+    virtual bool isValid() const = 0;
+
+    // Returns true if the profile supports the indicated render intent
+    virtual bool hasRenderIntent(ui::RenderIntent) const = 0;
+
+    // Returns true if the profile supports the indicated dataspace
+    virtual bool hasLegacyHdrSupport(ui::Dataspace) const = 0;
+
+    // Obtains the best combination of color mode and render intent for the
+    // input values
+    virtual void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
+                                  ui::Dataspace* outDataspace, ui::ColorMode* outMode,
+                                  ui::RenderIntent* outIntent) const = 0;
+
+    // Returns true if the profile supports a wide color gamut
+    virtual bool hasWideColorGamut() const = 0;
+
+    // Returns the per-frame metadata value for this profile
+    virtual int32_t getSupportedPerFrameMetadata() const = 0;
+
+    // Returns true if HWC for this profile supports HDR10Plus
+    virtual bool hasHDR10PlusSupport() const = 0;
+
+    // Returns true if HWC for this profile supports HDR10
+    virtual bool hasHDR10Support() const = 0;
+
+    // Returns true if HWC for this profile supports HLG
+    virtual bool hasHLGSupport() const = 0;
+
+    // Returns true if HWC for this profile supports DolbyVision
+    virtual bool hasDolbyVisionSupport() const = 0;
+
+    // Gets the supported HDR capabilities for the profile
+    virtual const HdrCapabilities& getHdrCapabilities() const = 0;
+
+    // Debugging
+    virtual void dump(std::string&) const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
new file mode 100644
index 0000000..ef0f925
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2019 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 <cstdint>
+#include <unordered_map>
+#include <vector>
+
+#include <ui/GraphicTypes.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine {
+
+/**
+ * A parameter object for creating DisplayColorProfile instances
+ */
+struct DisplayColorProfileCreationArgs {
+    using HwcColorModes = std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>;
+
+    // True if this display supports a wide color gamut
+    bool hasWideColorGamut;
+
+    // The HDR capabilities supported by the HWC
+    HdrCapabilities hdrCapabilities;
+
+    // The per-frame metadata supported by the HWC
+    int32_t supportedPerFrameMetadata;
+
+    // The mapping of color modes and render intents supported by the HWC
+    HwcColorModes hwcColorModes;
+};
+
+/**
+ * A helper for setting up a DisplayColorProfileCreationArgs value in-line.
+ *
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ *   DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
+ *                                   HwcColorModes()}
+ *
+ * Prefer:
+ *
+ *  DisplayColorProfileCreationArgsBuilder().setHasWideColorGamut(false)
+ *      .setIsVirtual(false).setDisplayId(displayId).Build();
+ */
+class DisplayColorProfileCreationArgsBuilder {
+public:
+    DisplayColorProfileCreationArgs Build() { return std::move(mArgs); }
+
+    DisplayColorProfileCreationArgsBuilder& setHasWideColorGamut(bool hasWideColorGamut) {
+        mArgs.hasWideColorGamut = hasWideColorGamut;
+        return *this;
+    }
+    DisplayColorProfileCreationArgsBuilder& setHdrCapabilities(HdrCapabilities&& hdrCapabilities) {
+        mArgs.hdrCapabilities = std::move(hdrCapabilities);
+        return *this;
+    }
+    DisplayColorProfileCreationArgsBuilder& setSupportedPerFrameMetadata(
+            int32_t supportedPerFrameMetadata) {
+        mArgs.supportedPerFrameMetadata = supportedPerFrameMetadata;
+        return *this;
+    }
+    DisplayColorProfileCreationArgsBuilder& setHwcColorModes(
+            DisplayColorProfileCreationArgs::HwcColorModes&& hwcColorModes) {
+        mArgs.hwcColorModes = std::move(hwcColorModes);
+        return *this;
+    }
+
+private:
+    DisplayColorProfileCreationArgs mArgs;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
new file mode 100644
index 0000000..0b6b4e4
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019 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 <cstdint>
+#include <optional>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+/**
+ * A parameter object for creating Display instances
+ */
+struct DisplayCreationArgs {
+    // True if this display is secure
+    bool isSecure = false;
+
+    // True if this display is a virtual display
+    bool isVirtual = false;
+
+    // Identifies the display to the HWC, if composition is supported by it
+    std::optional<DisplayId> displayId;
+};
+
+/**
+ * A helper for setting up a DisplayCreationArgs value in-line.
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ *   DisplayCreationArgs{false, false, displayId}
+ *
+ * Prefer:
+ *
+ *  DisplayCreationArgsBuilder().setIsSecure(false).setIsVirtual(false)
+ *      .setDisplayId(displayId).build();
+ */
+class DisplayCreationArgsBuilder {
+public:
+    DisplayCreationArgs build() { return std::move(mArgs); }
+
+    DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
+        mArgs.isSecure = isSecure;
+        return *this;
+    }
+    DisplayCreationArgsBuilder& setIsVirtual(bool isVirtual) {
+        mArgs.isVirtual = isVirtual;
+        return *this;
+    }
+    DisplayCreationArgsBuilder& setDisplayId(std::optional<DisplayId> displayId) {
+        mArgs.displayId = displayId;
+        return *this;
+    }
+
+private:
+    DisplayCreationArgs mArgs;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
similarity index 80%
rename from services/surfaceflinger/DisplayHardware/DisplaySurface.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index f744f5c..0e67acf 100644
--- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -14,23 +14,27 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SF_DISPLAY_SURFACE_H
-#define ANDROID_SF_DISPLAY_SURFACE_H
+#pragma once
 
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
 
-// ---------------------------------------------------------------------------
 namespace android {
-// ---------------------------------------------------------------------------
 
 class Fence;
 class IGraphicBufferProducer;
 class String8;
 
+namespace compositionengine {
+
+/**
+ * An abstraction for working with a display surface (buffer queue)
+ */
 class DisplaySurface : public virtual RefBase {
 public:
+    virtual ~DisplaySurface();
+
     // beginFrame is called at the beginning of the composition loop, before
     // the configuration is known. The DisplaySurface should do anything it
     // needs to do to enable HWComposer to decide how to compose the frame.
@@ -44,9 +48,9 @@
     // GLES and HWC for this frame.
     enum CompositionType {
         COMPOSITION_UNKNOWN = 0,
-        COMPOSITION_GLES    = 1,
-        COMPOSITION_HWC     = 2,
-        COMPOSITION_MIXED   = COMPOSITION_GLES | COMPOSITION_HWC
+        COMPOSITION_GLES = 1,
+        COMPOSITION_HWC = 2,
+        COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC
     };
     virtual status_t prepareFrame(CompositionType compositionType) = 0;
 
@@ -70,15 +74,7 @@
     virtual void resizeBuffers(const uint32_t w, const uint32_t h) = 0;
 
     virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
-
-protected:
-    DisplaySurface() {}
-    virtual ~DisplaySurface() {}
 };
 
-// ---------------------------------------------------------------------------
+} // namespace compositionengine
 } // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_SF_DISPLAY_SURFACE_H
-
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
new file mode 100644
index 0000000..6be4ad8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2019 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 <cstdint>
+#include <string>
+
+#include <math/mat4.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android::compositionengine {
+
+class DisplayColorProfile;
+class RenderSurface;
+
+namespace impl {
+struct OutputCompositionState;
+} // namespace impl
+
+/**
+ * Encapsulates all the state involved with composing layers for an output
+ */
+class Output {
+public:
+    // Returns true if the output is valid. This is meant to be checked post-
+    // construction and prior to use, as not everything is set up by the
+    // constructor.
+    virtual bool isValid() const = 0;
+
+    // Enables (or disables) composition on this output
+    virtual void setCompositionEnabled(bool) = 0;
+
+    // Sets the projection state to use
+    virtual void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+                               const Rect& viewport, const Rect& scissor, bool needsFiltering) = 0;
+    // Sets the bounds to use
+    virtual void setBounds(const ui::Size&) = 0;
+
+    // Sets the layer stack filtering settings for this output. See
+    // belongsInOutput for full details.
+    virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0;
+
+    // Sets the color transform matrix to use
+    virtual void setColorTransform(const mat4&) = 0;
+
+    // Sets the output color mode
+    virtual void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) = 0;
+
+    // Outputs a string with a state dump
+    virtual void dump(std::string&) const = 0;
+
+    // Gets the debug name for the output
+    virtual const std::string& getName() const = 0;
+
+    // Sets a debug name for the output
+    virtual void setName(const std::string&) = 0;
+
+    // Gets the current render color mode for the output
+    virtual DisplayColorProfile* getDisplayColorProfile() const = 0;
+
+    // Gets the current render surface for the output
+    virtual RenderSurface* getRenderSurface() const = 0;
+
+    using OutputCompositionState = compositionengine::impl::OutputCompositionState;
+
+    // Gets the raw composition state data for the output
+    // TODO(lpique): Make this protected once it is only internally called.
+    virtual const OutputCompositionState& getState() const = 0;
+
+    // Allows mutable access to the raw composition state data for the output.
+    // This is meant to be used by the various functions that are part of the
+    // composition process.
+    // TODO(lpique): Make this protected once it is only internally called.
+    virtual OutputCompositionState& editState() = 0;
+
+    // Gets the dirty region in layer stack space.
+    // If repaintEverything is true, this will be the full display bounds.
+    virtual Region getDirtyRegion(bool repaintEverything) const = 0;
+
+    // Tests whether a given layerStackId belongs in this output.
+    // A layer belongs to the output if its layerStackId matches the of the output layerStackId,
+    // unless the layer should display on the primary output only and this is not the primary output
+
+    // A layer belongs to the output if its layerStackId matches. Additionally
+    // if the layer should only show in the internal (primary) display only and
+    // this output allows that.
+    virtual bool belongsInOutput(uint32_t layerStackId, bool internalOnly) const = 0;
+
+protected:
+    ~Output() = default;
+
+    virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
+    virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
new file mode 100644
index 0000000..ddeb730
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 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 <cstdint>
+#include <vector>
+
+#include <ui/Fence.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Size.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class GraphicBuffer;
+
+struct CompositionInfo;
+
+namespace compositionengine {
+
+/**
+ * Encapsulates everything for composing to a render surface with RenderEngine
+ */
+class RenderSurface {
+public:
+    virtual ~RenderSurface();
+
+    // Returns true if the render surface is valid. This is meant to be checked
+    // post-construction and prior to use, as not everything is set up by the
+    // constructor.
+    virtual bool isValid() const = 0;
+
+    // Performs one-time initialization of the render surface. This is meant
+    // to be called after the validation check.
+    virtual void initialize() = 0;
+
+    // Returns the bounds of the surface
+    virtual const ui::Size& getSize() const = 0;
+
+    // Gets the latest fence to pass to the HWC to signal that the surface
+    // buffer is done rendering
+    virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
+
+    // Sets the size of the surface
+    virtual void setDisplaySize(const ui::Size&) = 0;
+
+    // Sets the dataspace used for rendering the surface
+    virtual void setBufferDataspace(ui::Dataspace) = 0;
+
+    // Configures the protected rendering on the surface
+    virtual void setProtected(bool useProtected) = 0;
+
+    // Called to signal that rendering has started. 'mustRecompose' should be
+    // true if the entire frame must be recomposed.
+    virtual status_t beginFrame(bool mustRecompose) = 0;
+
+    // Prepares the frame for rendering
+    virtual status_t prepareFrame(std::vector<CompositionInfo>& compositionData) = 0;
+
+    // Allocates a buffer as scratch space for GPU composition
+    virtual sp<GraphicBuffer> dequeueBuffer() = 0;
+
+    // Queues the drawn buffer for consumption by HWC. readyFence is the fence
+    // which will fire when the buffer is ready for consumption.
+    virtual void queueBuffer(base::unique_fd&& readyFence) = 0;
+
+    // Called after the HWC calls are made to present the display
+    virtual void onPresentDisplayCompleted() = 0;
+
+    // Called to set the viewport and projection state for rendering into this
+    // surface
+    virtual void setViewportAndProjection() = 0;
+
+    // Called after the surface has been rendering to signal the surface should
+    // be made ready for displaying
+    virtual void flip() = 0;
+
+    // Debugging - Dumps the state of the RenderSurface to a string
+    virtual void dump(std::string& result) const = 0;
+
+    // Debugging - gets the page flip count for the RenderSurface
+    virtual std::uint32_t getPageFlipCount() const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
new file mode 100644
index 0000000..a1230b3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2019 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 <cstdint>
+#include <memory>
+
+#include <compositionengine/DisplaySurface.h>
+#include <utils/StrongPointer.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+namespace compositionengine {
+
+class Display;
+
+/**
+ * A parameter object for creating RenderSurface instances
+ */
+struct RenderSurfaceCreationArgs {
+    // The initial width of the surface
+    int32_t displayWidth;
+
+    // The initial height of the surface
+    int32_t displayHeight;
+
+    // The ANativeWindow for the buffer queue for this surface
+    sp<ANativeWindow> nativeWindow;
+
+    // The DisplaySurface for this surface
+    sp<DisplaySurface> displaySurface;
+};
+
+/**
+ * A helper for setting up a RenderSurfaceCreationArgs value in-line.
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ *   RenderSurfaceCreationArgs{1000, 1000, nativeWindow, displaySurface}
+ *
+ * Prefer:
+ *
+ *  RenderSurfaceCreationArgsBuilder().setDisplayWidth(1000).setDisplayHeight(1000)
+ *      .setNativeWindow(nativeWindow).setDisplaySurface(displaySurface).Build();
+ */
+class RenderSurfaceCreationArgsBuilder {
+public:
+    RenderSurfaceCreationArgs build() { return std::move(mArgs); }
+
+    RenderSurfaceCreationArgsBuilder& setDisplayWidth(int32_t displayWidth) {
+        mArgs.displayWidth = displayWidth;
+        return *this;
+    }
+    RenderSurfaceCreationArgsBuilder& setDisplayHeight(int32_t displayHeight) {
+        mArgs.displayHeight = displayHeight;
+        return *this;
+    }
+    RenderSurfaceCreationArgsBuilder& setNativeWindow(sp<ANativeWindow> nativeWindow) {
+        mArgs.nativeWindow = nativeWindow;
+        return *this;
+    }
+    RenderSurfaceCreationArgsBuilder& setDisplaySurface(sp<DisplaySurface> displaySurface) {
+        mArgs.displaySurface = displaySurface;
+        return *this;
+    }
+
+private:
+    RenderSurfaceCreationArgs mArgs;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
new file mode 100644
index 0000000..e23c431
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <compositionengine/CompositionEngine.h>
+
+namespace android::compositionengine::impl {
+
+class CompositionEngine : public compositionengine::CompositionEngine {
+public:
+    CompositionEngine();
+    ~CompositionEngine() override;
+
+    std::shared_ptr<compositionengine::Display> createDisplay(
+            compositionengine::DisplayCreationArgs&&) override;
+
+    HWComposer& getHwComposer() const override;
+    void setHwComposer(std::unique_ptr<HWComposer>) override;
+
+    renderengine::RenderEngine& getRenderEngine() const override;
+    void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override;
+
+private:
+    std::unique_ptr<HWComposer> mHwComposer;
+    std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+};
+
+std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine();
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
new file mode 100644
index 0000000..0e20c43
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 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 <memory>
+
+#include <compositionengine/Display.h>
+#include <compositionengine/impl/Output.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+struct DisplayCreationArgs;
+
+namespace impl {
+
+class Display : public compositionengine::impl::Output, public compositionengine::Display {
+public:
+    Display(const CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+    virtual ~Display();
+
+    // compositionengine::Output overrides
+    void dump(std::string&) const override;
+    void setColorTransform(const mat4&) override;
+    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+
+    // compositionengine::Display overrides
+    const std::optional<DisplayId>& getId() const override;
+    bool isSecure() const override;
+    bool isVirtual() const override;
+    void disconnect() override;
+    void createDisplayColorProfile(compositionengine::DisplayColorProfileCreationArgs&&) override;
+    void createRenderSurface(compositionengine::RenderSurfaceCreationArgs&&) override;
+
+private:
+    const bool mIsVirtual;
+    std::optional<DisplayId> mId;
+};
+
+std::shared_ptr<compositionengine::Display> createDisplay(
+        const compositionengine::CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
new file mode 100644
index 0000000..49c2d2c
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2019 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 <cstdint>
+#include <unordered_map>
+#include <vector>
+
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+class Display;
+
+namespace impl {
+
+class DisplayColorProfile : public compositionengine::DisplayColorProfile {
+public:
+    DisplayColorProfile(DisplayColorProfileCreationArgs&&);
+    ~DisplayColorProfile() override;
+
+    bool isValid() const override;
+
+    bool hasRenderIntent(ui::RenderIntent intent) const override;
+    bool hasLegacyHdrSupport(ui::Dataspace dataspace) const override;
+    void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
+                          ui::Dataspace* outDataspace, ui::ColorMode* outMode,
+                          ui::RenderIntent* outIntent) const override;
+
+    bool hasWideColorGamut() const override;
+    int32_t getSupportedPerFrameMetadata() const override;
+
+    // Whether h/w composer has native support for specific HDR type.
+    bool hasHDR10PlusSupport() const override;
+    bool hasHDR10Support() const override;
+    bool hasHLGSupport() const override;
+    bool hasDolbyVisionSupport() const override;
+
+    const HdrCapabilities& getHdrCapabilities() const override;
+
+    void dump(std::string&) const override;
+
+private:
+    void populateColorModes(const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes);
+    void addColorMode(const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes,
+                      const ui::ColorMode mode, const ui::RenderIntent intent);
+
+    // Mappings from desired Dataspace/RenderIntent to the supported
+    // Dataspace/ColorMode/RenderIntent.
+    using ColorModeKey = uint64_t;
+    struct ColorModeValue {
+        ui::Dataspace dataspace;
+        ui::ColorMode colorMode;
+        ui::RenderIntent renderIntent;
+    };
+
+    static ColorModeKey getColorModeKey(ui::Dataspace dataspace, ui::RenderIntent intent) {
+        return (static_cast<uint64_t>(dataspace) << 32) | static_cast<uint32_t>(intent);
+    }
+
+    // Need to know if display is wide-color capable or not.
+    // Initialized by SurfaceFlinger when the DisplayDevice is created.
+    // Fed to RenderEngine during composition.
+    bool mHasWideColorGamut{false};
+    int32_t mSupportedPerFrameMetadata{0};
+    bool mHasHdr10Plus{false};
+    bool mHasHdr10{false};
+    bool mHasHLG{false};
+    bool mHasDolbyVision{false};
+    HdrCapabilities mHdrCapabilities;
+    std::unordered_map<ColorModeKey, ColorModeValue> mColorModes;
+};
+
+std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
+        DisplayColorProfileCreationArgs&&);
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
new file mode 100644
index 0000000..782c8d7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 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 <cstdint>
+#include <string>
+#include <type_traits>
+
+#include <math/mat4.h>
+#include <ui/FloatRect.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android::compositionengine::impl {
+
+void dumpVal(std::string& out, const char* name, bool);
+void dumpVal(std::string& out, const char* name, const void*);
+void dumpVal(std::string& out, const char* name, int);
+void dumpVal(std::string& out, const char* name, float);
+void dumpVal(std::string& out, const char* name, uint32_t);
+void dumpHex(std::string& out, const char* name, uint64_t);
+void dumpVal(std::string& out, const char* name, const char* value);
+void dumpVal(std::string& out, const char* name, const std::string& value);
+
+// For enums with named values
+void dumpVal(std::string& out, const char* name, const char*, int);
+void dumpVal(std::string& out, const char* name, const std::string&, int);
+
+template <typename EnumType>
+void dumpVal(std::string& out, const char* name, const char* valueName, EnumType value) {
+    dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
+}
+
+template <typename EnumType>
+void dumpVal(std::string& out, const char* name, const std::string& valueName, EnumType value) {
+    dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
+}
+
+void dumpVal(std::string& out, const char* name, const FloatRect& rect);
+void dumpVal(std::string& out, const char* name, const Rect& rect);
+void dumpVal(std::string& out, const char* name, const Region& region);
+void dumpVal(std::string& out, const char* name, const ui::Transform&);
+void dumpVal(std::string& out, const char* name, const ui::Size&);
+
+void dumpVal(std::string& out, const char* name, const mat4& tr);
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
new file mode 100644
index 0000000..4be0706
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019 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 <memory>
+
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+namespace impl {
+
+class Output : public virtual compositionengine::Output {
+public:
+    Output(const CompositionEngine&);
+    virtual ~Output();
+
+    bool isValid() const override;
+
+    void setCompositionEnabled(bool) override;
+    void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+                       const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
+    void setBounds(const ui::Size&) override;
+    void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
+
+    void setColorTransform(const mat4&) override;
+    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+
+    void dump(std::string&) const override;
+
+    const std::string& getName() const override;
+    void setName(const std::string&) override;
+
+    compositionengine::DisplayColorProfile* getDisplayColorProfile() const override;
+    void setDisplayColorProfile(std::unique_ptr<compositionengine::DisplayColorProfile>) override;
+
+    compositionengine::RenderSurface* getRenderSurface() const override;
+    void setRenderSurface(std::unique_ptr<compositionengine::RenderSurface>) override;
+
+    const OutputCompositionState& getState() const override;
+    OutputCompositionState& editState() override;
+
+    Region getDirtyRegion(bool repaintEverything) const override;
+    bool belongsInOutput(uint32_t, bool) const override;
+
+    // Testing
+    void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
+    void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
+
+protected:
+    const CompositionEngine& getCompositionEngine() const;
+    void dumpBase(std::string&) const;
+
+private:
+    void dirtyEntireOutput();
+
+    const CompositionEngine& mCompositionEngine;
+
+    std::string mName;
+
+    OutputCompositionState mState;
+
+    std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile;
+    std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
+};
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
new file mode 100644
index 0000000..024ed45
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2019 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 <cstdint>
+
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android {
+
+namespace compositionengine::impl {
+
+struct OutputCompositionState {
+    // If false, composition will not per performed for this display
+    bool isEnabled{false};
+
+    // If false, this output is not considered secure
+    bool isSecure{false};
+
+    // If true, this output displays layers that are internal-only
+    bool layerStackInternal{false};
+
+    // The layer stack to display on this display
+    uint32_t layerStackId{~0u};
+
+    // The physical space screen bounds
+    Rect bounds;
+
+    // The logical to physical transformation to use
+    ui::Transform transform;
+
+    // The physical orientation of the display, expressed as ui::Transform
+    // orientation flags.
+    uint32_t orientation{0};
+
+    // The logical space user visible bounds
+    Rect frame;
+
+    // The logical space user viewport rectangle
+    Rect viewport;
+
+    // The physical space scissor rectangle
+    Rect scissor;
+
+    // If true, RenderEngine filtering should be enabled
+    bool needsFiltering{false};
+
+    // The logical coordinates for the dirty region for the display.
+    // dirtyRegion is semi-persistent state. Dirty rectangles are added to it
+    // by the FE until composition happens, at which point it is cleared.
+    Region dirtyRegion;
+
+    // The logical coordinates for the undefined region for the display.
+    // The undefined region is internal to the composition engine. It is
+    // updated every time the geometry changes.
+    Region undefinedRegion;
+
+    // True if the last composition frame had visible layers
+    bool lastCompositionHadVisibleLayers{false};
+
+    // The color transform to apply
+    android_color_transform_t colorTransform{HAL_COLOR_TRANSFORM_IDENTITY};
+
+    // Current active color mode
+    ui::ColorMode colorMode{ui::ColorMode::NATIVE};
+
+    // Current active render intent
+    ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
+
+    // Current active dstaspace
+    ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+
+    // Debugging
+    void dump(std::string& result) const;
+};
+
+} // namespace compositionengine::impl
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
new file mode 100644
index 0000000..2f0fceb
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2019 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 <memory>
+
+#include <android-base/unique_fd.h>
+#include <compositionengine/RenderSurface.h>
+#include <utils/StrongPointer.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+namespace compositionengine {
+
+class CompositionEngine;
+class Display;
+class DisplaySurface;
+
+struct RenderSurfaceCreationArgs;
+
+namespace impl {
+
+class RenderSurface : public compositionengine::RenderSurface {
+public:
+    RenderSurface(const CompositionEngine&, compositionengine::Display&,
+                  compositionengine::RenderSurfaceCreationArgs&&);
+    ~RenderSurface() override;
+
+    bool isValid() const override;
+    void initialize() override;
+    const ui::Size& getSize() const override;
+
+    const sp<Fence>& getClientTargetAcquireFence() const override;
+    void setBufferDataspace(ui::Dataspace) override;
+    void setDisplaySize(const ui::Size&) override;
+    void setProtected(bool useProtected) override;
+    status_t beginFrame(bool mustRecompose) override;
+    status_t prepareFrame(std::vector<CompositionInfo>& compositionData) override;
+    sp<GraphicBuffer> dequeueBuffer() override;
+    void queueBuffer(base::unique_fd&& readyFence) override;
+    void onPresentDisplayCompleted() override;
+    void setViewportAndProjection() override;
+    void flip() override;
+
+    // Debugging
+    void dump(std::string& result) const override;
+    std::uint32_t getPageFlipCount() const override;
+
+    // Testing
+    void setPageFlipCountForTest(std::uint32_t);
+    void setSizeForTest(const ui::Size&);
+    sp<GraphicBuffer>& mutableGraphicBufferForTest();
+    base::unique_fd& mutableBufferReadyForTest();
+
+private:
+    const compositionengine::CompositionEngine& mCompositionEngine;
+    const compositionengine::Display& mDisplay;
+
+    // ANativeWindow being rendered into
+    const sp<ANativeWindow> mNativeWindow;
+    // Current buffer being rendered into
+    sp<GraphicBuffer> mGraphicBuffer;
+    const sp<DisplaySurface> mDisplaySurface;
+    ui::Size mSize;
+    std::uint32_t mPageFlipCount{0};
+};
+
+std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
+        const compositionengine::CompositionEngine&, compositionengine::Display&,
+        compositionengine::RenderSurfaceCreationArgs&&);
+
+} // namespace impl
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
new file mode 100644
index 0000000..1967666
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <gmock/gmock.h>
+#include <renderengine/RenderEngine.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine::mock {
+
+class CompositionEngine : public compositionengine::CompositionEngine {
+public:
+    CompositionEngine();
+    ~CompositionEngine() override;
+
+    MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(DisplayCreationArgs&&));
+
+    MOCK_CONST_METHOD0(getHwComposer, HWComposer&());
+    MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
+
+    MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
+    MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
new file mode 100644
index 0000000..d763aa6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 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 <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/mock/Output.h>
+#include <gmock/gmock.h>
+#include <system/window.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine::mock {
+
+class Display : public compositionengine::mock::Output, public compositionengine::Display {
+public:
+    Display();
+    virtual ~Display();
+
+    MOCK_CONST_METHOD0(getId, const std::optional<DisplayId>&());
+    MOCK_CONST_METHOD0(isSecure, bool());
+    MOCK_CONST_METHOD0(isVirtual, bool());
+
+    MOCK_METHOD0(disconnect, void());
+
+    MOCK_METHOD1(createDisplayColorProfile, void(DisplayColorProfileCreationArgs&&));
+    MOCK_METHOD1(createRenderSurface, void(RenderSurfaceCreationArgs&&));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
new file mode 100644
index 0000000..8056c9d
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 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 <compositionengine/DisplayColorProfile.h>
+#include <gmock/gmock.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine::mock {
+
+class DisplayColorProfile : public compositionengine::DisplayColorProfile {
+public:
+    DisplayColorProfile();
+    ~DisplayColorProfile() override;
+
+    MOCK_CONST_METHOD0(isValid, bool());
+
+    MOCK_CONST_METHOD1(hasRenderIntent, bool(ui::RenderIntent));
+    MOCK_CONST_METHOD1(hasLegacyHdrSupport, bool(ui::Dataspace));
+    MOCK_CONST_METHOD5(getBestColorMode,
+                       void(ui::Dataspace, ui::RenderIntent, ui::Dataspace*, ui::ColorMode*,
+                            ui::RenderIntent*));
+    MOCK_CONST_METHOD0(hasWideColorGamut, bool());
+    MOCK_CONST_METHOD0(getSupportedPerFrameMetadata, int32_t());
+    MOCK_CONST_METHOD0(hasHDR10PlusSupport, bool());
+    MOCK_CONST_METHOD0(hasHDR10Support, bool());
+    MOCK_CONST_METHOD0(hasHLGSupport, bool());
+    MOCK_CONST_METHOD0(hasDolbyVisionSupport, bool());
+
+    MOCK_CONST_METHOD0(getHdrCapabilities, const HdrCapabilities&());
+
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
similarity index 81%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
index d6c9aa4..31b5f95 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,16 +16,13 @@
 
 #pragma once
 
+#include <compositionengine/DisplaySurface.h>
 #include <gmock/gmock.h>
-
 #include <utils/String8.h>
 
-#include "DisplayHardware/DisplaySurface.h"
+namespace android::compositionengine::mock {
 
-namespace android {
-namespace mock {
-
-class DisplaySurface : public android::DisplaySurface {
+class DisplaySurface : public compositionengine::DisplaySurface {
 public:
     DisplaySurface();
     ~DisplaySurface() override;
@@ -39,5 +36,4 @@
     MOCK_CONST_METHOD0(getClientTargetAcquireFence, const sp<Fence>&());
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
new file mode 100644
index 0000000..e767ad3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 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 <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/Output.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <gmock/gmock.h>
+
+namespace android::compositionengine::mock {
+
+class Output : public virtual compositionengine::Output {
+public:
+    Output();
+    virtual ~Output();
+
+    MOCK_CONST_METHOD0(isValid, bool());
+
+    MOCK_METHOD1(setCompositionEnabled, void(bool));
+    MOCK_METHOD6(setProjection,
+                 void(const ui::Transform&, int32_t, const Rect&, const Rect&, const Rect&, bool));
+    MOCK_METHOD1(setBounds, void(const ui::Size&));
+    MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
+
+    MOCK_METHOD1(setColorTransform, void(const mat4&));
+    MOCK_METHOD3(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent));
+
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+    MOCK_CONST_METHOD0(getName, const std::string&());
+    MOCK_METHOD1(setName, void(const std::string&));
+
+    MOCK_CONST_METHOD0(getDisplayColorProfile, DisplayColorProfile*());
+    MOCK_METHOD1(setDisplayColorProfile, void(std::unique_ptr<DisplayColorProfile>));
+
+    MOCK_CONST_METHOD0(getRenderSurface, RenderSurface*());
+    MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<RenderSurface>));
+
+    MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
+    MOCK_METHOD0(editState, OutputCompositionState&());
+
+    MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+    MOCK_CONST_METHOD2(belongsInOutput, bool(uint32_t, bool));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
new file mode 100644
index 0000000..e9ff330
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 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 <compositionengine/RenderSurface.h>
+#include <gmock/gmock.h>
+#include <ui/GraphicBuffer.h>
+
+#include "LayerBE.h"
+
+namespace android::compositionengine::mock {
+
+class RenderSurface : public compositionengine::RenderSurface {
+public:
+    RenderSurface();
+    ~RenderSurface() override;
+
+    MOCK_CONST_METHOD0(isValid, bool());
+    MOCK_METHOD0(initialize, void());
+    MOCK_CONST_METHOD0(getSize, const ui::Size&());
+    MOCK_CONST_METHOD0(getClientTargetAcquireFence, const sp<Fence>&());
+    MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
+    MOCK_METHOD1(setProtected, void(bool));
+    MOCK_METHOD1(setBufferDataspace, void(ui::Dataspace));
+    MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
+    MOCK_METHOD1(prepareFrame, status_t(std::vector<CompositionInfo>& compositionData));
+    MOCK_METHOD0(dequeueBuffer, sp<GraphicBuffer>());
+    MOCK_METHOD1(queueBuffer, void(base::unique_fd&&));
+    MOCK_METHOD0(onPresentDisplayCompleted, void());
+    MOCK_METHOD0(setViewportAndProjection, void());
+    MOCK_METHOD0(flip, void());
+    MOCK_CONST_METHOD1(dump, void(std::string& result));
+    MOCK_CONST_METHOD0(getPageFlipCount, std::uint32_t());
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/mock/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/mock/CompositionEngine.cpp
new file mode 100644
index 0000000..778a09e
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/mock/CompositionEngine.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/mock/CompositionEngine.h>
+
+namespace android::compositionengine::mock {
+
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+CompositionEngine::CompositionEngine() = default;
+CompositionEngine::~CompositionEngine() = default;
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/Display.cpp
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/Display.cpp
index e6ac6bf..01cf112 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Display.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/Display.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Display::Display() = default;
+Display::~Display() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
similarity index 66%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
index e6ac6bf..581d1f7 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * Copyright 2019 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
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/DisplayColorProfile.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
 // Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+DisplayColorProfile::DisplayColorProfile() = default;
+DisplayColorProfile::~DisplayColorProfile() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
index e6ac6bf..bbbd7c1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "compositionengine/mock/DisplaySurface.h"
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
-// Explicit default instantiation is recommended.
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
 DisplaySurface::DisplaySurface() = default;
 DisplaySurface::~DisplaySurface() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/Output.cpp
index e6ac6bf..44df4c3 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/Output.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Output::Output() = default;
+Output::~Output() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
similarity index 68%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
index e6ac6bf..fe718d6 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * Copyright 2019 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
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/RenderSurface.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
 // Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+RenderSurface::RenderSurface() = default;
+RenderSurface::~RenderSurface() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
new file mode 100644
index 0000000..127d729
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/CompositionEngine.h>
+#include <compositionengine/impl/Display.h>
+#include <renderengine/RenderEngine.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine {
+
+CompositionEngine::~CompositionEngine() = default;
+
+namespace impl {
+
+std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() {
+    return std::make_unique<CompositionEngine>();
+}
+
+CompositionEngine::CompositionEngine() = default;
+CompositionEngine::~CompositionEngine() = default;
+
+std::shared_ptr<compositionengine::Display> CompositionEngine::createDisplay(
+        DisplayCreationArgs&& args) {
+    return compositionengine::impl::createDisplay(*this, std::move(args));
+}
+
+HWComposer& CompositionEngine::getHwComposer() const {
+    return *mHwComposer.get();
+}
+
+void CompositionEngine::setHwComposer(std::unique_ptr<HWComposer> hwComposer) {
+    mHwComposer = std::move(hwComposer);
+}
+
+renderengine::RenderEngine& CompositionEngine::getRenderEngine() const {
+    return *mRenderEngine.get();
+}
+
+void CompositionEngine::setRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
+    mRenderEngine = std::move(renderEngine);
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
new file mode 100644
index 0000000..f9d70e3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2019 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 <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/RenderSurface.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine::impl {
+
+std::shared_ptr<compositionengine::Display> createDisplay(
+        const compositionengine::CompositionEngine& compositionEngine,
+        compositionengine::DisplayCreationArgs&& args) {
+    return std::make_shared<Display>(compositionEngine, std::move(args));
+}
+
+Display::Display(const CompositionEngine& compositionEngine, DisplayCreationArgs&& args)
+      : compositionengine::impl::Output(compositionEngine),
+        mIsVirtual(args.isVirtual),
+        mId(args.displayId) {
+    editState().isSecure = args.isSecure;
+}
+
+Display::~Display() = default;
+
+const std::optional<DisplayId>& Display::getId() const {
+    return mId;
+}
+
+bool Display::isSecure() const {
+    return getState().isSecure;
+}
+
+bool Display::isVirtual() const {
+    return mIsVirtual;
+}
+
+void Display::disconnect() {
+    if (!mId) {
+        return;
+    }
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    hwc.disconnectDisplay(*mId);
+    mId.reset();
+}
+
+void Display::setColorTransform(const mat4& transform) {
+    Output::setColorTransform(transform);
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    status_t result = hwc.setColorTransform(*mId, transform);
+    ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
+             mId ? to_string(*mId).c_str() : "", result);
+}
+
+void Display::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
+                           ui::RenderIntent renderIntent) {
+    if (mode == getState().colorMode && dataspace == getState().dataspace &&
+        renderIntent == getState().renderIntent) {
+        return;
+    }
+
+    if (mIsVirtual) {
+        ALOGW("%s: Invalid operation on virtual display", __FUNCTION__);
+        return;
+    }
+
+    Output::setColorMode(mode, dataspace, renderIntent);
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    hwc.setActiveColorMode(*mId, mode, renderIntent);
+}
+
+void Display::dump(std::string& out) const {
+    using android::base::StringAppendF;
+
+    StringAppendF(&out, "   Composition Display State: [\"%s\"]", getName().c_str());
+
+    out.append("\n   ");
+
+    dumpVal(out, "isVirtual", mIsVirtual);
+    if (mId) {
+        dumpVal(out, "hwcId", to_string(*mId));
+    } else {
+        StringAppendF(&out, "no hwcId, ");
+    }
+
+    out.append("\n");
+
+    Output::dumpBase(out);
+}
+
+void Display::createDisplayColorProfile(DisplayColorProfileCreationArgs&& args) {
+    setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(std::move(args)));
+}
+
+void Display::createRenderSurface(RenderSurfaceCreationArgs&& args) {
+    setRenderSurface(compositionengine::impl::createRenderSurface(getCompositionEngine(), *this,
+                                                                  std::move(args)));
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
new file mode 100644
index 0000000..6e6f3c0
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2019 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 <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <log/log.h>
+#include <ui/DebugUtils.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine {
+
+DisplayColorProfile::~DisplayColorProfile() = default;
+
+namespace impl {
+namespace {
+
+using ui::ColorMode;
+using ui::Dataspace;
+using ui::RenderIntent;
+
+// ordered list of known SDR color modes
+const std::array<ColorMode, 3> sSdrColorModes = {
+        ColorMode::DISPLAY_BT2020,
+        ColorMode::DISPLAY_P3,
+        ColorMode::SRGB,
+};
+
+// ordered list of known HDR color modes
+const std::array<ColorMode, 2> sHdrColorModes = {
+        ColorMode::BT2100_PQ,
+        ColorMode::BT2100_HLG,
+};
+
+// ordered list of known SDR render intents
+const std::array<RenderIntent, 2> sSdrRenderIntents = {
+        RenderIntent::ENHANCE,
+        RenderIntent::COLORIMETRIC,
+};
+
+// ordered list of known HDR render intents
+const std::array<RenderIntent, 2> sHdrRenderIntents = {
+        RenderIntent::TONE_MAP_ENHANCE,
+        RenderIntent::TONE_MAP_COLORIMETRIC,
+};
+
+// map known color mode to dataspace
+Dataspace colorModeToDataspace(ColorMode mode) {
+    switch (mode) {
+        case ColorMode::SRGB:
+            return Dataspace::V0_SRGB;
+        case ColorMode::DISPLAY_P3:
+            return Dataspace::DISPLAY_P3;
+        case ColorMode::DISPLAY_BT2020:
+            return Dataspace::DISPLAY_BT2020;
+        case ColorMode::BT2100_HLG:
+            return Dataspace::BT2020_HLG;
+        case ColorMode::BT2100_PQ:
+            return Dataspace::BT2020_PQ;
+        default:
+            return Dataspace::UNKNOWN;
+    }
+}
+
+// Return a list of candidate color modes.
+std::vector<ColorMode> getColorModeCandidates(ColorMode mode) {
+    std::vector<ColorMode> candidates;
+
+    // add mode itself
+    candidates.push_back(mode);
+
+    // check if mode is HDR
+    bool isHdr = false;
+    for (auto hdrMode : sHdrColorModes) {
+        if (hdrMode == mode) {
+            isHdr = true;
+            break;
+        }
+    }
+
+    // add other HDR candidates when mode is HDR
+    if (isHdr) {
+        for (auto hdrMode : sHdrColorModes) {
+            if (hdrMode != mode) {
+                candidates.push_back(hdrMode);
+            }
+        }
+    }
+
+    // add other SDR candidates
+    for (auto sdrMode : sSdrColorModes) {
+        if (sdrMode != mode) {
+            candidates.push_back(sdrMode);
+        }
+    }
+
+    return candidates;
+}
+
+// Return a list of candidate render intents.
+std::vector<RenderIntent> getRenderIntentCandidates(RenderIntent intent) {
+    std::vector<RenderIntent> candidates;
+
+    // add intent itself
+    candidates.push_back(intent);
+
+    // check if intent is HDR
+    bool isHdr = false;
+    for (auto hdrIntent : sHdrRenderIntents) {
+        if (hdrIntent == intent) {
+            isHdr = true;
+            break;
+        }
+    }
+
+    if (isHdr) {
+        // add other HDR candidates when intent is HDR
+        for (auto hdrIntent : sHdrRenderIntents) {
+            if (hdrIntent != intent) {
+                candidates.push_back(hdrIntent);
+            }
+        }
+    } else {
+        // add other SDR candidates when intent is SDR
+        for (auto sdrIntent : sSdrRenderIntents) {
+            if (sdrIntent != intent) {
+                candidates.push_back(sdrIntent);
+            }
+        }
+    }
+
+    return candidates;
+}
+
+// Return the best color mode supported by HWC.
+ColorMode getHwcColorMode(
+        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
+        ColorMode mode) {
+    std::vector<ColorMode> candidates = getColorModeCandidates(mode);
+    for (auto candidate : candidates) {
+        auto iter = hwcColorModes.find(candidate);
+        if (iter != hwcColorModes.end()) {
+            return candidate;
+        }
+    }
+
+    return ColorMode::NATIVE;
+}
+
+// Return the best render intent supported by HWC.
+RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, RenderIntent intent) {
+    std::vector<RenderIntent> candidates = getRenderIntentCandidates(intent);
+    for (auto candidate : candidates) {
+        for (auto hwcIntent : hwcIntents) {
+            if (candidate == hwcIntent) {
+                return candidate;
+            }
+        }
+    }
+
+    return RenderIntent::COLORIMETRIC;
+}
+
+} // anonymous namespace
+
+std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
+        DisplayColorProfileCreationArgs&& args) {
+    return std::make_unique<DisplayColorProfile>(std::move(args));
+}
+
+DisplayColorProfile::DisplayColorProfile(DisplayColorProfileCreationArgs&& args)
+      : mHasWideColorGamut(args.hasWideColorGamut),
+        mSupportedPerFrameMetadata(args.supportedPerFrameMetadata) {
+    populateColorModes(args.hwcColorModes);
+
+    std::vector<ui::Hdr> types = args.hdrCapabilities.getSupportedHdrTypes();
+    for (ui::Hdr hdrType : types) {
+        switch (hdrType) {
+            case ui::Hdr::HDR10_PLUS:
+                mHasHdr10Plus = true;
+                break;
+            case ui::Hdr::HDR10:
+                mHasHdr10 = true;
+                break;
+            case ui::Hdr::HLG:
+                mHasHLG = true;
+                break;
+            case ui::Hdr::DOLBY_VISION:
+                mHasDolbyVision = true;
+                break;
+            default:
+                ALOGE("UNKNOWN HDR capability: %d", static_cast<int32_t>(hdrType));
+        }
+    }
+
+    float minLuminance = args.hdrCapabilities.getDesiredMinLuminance();
+    float maxLuminance = args.hdrCapabilities.getDesiredMaxLuminance();
+    float maxAverageLuminance = args.hdrCapabilities.getDesiredMaxAverageLuminance();
+
+    minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance;
+    maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance;
+    maxAverageLuminance = maxAverageLuminance <= 0.0 ? sDefaultMaxLumiance : maxAverageLuminance;
+    if (args.hasWideColorGamut) {
+        // insert HDR10/HLG as we will force client composition for HDR10/HLG
+        // layers
+        if (!hasHDR10Support()) {
+            types.push_back(ui::Hdr::HDR10);
+        }
+
+        if (!hasHLGSupport()) {
+            types.push_back(ui::Hdr::HLG);
+        }
+    }
+
+    mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
+}
+
+DisplayColorProfile::~DisplayColorProfile() = default;
+
+bool DisplayColorProfile::isValid() const {
+    return true;
+}
+
+bool DisplayColorProfile::hasWideColorGamut() const {
+    return mHasWideColorGamut;
+}
+
+int32_t DisplayColorProfile::getSupportedPerFrameMetadata() const {
+    return mSupportedPerFrameMetadata;
+}
+
+bool DisplayColorProfile::hasHDR10PlusSupport() const {
+    return mHasHdr10Plus;
+}
+
+bool DisplayColorProfile::hasHDR10Support() const {
+    return mHasHdr10;
+}
+
+bool DisplayColorProfile::hasHLGSupport() const {
+    return mHasHLG;
+}
+
+bool DisplayColorProfile::hasDolbyVisionSupport() const {
+    return mHasDolbyVision;
+}
+
+const HdrCapabilities& DisplayColorProfile::getHdrCapabilities() const {
+    return mHdrCapabilities;
+}
+
+void DisplayColorProfile::populateColorModes(
+        const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes) {
+    if (!hasWideColorGamut()) {
+        return;
+    }
+
+    // collect all known SDR render intents
+    std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
+                                                      sSdrRenderIntents.end());
+    auto iter = hwcColorModes.find(ColorMode::SRGB);
+    if (iter != hwcColorModes.end()) {
+        for (auto intent : iter->second) {
+            sdrRenderIntents.insert(intent);
+        }
+    }
+
+    // add all known SDR combinations
+    for (auto intent : sdrRenderIntents) {
+        for (auto mode : sSdrColorModes) {
+            addColorMode(hwcColorModes, mode, intent);
+        }
+    }
+
+    // collect all known HDR render intents
+    std::unordered_set<RenderIntent> hdrRenderIntents(sHdrRenderIntents.begin(),
+                                                      sHdrRenderIntents.end());
+    iter = hwcColorModes.find(ColorMode::BT2100_PQ);
+    if (iter != hwcColorModes.end()) {
+        for (auto intent : iter->second) {
+            hdrRenderIntents.insert(intent);
+        }
+    }
+
+    // add all known HDR combinations
+    for (auto intent : sHdrRenderIntents) {
+        for (auto mode : sHdrColorModes) {
+            addColorMode(hwcColorModes, mode, intent);
+        }
+    }
+}
+
+// Map dataspace/intent to the best matched dataspace/colorMode/renderIntent
+// supported by HWC.
+void DisplayColorProfile::addColorMode(
+        const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes, const ColorMode mode,
+        const RenderIntent intent) {
+    // find the best color mode
+    const ColorMode hwcColorMode = getHwcColorMode(hwcColorModes, mode);
+
+    // find the best render intent
+    auto iter = hwcColorModes.find(hwcColorMode);
+    const auto& hwcIntents =
+            iter != hwcColorModes.end() ? iter->second : std::vector<RenderIntent>();
+    const RenderIntent hwcIntent = getHwcRenderIntent(hwcIntents, intent);
+
+    const Dataspace dataspace = colorModeToDataspace(mode);
+    const Dataspace hwcDataspace = colorModeToDataspace(hwcColorMode);
+
+    ALOGV("DisplayColorProfile: map (%s, %s) to (%s, %s, %s)",
+          dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+          decodeRenderIntent(intent).c_str(),
+          dataspaceDetails(static_cast<android_dataspace_t>(hwcDataspace)).c_str(),
+          decodeColorMode(hwcColorMode).c_str(), decodeRenderIntent(hwcIntent).c_str());
+
+    mColorModes[getColorModeKey(dataspace, intent)] = {hwcDataspace, hwcColorMode, hwcIntent};
+}
+
+bool DisplayColorProfile::hasRenderIntent(RenderIntent intent) const {
+    // assume a render intent is supported when SRGB supports it; we should
+    // get rid of that assumption.
+    auto iter = mColorModes.find(getColorModeKey(Dataspace::V0_SRGB, intent));
+    return iter != mColorModes.end() && iter->second.renderIntent == intent;
+}
+
+bool DisplayColorProfile::hasLegacyHdrSupport(Dataspace dataspace) const {
+    if ((dataspace == Dataspace::BT2020_PQ && hasHDR10Support()) ||
+        (dataspace == Dataspace::BT2020_HLG && hasHLGSupport())) {
+        auto iter =
+                mColorModes.find(getColorModeKey(dataspace, RenderIntent::TONE_MAP_COLORIMETRIC));
+        return iter == mColorModes.end() || iter->second.dataspace != dataspace;
+    }
+
+    return false;
+}
+
+void DisplayColorProfile::getBestColorMode(Dataspace dataspace, RenderIntent intent,
+                                           Dataspace* outDataspace, ColorMode* outMode,
+                                           RenderIntent* outIntent) const {
+    auto iter = mColorModes.find(getColorModeKey(dataspace, intent));
+    if (iter != mColorModes.end()) {
+        *outDataspace = iter->second.dataspace;
+        *outMode = iter->second.colorMode;
+        *outIntent = iter->second.renderIntent;
+    } else {
+        // this is unexpected on a WCG display
+        if (hasWideColorGamut()) {
+            ALOGE("map unknown (%s)/(%s) to default color mode",
+                  dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+                  decodeRenderIntent(intent).c_str());
+        }
+
+        *outDataspace = Dataspace::UNKNOWN;
+        *outMode = ColorMode::NATIVE;
+        *outIntent = RenderIntent::COLORIMETRIC;
+    }
+}
+
+void DisplayColorProfile::dump(std::string& out) const {
+    out.append("   Composition Display Color State:");
+
+    out.append("\n   HWC Support: ");
+
+    dumpVal(out, "wideColorGamut", hasWideColorGamut());
+    dumpVal(out, "hdr10plus", hasHDR10PlusSupport());
+    dumpVal(out, "hdr10", hasHDR10Support());
+    dumpVal(out, "hlg", hasHLGSupport());
+    dumpVal(out, "dv", hasDolbyVisionSupport());
+    dumpVal(out, "metadata", getSupportedPerFrameMetadata());
+
+    out.append("\n");
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
similarity index 68%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
index e6ac6bf..db6d4f2 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -14,14 +14,10 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/DisplaySurface.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
 DisplaySurface::~DisplaySurface() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
new file mode 100644
index 0000000..c497013
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2019 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 <cinttypes>
+
+#include <android-base/stringprintf.h>
+#include <compositionengine/impl/DumpHelpers.h>
+
+namespace android::compositionengine::impl {
+
+using android::base::StringAppendF;
+
+void dumpVal(std::string& out, const char* name, bool value) {
+    StringAppendF(&out, "%s=%c ", name, value ? 'T' : 'F');
+}
+
+void dumpVal(std::string& out, const char* name, const void* value) {
+    StringAppendF(&out, "%s=%p ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, int value) {
+    StringAppendF(&out, "%s=%d ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, float value) {
+    StringAppendF(&out, "%s=%f ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, uint32_t value) {
+    StringAppendF(&out, "%s=%u ", name, value);
+}
+
+void dumpHex(std::string& out, const char* name, uint64_t value) {
+    StringAppendF(&out, "%s=0x08%" PRIx64 " ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, const char* value) {
+    StringAppendF(&out, "%s=%s ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, const std::string& value) {
+    dumpVal(out, name, value.c_str());
+}
+
+void dumpVal(std::string& out, const char* name, const char* valueName, int value) {
+    StringAppendF(&out, "%s=%s (%d)", name, valueName, value);
+}
+
+void dumpVal(std::string& out, const char* name, const std::string& valueName, int value) {
+    dumpVal(out, name, valueName.c_str(), value);
+}
+
+void dumpVal(std::string& out, const char* name, const FloatRect& rect) {
+    StringAppendF(&out, "%s=[%f %f %f %f] ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+void dumpVal(std::string& out, const char* name, const Rect& rect) {
+    StringAppendF(&out, "%s=[%d %d %d %d] ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+void dumpVal(std::string& out, const char* name, const Region& region) {
+    region.dump(out, name, 0);
+}
+
+void dumpVal(std::string& out, const char* name, const ui::Transform& transform) {
+    transform.dump(out, name);
+}
+
+void dumpVal(std::string& out, const char* name, const ui::Size& size) {
+    StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height);
+}
+
+void dumpVal(std::string& out, const char* name, const mat4& tr) {
+    StringAppendF(&out,
+                  "%s=["
+                  /* clang-format off */
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]"
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]"
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]"
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]]",
+                  name,
+                  tr[0][0], tr[1][0], tr[2][0], tr[3][0],
+                  tr[0][1], tr[1][1], tr[2][1], tr[3][1],
+                  tr[0][2], tr[1][2], tr[2][2], tr[3][2],
+                  tr[0][3], tr[1][3], tr[2][3], tr[3][3]
+                  ); /* clang-format on */
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
new file mode 100644
index 0000000..c4d563f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2019 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 <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/Output.h>
+#include <ui/DebugUtils.h>
+
+namespace android::compositionengine::impl {
+
+Output::Output(const CompositionEngine& compositionEngine)
+      : mCompositionEngine(compositionEngine) {}
+
+Output::~Output() = default;
+
+const CompositionEngine& Output::getCompositionEngine() const {
+    return mCompositionEngine;
+}
+
+bool Output::isValid() const {
+    return mDisplayColorProfile && mDisplayColorProfile->isValid() && mRenderSurface &&
+            mRenderSurface->isValid();
+}
+
+const std::string& Output::getName() const {
+    return mName;
+}
+
+void Output::setName(const std::string& name) {
+    mName = name;
+}
+
+void Output::setCompositionEnabled(bool enabled) {
+    if (mState.isEnabled == enabled) {
+        return;
+    }
+
+    mState.isEnabled = enabled;
+    dirtyEntireOutput();
+}
+
+void Output::setProjection(const ui::Transform& transform, int32_t orientation, const Rect& frame,
+                           const Rect& viewport, const Rect& scissor, bool needsFiltering) {
+    mState.transform = transform;
+    mState.orientation = orientation;
+    mState.scissor = scissor;
+    mState.frame = frame;
+    mState.viewport = viewport;
+    mState.needsFiltering = needsFiltering;
+
+    dirtyEntireOutput();
+}
+
+// TODO(lpique): Rename setSize() once more is moved.
+void Output::setBounds(const ui::Size& size) {
+    mRenderSurface->setDisplaySize(size);
+    // TODO(lpique): Rename mState.size once more is moved.
+    mState.bounds = Rect(mRenderSurface->getSize());
+
+    dirtyEntireOutput();
+}
+
+void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
+    mState.layerStackId = layerStackId;
+    mState.layerStackInternal = isInternal;
+
+    dirtyEntireOutput();
+}
+
+void Output::setColorTransform(const mat4& transform) {
+    const bool isIdentity = (transform == mat4());
+
+    mState.colorTransform =
+            isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
+}
+
+void Output::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
+                          ui::RenderIntent renderIntent) {
+    mState.colorMode = mode;
+    mState.dataspace = dataspace;
+    mState.renderIntent = renderIntent;
+
+    mRenderSurface->setBufferDataspace(dataspace);
+
+    ALOGV("Set active color mode: %s (%d), active render intent: %s (%d)",
+          decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
+          renderIntent);
+}
+
+void Output::dump(std::string& out) const {
+    using android::base::StringAppendF;
+
+    StringAppendF(&out, "   Composition Output State: [\"%s\"]", mName.c_str());
+
+    out.append("\n   ");
+
+    dumpBase(out);
+}
+
+void Output::dumpBase(std::string& out) const {
+    mState.dump(out);
+
+    if (mDisplayColorProfile) {
+        mDisplayColorProfile->dump(out);
+    } else {
+        out.append("    No display color profile!\n");
+    }
+
+    if (mRenderSurface) {
+        mRenderSurface->dump(out);
+    } else {
+        out.append("    No render surface!\n");
+    }
+}
+
+compositionengine::DisplayColorProfile* Output::getDisplayColorProfile() const {
+    return mDisplayColorProfile.get();
+}
+
+void Output::setDisplayColorProfile(std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
+    mDisplayColorProfile = std::move(mode);
+}
+
+void Output::setDisplayColorProfileForTest(
+        std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
+    mDisplayColorProfile = std::move(mode);
+}
+
+compositionengine::RenderSurface* Output::getRenderSurface() const {
+    return mRenderSurface.get();
+}
+
+void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
+    mRenderSurface = std::move(surface);
+    mState.bounds = Rect(mRenderSurface->getSize());
+
+    dirtyEntireOutput();
+}
+
+void Output::setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface> surface) {
+    mRenderSurface = std::move(surface);
+}
+
+const OutputCompositionState& Output::getState() const {
+    return mState;
+}
+
+OutputCompositionState& Output::editState() {
+    return mState;
+}
+
+Region Output::getDirtyRegion(bool repaintEverything) const {
+    Region dirty(mState.viewport);
+    if (!repaintEverything) {
+        dirty.andSelf(mState.dirtyRegion);
+    }
+    return dirty;
+}
+
+bool Output::belongsInOutput(uint32_t layerStackId, bool internalOnly) const {
+    // The layerStackId's must match, and also the layer must not be internal
+    // only when not on an internal output.
+    return (layerStackId == mState.layerStackId) && (!internalOnly || mState.layerStackInternal);
+}
+
+void Output::dirtyEntireOutput() {
+    mState.dirtyRegion.set(mState.bounds);
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
new file mode 100644
index 0000000..78807ff
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 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 <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+
+namespace android::compositionengine::impl {
+
+void OutputCompositionState::dump(std::string& out) const {
+    dumpVal(out, "isEnabled", isEnabled);
+    dumpVal(out, "isSecure", isSecure);
+
+    dumpVal(out, "layerStack", layerStackId);
+    dumpVal(out, "layerStackInternal", layerStackInternal);
+
+    out.append("\n   ");
+
+    dumpVal(out, "transform", transform);
+
+    out.append("\n   ");
+
+    dumpVal(out, "frame", frame);
+    dumpVal(out, "viewport", viewport);
+    dumpVal(out, "scissor", scissor);
+    dumpVal(out, "needsFiltering", needsFiltering);
+
+    out.append("\n");
+
+    dumpVal(out, "colorMode", toString(colorMode), colorMode);
+    dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
+    dumpVal(out, "dataspace", toString(dataspace), dataspace);
+    dumpVal(out, "colorTransform", colorTransform);
+
+    out.append("\n");
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
new file mode 100644
index 0000000..3f841d2
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2019 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 <android-base/stringprintf.h>
+#include <android/native_window.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/RenderSurface.h>
+#include <log/log.h>
+#include <renderengine/RenderEngine.h>
+#include <sync/sync.h>
+#include <system/window.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine {
+
+RenderSurface::~RenderSurface() = default;
+
+namespace impl {
+
+std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
+        const compositionengine::CompositionEngine& compositionEngine,
+        compositionengine::Display& display, compositionengine::RenderSurfaceCreationArgs&& args) {
+    return std::make_unique<RenderSurface>(compositionEngine, display, std::move(args));
+}
+
+RenderSurface::RenderSurface(const CompositionEngine& compositionEngine, Display& display,
+                             RenderSurfaceCreationArgs&& args)
+      : mCompositionEngine(compositionEngine),
+        mDisplay(display),
+        mNativeWindow(args.nativeWindow),
+        mDisplaySurface(args.displaySurface),
+        mSize(args.displayWidth, args.displayHeight) {}
+
+RenderSurface::~RenderSurface() = default;
+
+bool RenderSurface::isValid() const {
+    return mSize.isValid();
+}
+
+void RenderSurface::initialize() {
+    ANativeWindow* const window = mNativeWindow.get();
+
+    int status = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+    ALOGE_IF(status != NO_ERROR, "Unable to connect BQ producer: %d", status);
+    status = native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
+    ALOGE_IF(status != NO_ERROR, "Unable to set BQ format to RGBA888: %d", status);
+    status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER);
+    ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for GPU rendering: %d", status);
+}
+
+const ui::Size& RenderSurface::getSize() const {
+    return mSize;
+}
+
+const sp<Fence>& RenderSurface::getClientTargetAcquireFence() const {
+    return mDisplaySurface->getClientTargetAcquireFence();
+}
+
+void RenderSurface::setDisplaySize(const ui::Size& size) {
+    mDisplaySurface->resizeBuffers(size.width, size.height);
+    mSize = size;
+}
+
+void RenderSurface::setBufferDataspace(ui::Dataspace dataspace) {
+    native_window_set_buffers_data_space(mNativeWindow.get(),
+                                         static_cast<android_dataspace>(dataspace));
+}
+
+void RenderSurface::setProtected(bool useProtected) {
+    uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
+    if (useProtected) {
+        usageFlags |= GRALLOC_USAGE_PROTECTED;
+    }
+    const int status = native_window_set_usage(mNativeWindow.get(), usageFlags);
+    ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for protected content: %d", status);
+}
+
+status_t RenderSurface::beginFrame(bool mustRecompose) {
+    return mDisplaySurface->beginFrame(mustRecompose);
+}
+
+status_t RenderSurface::prepareFrame(std::vector<CompositionInfo>& compositionData) {
+    auto& hwc = mCompositionEngine.getHwComposer();
+    const auto id = mDisplay.getId();
+    if (id) {
+        status_t error = hwc.prepare(*id, compositionData);
+        if (error != NO_ERROR) {
+            return error;
+        }
+    }
+
+    DisplaySurface::CompositionType compositionType;
+    const bool hasClient = hwc.hasClientComposition(id);
+    const bool hasDevice = hwc.hasDeviceComposition(id);
+    if (hasClient && hasDevice) {
+        compositionType = DisplaySurface::COMPOSITION_MIXED;
+    } else if (hasClient) {
+        compositionType = DisplaySurface::COMPOSITION_GLES;
+    } else if (hasDevice) {
+        compositionType = DisplaySurface::COMPOSITION_HWC;
+    } else {
+        // Nothing to do -- when turning the screen off we get a frame like
+        // this. Call it a HWC frame since we won't be doing any GLES work but
+        // will do a prepare/set cycle.
+        compositionType = DisplaySurface::COMPOSITION_HWC;
+    }
+    return mDisplaySurface->prepareFrame(compositionType);
+}
+
+sp<GraphicBuffer> RenderSurface::dequeueBuffer() {
+    int fd = -1;
+    ANativeWindowBuffer* buffer = nullptr;
+
+    status_t result = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fd);
+
+    if (result != NO_ERROR) {
+        ALOGE("ANativeWindow::dequeueBuffer failed for display [%s] with error: %d",
+              mDisplay.getName().c_str(), result);
+        // Return fast here as we can't do much more - any rendering we do
+        // now will just be wrong.
+        return mGraphicBuffer;
+    }
+
+    ALOGW_IF(mGraphicBuffer != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
+             mGraphicBuffer->getNativeBuffer()->handle);
+    mGraphicBuffer = GraphicBuffer::from(buffer);
+
+    // Block until the buffer is ready
+    // TODO(alecmouri): it's perhaps more appropriate to block renderengine so
+    // that the gl driver can block instead.
+    if (fd >= 0) {
+        sync_wait(fd, -1);
+        close(fd);
+    }
+
+    return mGraphicBuffer;
+}
+
+void RenderSurface::queueBuffer(base::unique_fd&& readyFence) {
+    auto& hwc = mCompositionEngine.getHwComposer();
+    const auto id = mDisplay.getId();
+
+    if (hwc.hasClientComposition(id) || hwc.hasFlipClientTargetRequest(id)) {
+        // hasFlipClientTargetRequest could return true even if we haven't
+        // dequeued a buffer before. Try dequeueing one if we don't have a
+        // buffer ready.
+        if (mGraphicBuffer == nullptr) {
+            ALOGI("Attempting to queue a client composited buffer without one "
+                  "previously dequeued for display [%s]. Attempting to dequeue "
+                  "a scratch buffer now",
+                  mDisplay.getName().c_str());
+            // We shouldn't deadlock here, since mGraphicBuffer == nullptr only
+            // after a successful call to queueBuffer, or if dequeueBuffer has
+            // never been called.
+            dequeueBuffer();
+        }
+
+        if (mGraphicBuffer == nullptr) {
+            ALOGE("No buffer is ready for display [%s]", mDisplay.getName().c_str());
+        } else {
+            status_t result =
+                    mNativeWindow->queueBuffer(mNativeWindow.get(),
+                                               mGraphicBuffer->getNativeBuffer(), dup(readyFence));
+            if (result != NO_ERROR) {
+                ALOGE("Error when queueing buffer for display [%s]: %d", mDisplay.getName().c_str(),
+                      result);
+                // We risk blocking on dequeueBuffer if the primary display failed
+                // to queue up its buffer, so crash here.
+                if (!mDisplay.isVirtual()) {
+                    LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", result);
+                } else {
+                    mNativeWindow->cancelBuffer(mNativeWindow.get(),
+                                                mGraphicBuffer->getNativeBuffer(), dup(readyFence));
+                }
+            }
+
+            mGraphicBuffer = nullptr;
+        }
+    }
+
+    status_t result = mDisplaySurface->advanceFrame();
+    if (result != NO_ERROR) {
+        ALOGE("[%s] failed pushing new frame to HWC: %d", mDisplay.getName().c_str(), result);
+    }
+}
+
+void RenderSurface::onPresentDisplayCompleted() {
+    mDisplaySurface->onFrameCommitted();
+}
+
+void RenderSurface::setViewportAndProjection() {
+    auto& renderEngine = mCompositionEngine.getRenderEngine();
+    Rect sourceCrop = Rect(mSize);
+    renderEngine.setViewportAndProjection(mSize.width, mSize.height, sourceCrop,
+                                          ui::Transform::ROT_0);
+}
+
+void RenderSurface::flip() {
+    mPageFlipCount++;
+}
+
+void RenderSurface::dump(std::string& out) const {
+    using android::base::StringAppendF;
+
+    out.append("   Composition RenderSurface State:");
+
+    out.append("\n   ");
+
+    dumpVal(out, "size", mSize);
+    StringAppendF(&out, "ANativeWindow=%p (format %d) ", mNativeWindow.get(),
+                  ANativeWindow_getFormat(mNativeWindow.get()));
+    dumpVal(out, "flips", mPageFlipCount);
+    out.append("\n");
+
+    String8 surfaceDump;
+    mDisplaySurface->dumpAsString(surfaceDump);
+    out.append(surfaceDump);
+}
+
+std::uint32_t RenderSurface::getPageFlipCount() const {
+    return mPageFlipCount;
+}
+
+void RenderSurface::setPageFlipCountForTest(std::uint32_t count) {
+    mPageFlipCount = count;
+}
+
+void RenderSurface::setSizeForTest(const ui::Size& size) {
+    mSize = size;
+}
+
+sp<GraphicBuffer>& RenderSurface::mutableGraphicBufferForTest() {
+    return mGraphicBuffer;
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
new file mode 100644
index 0000000..3766f27
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/CompositionEngine.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+
+#include "MockHWComposer.h"
+
+namespace android::compositionengine {
+namespace {
+
+using ::testing::StrictMock;
+
+class CompositionEngineTest : public testing::Test {
+public:
+    ~CompositionEngineTest() override;
+    mock::HWComposer* mHwc = new StrictMock<mock::HWComposer>();
+    renderengine::mock::RenderEngine* mRenderEngine =
+            new StrictMock<renderengine::mock::RenderEngine>();
+    impl::CompositionEngine mEngine;
+};
+
+CompositionEngineTest::~CompositionEngineTest() = default;
+
+TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) {
+    auto engine = impl::createCompositionEngine();
+    EXPECT_TRUE(engine.get() != nullptr);
+}
+
+TEST_F(CompositionEngineTest, canSetHWComposer) {
+    mEngine.setHwComposer(std::unique_ptr<android::HWComposer>(mHwc));
+
+    EXPECT_EQ(mHwc, &mEngine.getHwComposer());
+}
+
+TEST_F(CompositionEngineTest, canSetRenderEngine) {
+    mEngine.setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+
+    EXPECT_EQ(mRenderEngine, &mEngine.getRenderEngine());
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
new file mode 100644
index 0000000..d20fdda
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -0,0 +1,644 @@
+/*
+ * Copyright 2019 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 <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <gtest/gtest.h>
+
+namespace android::hardware::graphics::common::V1_1 {
+
+// Note: These operator overloads need to be defined in the same namespace as
+// the values they print.
+
+std::ostream& operator<<(std::ostream& os, const RenderIntent& value) {
+    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+              << ")";
+}
+
+} // namespace android::hardware::graphics::common::V1_1
+
+namespace android::hardware::graphics::common::V1_2 {
+
+// Note: These operator overloads need to be defined in the same namespace as
+// the values they print.
+
+std::ostream& operator<<(std::ostream& os, const Dataspace& value) {
+    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+              << ")";
+}
+
+std::ostream& operator<<(std::ostream& os, const ColorMode& value) {
+    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+              << ")";
+}
+
+} // namespace android::hardware::graphics::common::V1_2
+
+namespace android::compositionengine {
+namespace {
+
+using testing::_;
+using testing::Contains;
+using testing::IsEmpty;
+using testing::Ref;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SizeIs;
+using testing::StrictMock;
+
+using ui::ColorMode;
+using ui::Dataspace;
+using ui::Hdr;
+using ui::RenderIntent;
+
+// This allows us to simulate a vendor-specified intent being used.
+constexpr RenderIntent VendorRenderIntent = static_cast<RenderIntent>(0x100);
+
+class DisplayColorProfileTest : public testing::Test {
+public:
+    ~DisplayColorProfileTest() override = default;
+
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+};
+
+class ProfileFactory {
+public:
+    impl::DisplayColorProfile build() const {
+        return impl::DisplayColorProfile{DisplayColorProfileCreationArgs{
+                mHasWideColorGamut,
+                HdrCapabilities(mSupportedHdrTypes, mMaxLuminance, mMaxAverageLuminance,
+                                mMinLuminance),
+                mSupportedPerFrameMetadata,
+                mSupportedColorModes,
+        }};
+    }
+
+    ProfileFactory& setHasWideColorGamut(bool value) {
+        mHasWideColorGamut = value;
+        return *this;
+    }
+
+    ProfileFactory& setPerFrameMetadata(int32_t value) {
+        mSupportedPerFrameMetadata = value;
+        return *this;
+    }
+
+    ProfileFactory& addHdrType(Hdr value) {
+        mSupportedHdrTypes.emplace_back(value);
+        return *this;
+    }
+
+    ProfileFactory& addHdrTypes(std::initializer_list<Hdr> values) {
+        for (auto value : values) {
+            mSupportedHdrTypes.emplace_back(value);
+        }
+        return *this;
+    }
+
+    ProfileFactory& setMaxLuminance(float value) {
+        mMaxLuminance = value;
+        return *this;
+    }
+
+    ProfileFactory& setMaxAverageLuminance(float value) {
+        mMaxAverageLuminance = value;
+        return *this;
+    }
+
+    ProfileFactory& setMinLuminance(float value) {
+        mMinLuminance = value;
+        return *this;
+    }
+
+    ProfileFactory& addColorModeRenderIntent(ColorMode colorMode, RenderIntent renderIntent) {
+        mSupportedColorModes[colorMode].emplace_back(renderIntent);
+        return *this;
+    }
+
+    ProfileFactory& addColorModeRenderIntents(ColorMode colorMode,
+                                              std::initializer_list<RenderIntent> renderIntents) {
+        auto& profileedRenderIntents = mSupportedColorModes[colorMode];
+        for (auto renderIntent : renderIntents) {
+            profileedRenderIntents.emplace_back(renderIntent);
+        }
+        return *this;
+    }
+
+    static impl::DisplayColorProfile createProfileWithNoColorModeSupport() {
+        return ProfileFactory().build();
+    }
+
+    static impl::DisplayColorProfile createProfileWithBT2020ColorModeSupport() {
+        return ProfileFactory()
+                .setHasWideColorGamut(true)
+                .addHdrType(Hdr::HDR10)
+                .addColorModeRenderIntent(ColorMode::DISPLAY_BT2020, VendorRenderIntent)
+                .build();
+    }
+
+    static impl::DisplayColorProfile createProfileWithSRGBColorModeSupport() {
+        return ProfileFactory()
+                .setHasWideColorGamut(true)
+                .addHdrType(Hdr::HDR10)
+                .addColorModeRenderIntent(ColorMode::SRGB, VendorRenderIntent)
+                .build();
+    }
+
+    static impl::DisplayColorProfile createProfileWithBT2100PQSupport() {
+        return ProfileFactory()
+                .setHasWideColorGamut(true)
+                .addHdrType(Hdr::HLG)
+                .addColorModeRenderIntent(ColorMode::BT2100_PQ, VendorRenderIntent)
+                .build();
+    }
+
+private:
+    bool mHasWideColorGamut = false;
+    std::vector<Hdr> mSupportedHdrTypes;
+    float mMaxLuminance = -1.f;
+    float mMaxAverageLuminance = -1.f;
+    float mMinLuminance = -1.f;
+    int32_t mSupportedPerFrameMetadata = 0;
+    std::unordered_map<ColorMode, std::vector<RenderIntent>> mSupportedColorModes;
+};
+
+/* ------------------------------------------------------------------------
+ * RenderSurface Construction
+ */
+
+TEST_F(DisplayColorProfileTest, ctorSetsHasWideColorGamutFromInputArgs) {
+    {
+        auto profile = ProfileFactory().setHasWideColorGamut(false).build();
+
+        EXPECT_FALSE(profile.hasWideColorGamut());
+    }
+
+    {
+        auto profile = ProfileFactory().setHasWideColorGamut(true).build();
+
+        EXPECT_TRUE(profile.hasWideColorGamut());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorSetsSupportedPerFrameMetadataFromInputArgs) {
+    {
+        auto profile = ProfileFactory().setPerFrameMetadata(0).build();
+
+        EXPECT_EQ(0, profile.getSupportedPerFrameMetadata());
+    }
+
+    {
+        impl::DisplayColorProfile profile = ProfileFactory().setPerFrameMetadata(123).build();
+
+        EXPECT_EQ(123, profile.getSupportedPerFrameMetadata());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorDetectsSupportedHdrTypesFromInputArgs) {
+    {
+        // The constructor will set the internal state to not indicate any
+        // profile for HDR modes if none are profileed.
+        auto profile = ProfileFactory().build();
+
+        EXPECT_FALSE(profile.hasHDR10PlusSupport());
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate HDR10Plus
+        // profile if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::HDR10_PLUS).build();
+
+        EXPECT_TRUE(profile.hasHDR10PlusSupport());
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate HDR10 profile
+        // if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::HDR10).build();
+
+        EXPECT_FALSE(profile.hasHDR10PlusSupport());
+        EXPECT_TRUE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate HLG profile
+        // if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::HLG).build();
+
+        EXPECT_FALSE(profile.hasHDR10PlusSupport());
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_TRUE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate Dolbyvision profile
+        // if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::DOLBY_VISION).build();
+
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_TRUE(profile.hasDolbyVisionSupport());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorUsesOrDefaultsLuminanceValuesFromInputArgs) {
+    {
+        // The constructor will use a default value for each luminance setting
+        // that is negative.
+        auto profile = ProfileFactory()
+                               .setMaxLuminance(-1.f)
+                               .setMaxAverageLuminance(-1.f)
+                               .setMinLuminance(-1.f)
+                               .build();
+
+        EXPECT_EQ(DisplayColorProfile::sDefaultMaxLumiance,
+                  profile.getHdrCapabilities().getDesiredMaxLuminance());
+        EXPECT_EQ(DisplayColorProfile::sDefaultMaxLumiance,
+                  profile.getHdrCapabilities().getDesiredMaxAverageLuminance());
+        EXPECT_EQ(DisplayColorProfile::sDefaultMinLumiance,
+                  profile.getHdrCapabilities().getDesiredMinLuminance());
+    }
+
+    {
+        // The constructor will otherwise take and use a positive value for each
+        // of the luminance settings.
+        auto profile = ProfileFactory()
+                               .setMaxLuminance(1001.f)
+                               .setMaxAverageLuminance(1002.f)
+                               .setMinLuminance(1003.f)
+                               .build();
+
+        EXPECT_EQ(1001.f, profile.getHdrCapabilities().getDesiredMaxLuminance());
+        EXPECT_EQ(1002.f, profile.getHdrCapabilities().getDesiredMaxAverageLuminance());
+        EXPECT_EQ(1003.f, profile.getHdrCapabilities().getDesiredMinLuminance());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorSignalsHdrSupportForAnyWideColorGamutDevice) {
+    {
+        // If the output does not profile wide color gamut, then no HDR modes
+        // will be profileed in the generated HDR capabilities.
+        auto profile = ProfileFactory().setHasWideColorGamut(false).build();
+
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), IsEmpty());
+    }
+
+    {
+        // If the HWC does not show profile for certain HDR modes, then the
+        // generated HDR capabilities will indicate profile anyway.
+        auto profile = ProfileFactory().setHasWideColorGamut(true).build();
+
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG));
+    }
+
+    {
+        // If the HWC profiles the HDR modes, then the generated capabilities
+        // still has one entry for each HDR type.
+        auto profile = ProfileFactory()
+                               .setHasWideColorGamut(true)
+                               .addHdrTypes({Hdr::HLG, Hdr::HDR10})
+                               .build();
+
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG));
+    }
+}
+
+/* ------------------------------------------------------------------------
+ * DisplayColorProfile::hasRenderIntent
+ */
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasNoSupport) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasBT2020upport) {
+    auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasSRGBSupport) {
+    auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_TRUE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasBTG2100PQSupport) {
+    auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+/* ------------------------------------------------------------------------
+ * DisplayColorProfile::hasLegacyHdrSupport
+ */
+
+TEST_F(DisplayColorProfileTest, hasLegacyHdrSupport) {
+    {
+        auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+
+    {
+        auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+        EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+
+    {
+        auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+        EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+
+    {
+        auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getBestColorMode()
+ */
+
+void checkGetBestColorMode(
+        DisplayColorProfile& profile,
+        const std::array<std::tuple<Dataspace, ColorMode, RenderIntent>, 25>& expected) {
+    using ArgsType = std::tuple<Dataspace, RenderIntent>;
+
+    // These are the combinations of dataspaces and render intents that could be
+    // passed to RenderSurface::getBestColorMode()
+    const std::array<std::tuple<Dataspace, RenderIntent>, 25> kArgs = {
+            /* clang-format off */
+
+            // Non-HDR combinations
+
+            /*  0 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  1 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::ENHANCE},
+            /*  2 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::TONE_MAP_COLORIMETRIC}, // Vendor explicit setting
+            /*  3 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::TONE_MAP_ENHANCE},      // Vendor explicit setting
+            /*  4 */ ArgsType{Dataspace::DISPLAY_BT2020, VendorRenderIntent},                  // Vendor explicit setting
+
+            /*  5 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::COLORIMETRIC},
+            /*  6 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::ENHANCE},
+            /*  7 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::TONE_MAP_COLORIMETRIC}, // Vendor explicit setting
+            /*  8 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::TONE_MAP_ENHANCE},      // Vendor explicit setting
+            /*  9 */ ArgsType{Dataspace::DISPLAY_P3, VendorRenderIntent},                  // Vendor explicit setting
+
+            /* 10 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::COLORIMETRIC},
+            /* 11 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::ENHANCE},
+            /* 12 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::TONE_MAP_COLORIMETRIC}, // Vendor explicit setting
+            /* 13 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::TONE_MAP_ENHANCE},      // Vendor explicit setting
+            /* 14 */ ArgsType{Dataspace::V0_SRGB, VendorRenderIntent},                  // Vendor explicit setting
+
+            // HDR combinations
+
+            /* 15 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::TONE_MAP_COLORIMETRIC},
+            /* 16 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::TONE_MAP_ENHANCE},
+            /* 17 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::COLORIMETRIC},       // Vendor explicit setting
+            /* 18 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::ENHANCE},            // Vendor explicit setting
+            /* 19 */ ArgsType{Dataspace::BT2020_PQ, VendorRenderIntent},               // Vendor explicit setting
+
+            /* 20 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::TONE_MAP_COLORIMETRIC},
+            /* 21 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::TONE_MAP_ENHANCE},
+            /* 22 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::COLORIMETRIC},       // Vendor explicit setting
+            /* 23 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::ENHANCE},            // Vendor explicit setting
+            /* 24 */ ArgsType{Dataspace::BT2020_HLG, VendorRenderIntent},               // Vendor explicit setting
+            /* clang-format on */
+    };
+
+    for (size_t i = 0; i < kArgs.size(); i++) {
+        std::tuple<Dataspace, ColorMode, RenderIntent> actual;
+        profile.getBestColorMode(std::get<0>(kArgs[i]), std::get<1>(kArgs[i]), &std::get<0>(actual),
+                                 &std::get<1>(actual), &std::get<2>(actual));
+
+        EXPECT_EQ(expected[i], actual) << " for index " << i;
+    }
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasNoSupport) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /*  5 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 10 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 15 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasBT2020Support) {
+    auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /*  5 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 10 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 15 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasSRGBSupport) {
+    auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /*  5 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /* 10 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /* 15 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasBT2100PQSupport) {
+    auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /*  5 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 15 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
new file mode 100644
index 0000000..8cb6936
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2019 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 <cmath>
+
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gtest/gtest.h>
+#include <system/window.h>
+
+#include "MockHWComposer.h"
+
+namespace android::compositionengine {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+using testing::StrictMock;
+
+constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+
+class DisplayTest : public testing::Test {
+public:
+    ~DisplayTest() override = default;
+
+    StrictMock<android::mock::HWComposer> mHwComposer;
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    impl::Display mDisplay{mCompositionEngine,
+                           DisplayCreationArgsBuilder().setDisplayId(DEFAULT_DISPLAY_ID).build()};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(DisplayTest, canInstantiateDisplay) {
+    {
+        constexpr DisplayId display1 = DisplayId{123u};
+        auto display =
+                impl::createDisplay(mCompositionEngine,
+                                    DisplayCreationArgsBuilder().setDisplayId(display1).build());
+        EXPECT_FALSE(display->isSecure());
+        EXPECT_FALSE(display->isVirtual());
+        EXPECT_EQ(display1, display->getId());
+    }
+
+    {
+        constexpr DisplayId display2 = DisplayId{546u};
+        auto display = impl::createDisplay(mCompositionEngine,
+                                           DisplayCreationArgsBuilder()
+                                                   .setIsSecure(true)
+                                                   .setDisplayId(display2)
+                                                   .build());
+        EXPECT_TRUE(display->isSecure());
+        EXPECT_FALSE(display->isVirtual());
+        EXPECT_EQ(display2, display->getId());
+    }
+
+    {
+        constexpr DisplayId display3 = DisplayId{789u};
+        auto display = impl::createDisplay(mCompositionEngine,
+                                           DisplayCreationArgsBuilder()
+                                                   .setIsVirtual(true)
+                                                   .setDisplayId(display3)
+                                                   .build());
+        EXPECT_FALSE(display->isSecure());
+        EXPECT_TRUE(display->isVirtual());
+        EXPECT_EQ(display3, display->getId());
+    }
+}
+
+/* ------------------------------------------------------------------------
+ * Display::disconnect()
+ */
+
+TEST_F(DisplayTest, disconnectDisconnectsDisplay) {
+    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+    // The first call to disconnect will disconnect the display with the HWC and
+    // set mHwcId to -1.
+    EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
+    mDisplay.disconnect();
+    EXPECT_FALSE(mDisplay.getId());
+
+    // Subsequent calls will do nothing,
+    EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0);
+    mDisplay.disconnect();
+    EXPECT_FALSE(mDisplay.getId());
+}
+
+/* ------------------------------------------------------------------------
+ * Display::setColorTransform()
+ */
+
+TEST_F(DisplayTest, setColorTransformSetsTransform) {
+    // Identity matrix sets an identity state value
+    const mat4 identity;
+
+    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, identity)).Times(1);
+
+    mDisplay.setColorTransform(identity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mDisplay.getState().colorTransform);
+
+    // Non-identity matrix sets a non-identity state value
+    const mat4 nonIdentity = mat4() * 2;
+
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, nonIdentity)).Times(1);
+
+    mDisplay.setColorTransform(nonIdentity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mDisplay.getState().colorTransform);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::setColorMode()
+ */
+
+TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) {
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    mDisplay.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+    // These values are expected to be the initial state.
+    ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
+    ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+
+    // Otherwise if the values are unchanged, nothing happens
+    mDisplay.setColorMode(ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+                          ui::RenderIntent::COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+
+    // Otherwise if the values are different, updates happen
+    EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::SRGB)).Times(1);
+    EXPECT_CALL(mHwComposer,
+                setActiveColorMode(DEFAULT_DISPLAY_ID, ui::ColorMode::BT2100_PQ,
+                                   ui::RenderIntent::TONE_MAP_COLORIMETRIC))
+            .Times(1);
+
+    mDisplay.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+                          ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::BT2100_PQ, mDisplay.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::SRGB, mDisplay.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay.getState().renderIntent);
+}
+
+TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) {
+    impl::Display virtualDisplay{mCompositionEngine,
+                                 DisplayCreationArgs{false, true, DEFAULT_DISPLAY_ID}};
+
+    virtualDisplay.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+                                ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay.getState().renderIntent);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::createDisplayColorProfile()
+ */
+
+TEST_F(DisplayTest, createDisplayColorProfileSetsDisplayColorProfile) {
+    EXPECT_TRUE(mDisplay.getDisplayColorProfile() == nullptr);
+    mDisplay.createDisplayColorProfile(
+            DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
+                                            DisplayColorProfileCreationArgs::HwcColorModes()});
+    EXPECT_TRUE(mDisplay.getDisplayColorProfile() != nullptr);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::createRenderSurface()
+ */
+
+TEST_F(DisplayTest, createRenderSurfaceSetsRenderSurface) {
+    EXPECT_TRUE(mDisplay.getRenderSurface() == nullptr);
+    mDisplay.createRenderSurface(RenderSurfaceCreationArgs{640, 480, nullptr, nullptr});
+    EXPECT_TRUE(mDisplay.getRenderSurface() != nullptr);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
index e6ac6bf..ae52670 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * Copyright 2018 The Android Open Source Project
+
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -14,14 +14,19 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "MockHWComposer.h"
 
 namespace android {
+
+// This will go away once HWComposer is moved into the "backend" library
+HWComposer::~HWComposer() = default;
+
 namespace mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+HWComposer::HWComposer() = default;
+HWComposer::~HWComposer() = default;
 
 } // namespace mock
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
new file mode 100644
index 0000000..ece412f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "LayerBE.h"
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android {
+namespace mock {
+
+class HWComposer : public android::HWComposer {
+public:
+    HWComposer();
+    ~HWComposer() override;
+
+    MOCK_METHOD2(registerCallback, void(HWC2::ComposerCallback*, int32_t));
+    MOCK_CONST_METHOD3(getDisplayIdentificationData,
+                       bool(hwc2_display_t, uint8_t*, DisplayIdentificationData*));
+    MOCK_CONST_METHOD1(hasCapability, bool(HWC2::Capability));
+    MOCK_CONST_METHOD2(hasDisplayCapability,
+                       bool(const std::optional<DisplayId>&, HWC2::DisplayCapability));
+
+    MOCK_METHOD3(allocateVirtualDisplay,
+                 std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
+    MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
+    MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
+    MOCK_METHOD2(prepare, status_t(DisplayId, std::vector<CompositionInfo>&));
+    MOCK_METHOD5(setClientTarget,
+                 status_t(DisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
+                          ui::Dataspace));
+    MOCK_METHOD1(presentAndGetReleaseFences, status_t(DisplayId));
+    MOCK_METHOD2(setPowerMode, status_t(DisplayId, int));
+    MOCK_METHOD2(setActiveConfig, status_t(DisplayId, size_t));
+    MOCK_METHOD2(setColorTransform, status_t(DisplayId, const mat4&));
+    MOCK_METHOD1(disconnectDisplay, void(DisplayId));
+    MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
+    MOCK_CONST_METHOD1(hasFlipClientTargetRequest, bool(const std::optional<DisplayId>&));
+    MOCK_CONST_METHOD1(hasClientComposition, bool(const std::optional<DisplayId>&));
+    MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(DisplayId));
+    MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(DisplayId, HWC2::Layer*));
+    MOCK_METHOD3(setOutputBuffer, status_t(DisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
+    MOCK_METHOD1(clearReleaseFences, void(DisplayId));
+    MOCK_METHOD2(getHdrCapabilities, status_t(DisplayId, HdrCapabilities*));
+    MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(DisplayId));
+    MOCK_CONST_METHOD2(getRenderIntents, std::vector<ui::RenderIntent>(DisplayId, ui::ColorMode));
+    MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(DisplayId, ui::Dataspace));
+    MOCK_METHOD4(getDisplayedContentSamplingAttributes,
+                 status_t(DisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
+    MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(DisplayId, bool, uint8_t, uint64_t));
+    MOCK_METHOD4(getDisplayedContentSample,
+                 status_t(DisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
+
+    MOCK_METHOD2(onHotplug,
+                 std::optional<DisplayIdentificationInfo>(hwc2_display_t, HWC2::Connection));
+    MOCK_METHOD2(onVsync, bool(hwc2_display_t, int64_t));
+    MOCK_METHOD2(setVsyncEnabled, void(DisplayId, HWC2::Vsync));
+    MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(DisplayId));
+    MOCK_CONST_METHOD1(isConnected, bool(DisplayId));
+    MOCK_CONST_METHOD1(getConfigs,
+                       std::vector<std::shared_ptr<const HWC2::Display::Config>>(DisplayId));
+    MOCK_CONST_METHOD1(getActiveConfig, std::shared_ptr<const HWC2::Display::Config>(DisplayId));
+    MOCK_CONST_METHOD1(getActiveConfigIndex, int(DisplayId));
+    MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(DisplayId));
+    MOCK_METHOD3(setActiveColorMode, status_t(DisplayId, ui::ColorMode, ui::RenderIntent));
+    MOCK_CONST_METHOD0(isUsingVrComposer, bool());
+
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+    MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
+    MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hwc2_display_t>(int32_t));
+    MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hwc2_display_t>());
+    MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hwc2_display_t>());
+    MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<DisplayId>(hwc2_display_t));
+    MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hwc2_display_t>(DisplayId));
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
new file mode 100644
index 0000000..0df1dab
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2019 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 <cmath>
+
+#include <compositionengine/impl/Output.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gtest/gtest.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+#include "RegionMatcher.h"
+#include "TransformMatcher.h"
+
+namespace android::compositionengine {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+using testing::StrictMock;
+
+class OutputTest : public testing::Test {
+public:
+    OutputTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+    ~OutputTest() override = default;
+
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    impl::Output mOutput{mCompositionEngine};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(OutputTest, canInstantiateOutput) {
+    // The validation check checks each required component.
+    EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderSurface, isValid()).WillOnce(Return(true));
+
+    EXPECT_TRUE(mOutput.isValid());
+
+    // If we take away the required components, it is no longer valid.
+    mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>());
+
+    EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
+
+    EXPECT_FALSE(mOutput.isValid());
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setCompositionEnabled()
+ */
+
+TEST_F(OutputTest, setCompositionEnabledDoesNothingIfAlreadyEnabled) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().isEnabled = true;
+
+    mOutput.setCompositionEnabled(true);
+
+    EXPECT_TRUE(mOutput.getState().isEnabled);
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+}
+
+TEST_F(OutputTest, setCompositionEnabledSetsEnabledAndDirtiesEntireOutput) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().isEnabled = false;
+
+    mOutput.setCompositionEnabled(true);
+
+    EXPECT_TRUE(mOutput.getState().isEnabled);
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().isEnabled = true;
+
+    mOutput.setCompositionEnabled(false);
+
+    EXPECT_FALSE(mOutput.getState().isEnabled);
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setProjection()
+ */
+
+TEST_F(OutputTest, setProjectionTriviallyWorks) {
+    const ui::Transform transform{ui::Transform::ROT_180};
+    const int32_t orientation = 123;
+    const Rect frame{1, 2, 3, 4};
+    const Rect viewport{5, 6, 7, 8};
+    const Rect scissor{9, 10, 11, 12};
+    const bool needsFiltering = true;
+
+    mOutput.setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
+
+    EXPECT_THAT(mOutput.getState().transform, TransformEq(transform));
+    EXPECT_EQ(orientation, mOutput.getState().orientation);
+    EXPECT_EQ(frame, mOutput.getState().frame);
+    EXPECT_EQ(viewport, mOutput.getState().viewport);
+    EXPECT_EQ(scissor, mOutput.getState().scissor);
+    EXPECT_EQ(needsFiltering, mOutput.getState().needsFiltering);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setBounds()
+ */
+
+TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
+    const ui::Size displaySize{100, 200};
+
+    EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
+    EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
+
+    mOutput.setBounds(displaySize);
+
+    EXPECT_EQ(Rect(displaySize), mOutput.getState().bounds);
+
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setLayerStackFilter()
+ */
+
+TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+
+    const uint32_t layerStack = 123u;
+    mOutput.setLayerStackFilter(layerStack, true);
+
+    EXPECT_TRUE(mOutput.getState().layerStackInternal);
+    EXPECT_EQ(layerStack, mOutput.getState().layerStackId);
+
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setColorTransform
+ */
+
+TEST_F(OutputTest, setColorTransformSetsTransform) {
+    // Identity matrix sets an identity state value
+    const mat4 identity;
+
+    mOutput.setColorTransform(identity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mOutput.getState().colorTransform);
+
+    // Non-identity matrix sets a non-identity state value
+    const mat4 nonIdentity = mat4() * 2;
+
+    mOutput.setColorTransform(nonIdentity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setColorMode
+ */
+
+TEST_F(OutputTest, setColorModeSetsModeUnlessNoChange) {
+    EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::SRGB)).Times(1);
+
+    mOutput.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+                         ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::BT2100_PQ, mOutput.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::SRGB, mOutput.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput.getState().renderIntent);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setRenderSurface()
+ */
+
+TEST_F(OutputTest, setRenderSurfaceResetsBounds) {
+    const ui::Size newDisplaySize{640, 480};
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    EXPECT_CALL(*renderSurface, getSize()).WillOnce(ReturnRef(newDisplaySize));
+
+    mOutput.setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
+
+    EXPECT_EQ(Rect(newDisplaySize), mOutput.getState().bounds);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::getDirtyRegion()
+ */
+
+TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
+    const Rect viewport{100, 200};
+    mOutput.editState().viewport = viewport;
+    mOutput.editState().dirtyRegion.set(50, 300);
+
+    {
+        Region result = mOutput.getDirtyRegion(true);
+
+        EXPECT_THAT(result, RegionEq(Region(viewport)));
+    }
+}
+
+TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
+    const Rect viewport{100, 200};
+    mOutput.editState().viewport = viewport;
+    mOutput.editState().dirtyRegion.set(50, 300);
+
+    {
+        Region result = mOutput.getDirtyRegion(false);
+
+        // The dirtyRegion should be clipped to the display bounds.
+        EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
+    }
+}
+
+/* ------------------------------------------------------------------------
+ * Output::belongsInOutput()
+ */
+
+TEST_F(OutputTest, belongsInOutputFiltersAsExpected) {
+    const uint32_t layerStack1 = 123u;
+    const uint32_t layerStack2 = 456u;
+
+    // If the output accepts layerStack1 and internal-only layers....
+    mOutput.setLayerStackFilter(layerStack1, true);
+
+    // Any layer with layerStack1 belongs to it, internal-only or not.
+    EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
+    EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, true));
+    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
+    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+
+    // If the output accepts layerStack21 but not internal-only layers...
+    mOutput.setLayerStackFilter(layerStack1, false);
+
+    // Only non-internal layers with layerStack1 belong to it.
+    EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
+    EXPECT_FALSE(mOutput.belongsInOutput(layerStack1, true));
+    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
+    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
new file mode 100644
index 0000000..5a4efa9
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 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 <gmock/gmock.h>
+
+namespace {
+
+// Checks for a region match
+MATCHER_P(RegionEq, expected, "") {
+    std::string buf;
+    buf.append("Regions are not equal\n");
+    expected.dump(buf, "expected region");
+    arg.dump(buf, "actual region");
+    *result_listener << buf;
+
+    size_t expectedRectCount = 0;
+    android::Rect const* expectedRects = expected.getArray(&expectedRectCount);
+    size_t actualRectCount = 0;
+    android::Rect const* actualRects = arg.getArray(&actualRectCount);
+
+    if (expectedRectCount != actualRectCount) return false;
+    for (size_t i = 0; i < expectedRectCount; i++) {
+        if (expectedRects[i] != actualRects[i]) return false;
+    }
+    return true;
+}
+
+} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
new file mode 100644
index 0000000..c56d92a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -0,0 +1,481 @@
+/*
+ * Copyright 2019 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 <cstdarg>
+#include <cstdint>
+
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/RenderSurface.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/Display.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <system/window.h>
+#include <ui/ANativeObjectBase.h>
+
+#include "MockHWComposer.h"
+
+namespace android::compositionengine {
+namespace {
+
+/* ------------------------------------------------------------------------
+ * MockNativeWindow
+ *
+ * An intentionally simplified Mock which implements a minimal subset of the full
+ * ANativeWindow interface.
+ */
+
+class MockNativeWindow : public ANativeObjectBase<ANativeWindow, MockNativeWindow, RefBase> {
+public:
+    MockNativeWindow() {
+        ANativeWindow::setSwapInterval = &forwardSetSwapInterval;
+        ANativeWindow::dequeueBuffer = &forwardDequeueBuffer;
+        ANativeWindow::cancelBuffer = &forwardCancelBuffer;
+        ANativeWindow::queueBuffer = &forwardQueueBuffer;
+        ANativeWindow::query = &forwardQuery;
+        ANativeWindow::perform = &forwardPerform;
+
+        ANativeWindow::dequeueBuffer_DEPRECATED = &forwardDequeueBufferDeprecated;
+        ANativeWindow::cancelBuffer_DEPRECATED = &forwardCancelBufferDeprecated;
+        ANativeWindow::lockBuffer_DEPRECATED = &forwardLockBufferDeprecated;
+        ANativeWindow::queueBuffer_DEPRECATED = &forwardQueueBufferDeprecated;
+    }
+
+    MOCK_METHOD1(setSwapInterval, int(int));
+    MOCK_METHOD2(dequeueBuffer, int(struct ANativeWindowBuffer**, int*));
+    MOCK_METHOD2(cancelBuffer, int(struct ANativeWindowBuffer*, int));
+    MOCK_METHOD2(queueBuffer, int(struct ANativeWindowBuffer*, int));
+    MOCK_CONST_METHOD2(query, int(int, int*));
+    MOCK_METHOD1(connect, int(int));
+    MOCK_METHOD1(lockBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
+    MOCK_METHOD1(setBuffersFormat, int(PixelFormat));
+    MOCK_METHOD1(setBuffersDataSpace, int(ui::Dataspace));
+    MOCK_METHOD1(setUsage, int(uint64_t));
+
+    static void unexpectedCall(...) { LOG_ALWAYS_FATAL("Unexpected ANativeWindow API call"); }
+
+    static int forwardSetSwapInterval(ANativeWindow* window, int interval) {
+        return getSelf(window)->setSwapInterval(interval);
+    }
+
+    static int forwardDequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                                    int* fenceFd) {
+        return getSelf(window)->dequeueBuffer(buffer, fenceFd);
+    }
+
+    static int forwardCancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                                   int fenceFd) {
+        return getSelf(window)->cancelBuffer(buffer, fenceFd);
+    }
+
+    static int forwardQueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+        return getSelf(window)->queueBuffer(buffer, fenceFd);
+    }
+
+    static int forwardQuery(const ANativeWindow* window, int what, int* value) {
+        return getSelf(window)->query(what, value);
+    }
+
+    static int forwardPerform(ANativeWindow* window, int operation, ...) {
+        va_list args;
+        va_start(args, operation);
+        int result = NO_ERROR;
+        switch (operation) {
+            case NATIVE_WINDOW_API_CONNECT: {
+                int api = va_arg(args, int);
+                result = getSelf(window)->connect(api);
+                break;
+            }
+            case NATIVE_WINDOW_SET_BUFFERS_FORMAT: {
+                PixelFormat format = va_arg(args, PixelFormat);
+                result = getSelf(window)->setBuffersFormat(format);
+                break;
+            }
+            case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: {
+                ui::Dataspace dataspace = static_cast<ui::Dataspace>(va_arg(args, int));
+                result = getSelf(window)->setBuffersDataSpace(dataspace);
+                break;
+            }
+            case NATIVE_WINDOW_SET_USAGE: {
+                // Note: Intentionally widens usage from 32 to 64 bits so we
+                // just have one implementation.
+                uint64_t usage = va_arg(args, uint32_t);
+                result = getSelf(window)->setUsage(usage);
+                break;
+            }
+            case NATIVE_WINDOW_SET_USAGE64: {
+                uint64_t usage = va_arg(args, uint64_t);
+                result = getSelf(window)->setUsage(usage);
+                break;
+            }
+            default:
+                LOG_ALWAYS_FATAL("Unexpected operation %d", operation);
+                break;
+        }
+
+        va_end(args);
+        return result;
+    }
+
+    static int forwardDequeueBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer** buffer) {
+        int ignoredFenceFd = -1;
+        return getSelf(window)->dequeueBuffer(buffer, &ignoredFenceFd);
+    }
+
+    static int forwardCancelBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+        return getSelf(window)->cancelBuffer(buffer, -1);
+    }
+
+    static int forwardLockBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+        return getSelf(window)->lockBuffer_DEPRECATED(buffer);
+    }
+
+    static int forwardQueueBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+        return getSelf(window)->queueBuffer(buffer, -1);
+    }
+};
+
+/* ------------------------------------------------------------------------
+ * RenderSurfaceTest
+ */
+
+constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
+constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = std::make_optional(DisplayId{123u});
+const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
+
+using testing::_;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Ref;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+using testing::StrictMock;
+
+class RenderSurfaceTest : public testing::Test {
+public:
+    RenderSurfaceTest() {
+        EXPECT_CALL(mDisplay, getId()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_ID));
+        EXPECT_CALL(mDisplay, getName()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_NAME));
+        EXPECT_CALL(mCompositionEngine, getHwComposer).WillRepeatedly(ReturnRef(mHwComposer));
+        EXPECT_CALL(mCompositionEngine, getRenderEngine).WillRepeatedly(ReturnRef(mRenderEngine));
+    }
+    ~RenderSurfaceTest() override = default;
+
+    StrictMock<android::mock::HWComposer> mHwComposer;
+    StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    StrictMock<mock::Display> mDisplay;
+    sp<MockNativeWindow> mNativeWindow = new StrictMock<MockNativeWindow>();
+    sp<mock::DisplaySurface> mDisplaySurface = new StrictMock<mock::DisplaySurface>();
+    impl::RenderSurface mSurface{mCompositionEngine, mDisplay,
+                                 RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
+                                                           DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
+                                                           mDisplaySurface}};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(RenderSurfaceTest, canInstantiate) {
+    EXPECT_TRUE(mSurface.isValid());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::initialize()
+ */
+
+TEST_F(RenderSurfaceTest, initializeConfiguresNativeWindow) {
+    EXPECT_CALL(*mNativeWindow, connect(NATIVE_WINDOW_API_EGL)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mNativeWindow, setBuffersFormat(HAL_PIXEL_FORMAT_RGBA_8888))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+
+    mSurface.initialize();
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getSize()
+ */
+
+TEST_F(RenderSurfaceTest, sizeReturnsConstructedSize) {
+    const ui::Size expected{DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT};
+
+    EXPECT_EQ(expected, mSurface.getSize());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getClientTargetAcquireFence()
+ */
+
+TEST_F(RenderSurfaceTest, getClientTargetAcquireFenceForwardsCall) {
+    sp<Fence> fence = new Fence();
+
+    EXPECT_CALL(*mDisplaySurface, getClientTargetAcquireFence()).WillOnce(ReturnRef(fence));
+
+    EXPECT_EQ(fence.get(), mSurface.getClientTargetAcquireFence().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setDisplaySize()
+ */
+
+TEST_F(RenderSurfaceTest, setDisplaySizeAppliesChange) {
+    EXPECT_CALL(*mDisplaySurface, resizeBuffers(640, 480)).Times(1);
+
+    mSurface.setDisplaySize(ui::Size(640, 480));
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setBufferDataspace()
+ */
+
+TEST_F(RenderSurfaceTest, setBufferDataspaceAppliesChange) {
+    EXPECT_CALL(*mNativeWindow, setBuffersDataSpace(ui::Dataspace::DISPLAY_P3))
+            .WillOnce(Return(NO_ERROR));
+
+    mSurface.setBufferDataspace(ui::Dataspace::DISPLAY_P3);
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setProtected()
+ */
+
+TEST_F(RenderSurfaceTest, setProtectedTrueEnablesProtection) {
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+            .WillOnce(Return(NO_ERROR));
+
+    mSurface.setProtected(true);
+}
+
+TEST_F(RenderSurfaceTest, setProtectedFalseDisablesProtection) {
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+
+    mSurface.setProtected(false);
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::beginFrame()
+ */
+
+TEST_F(RenderSurfaceTest, beginFrameAppliesChange) {
+    EXPECT_CALL(*mDisplaySurface, beginFrame(true)).WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.beginFrame(true));
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::prepareFrame()
+ */
+
+TEST_F(RenderSurfaceTest, prepareFrameTakesEarlyOutOnHwcError) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data)))
+            .WillOnce(Return(INVALID_OPERATION));
+
+    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesMixedComposition) {
+    std::vector<CompositionInfo> data;
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_MIXED))
+            .WillOnce(Return(INVALID_OPERATION));
+
+    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGlesComposition) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES))
+            .WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyHwcComposition) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+            .WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+            .WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame(data));
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::dequeueBuffer()
+ */
+
+TEST_F(RenderSurfaceTest, dequeueBufferObtainsABuffer) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+
+    EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _))
+            .WillOnce(
+                    DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
+
+    EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer().get());
+
+    EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::queueBuffer()
+ */
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID))
+            .WillOnce(Return(false));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer(base::unique_fd());
+
+    EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer(base::unique_fd());
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer(base::unique_fd());
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferYetDequeued) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _))
+            .WillOnce(
+                    DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer(base::unique_fd());
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirtualDisplay) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(INVALID_OPERATION));
+    EXPECT_CALL(mDisplay, isVirtual()).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, cancelBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer(base::unique_fd());
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::onPresentDisplayCompleted()
+ */
+
+TEST_F(RenderSurfaceTest, onPresentDisplayCompletedForwardsSignal) {
+    EXPECT_CALL(*mDisplaySurface, onFrameCommitted()).Times(1);
+
+    mSurface.onPresentDisplayCompleted();
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setViewportAndProjection()
+ */
+
+TEST_F(RenderSurfaceTest, setViewportAndProjectionAppliesChang) {
+    mSurface.setSizeForTest(ui::Size(100, 200));
+
+    EXPECT_CALL(mRenderEngine,
+                setViewportAndProjection(100, 200, Rect(100, 200), ui::Transform::ROT_0))
+            .Times(1);
+
+    mSurface.setViewportAndProjection();
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::flip()
+ */
+
+TEST_F(RenderSurfaceTest, flipForwardsSignal) {
+    mSurface.setPageFlipCountForTest(500);
+
+    mSurface.flip();
+
+    EXPECT_EQ(501, mSurface.getPageFlipCount());
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
new file mode 100644
index 0000000..ea07bed
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 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 <string>
+
+#include <gmock/gmock.h>
+
+namespace {
+
+// Check for a transform match
+MATCHER_P(TransformEq, expected, "") {
+    std::string buf;
+    buf.append("Transforms are not equal\n");
+    expected.dump(buf, "expected transform");
+    arg.dump(buf, "actual transform");
+    *result_listener << buf;
+
+    const float TOLERANCE = 1e-3f;
+
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            if (std::fabs(expected[i][j] - arg[i][j]) > TOLERANCE) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+} // namespace
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index f259d93..9530398 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -22,18 +22,19 @@
 
 namespace android {
 
-ContainerLayer::ContainerLayer(SurfaceFlinger* flinger, const sp<Client>& client,
-                               const String8& name, uint32_t w, uint32_t h, uint32_t flags)
-      : Layer(flinger, client, name, w, h, flags) {
-    mDrawingState = mCurrentState;
-}
+ContainerLayer::ContainerLayer(const LayerCreationArgs& args) : Layer(args) {}
 
-void ContainerLayer::onDraw(const RenderArea&, const Region& /* clip */, bool) const {}
+ContainerLayer::~ContainerLayer() = default;
+
+bool ContainerLayer::prepareClientLayer(const RenderArea&, const Region&, bool, Region&,
+                                        renderengine::LayerSettings&) {
+    return false;
+}
 
 bool ContainerLayer::isVisible() const {
     return !isHiddenByPolicy();
 }
 
-void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&) {}
+void ContainerLayer::setPerFrameData(DisplayId, const ui::Transform&, const Rect&, int32_t) {}
 
 } // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index b352b96..409cf22 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -25,18 +25,23 @@
 
 class ContainerLayer : public Layer {
 public:
-    ContainerLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name,
-                   uint32_t w, uint32_t h, uint32_t flags);
-    virtual ~ContainerLayer() = default;
+    explicit ContainerLayer(const LayerCreationArgs&);
+    ~ContainerLayer() override;
 
     const char* getTypeId() const override { return "ContainerLayer"; }
-    void onDraw(const RenderArea& renderArea, const Region& clip,
-                bool useIdentityTransform) const override;
     bool isVisible() const override;
 
-    void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override;
+    void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport,
+                         int32_t supportedPerFrameMetadata) override;
 
     bool isCreatedFromMainThread() const override { return true; }
+
+    bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
+
+protected:
+    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                            bool useIdentityTransform, Region& clearRegion,
+                            renderengine::LayerSettings& layer);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 309fd0a..4a13bfb 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -18,49 +18,28 @@
 #undef LOG_TAG
 #define LOG_TAG "DisplayDevice"
 
-#include <array>
-#include <unordered_set>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-
-#include <cutils/properties.h>
-
-#include <utils/RefBase.h>
-#include <utils/Log.h>
-
-#include <ui/DebugUtils.h>
-#include <ui/DisplayInfo.h>
-#include <ui/PixelFormat.h>
-
-#include <gui/Surface.h>
-
-#include <hardware/gralloc.h>
-
-#include "DisplayHardware/DisplaySurface.h"
-#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/HWC2.h"
-#include "RenderEngine/RenderEngine.h"
-
-#include "clz.h"
-#include "DisplayDevice.h"
-#include "SurfaceFlinger.h"
-#include "Layer.h"
-
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <configstore/Utils.h>
+#include <log/log.h>
+#include <system/window.h>
+#include <ui/GraphicTypes.h>
+
+#include "DisplayDevice.h"
+#include "Layer.h"
+#include "SurfaceFlinger.h"
 
 namespace android {
 
-// retrieve triple buffer setting from configstore
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-using android::ui::ColorMode;
-using android::ui::Dataspace;
-using android::ui::Hdr;
-using android::ui::RenderIntent;
+using android::base::StringAppendF;
 
 /*
  * Initialize the display to the specified values.
@@ -69,333 +48,72 @@
 
 uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0;
 
-namespace {
+DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger,
+                                                     const wp<IBinder>& displayToken,
+                                                     const std::optional<DisplayId>& displayId)
+      : flinger(flinger), displayToken(displayToken), displayId(displayId) {}
 
-// ordered list of known SDR color modes
-const std::array<ColorMode, 2> sSdrColorModes = {
-        ColorMode::DISPLAY_P3,
-        ColorMode::SRGB,
-};
+DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args)
+      : mFlinger(args.flinger),
+        mDisplayToken(args.displayToken),
+        mSequenceId(args.sequenceId),
+        mDisplayInstallOrientation(args.displayInstallOrientation),
+        mCompositionDisplay{mFlinger->getCompositionEngine().createDisplay(
+                compositionengine::DisplayCreationArgs{args.isSecure, args.isVirtual,
+                                                       args.displayId})},
+        mIsVirtual(args.isVirtual),
+        mOrientation(),
+        mActiveConfig(0),
+        mIsPrimary(args.isPrimary) {
+    mCompositionDisplay->createRenderSurface(
+            compositionengine::RenderSurfaceCreationArgs{ANativeWindow_getWidth(
+                                                                 args.nativeWindow.get()),
+                                                         ANativeWindow_getHeight(
+                                                                 args.nativeWindow.get()),
+                                                         args.nativeWindow, args.displaySurface});
 
-// ordered list of known HDR color modes
-const std::array<ColorMode, 2> sHdrColorModes = {
-        ColorMode::BT2100_PQ,
-        ColorMode::BT2100_HLG,
-};
+    mCompositionDisplay->createDisplayColorProfile(
+            compositionengine::DisplayColorProfileCreationArgs{args.hasWideColorGamut,
+                                                               std::move(args.hdrCapabilities),
+                                                               args.supportedPerFrameMetadata,
+                                                               args.hwcColorModes});
 
-// ordered list of known SDR render intents
-const std::array<RenderIntent, 2> sSdrRenderIntents = {
-        RenderIntent::ENHANCE,
-        RenderIntent::COLORIMETRIC,
-};
-
-// ordered list of known HDR render intents
-const std::array<RenderIntent, 2> sHdrRenderIntents = {
-        RenderIntent::TONE_MAP_ENHANCE,
-        RenderIntent::TONE_MAP_COLORIMETRIC,
-};
-
-// map known color mode to dataspace
-Dataspace colorModeToDataspace(ColorMode mode) {
-    switch (mode) {
-        case ColorMode::SRGB:
-            return Dataspace::SRGB;
-        case ColorMode::DISPLAY_P3:
-            return Dataspace::DISPLAY_P3;
-        case ColorMode::BT2100_HLG:
-            return Dataspace::BT2020_HLG;
-        case ColorMode::BT2100_PQ:
-            return Dataspace::BT2020_PQ;
-        default:
-            return Dataspace::UNKNOWN;
-    }
-}
-
-// Return a list of candidate color modes.
-std::vector<ColorMode> getColorModeCandidates(ColorMode mode) {
-    std::vector<ColorMode> candidates;
-
-    // add mode itself
-    candidates.push_back(mode);
-
-    // check if mode is HDR
-    bool isHdr = false;
-    for (auto hdrMode : sHdrColorModes) {
-        if (hdrMode == mode) {
-            isHdr = true;
-            break;
-        }
+    if (!mCompositionDisplay->isValid()) {
+        ALOGE("Composition Display did not validate!");
     }
 
-    // add other HDR candidates when mode is HDR
-    if (isHdr) {
-        for (auto hdrMode : sHdrColorModes) {
-            if (hdrMode != mode) {
-                candidates.push_back(hdrMode);
-            }
-        }
-    }
+    mCompositionDisplay->getRenderSurface()->initialize();
 
-    // add other SDR candidates
-    for (auto sdrMode : sSdrColorModes) {
-        if (sdrMode != mode) {
-            candidates.push_back(sdrMode);
-        }
-    }
-
-    return candidates;
-}
-
-// Return a list of candidate render intents.
-std::vector<RenderIntent> getRenderIntentCandidates(RenderIntent intent) {
-    std::vector<RenderIntent> candidates;
-
-    // add intent itself
-    candidates.push_back(intent);
-
-    // check if intent is HDR
-    bool isHdr = false;
-    for (auto hdrIntent : sHdrRenderIntents) {
-        if (hdrIntent == intent) {
-            isHdr = true;
-            break;
-        }
-    }
-
-    if (isHdr) {
-        // add other HDR candidates when intent is HDR
-        for (auto hdrIntent : sHdrRenderIntents) {
-            if (hdrIntent != intent) {
-                candidates.push_back(hdrIntent);
-            }
-        }
-    } else {
-        // add other SDR candidates when intent is SDR
-        for (auto sdrIntent : sSdrRenderIntents) {
-            if (sdrIntent != intent) {
-                candidates.push_back(sdrIntent);
-            }
-        }
-    }
-
-    return candidates;
-}
-
-// Return the best color mode supported by HWC.
-ColorMode getHwcColorMode(
-        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
-        ColorMode mode) {
-    std::vector<ColorMode> candidates = getColorModeCandidates(mode);
-    for (auto candidate : candidates) {
-        auto iter = hwcColorModes.find(candidate);
-        if (iter != hwcColorModes.end()) {
-            return candidate;
-        }
-    }
-
-    return ColorMode::NATIVE;
-}
-
-// Return the best render intent supported by HWC.
-RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, RenderIntent intent) {
-    std::vector<RenderIntent> candidates = getRenderIntentCandidates(intent);
-    for (auto candidate : candidates) {
-        for (auto hwcIntent : hwcIntents) {
-            if (candidate == hwcIntent) {
-                return candidate;
-            }
-        }
-    }
-
-    return RenderIntent::COLORIMETRIC;
-}
-
-} // anonymous namespace
-
-// clang-format off
-DisplayDevice::DisplayDevice(
-        const sp<SurfaceFlinger>& flinger,
-        DisplayType type,
-        int32_t hwcId,
-        bool isSecure,
-        const wp<IBinder>& displayToken,
-        const sp<ANativeWindow>& nativeWindow,
-        const sp<DisplaySurface>& displaySurface,
-        std::unique_ptr<RE::Surface> renderSurface,
-        int displayWidth,
-        int displayHeight,
-        bool hasWideColorGamut,
-        const HdrCapabilities& hdrCapabilities,
-        const int32_t supportedPerFrameMetadata,
-        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
-        int initialPowerMode)
-    : lastCompositionHadVisibleLayers(false),
-      mFlinger(flinger),
-      mType(type),
-      mHwcDisplayId(hwcId),
-      mDisplayToken(displayToken),
-      mNativeWindow(nativeWindow),
-      mDisplaySurface(displaySurface),
-      mSurface{std::move(renderSurface)},
-      mDisplayWidth(displayWidth),
-      mDisplayHeight(displayHeight),
-      mPageFlipCount(0),
-      mIsSecure(isSecure),
-      mLayerStack(NO_LAYER_STACK),
-      mOrientation(),
-      mViewport(Rect::INVALID_RECT),
-      mFrame(Rect::INVALID_RECT),
-      mPowerMode(initialPowerMode),
-      mActiveConfig(0),
-      mColorTransform(HAL_COLOR_TRANSFORM_IDENTITY),
-      mHasWideColorGamut(hasWideColorGamut),
-      mHasHdr10(false),
-      mHasHLG(false),
-      mHasDolbyVision(false),
-      mSupportedPerFrameMetadata(supportedPerFrameMetadata)
-{
-    // clang-format on
-    populateColorModes(hwcColorModes);
-
-    std::vector<Hdr> types = hdrCapabilities.getSupportedHdrTypes();
-    for (Hdr hdrType : types) {
-        switch (hdrType) {
-            case Hdr::HDR10:
-                mHasHdr10 = true;
-                break;
-            case Hdr::HLG:
-                mHasHLG = true;
-                break;
-            case Hdr::DOLBY_VISION:
-                mHasDolbyVision = true;
-                break;
-            default:
-                ALOGE("UNKNOWN HDR capability: %d", static_cast<int32_t>(hdrType));
-        }
-    }
-
-    float minLuminance = hdrCapabilities.getDesiredMinLuminance();
-    float maxLuminance = hdrCapabilities.getDesiredMaxLuminance();
-    float maxAverageLuminance = hdrCapabilities.getDesiredMaxAverageLuminance();
-
-    minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance;
-    maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance;
-    maxAverageLuminance = maxAverageLuminance <= 0.0 ? sDefaultMaxLumiance : maxAverageLuminance;
-    if (this->hasWideColorGamut()) {
-        // insert HDR10/HLG as we will force client composition for HDR10/HLG
-        // layers
-        if (!hasHDR10Support()) {
-          types.push_back(Hdr::HDR10);
-        }
-
-        if (!hasHLGSupport()) {
-          types.push_back(Hdr::HLG);
-        }
-    }
-    mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
+    setPowerMode(args.initialPowerMode);
 
     // initialize the display orientation transform.
-    setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
+    setProjection(DisplayState::eOrientationDefault, Rect::INVALID_RECT, Rect::INVALID_RECT);
 }
 
 DisplayDevice::~DisplayDevice() = default;
 
-void DisplayDevice::disconnect(HWComposer& hwc) {
-    if (mHwcDisplayId >= 0) {
-        hwc.disconnectDisplay(mHwcDisplayId);
-        mHwcDisplayId = -1;
-    }
-}
-
-bool DisplayDevice::isValid() const {
-    return mFlinger != nullptr;
+void DisplayDevice::disconnect() {
+    mCompositionDisplay->disconnect();
 }
 
 int DisplayDevice::getWidth() const {
-    return mDisplayWidth;
+    return mCompositionDisplay->getState().bounds.getWidth();
 }
 
 int DisplayDevice::getHeight() const {
-    return mDisplayHeight;
+    return mCompositionDisplay->getState().bounds.getHeight();
 }
 
-void DisplayDevice::setDisplayName(const String8& displayName) {
-    if (!displayName.isEmpty()) {
+void DisplayDevice::setDisplayName(const std::string& displayName) {
+    if (!displayName.empty()) {
         // never override the name with an empty name
         mDisplayName = displayName;
+        mCompositionDisplay->setName(displayName);
     }
 }
 
 uint32_t DisplayDevice::getPageFlipCount() const {
-    return mPageFlipCount;
-}
-
-void DisplayDevice::flip() const
-{
-    mFlinger->getRenderEngine().checkErrors();
-    mPageFlipCount++;
-}
-
-status_t DisplayDevice::beginFrame(bool mustRecompose) const {
-    return mDisplaySurface->beginFrame(mustRecompose);
-}
-
-status_t DisplayDevice::prepareFrame(HWComposer& hwc) {
-    status_t error = hwc.prepare(*this);
-    if (error != NO_ERROR) {
-        return error;
-    }
-
-    DisplaySurface::CompositionType compositionType;
-    bool hasClient = hwc.hasClientComposition(mHwcDisplayId);
-    bool hasDevice = hwc.hasDeviceComposition(mHwcDisplayId);
-    if (hasClient && hasDevice) {
-        compositionType = DisplaySurface::COMPOSITION_MIXED;
-    } else if (hasClient) {
-        compositionType = DisplaySurface::COMPOSITION_GLES;
-    } else if (hasDevice) {
-        compositionType = DisplaySurface::COMPOSITION_HWC;
-    } else {
-        // Nothing to do -- when turning the screen off we get a frame like
-        // this. Call it a HWC frame since we won't be doing any GLES work but
-        // will do a prepare/set cycle.
-        compositionType = DisplaySurface::COMPOSITION_HWC;
-    }
-    return mDisplaySurface->prepareFrame(compositionType);
-}
-
-void DisplayDevice::swapBuffers(HWComposer& hwc) const {
-    if (hwc.hasClientComposition(mHwcDisplayId) || hwc.hasFlipClientTargetRequest(mHwcDisplayId)) {
-        mSurface->swapBuffers();
-    }
-
-    status_t result = mDisplaySurface->advanceFrame();
-    if (result != NO_ERROR) {
-        ALOGE("[%s] failed pushing new frame to HWC: %d",
-                mDisplayName.string(), result);
-    }
-}
-
-void DisplayDevice::onSwapBuffersCompleted() const {
-    mDisplaySurface->onFrameCommitted();
-}
-
-bool DisplayDevice::makeCurrent() const {
-    bool success = mFlinger->getRenderEngine().setCurrentSurface(*mSurface);
-    setViewportAndProjection();
-    return success;
-}
-
-void DisplayDevice::setViewportAndProjection() const {
-    size_t w = mDisplayWidth;
-    size_t h = mDisplayHeight;
-    Rect sourceCrop(0, 0, w, h);
-    mFlinger->getRenderEngine().setViewportAndProjection(w, h, sourceCrop, h,
-        false, Transform::ROT_0);
-}
-
-const sp<Fence>& DisplayDevice::getClientTargetAcquireFence() const {
-    return mDisplaySurface->getClientTargetAcquireFence();
+    return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
 }
 
 // ----------------------------------------------------------------------------
@@ -416,29 +134,18 @@
     return mLayersNeedingFences;
 }
 
-Region DisplayDevice::getDirtyRegion(bool repaintEverything) const {
-    Region dirty;
-    if (repaintEverything) {
-        dirty.set(getBounds());
-    } else {
-        const Transform& planeTransform(mGlobalTransform);
-        dirty = planeTransform.transform(this->dirtyRegion);
-        dirty.andSelf(getBounds());
-    }
-    return dirty;
-}
-
 // ----------------------------------------------------------------------------
 void DisplayDevice::setPowerMode(int mode) {
     mPowerMode = mode;
+    getCompositionDisplay()->setCompositionEnabled(mPowerMode != HWC_POWER_MODE_OFF);
 }
 
 int DisplayDevice::getPowerMode()  const {
     return mPowerMode;
 }
 
-bool DisplayDevice::isDisplayOn() const {
-    return (mPowerMode != HWC_POWER_MODE_OFF);
+bool DisplayDevice::isPoweredOn() const {
+    return mPowerMode != HWC_POWER_MODE_OFF;
 }
 
 // ----------------------------------------------------------------------------
@@ -451,88 +158,37 @@
 }
 
 // ----------------------------------------------------------------------------
-void DisplayDevice::setActiveColorMode(ColorMode mode) {
-    mActiveColorMode = mode;
-}
-
-ColorMode DisplayDevice::getActiveColorMode() const {
-    return mActiveColorMode;
-}
-
-RenderIntent DisplayDevice::getActiveRenderIntent() const {
-    return mActiveRenderIntent;
-}
-
-void DisplayDevice::setActiveRenderIntent(RenderIntent renderIntent) {
-    mActiveRenderIntent = renderIntent;
-}
-
-void DisplayDevice::setColorTransform(const mat4& transform) {
-    const bool isIdentity = (transform == mat4());
-    mColorTransform =
-            isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
-}
-
-android_color_transform_t DisplayDevice::getColorTransform() const {
-    return mColorTransform;
-}
-
-void DisplayDevice::setCompositionDataSpace(ui::Dataspace dataspace) {
-    mCompositionDataSpace = dataspace;
-    ANativeWindow* const window = mNativeWindow.get();
-    native_window_set_buffers_data_space(window, static_cast<android_dataspace>(dataspace));
-}
 
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
-    return mCompositionDataSpace;
+    return mCompositionDisplay->getState().dataspace;
 }
 
 // ----------------------------------------------------------------------------
 
 void DisplayDevice::setLayerStack(uint32_t stack) {
-    mLayerStack = stack;
-    dirtyRegion.set(bounds());
+    mCompositionDisplay->setLayerStackFilter(stack, isPrimary());
 }
 
 // ----------------------------------------------------------------------------
 
-uint32_t DisplayDevice::getOrientationTransform() const {
-    uint32_t transform = 0;
-    switch (mOrientation) {
-        case DisplayState::eOrientationDefault:
-            transform = Transform::ROT_0;
-            break;
-        case DisplayState::eOrientation90:
-            transform = Transform::ROT_90;
-            break;
-        case DisplayState::eOrientation180:
-            transform = Transform::ROT_180;
-            break;
-        case DisplayState::eOrientation270:
-            transform = Transform::ROT_270;
-            break;
-    }
-    return transform;
-}
-
-status_t DisplayDevice::orientationToTransfrom(
-        int orientation, int w, int h, Transform* tr)
-{
-    uint32_t flags = 0;
+uint32_t DisplayDevice::displayStateOrientationToTransformOrientation(int orientation) {
     switch (orientation) {
     case DisplayState::eOrientationDefault:
-        flags = Transform::ROT_0;
-        break;
+        return ui::Transform::ROT_0;
     case DisplayState::eOrientation90:
-        flags = Transform::ROT_90;
-        break;
+        return ui::Transform::ROT_90;
     case DisplayState::eOrientation180:
-        flags = Transform::ROT_180;
-        break;
+        return ui::Transform::ROT_180;
     case DisplayState::eOrientation270:
-        flags = Transform::ROT_270;
-        break;
+        return ui::Transform::ROT_270;
     default:
+        return ui::Transform::ROT_INVALID;
+    }
+}
+
+status_t DisplayDevice::orientationToTransfrom(int orientation, int w, int h, ui::Transform* tr) {
+    uint32_t flags = displayStateOrientationToTransformOrientation(orientation);
+    if (flags == ui::Transform::ROT_INVALID) {
         return BAD_VALUE;
     }
     tr->set(flags, w, h);
@@ -540,21 +196,7 @@
 }
 
 void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
-    dirtyRegion.set(getBounds());
-
-    mSurface->setNativeWindow(nullptr);
-
-    mDisplaySurface->resizeBuffers(newWidth, newHeight);
-
-    ANativeWindow* const window = mNativeWindow.get();
-    mSurface->setNativeWindow(window);
-    mDisplayWidth = mSurface->queryWidth();
-    mDisplayHeight = mSurface->queryHeight();
-
-    LOG_FATAL_IF(mDisplayWidth != newWidth,
-                "Unable to set new width to %d", newWidth);
-    LOG_FATAL_IF(mDisplayHeight != newHeight,
-                "Unable to set new height to %d", newHeight);
+    mCompositionDisplay->setBounds(ui::Size(newWidth, newHeight));
 }
 
 void DisplayDevice::setProjection(int orientation,
@@ -562,10 +204,13 @@
     Rect viewport(newViewport);
     Rect frame(newFrame);
 
-    const int w = mDisplayWidth;
-    const int h = mDisplayHeight;
+    mOrientation = orientation;
 
-    Transform R;
+    const Rect& displayBounds = getCompositionDisplay()->getState().bounds;
+    const int w = displayBounds.width();
+    const int h = displayBounds.height();
+
+    ui::Transform R;
     DisplayDevice::orientationToTransfrom(orientation, w, h, &R);
 
     if (!frame.isValid()) {
@@ -580,16 +225,14 @@
         // it's also invalid to have an empty viewport, so we handle that
         // case in the same way.
         viewport = Rect(w, h);
-        if (R.getOrientation() & Transform::ROT_90) {
+        if (R.getOrientation() & ui::Transform::ROT_90) {
             // viewport is always specified in the logical orientation
             // of the display (ie: post-rotation).
-            swap(viewport.right, viewport.bottom);
+            std::swap(viewport.right, viewport.bottom);
         }
     }
 
-    dirtyRegion.set(getBounds());
-
-    Transform TL, TP, S;
+    ui::Transform TL, TP, S;
     float src_width  = viewport.width();
     float src_height = viewport.height();
     float dst_width  = frame.width();
@@ -607,205 +250,133 @@
     TL.set(-src_x, -src_y);
     TP.set(dst_x, dst_y);
 
-    // need to take care of primary display rotation for mGlobalTransform
+    // need to take care of primary display rotation for globalTransform
     // for case if the panel is not installed aligned with device orientation
-    if (mType == DisplayType::DISPLAY_PRIMARY) {
-        int primaryDisplayOrientation = mFlinger->getPrimaryDisplayOrientation();
+    if (isPrimary()) {
         DisplayDevice::orientationToTransfrom(
-                (orientation + primaryDisplayOrientation) % (DisplayState::eOrientation270 + 1),
+                (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1),
                 w, h, &R);
     }
 
     // The viewport and frame are both in the logical orientation.
     // Apply the logical translation, scale to physical size, apply the
     // physical translation and finally rotate to the physical orientation.
-    mGlobalTransform = R * TP * S * TL;
+    ui::Transform globalTransform = R * TP * S * TL;
 
-    const uint8_t type = mGlobalTransform.getType();
-    mNeedsFiltering = (!mGlobalTransform.preserveRects() ||
-            (type >= Transform::SCALE));
+    const uint8_t type = globalTransform.getType();
+    const bool needsFiltering =
+            (!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
 
-    mScissor = mGlobalTransform.transform(viewport);
-    if (mScissor.isEmpty()) {
-        mScissor = getBounds();
+    Rect scissor = globalTransform.transform(viewport);
+    if (scissor.isEmpty()) {
+        scissor = displayBounds;
     }
 
-    mOrientation = orientation;
-    if (mType == DisplayType::DISPLAY_PRIMARY) {
-        uint32_t transform = 0;
-        switch (mOrientation) {
-            case DisplayState::eOrientationDefault:
-                transform = Transform::ROT_0;
-                break;
-            case DisplayState::eOrientation90:
-                transform = Transform::ROT_90;
-                break;
-            case DisplayState::eOrientation180:
-                transform = Transform::ROT_180;
-                break;
-            case DisplayState::eOrientation270:
-                transform = Transform::ROT_270;
-                break;
-        }
-        sPrimaryDisplayOrientation = transform;
+    if (isPrimary()) {
+        sPrimaryDisplayOrientation = displayStateOrientationToTransformOrientation(orientation);
     }
-    mViewport = viewport;
-    mFrame = frame;
+
+    getCompositionDisplay()->setProjection(globalTransform,
+                                           displayStateOrientationToTransformOrientation(
+                                                   orientation),
+                                           frame, viewport, scissor, needsFiltering);
 }
 
 uint32_t DisplayDevice::getPrimaryDisplayOrientationTransform() {
     return sPrimaryDisplayOrientation;
 }
 
-void DisplayDevice::dump(String8& result) const {
-    const Transform& tr(mGlobalTransform);
-    ANativeWindow* const window = mNativeWindow.get();
-    result.appendFormat("+ DisplayDevice: %s\n", mDisplayName.string());
-    result.appendFormat("   type=%x, hwcId=%d, layerStack=%u, (%4dx%4d), ANativeWindow=%p "
-                        "(%d:%d:%d:%d), orient=%2d (type=%08x), "
-                        "flips=%u, isSecure=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n",
-                        mType, mHwcDisplayId, mLayerStack, mDisplayWidth, mDisplayHeight, window,
-                        mSurface->queryRedSize(), mSurface->queryGreenSize(),
-                        mSurface->queryBlueSize(), mSurface->queryAlphaSize(), mOrientation,
-                        tr.getType(), getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig,
-                        mVisibleLayersSortedByZ.size());
-    result.appendFormat("   v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d],"
-                        "transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n",
-                        mViewport.left, mViewport.top, mViewport.right, mViewport.bottom,
-                        mFrame.left, mFrame.top, mFrame.right, mFrame.bottom, mScissor.left,
-                        mScissor.top, mScissor.right, mScissor.bottom, tr[0][0], tr[1][0], tr[2][0],
-                        tr[0][1], tr[1][1], tr[2][1], tr[0][2], tr[1][2], tr[2][2]);
-    auto const surface = static_cast<Surface*>(window);
-    ui::Dataspace dataspace = surface->getBuffersDataSpace();
-    result.appendFormat("   wideColorGamut=%d, hdr10=%d, colorMode=%s, dataspace: %s (%d)\n",
-                        mHasWideColorGamut, mHasHdr10,
-                        decodeColorMode(mActiveColorMode).c_str(),
-                        dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(), dataspace);
-
-    String8 surfaceDump;
-    mDisplaySurface->dumpAsString(surfaceDump);
-    result.append(surfaceDump);
+std::string DisplayDevice::getDebugName() const {
+    const auto id = getId() ? to_string(*getId()) + ", " : std::string();
+    return base::StringPrintf("DisplayDevice{%s%s%s\"%s\"}", id.c_str(),
+                              isPrimary() ? "primary, " : "", isVirtual() ? "virtual, " : "",
+                              mDisplayName.c_str());
 }
 
-// Map dataspace/intent to the best matched dataspace/colorMode/renderIntent
-// supported by HWC.
-void DisplayDevice::addColorMode(
-        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
-        const ColorMode mode, const RenderIntent intent) {
-    // find the best color mode
-    const ColorMode hwcColorMode = getHwcColorMode(hwcColorModes, mode);
+void DisplayDevice::dump(std::string& result) const {
+    StringAppendF(&result, "+ %s\n", getDebugName().c_str());
 
-    // find the best render intent
-    auto iter = hwcColorModes.find(hwcColorMode);
-    const auto& hwcIntents =
-            iter != hwcColorModes.end() ? iter->second : std::vector<RenderIntent>();
-    const RenderIntent hwcIntent = getHwcRenderIntent(hwcIntents, intent);
-
-    const Dataspace dataspace = colorModeToDataspace(mode);
-    const Dataspace hwcDataspace = colorModeToDataspace(hwcColorMode);
-
-    ALOGV("DisplayDevice %d/%d: map (%s, %s) to (%s, %s, %s)", mType, mHwcDisplayId,
-          dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
-          decodeRenderIntent(intent).c_str(),
-          dataspaceDetails(static_cast<android_dataspace_t>(hwcDataspace)).c_str(),
-          decodeColorMode(hwcColorMode).c_str(), decodeRenderIntent(hwcIntent).c_str());
-
-    mColorModes[getColorModeKey(dataspace, intent)] = {hwcDataspace, hwcColorMode, hwcIntent};
+    result.append("   ");
+    StringAppendF(&result, "powerMode=%d, ", mPowerMode);
+    StringAppendF(&result, "activeConfig=%d, ", mActiveConfig);
+    StringAppendF(&result, "numLayers=%zu\n", mVisibleLayersSortedByZ.size());
+    getCompositionDisplay()->dump(result);
 }
 
-void DisplayDevice::populateColorModes(
-        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes) {
-    if (!hasWideColorGamut()) {
-        return;
-    }
-
-    // collect all known SDR render intents
-    std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
-                                                      sSdrRenderIntents.end());
-    auto iter = hwcColorModes.find(ColorMode::SRGB);
-    if (iter != hwcColorModes.end()) {
-        for (auto intent : iter->second) {
-            sdrRenderIntents.insert(intent);
-        }
-    }
-
-    // add all known SDR combinations
-    for (auto intent : sdrRenderIntents) {
-        for (auto mode : sSdrColorModes) {
-            addColorMode(hwcColorModes, mode, intent);
-        }
-    }
-
-    // collect all known HDR render intents
-    std::unordered_set<RenderIntent> hdrRenderIntents(sHdrRenderIntents.begin(),
-                                                      sHdrRenderIntents.end());
-    iter = hwcColorModes.find(ColorMode::BT2100_PQ);
-    if (iter != hwcColorModes.end()) {
-        for (auto intent : iter->second) {
-            hdrRenderIntents.insert(intent);
-        }
-    }
-
-    // add all known HDR combinations
-    for (auto intent : sHdrRenderIntents) {
-        for (auto mode : sHdrColorModes) {
-            addColorMode(hwcColorModes, mode, intent);
-        }
-    }
+bool DisplayDevice::hasRenderIntent(ui::RenderIntent intent) const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasRenderIntent(intent);
 }
 
-bool DisplayDevice::hasRenderIntent(RenderIntent intent) const {
-    // assume a render intent is supported when SRGB supports it; we should
-    // get rid of that assumption.
-    auto iter = mColorModes.find(getColorModeKey(Dataspace::SRGB, intent));
-    return iter != mColorModes.end() && iter->second.renderIntent == intent;
+// ----------------------------------------------------------------------------
+
+const std::optional<DisplayId>& DisplayDevice::getId() const {
+    return mCompositionDisplay->getId();
 }
 
-bool DisplayDevice::hasLegacyHdrSupport(Dataspace dataspace) const {
-    if ((dataspace == Dataspace::BT2020_PQ && hasHDR10Support()) ||
-        (dataspace == Dataspace::BT2020_HLG && hasHLGSupport())) {
-        auto iter =
-                mColorModes.find(getColorModeKey(dataspace, RenderIntent::TONE_MAP_COLORIMETRIC));
-        return iter == mColorModes.end() || iter->second.dataspace != dataspace;
-    }
-
-    return false;
+bool DisplayDevice::isSecure() const {
+    return mCompositionDisplay->isSecure();
 }
 
-void DisplayDevice::getBestColorMode(Dataspace dataspace, RenderIntent intent,
-                                     Dataspace* outDataspace, ColorMode* outMode,
-                                     RenderIntent* outIntent) const {
-    auto iter = mColorModes.find(getColorModeKey(dataspace, intent));
-    if (iter != mColorModes.end()) {
-        *outDataspace = iter->second.dataspace;
-        *outMode = iter->second.colorMode;
-        *outIntent = iter->second.renderIntent;
-    } else {
-        // this is unexpected on a WCG display
-        if (hasWideColorGamut()) {
-            ALOGE("map unknown (%s)/(%s) to default color mode",
-                  dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
-                  decodeRenderIntent(intent).c_str());
-        }
-
-        *outDataspace = Dataspace::UNKNOWN;
-        *outMode = ColorMode::NATIVE;
-        *outIntent = RenderIntent::COLORIMETRIC;
-    }
+const Rect& DisplayDevice::getBounds() const {
+    return mCompositionDisplay->getState().bounds;
 }
 
-std::atomic<int32_t> DisplayDeviceState::nextDisplayId(1);
-
-DisplayDeviceState::DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure)
-    : type(type),
-      layerStack(DisplayDevice::NO_LAYER_STACK),
-      orientation(0),
-      width(0),
-      height(0),
-      isSecure(isSecure)
-{
-    viewport.makeInvalid();
-    frame.makeInvalid();
+const Region& DisplayDevice::getUndefinedRegion() const {
+    return mCompositionDisplay->getState().undefinedRegion;
 }
 
+bool DisplayDevice::needsFiltering() const {
+    return mCompositionDisplay->getState().needsFiltering;
+}
+
+uint32_t DisplayDevice::getLayerStack() const {
+    return mCompositionDisplay->getState().layerStackId;
+}
+
+const ui::Transform& DisplayDevice::getTransform() const {
+    return mCompositionDisplay->getState().transform;
+}
+
+const Rect& DisplayDevice::getViewport() const {
+    return mCompositionDisplay->getState().viewport;
+}
+
+const Rect& DisplayDevice::getFrame() const {
+    return mCompositionDisplay->getState().frame;
+}
+
+const Rect& DisplayDevice::getScissor() const {
+    return mCompositionDisplay->getState().scissor;
+}
+
+bool DisplayDevice::hasWideColorGamut() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasWideColorGamut();
+}
+
+bool DisplayDevice::hasHDR10PlusSupport() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasHDR10PlusSupport();
+}
+
+bool DisplayDevice::hasHDR10Support() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasHDR10Support();
+}
+
+bool DisplayDevice::hasHLGSupport() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasHLGSupport();
+}
+
+bool DisplayDevice::hasDolbyVisionSupport() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasDolbyVisionSupport();
+}
+
+int DisplayDevice::getSupportedPerFrameMetadata() const {
+    return mCompositionDisplay->getDisplayColorProfile()->getSupportedPerFrameMetadata();
+}
+
+const HdrCapabilities& DisplayDevice::getHdrCapabilities() const {
+    return mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities();
+}
+
+std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
+
 }  // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 6c3bd91..9674f0d 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -17,142 +17,107 @@
 #ifndef ANDROID_DISPLAY_DEVICE_H
 #define ANDROID_DISPLAY_DEVICE_H
 
-#include "Transform.h"
-
 #include <stdlib.h>
+
+#include <memory>
+#include <optional>
+#include <string>
 #include <unordered_map>
 
-#include <math/mat4.h>
-
+#include <android/native_window.h>
 #include <binder/IBinder.h>
-#include <gui/ISurfaceComposer.h>
+#include <gui/LayerState.h>
 #include <hardware/hwcomposer_defs.h>
+#include <math/mat4.h>
+#include <renderengine/RenderEngine.h>
+#include <system/window.h>
 #include <ui/GraphicTypes.h>
 #include <ui/HdrCapabilities.h>
 #include <ui/Region.h>
-#include <utils/RefBase.h>
+#include <ui/Transform.h>
 #include <utils/Mutex.h>
-#include <utils/String8.h>
+#include <utils/RefBase.h>
 #include <utils/Timers.h>
 
+#include "DisplayHardware/DisplayIdentification.h"
 #include "RenderArea.h"
-#include "RenderEngine/Surface.h"
-
-#include <memory>
-
-struct ANativeWindow;
 
 namespace android {
 
-struct DisplayInfo;
-class DisplaySurface;
 class Fence;
+class HWComposer;
 class IGraphicBufferProducer;
 class Layer;
 class SurfaceFlinger;
-class HWComposer;
 
-class DisplayDevice : public LightRefBase<DisplayDevice>
-{
+struct CompositionInfo;
+struct DisplayDeviceCreationArgs;
+struct DisplayInfo;
+
+namespace compositionengine {
+class Display;
+class DisplaySurface;
+} // namespace compositionengine
+
+class DisplayDevice : public LightRefBase<DisplayDevice> {
 public:
     constexpr static float sDefaultMinLumiance = 0.0;
     constexpr static float sDefaultMaxLumiance = 500.0;
 
-    // region in layer-stack space
-    mutable Region dirtyRegion;
-    // region in screen space
-    Region undefinedRegion;
-    bool lastCompositionHadVisibleLayers;
-
-    enum DisplayType {
-        DISPLAY_ID_INVALID = -1,
-        DISPLAY_PRIMARY     = HWC_DISPLAY_PRIMARY,
-        DISPLAY_EXTERNAL    = HWC_DISPLAY_EXTERNAL,
-        DISPLAY_VIRTUAL     = HWC_DISPLAY_VIRTUAL,
-        NUM_BUILTIN_DISPLAY_TYPES = HWC_NUM_PHYSICAL_DISPLAY_TYPES,
-    };
-
     enum {
         NO_LAYER_STACK = 0xFFFFFFFF,
     };
 
-    // clang-format off
-    DisplayDevice(
-            const sp<SurfaceFlinger>& flinger,
-            DisplayType type,
-            int32_t hwcId,
-            bool isSecure,
-            const wp<IBinder>& displayToken,
-            const sp<ANativeWindow>& nativeWindow,
-            const sp<DisplaySurface>& displaySurface,
-            std::unique_ptr<RE::Surface> renderSurface,
-            int displayWidth,
-            int displayHeight,
-            bool hasWideColorGamut,
-            const HdrCapabilities& hdrCapabilities,
-            const int32_t supportedPerFrameMetadata,
-            const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes,
-            int initialPowerMode);
-    // clang-format on
+    explicit DisplayDevice(DisplayDeviceCreationArgs&& args);
+    virtual ~DisplayDevice();
 
-    ~DisplayDevice();
+    std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
+        return mCompositionDisplay;
+    }
 
-    // whether this is a valid object. An invalid DisplayDevice is returned
-    // when an non existing id is requested
-    bool isValid() const;
+    bool isVirtual() const { return mIsVirtual; }
+    bool isPrimary() const { return mIsPrimary; }
 
     // isSecure indicates whether this display can be trusted to display
     // secure surfaces.
-    bool isSecure() const { return mIsSecure; }
-
-    // Flip the front and back buffers if the back buffer is "dirty".  Might
-    // be instantaneous, might involve copying the frame buffer around.
-    void flip() const;
+    bool isSecure() const;
 
     int         getWidth() const;
     int         getHeight() const;
+    int         getInstallOrientation() const { return mDisplayInstallOrientation; }
 
     void                    setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers);
     const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const;
     void                    setLayersNeedingFences(const Vector< sp<Layer> >& layers);
     const Vector< sp<Layer> >& getLayersNeedingFences() const;
-    Region                  getDirtyRegion(bool repaintEverything) const;
 
     void                    setLayerStack(uint32_t stack);
     void                    setDisplaySize(const int newWidth, const int newHeight);
     void                    setProjection(int orientation, const Rect& viewport, const Rect& frame);
 
     int                     getOrientation() const { return mOrientation; }
-    uint32_t                getOrientationTransform() const;
     static uint32_t         getPrimaryDisplayOrientationTransform();
-    const Transform&        getTransform() const { return mGlobalTransform; }
-    const Rect              getViewport() const { return mViewport; }
-    const Rect              getFrame() const { return mFrame; }
-    const Rect&             getScissor() const { return mScissor; }
-    bool                    needsFiltering() const { return mNeedsFiltering; }
+    const ui::Transform& getTransform() const;
+    const Rect& getViewport() const;
+    const Rect& getFrame() const;
+    const Rect& getScissor() const;
+    bool needsFiltering() const;
+    uint32_t getLayerStack() const;
 
-    uint32_t                getLayerStack() const { return mLayerStack; }
-    int32_t                 getDisplayType() const { return mType; }
-    bool                    isPrimary() const { return mType == DISPLAY_PRIMARY; }
-    int32_t                 getHwcDisplayId() const { return mHwcDisplayId; }
-    const wp<IBinder>&      getDisplayToken() const { return mDisplayToken; }
+    const std::optional<DisplayId>& getId() const;
+    const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
+    int32_t getSequenceId() const { return mSequenceId; }
 
-    int32_t getSupportedPerFrameMetadata() const { return mSupportedPerFrameMetadata; }
+    const Region& getUndefinedRegion() const;
 
-    // We pass in mustRecompose so we can keep VirtualDisplaySurface's state
-    // machine happy without actually queueing a buffer if nothing has changed
-    status_t beginFrame(bool mustRecompose) const;
-    status_t prepareFrame(HWComposer& hwc);
+    int32_t getSupportedPerFrameMetadata() const;
 
-    bool hasWideColorGamut() const { return mHasWideColorGamut; }
+    bool hasWideColorGamut() const;
     // Whether h/w composer has native support for specific HDR type.
-    bool hasHDR10Support() const { return mHasHdr10; }
-    bool hasHLGSupport() const { return mHasHLG; }
-    bool hasDolbyVisionSupport() const { return mHasDolbyVision; }
-
-    // Return true if the HDR dataspace is supported but
-    // there is no corresponding color mode.
-    bool hasLegacyHdrSupport(ui::Dataspace dataspace) const;
+    bool hasHDR10PlusSupport() const;
+    bool hasHDR10Support() const;
+    bool hasHLGSupport() const;
+    bool hasDolbyVisionSupport() const;
 
     // The returned HdrCapabilities is the combination of HDR capabilities from
     // hardware composer and RenderEngine. When the DisplayDevice supports wide
@@ -160,47 +125,24 @@
     // color space for both PQ and HLG HDR contents. The minimum and maximum
     // luminance will be set to sDefaultMinLumiance and sDefaultMaxLumiance
     // respectively if hardware composer doesn't return meaningful values.
-    const HdrCapabilities& getHdrCapabilities() const { return mHdrCapabilities; }
+    const HdrCapabilities& getHdrCapabilities() const;
 
     // Return true if intent is supported by the display.
     bool hasRenderIntent(ui::RenderIntent intent) const;
 
-    void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
-                          ui::Dataspace* outDataspace, ui::ColorMode* outMode,
-                          ui::RenderIntent* outIntent) const;
+    const Rect& getBounds() const;
+    const Rect& bounds() const { return getBounds(); }
 
-    void swapBuffers(HWComposer& hwc) const;
-
-    // called after h/w composer has completed its set() call
-    void onSwapBuffersCompleted() const;
-
-    Rect getBounds() const {
-        return Rect(mDisplayWidth, mDisplayHeight);
-    }
-    inline Rect bounds() const { return getBounds(); }
-
-    void setDisplayName(const String8& displayName);
-    const String8& getDisplayName() const { return mDisplayName; }
-
-    bool makeCurrent() const;
-    void setViewportAndProjection() const;
-
-    const sp<Fence>& getClientTargetAcquireFence() const;
+    void setDisplayName(const std::string& displayName);
+    const std::string& getDisplayName() const { return mDisplayName; }
 
     /* ------------------------------------------------------------------------
      * Display power mode management.
      */
     int getPowerMode() const;
     void setPowerMode(int mode);
-    bool isDisplayOn() const;
+    bool isPoweredOn() const;
 
-    ui::ColorMode getActiveColorMode() const;
-    void setActiveColorMode(ui::ColorMode mode);
-    ui::RenderIntent getActiveRenderIntent() const;
-    void setActiveRenderIntent(ui::RenderIntent renderIntent);
-    android_color_transform_t getColorTransform() const;
-    void setColorTransform(const mat4& transform);
-    void setCompositionDataSpace(ui::Dataspace dataspace);
     ui::Dataspace getCompositionDataSpace() const;
 
     /* ------------------------------------------------------------------------
@@ -210,33 +152,28 @@
     void setActiveConfig(int mode);
 
     // release HWC resources (if any) for removable displays
-    void disconnect(HWComposer& hwc);
+    void disconnect();
 
     /* ------------------------------------------------------------------------
      * Debugging
      */
     uint32_t getPageFlipCount() const;
-    void dump(String8& result) const;
+    std::string getDebugName() const;
+    void dump(std::string& result) const;
 
 private:
     /*
      *  Constants, set during initialization
      */
-    sp<SurfaceFlinger> mFlinger;
-    DisplayType mType;
-    int32_t mHwcDisplayId;
-    wp<IBinder> mDisplayToken;
+    const sp<SurfaceFlinger> mFlinger;
+    const wp<IBinder> mDisplayToken;
+    const int32_t mSequenceId;
 
-    // ANativeWindow this display is rendering into
-    sp<ANativeWindow> mNativeWindow;
-    sp<DisplaySurface> mDisplaySurface;
+    const int mDisplayInstallOrientation;
+    const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
 
-    std::unique_ptr<RE::Surface> mSurface;
-    int             mDisplayWidth;
-    int             mDisplayHeight;
-    mutable uint32_t mPageFlipCount;
-    String8         mDisplayName;
-    bool            mIsSecure;
+    std::string mDisplayName;
+    const bool mIsVirtual;
 
     /*
      * Can only accessed from the main thread, these members
@@ -251,78 +188,27 @@
     /*
      * Transaction state
      */
+    static uint32_t displayStateOrientationToTransformOrientation(int orientation);
     static status_t orientationToTransfrom(int orientation,
-            int w, int h, Transform* tr);
-
-    // The identifier of the active layer stack for this display. Several displays
-    // can use the same layer stack: A z-ordered group of layers (sometimes called
-    // "surfaces"). Any given layer can only be on a single layer stack.
-    uint32_t mLayerStack;
+                                           int w, int h, ui::Transform* tr);
 
     int mOrientation;
     static uint32_t sPrimaryDisplayOrientation;
-    // user-provided visible area of the layer stack
-    Rect mViewport;
-    // user-provided rectangle where mViewport gets mapped to
-    Rect mFrame;
-    // pre-computed scissor to apply to the display
-    Rect mScissor;
-    Transform mGlobalTransform;
-    bool mNeedsFiltering;
+
     // Current power mode
     int mPowerMode;
     // Current active config
     int mActiveConfig;
-    // current active color mode
-    ui::ColorMode mActiveColorMode = ui::ColorMode::NATIVE;
-    // Current active render intent.
-    ui::RenderIntent mActiveRenderIntent = ui::RenderIntent::COLORIMETRIC;
-    ui::Dataspace mCompositionDataSpace = ui::Dataspace::UNKNOWN;
-    // Current color transform
-    android_color_transform_t mColorTransform;
 
-    // Need to know if display is wide-color capable or not.
-    // Initialized by SurfaceFlinger when the DisplayDevice is created.
-    // Fed to RenderEngine during composition.
-    bool mHasWideColorGamut;
-    bool mHasHdr10;
-    bool mHasHLG;
-    bool mHasDolbyVision;
-    HdrCapabilities mHdrCapabilities;
-    const int32_t mSupportedPerFrameMetadata;
-
-    // Mappings from desired Dataspace/RenderIntent to the supported
-    // Dataspace/ColorMode/RenderIntent.
-    using ColorModeKey = uint64_t;
-    struct ColorModeValue {
-        ui::Dataspace dataspace;
-        ui::ColorMode colorMode;
-        ui::RenderIntent renderIntent;
-    };
-
-    static ColorModeKey getColorModeKey(ui::Dataspace dataspace, ui::RenderIntent intent) {
-        return (static_cast<uint64_t>(dataspace) << 32) | static_cast<uint32_t>(intent);
-    }
-    void populateColorModes(
-            const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes);
-    void addColorMode(
-            const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes,
-            const ui::ColorMode mode, const ui::RenderIntent intent);
-
-    std::unordered_map<ColorModeKey, ColorModeValue> mColorModes;
+    // TODO(b/74619554): Remove special cases for primary display.
+    const bool mIsPrimary;
 };
 
 struct DisplayDeviceState {
-    DisplayDeviceState() = default;
-    DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure);
+    bool isVirtual() const { return !displayId.has_value(); }
 
-    bool isValid() const { return type >= 0; }
-    bool isMainDisplay() const { return type == DisplayDevice::DISPLAY_PRIMARY; }
-    bool isVirtualDisplay() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; }
-
-    static std::atomic<int32_t> nextDisplayId;
-    int32_t displayId = nextDisplayId++;
-    DisplayDevice::DisplayType type = DisplayDevice::DISPLAY_ID_INVALID;
+    int32_t sequenceId = sNextSequenceId++;
+    std::optional<DisplayId> displayId;
     sp<IGraphicBufferProducer> surface;
     uint32_t layerStack = DisplayDevice::NO_LAYER_STACK;
     Rect viewport;
@@ -330,30 +216,143 @@
     uint8_t orientation = 0;
     uint32_t width = 0;
     uint32_t height = 0;
-    String8 displayName;
+    std::string displayName;
     bool isSecure = false;
+
+private:
+    static std::atomic<int32_t> sNextSequenceId;
+};
+
+struct DisplayDeviceCreationArgs {
+    // We use a constructor to ensure some of the values are set, without
+    // assuming a default value.
+    DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger, const wp<IBinder>& displayToken,
+                              const std::optional<DisplayId>& displayId);
+
+    const sp<SurfaceFlinger> flinger;
+    const wp<IBinder> displayToken;
+    const std::optional<DisplayId> displayId;
+
+    int32_t sequenceId{0};
+    bool isVirtual{false};
+    bool isSecure{false};
+    sp<ANativeWindow> nativeWindow;
+    sp<compositionengine::DisplaySurface> displaySurface;
+    int displayInstallOrientation{DisplayState::eOrientationDefault};
+    bool hasWideColorGamut{false};
+    HdrCapabilities hdrCapabilities;
+    int32_t supportedPerFrameMetadata{0};
+    std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
+    int initialPowerMode{HWC_POWER_MODE_NORMAL};
+    bool isPrimary{false};
 };
 
 class DisplayRenderArea : public RenderArea {
 public:
     DisplayRenderArea(const sp<const DisplayDevice> device,
-                      ISurfaceComposer::Rotation rotation = ISurfaceComposer::eRotateNone)
-          : DisplayRenderArea(device, device->getBounds(), device->getHeight(), device->getWidth(),
-                              rotation) {}
-    DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqHeight,
-                      uint32_t reqWidth, ISurfaceComposer::Rotation rotation)
-          : RenderArea(reqHeight, reqWidth, CaptureFill::OPAQUE, rotation), mDevice(device),
-                              mSourceCrop(sourceCrop) {}
+                      ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
+          : DisplayRenderArea(device, device->getBounds(), device->getWidth(), device->getHeight(),
+                              device->getCompositionDataSpace(), rotation) {}
+    DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqWidth,
+                      uint32_t reqHeight, ui::Dataspace reqDataSpace,
+                      ui::Transform::orientation_flags rotation)
+          : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
+                       getDisplayRotation(rotation, device->getInstallOrientation())),
+            mDevice(device),
+            mSourceCrop(sourceCrop) {}
 
-    const Transform& getTransform() const override { return mDevice->getTransform(); }
+    const ui::Transform& getTransform() const override { return mDevice->getTransform(); }
     Rect getBounds() const override { return mDevice->getBounds(); }
     int getHeight() const override { return mDevice->getHeight(); }
     int getWidth() const override { return mDevice->getWidth(); }
     bool isSecure() const override { return mDevice->isSecure(); }
-    bool needsFiltering() const override { return mDevice->needsFiltering(); }
-    Rect getSourceCrop() const override { return mSourceCrop; }
+
+    bool needsFiltering() const override {
+        // check if the projection from the logical display to the physical
+        // display needs filtering
+        if (mDevice->needsFiltering()) {
+            return true;
+        }
+
+        // check if the projection from the logical render area (i.e., the
+        // physical display) to the physical render area requires filtering
+        const Rect sourceCrop = getSourceCrop();
+        int width = sourceCrop.width();
+        int height = sourceCrop.height();
+        if (getRotationFlags() & ui::Transform::ROT_90) {
+            std::swap(width, height);
+        }
+        return width != getReqWidth() || height != getReqHeight();
+    }
+
+    Rect getSourceCrop() const override {
+        // use the (projected) logical display viewport by default
+        if (mSourceCrop.isEmpty()) {
+            return mDevice->getScissor();
+        }
+
+        const int orientation = mDevice->getInstallOrientation();
+        if (orientation == DisplayState::eOrientationDefault) {
+            return mSourceCrop;
+        }
+
+        // Install orientation is transparent to the callers.  Apply it now.
+        uint32_t flags = 0x00;
+        switch (orientation) {
+            case DisplayState::eOrientation90:
+                flags = ui::Transform::ROT_90;
+                break;
+            case DisplayState::eOrientation180:
+                flags = ui::Transform::ROT_180;
+                break;
+            case DisplayState::eOrientation270:
+                flags = ui::Transform::ROT_270;
+                break;
+        }
+        ui::Transform tr;
+        tr.set(flags, getWidth(), getHeight());
+        return tr.transform(mSourceCrop);
+    }
 
 private:
+    // Install orientation is transparent to the callers.  We need to cancel
+    // it out by modifying rotation flags.
+    static ui::Transform::orientation_flags getDisplayRotation(
+            ui::Transform::orientation_flags rotation, int orientation) {
+        if (orientation == DisplayState::eOrientationDefault) {
+            return rotation;
+        }
+
+        // convert hw orientation into flag presentation
+        // here inverse transform needed
+        uint8_t hw_rot_90 = 0x00;
+        uint8_t hw_flip_hv = 0x00;
+        switch (orientation) {
+            case DisplayState::eOrientation90:
+                hw_rot_90 = ui::Transform::ROT_90;
+                hw_flip_hv = ui::Transform::ROT_180;
+                break;
+            case DisplayState::eOrientation180:
+                hw_flip_hv = ui::Transform::ROT_180;
+                break;
+            case DisplayState::eOrientation270:
+                hw_rot_90 = ui::Transform::ROT_90;
+                break;
+        }
+
+        // transform flags operation
+        // 1) flip H V if both have ROT_90 flag
+        // 2) XOR these flags
+        uint8_t rotation_rot_90 = rotation & ui::Transform::ROT_90;
+        uint8_t rotation_flip_hv = rotation & ui::Transform::ROT_180;
+        if (rotation_rot_90 & hw_rot_90) {
+            rotation_flip_hv = (~rotation_flip_hv) & ui::Transform::ROT_180;
+        }
+
+        return static_cast<ui::Transform::orientation_flags>(
+                (rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv));
+    }
+
     const sp<const DisplayDevice> mDevice;
     const Rect mSourceCrop;
 };
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index f190b71..8343e5a 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -22,7 +22,6 @@
 
 #include "ComposerHal.h"
 
-#include <android/hardware/graphics/composer/2.2/IComposer.h>
 #include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
 #include <gui/BufferQueue.h>
 #include <hidl/HidlTransportUtils.h>
@@ -171,22 +170,31 @@
         LOG_ALWAYS_FATAL("failed to get hwcomposer service");
     }
 
-    mComposer->createClient(
-            [&](const auto& tmpError, const auto& tmpClient)
-            {
-                if (tmpError == Error::NONE) {
-                    mClient = tmpClient;
-                }
-            });
-    if (mClient == nullptr) {
-        LOG_ALWAYS_FATAL("failed to create composer client");
+    if (sp<IComposer> composer_2_3 = IComposer::castFrom(mComposer)) {
+        composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
+            if (tmpError == Error::NONE) {
+                mClient = tmpClient;
+                mClient_2_2 = tmpClient;
+                mClient_2_3 = tmpClient;
+            }
+        });
+    } else {
+        mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
+            if (tmpError != Error::NONE) {
+                return;
+            }
+
+            mClient = tmpClient;
+            if (sp<V2_2::IComposer> composer_2_2 = V2_2::IComposer::castFrom(mComposer)) {
+                mClient_2_2 = V2_2::IComposerClient::castFrom(mClient);
+                LOG_ALWAYS_FATAL_IF(mClient_2_2 == nullptr,
+                                    "IComposer 2.2 did not return IComposerClient 2.2");
+            }
+        });
     }
 
-    // 2.2 support is optional
-    sp<IComposer> composer_2_2 = IComposer::castFrom(mComposer);
-    if (composer_2_2 != nullptr) {
-        mClient_2_2 = IComposerClient::castFrom(mClient);
-        LOG_ALWAYS_FATAL_IF(mClient_2_2 == nullptr, "IComposer 2.2 did not return IComposerClient 2.2");
+    if (mClient == nullptr) {
+        LOG_ALWAYS_FATAL("failed to create composer client");
     }
 
     if (mIsUsingVrComposer) {
@@ -252,17 +260,20 @@
     const uint32_t bufferSlotCount = 1;
     Error error = kDefaultError;
     if (mClient_2_2) {
-        mClient_2_2->createVirtualDisplay_2_2(width, height, *format, bufferSlotCount,
-                [&](const auto& tmpError, const auto& tmpDisplay,
-                    const auto& tmpFormat) {
-                    error = tmpError;
-                    if (error != Error::NONE) {
-                        return;
-                    }
+        mClient_2_2->createVirtualDisplay_2_2(width, height,
+                                              static_cast<types::V1_1::PixelFormat>(*format),
+                                              bufferSlotCount,
+                                              [&](const auto& tmpError, const auto& tmpDisplay,
+                                                  const auto& tmpFormat) {
+                                                  error = tmpError;
+                                                  if (error != Error::NONE) {
+                                                      return;
+                                                  }
 
-                    *outDisplay = tmpDisplay;
-                    *format = tmpFormat;
-                });
+                                                  *outDisplay = tmpDisplay;
+                                                  *format = static_cast<types::V1_2::PixelFormat>(
+                                                          tmpFormat);
+                                              });
     } else {
         mClient->createVirtualDisplay(width, height,
                 static_cast<types::V1_0::PixelFormat>(*format), bufferSlotCount,
@@ -345,16 +356,26 @@
 {
     Error error = kDefaultError;
 
-    if (mClient_2_2) {
-        mClient_2_2->getColorModes_2_2(display,
-                [&](const auto& tmpError, const auto& tmpModes) {
-                    error = tmpError;
-                    if (error != Error::NONE) {
-                        return;
-                    }
+    if (mClient_2_3) {
+        mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
+            error = tmpError;
+            if (error != Error::NONE) {
+                return;
+            }
 
-                    *outModes = tmpModes;
-                });
+            *outModes = tmpModes;
+        });
+    } else if (mClient_2_2) {
+        mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
+            error = tmpError;
+            if (error != Error::NONE) {
+                return;
+            }
+
+            for (types::V1_1::ColorMode colorMode : tmpModes) {
+                outModes->push_back(static_cast<ColorMode>(colorMode));
+            }
+        });
     } else {
         mClient->getColorModes(display,
                 [&](const auto& tmpError, const auto& tmpModes) {
@@ -468,21 +489,43 @@
         float* outMaxAverageLuminance, float* outMinLuminance)
 {
     Error error = kDefaultError;
-    mClient->getHdrCapabilities(display,
-            [&](const auto& tmpError, const auto& tmpTypes,
-                const auto& tmpMaxLuminance,
-                const auto& tmpMaxAverageLuminance,
-                const auto& tmpMinLuminance) {
-                error = tmpError;
-                if (error != Error::NONE) {
-                    return;
-                }
+    if (mClient_2_3) {
+        mClient_2_3->getHdrCapabilities_2_3(display,
+                                            [&](const auto& tmpError, const auto& tmpTypes,
+                                                const auto& tmpMaxLuminance,
+                                                const auto& tmpMaxAverageLuminance,
+                                                const auto& tmpMinLuminance) {
+                                                error = tmpError;
+                                                if (error != Error::NONE) {
+                                                    return;
+                                                }
 
-                *outTypes = tmpTypes;
-                *outMaxLuminance = tmpMaxLuminance;
-                *outMaxAverageLuminance = tmpMaxAverageLuminance;
-                *outMinLuminance = tmpMinLuminance;
-            });
+                                                *outTypes = tmpTypes;
+                                                *outMaxLuminance = tmpMaxLuminance;
+                                                *outMaxAverageLuminance = tmpMaxAverageLuminance;
+                                                *outMinLuminance = tmpMinLuminance;
+                                            });
+    } else {
+        mClient->getHdrCapabilities(display,
+                                    [&](const auto& tmpError, const auto& tmpTypes,
+                                        const auto& tmpMaxLuminance,
+                                        const auto& tmpMaxAverageLuminance,
+                                        const auto& tmpMinLuminance) {
+                                        error = tmpError;
+                                        if (error != Error::NONE) {
+                                            return;
+                                        }
+
+                                        outTypes->clear();
+                                        for (auto type : tmpTypes) {
+                                            outTypes->push_back(static_cast<Hdr>(type));
+                                        }
+
+                                        *outMaxLuminance = tmpMaxLuminance;
+                                        *outMaxAverageLuminance = tmpMaxAverageLuminance;
+                                        *outMinLuminance = tmpMinLuminance;
+                                    });
+    }
 
     return error;
 }
@@ -546,8 +589,11 @@
         RenderIntent renderIntent)
 {
     hardware::Return<Error> ret(kDefaultError);
-    if (mClient_2_2) {
-        ret = mClient_2_2->setColorMode_2_2(display, mode, renderIntent);
+    if (mClient_2_3) {
+        ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
+    } else if (mClient_2_2) {
+        ret = mClient_2_2->setColorMode_2_2(display, static_cast<types::V1_1::ColorMode>(mode),
+                                            renderIntent);
     } else {
         ret = mClient->setColorMode(display,
                 static_cast<types::V1_0::ColorMode>(mode));
@@ -896,23 +942,43 @@
     return Error::NONE;
 }
 
-Error Composer::getPerFrameMetadataKeys(
-        Display display, std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) {
+std::vector<IComposerClient::PerFrameMetadataKey> Composer::getPerFrameMetadataKeys(
+        Display display) {
+    std::vector<IComposerClient::PerFrameMetadataKey>  keys;
     if (!mClient_2_2) {
-        return Error::UNSUPPORTED;
+        return keys;
     }
 
     Error error = kDefaultError;
-    mClient_2_2->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
-        error = tmpError;
-        if (error != Error::NONE) {
-            return;
-        }
+    if (mClient_2_3) {
+        mClient_2_3->getPerFrameMetadataKeys_2_3(display,
+                                                 [&](const auto& tmpError, const auto& tmpKeys) {
+                                                     error = tmpError;
+                                                     if (error != Error::NONE) {
+                                                         ALOGW("getPerFrameMetadataKeys failed "
+                                                               "with %d",
+                                                               tmpError);
+                                                         return;
+                                                     }
+                                                     keys = tmpKeys;
+                                                 });
+    } else {
+        mClient_2_2
+                ->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
+                    error = tmpError;
+                    if (error != Error::NONE) {
+                        ALOGW("getPerFrameMetadataKeys failed with %d", tmpError);
+                        return;
+                    }
 
-        *outKeys = tmpKeys;
-    });
+                    keys.clear();
+                    for (auto key : tmpKeys) {
+                        keys.push_back(static_cast<IComposerClient::PerFrameMetadataKey>(key));
+                    }
+                });
+    }
 
-    return error;
+    return keys;
 }
 
 Error Composer::getRenderIntents(Display display, ColorMode colorMode,
@@ -923,15 +989,22 @@
     }
 
     Error error = kDefaultError;
-    mClient_2_2->getRenderIntents(display, colorMode,
-            [&](const auto& tmpError, const auto& tmpKeys) {
+
+    auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
         error = tmpError;
         if (error != Error::NONE) {
             return;
         }
 
         *outRenderIntents = tmpKeys;
-    });
+    };
+
+    if (mClient_2_3) {
+        mClient_2_3->getRenderIntents_2_3(display, colorMode, getRenderIntentsLambda);
+    } else {
+        mClient_2_2->getRenderIntents(display, static_cast<types::V1_1::ColorMode>(colorMode),
+                                      getRenderIntentsLambda);
+    }
 
     return error;
 }
@@ -944,18 +1017,148 @@
     }
 
     Error error = kDefaultError;
-    mClient_2_2->getDataspaceSaturationMatrix(dataspace, [&](const auto& tmpError, const auto& tmpMatrix) {
-        error = tmpError;
-        if (error != Error::NONE) {
-            return;
-        }
-
-        *outMatrix = mat4(tmpMatrix.data());
-    });
+    mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
+                                              [&](const auto& tmpError, const auto& tmpMatrix) {
+                                                  error = tmpError;
+                                                  if (error != Error::NONE) {
+                                                      return;
+                                                  }
+                                                  *outMatrix = mat4(tmpMatrix.data());
+                                              });
 
     return error;
 }
 
+// Composer HAL 2.3
+
+Error Composer::getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                             std::vector<uint8_t>* outData) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError;
+    mClient_2_3->getDisplayIdentificationData(display,
+                                              [&](const auto& tmpError, const auto& tmpPort,
+                                                  const auto& tmpData) {
+                                                  error = tmpError;
+                                                  if (error != Error::NONE) {
+                                                      return;
+                                                  }
+
+                                                  *outPort = tmpPort;
+                                                  *outData = tmpData;
+                                              });
+
+    return error;
+}
+
+Error Composer::setLayerColorTransform(Display display, Layer layer, const float* matrix)
+{
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerColorTransform(matrix);
+    return Error::NONE;
+}
+
+Error Composer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+                                                      Dataspace* outDataspace,
+                                                      uint8_t* outComponentMask) {
+    if (!outFormat || !outDataspace || !outComponentMask) {
+        return Error::BAD_PARAMETER;
+    }
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+    Error error = kDefaultError;
+    mClient_2_3->getDisplayedContentSamplingAttributes(display,
+                                                       [&](const auto tmpError,
+                                                           const auto& tmpFormat,
+                                                           const auto& tmpDataspace,
+                                                           const auto& tmpComponentMask) {
+                                                           error = tmpError;
+                                                           if (error == Error::NONE) {
+                                                               *outFormat = tmpFormat;
+                                                               *outDataspace = tmpDataspace;
+                                                               *outComponentMask =
+                                                                       static_cast<uint8_t>(
+                                                                               tmpComponentMask);
+                                                           }
+                                                       });
+    return error;
+}
+
+Error Composer::getDisplayCapabilities(Display display,
+                                       std::vector<DisplayCapability>* outCapabilities) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+    Error error = kDefaultError;
+    mClient_2_3->getDisplayCapabilities(display,
+                                        [&](const auto& tmpError, const auto& tmpCapabilities) {
+                                            error = tmpError;
+                                            if (error != Error::NONE) {
+                                                return;
+                                            }
+                                            *outCapabilities = tmpCapabilities;
+                                        });
+    return error;
+}
+
+Error Composer::setDisplayContentSamplingEnabled(Display display, bool enabled,
+                                                 uint8_t componentMask, uint64_t maxFrames) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+
+    auto enable = enabled ? V2_3::IComposerClient::DisplayedContentSampling::ENABLE
+                          : V2_3::IComposerClient::DisplayedContentSampling::DISABLE;
+    return mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
+                                                           maxFrames);
+}
+
+Error Composer::getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+                                          DisplayedFrameStats* outStats) {
+    if (!outStats) {
+        return Error::BAD_PARAMETER;
+    }
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+    Error error = kDefaultError;
+    mClient_2_3->getDisplayedContentSample(display, maxFrames, timestamp,
+                                           [&](const auto tmpError, auto tmpNumFrames,
+                                               const auto& tmpSamples0, const auto& tmpSamples1,
+                                               const auto& tmpSamples2, const auto& tmpSamples3) {
+                                               error = tmpError;
+                                               if (error == Error::NONE) {
+                                                   outStats->numFrames = tmpNumFrames;
+                                                   outStats->component_0_sample = tmpSamples0;
+                                                   outStats->component_1_sample = tmpSamples1;
+                                                   outStats->component_2_sample = tmpSamples2;
+                                                   outStats->component_3_sample = tmpSamples3;
+                                               }
+                                           });
+    return error;
+}
+
+Error Composer::setLayerPerFrameMetadataBlobs(
+        Display display, Layer layer,
+        const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerPerFrameMetadataBlobs(metadata);
+    return Error::NONE;
+}
+
 CommandReader::~CommandReader()
 {
     resetData();
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 442006c..542e840 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -25,11 +25,12 @@
 
 #include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
 #include <android/hardware/graphics/common/1.1/types.h>
-#include <android/hardware/graphics/composer/2.2/IComposer.h>
-#include <android/hardware/graphics/composer/2.2/IComposerClient.h>
-#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
+#include <android/hardware/graphics/composer/2.3/IComposer.h>
+#include <android/hardware/graphics/composer/2.3/IComposerClient.h>
+#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
+#include <ui/DisplayedFrameStats.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/StrongPointer.h>
 
@@ -43,29 +44,29 @@
 
 namespace V2_1 = hardware::graphics::composer::V2_1;
 namespace V2_2 = hardware::graphics::composer::V2_2;
+namespace V2_3 = hardware::graphics::composer::V2_3;
 
 using types::V1_0::ColorTransform;
-using types::V1_0::Hdr;
 using types::V1_0::Transform;
-
-using types::V1_1::ColorMode;
-using types::V1_1::Dataspace;
-using types::V1_1::PixelFormat;
 using types::V1_1::RenderIntent;
+using types::V1_2::ColorMode;
+using types::V1_2::Dataspace;
+using types::V1_2::Hdr;
+using types::V1_2::PixelFormat;
 
 using V2_1::Config;
 using V2_1::Display;
 using V2_1::Error;
 using V2_1::IComposerCallback;
 using V2_1::Layer;
-
-using V2_2::CommandReaderBase;
-using V2_2::CommandWriterBase;
-using V2_2::IComposer;
-using V2_2::IComposerClient;
-
+using V2_3::CommandReaderBase;
+using V2_3::CommandWriterBase;
+using V2_3::IComposer;
+using V2_3::IComposerClient;
+using DisplayCapability = IComposerClient::DisplayCapability;
 using PerFrameMetadata = IComposerClient::PerFrameMetadata;
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
+using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
 
 class Composer {
 public:
@@ -180,11 +181,28 @@
     virtual Error setLayerPerFrameMetadata(
             Display display, Layer layer,
             const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) = 0;
-    virtual Error getPerFrameMetadataKeys(
-            Display display, std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) = 0;
+    virtual std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
+            Display display) = 0;
     virtual Error getRenderIntents(Display display, ColorMode colorMode,
             std::vector<RenderIntent>* outRenderIntents) = 0;
     virtual Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) = 0;
+
+    // Composer HAL 2.3
+    virtual Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                               std::vector<uint8_t>* outData) = 0;
+    virtual Error setLayerColorTransform(Display display, Layer layer,
+                                         const float* matrix) = 0;
+    virtual Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+                                                        Dataspace* outDataspace,
+                                                        uint8_t* outComponentMask) = 0;
+    virtual Error setDisplayContentSamplingEnabled(Display display, bool enabled,
+                                                   uint8_t componentMask, uint64_t maxFrames) = 0;
+    virtual Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+                                            DisplayedFrameStats* outStats) = 0;
+    virtual Error getDisplayCapabilities(Display display,
+                                         std::vector<DisplayCapability>* outCapabilities) = 0;
+    virtual Error setLayerPerFrameMetadataBlobs(
+            Display display, Layer layer, const std::vector<PerFrameMetadataBlob>& metadata) = 0;
 };
 
 namespace impl {
@@ -374,12 +392,29 @@
     Error setLayerPerFrameMetadata(
             Display display, Layer layer,
             const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) override;
-    Error getPerFrameMetadataKeys(
-            Display display, std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) override;
+    std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
+            Display display) override;
     Error getRenderIntents(Display display, ColorMode colorMode,
             std::vector<RenderIntent>* outRenderIntents) override;
     Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) override;
 
+    // Composer HAL 2.3
+    Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                       std::vector<uint8_t>* outData) override;
+    Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
+    Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+                                                Dataspace* outDataspace,
+                                                uint8_t* outComponentMask) override;
+    Error setDisplayContentSamplingEnabled(Display display, bool enabled, uint8_t componentMask,
+                                           uint64_t maxFrames) override;
+    Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+                                    DisplayedFrameStats* outStats) override;
+    Error getDisplayCapabilities(Display display,
+                                 std::vector<DisplayCapability>* outCapabilities) override;
+    Error setLayerPerFrameMetadataBlobs(
+            Display display, Layer layer,
+            const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
+
 private:
     class CommandWriter : public CommandWriterBase {
     public:
@@ -405,7 +440,8 @@
     sp<V2_1::IComposer> mComposer;
 
     sp<V2_1::IComposerClient> mClient;
-    sp<IComposerClient> mClient_2_2;
+    sp<V2_2::IComposerClient> mClient_2_2;
+    sp<IComposerClient> mClient_2_3;
 
     // 64KiB minus a small space for metadata such as read/write pointers
     static constexpr size_t kWriterInitialSize =
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
new file mode 100644
index 0000000..ba7818d
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "DisplayIdentification"
+
+#include <algorithm>
+#include <cctype>
+#include <numeric>
+#include <optional>
+
+#include <log/log.h>
+
+#include "DisplayIdentification.h"
+
+namespace android {
+namespace {
+
+using byte_view = std::basic_string_view<uint8_t>;
+
+constexpr size_t kEdidHeaderLength = 5;
+
+constexpr uint16_t kFallbackEdidManufacturerId = 0;
+constexpr uint16_t kVirtualEdidManufacturerId = 0xffffu;
+
+std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) {
+    if (view.size() < kEdidHeaderLength || view[0] || view[1] || view[2] || view[4]) {
+        return {};
+    }
+
+    return view[3];
+}
+
+std::string_view parseEdidText(const byte_view& view) {
+    std::string_view text(reinterpret_cast<const char*>(view.data()), view.size());
+    text = text.substr(0, text.find('\n'));
+
+    if (!std::all_of(text.begin(), text.end(), ::isprint)) {
+        ALOGW("Invalid EDID: ASCII text is not printable.");
+        return {};
+    }
+
+    return text;
+}
+
+// Big-endian 16-bit value encodes three 5-bit letters where A is 0b00001.
+template <size_t I>
+char getPnpLetter(uint16_t id) {
+    static_assert(I < 3);
+    const char letter = 'A' + (static_cast<uint8_t>(id >> ((2 - I) * 5)) & 0b00011111) - 1;
+    return letter < 'A' || letter > 'Z' ? '\0' : letter;
+}
+
+} // namespace
+
+uint16_t DisplayId::manufacturerId() const {
+    return static_cast<uint16_t>(value >> 40);
+}
+
+DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash) {
+    return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(displayNameHash) << 8) |
+            port};
+}
+
+bool isEdid(const DisplayIdentificationData& data) {
+    const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
+    return data.size() >= sizeof(kMagic) &&
+            std::equal(std::begin(kMagic), std::end(kMagic), data.begin());
+}
+
+std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
+    constexpr size_t kMinLength = 128;
+    if (edid.size() < kMinLength) {
+        ALOGW("Invalid EDID: structure is truncated.");
+        // Attempt parsing even if EDID is malformed.
+    } else {
+        ALOGW_IF(edid[126] != 0, "EDID extensions are currently unsupported.");
+        ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kMinLength, static_cast<uint8_t>(0)),
+                 "Invalid EDID: structure does not checksum.");
+    }
+
+    constexpr size_t kManufacturerOffset = 8;
+    if (edid.size() < kManufacturerOffset + sizeof(uint16_t)) {
+        ALOGE("Invalid EDID: manufacturer ID is truncated.");
+        return {};
+    }
+
+    // Plug and play ID encoded as big-endian 16-bit value.
+    const uint16_t manufacturerId =
+            (edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1];
+
+    const auto pnpId = getPnpId(manufacturerId);
+    if (!pnpId) {
+        ALOGE("Invalid EDID: manufacturer ID is not a valid PnP ID.");
+        return {};
+    }
+
+    constexpr size_t kDescriptorOffset = 54;
+    if (edid.size() < kDescriptorOffset) {
+        ALOGE("Invalid EDID: descriptors are missing.");
+        return {};
+    }
+
+    byte_view view(edid.data(), edid.size());
+    view.remove_prefix(kDescriptorOffset);
+
+    std::string_view displayName;
+    std::string_view serialNumber;
+    std::string_view asciiText;
+
+    constexpr size_t kDescriptorCount = 4;
+    constexpr size_t kDescriptorLength = 18;
+
+    for (size_t i = 0; i < kDescriptorCount; i++) {
+        if (view.size() < kDescriptorLength) {
+            break;
+        }
+
+        if (const auto type = getEdidDescriptorType(view)) {
+            byte_view descriptor(view.data(), kDescriptorLength);
+            descriptor.remove_prefix(kEdidHeaderLength);
+
+            switch (*type) {
+                case 0xfc:
+                    displayName = parseEdidText(descriptor);
+                    break;
+                case 0xfe:
+                    asciiText = parseEdidText(descriptor);
+                    break;
+                case 0xff:
+                    serialNumber = parseEdidText(descriptor);
+                    break;
+            }
+        }
+
+        view.remove_prefix(kDescriptorLength);
+    }
+
+    if (displayName.empty()) {
+        ALOGW("Invalid EDID: falling back to serial number due to missing display name.");
+        displayName = serialNumber;
+    }
+    if (displayName.empty()) {
+        ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number.");
+        displayName = asciiText;
+    }
+    if (displayName.empty()) {
+        ALOGE("Invalid EDID: display name and fallback descriptors are missing.");
+        return {};
+    }
+
+    return Edid{manufacturerId, *pnpId, displayName};
+}
+
+std::optional<PnpId> getPnpId(uint16_t manufacturerId) {
+    const char a = getPnpLetter<0>(manufacturerId);
+    const char b = getPnpLetter<1>(manufacturerId);
+    const char c = getPnpLetter<2>(manufacturerId);
+    return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt;
+}
+
+std::optional<PnpId> getPnpId(DisplayId displayId) {
+    return getPnpId(displayId.manufacturerId());
+}
+
+std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
+        uint8_t port, const DisplayIdentificationData& data) {
+    if (!isEdid(data)) {
+        ALOGE("Display identification data has unknown format.");
+        return {};
+    }
+
+    const auto edid = parseEdid(data);
+    if (!edid) {
+        return {};
+    }
+
+    // Hash display name instead of using product code or serial number, since the latter have been
+    // observed to change on some displays with multiple inputs.
+    const auto hash = static_cast<uint32_t>(std::hash<std::string_view>()(edid->displayName));
+    return DisplayIdentificationInfo{DisplayId::fromEdid(port, edid->manufacturerId, hash),
+                                     std::string(edid->displayName)};
+}
+
+DisplayId getFallbackDisplayId(uint8_t port) {
+    return DisplayId::fromEdid(port, kFallbackEdidManufacturerId, 0);
+}
+
+DisplayId getVirtualDisplayId(uint32_t id) {
+    return DisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
new file mode 100644
index 0000000..1599995
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <vector>
+
+namespace android {
+
+struct DisplayId {
+    using Type = uint64_t;
+    Type value;
+
+    uint16_t manufacturerId() const;
+
+    static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash);
+};
+
+inline bool operator==(DisplayId lhs, DisplayId rhs) {
+    return lhs.value == rhs.value;
+}
+
+inline bool operator!=(DisplayId lhs, DisplayId rhs) {
+    return !(lhs == rhs);
+}
+
+inline std::string to_string(DisplayId displayId) {
+    return std::to_string(displayId.value);
+}
+
+using DisplayIdentificationData = std::vector<uint8_t>;
+
+struct DisplayIdentificationInfo {
+    DisplayId id;
+    std::string name;
+};
+
+// NUL-terminated plug and play ID.
+using PnpId = std::array<char, 4>;
+
+struct Edid {
+    uint16_t manufacturerId;
+    PnpId pnpId;
+    std::string_view displayName;
+};
+
+bool isEdid(const DisplayIdentificationData&);
+std::optional<Edid> parseEdid(const DisplayIdentificationData&);
+std::optional<PnpId> getPnpId(uint16_t manufacturerId);
+std::optional<PnpId> getPnpId(DisplayId);
+
+std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
+        uint8_t port, const DisplayIdentificationData&);
+
+DisplayId getFallbackDisplayId(uint8_t port);
+DisplayId getVirtualDisplayId(uint32_t id);
+
+} // namespace android
+
+namespace std {
+
+template <>
+struct hash<android::DisplayId> {
+    size_t operator()(android::DisplayId displayId) const {
+        return hash<android::DisplayId::Type>()(displayId.value);
+    }
+};
+
+} // namespace std
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index e6d7834..27812f7 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -52,26 +52,25 @@
  *
  */
 
-FramebufferSurface::FramebufferSurface(HWComposer& hwc, int disp,
-        const sp<IGraphicBufferConsumer>& consumer) :
-    ConsumerBase(consumer),
-    mDisplayType(disp),
-    mCurrentBufferSlot(-1),
-    mCurrentBuffer(),
-    mCurrentFence(Fence::NO_FENCE),
-    mHwc(hwc),
-    mHasPendingRelease(false),
-    mPreviousBufferSlot(BufferQueue::INVALID_BUFFER_SLOT),
-    mPreviousBuffer()
-{
-    ALOGV("Creating for display %d", disp);
+FramebufferSurface::FramebufferSurface(HWComposer& hwc, DisplayId displayId,
+                                       const sp<IGraphicBufferConsumer>& consumer)
+      : ConsumerBase(consumer),
+        mDisplayId(displayId),
+        mCurrentBufferSlot(-1),
+        mCurrentBuffer(),
+        mCurrentFence(Fence::NO_FENCE),
+        mHwc(hwc),
+        mHasPendingRelease(false),
+        mPreviousBufferSlot(BufferQueue::INVALID_BUFFER_SLOT),
+        mPreviousBuffer() {
+    ALOGV("Creating for display %s", to_string(displayId).c_str());
 
     mName = "FramebufferSurface";
     mConsumer->setConsumerName(mName);
     mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB |
                                        GRALLOC_USAGE_HW_RENDER |
                                        GRALLOC_USAGE_HW_COMPOSER);
-    const auto& activeConfig = mHwc.getActiveConfig(disp);
+    const auto& activeConfig = mHwc.getActiveConfig(displayId);
     mConsumer->setDefaultBufferSize(activeConfig->getWidth(),
             activeConfig->getHeight());
     mConsumer->setMaxAcquiredBufferCount(
@@ -142,8 +141,7 @@
     mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
             &outSlot, &outBuffer);
     outDataspace = static_cast<Dataspace>(item.mDataSpace);
-    status_t result =
-            mHwc.setClientTarget(mDisplayType, outSlot, outFence, outBuffer, outDataspace);
+    status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
     if (result != NO_ERROR) {
         ALOGE("error posting framebuffer: %d", result);
         return result;
@@ -161,7 +159,7 @@
 
 void FramebufferSurface::onFrameCommitted() {
     if (mHasPendingRelease) {
-        sp<Fence> fence = mHwc.getPresentFence(mDisplayType);
+        sp<Fence> fence = mHwc.getPresentFence(mDisplayId);
         if (fence->isValid()) {
             status_t result = addReleaseFence(mPreviousBufferSlot,
                     mPreviousBuffer, fence);
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 0fd8e9e..18c524f 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -17,14 +17,15 @@
 #ifndef ANDROID_SF_FRAMEBUFFER_SURFACE_H
 #define ANDROID_SF_FRAMEBUFFER_SURFACE_H
 
-#include "DisplaySurface.h"
-#include "HWComposerBufferCache.h"
-
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <compositionengine/DisplaySurface.h>
 #include <gui/ConsumerBase.h>
 
+#include "DisplayIdentification.h"
+#include "HWComposerBufferCache.h"
+
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -35,10 +36,10 @@
 
 // ---------------------------------------------------------------------------
 
-class FramebufferSurface : public ConsumerBase,
-                           public DisplaySurface {
+class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
 public:
-    FramebufferSurface(HWComposer& hwc, int disp, const sp<IGraphicBufferConsumer>& consumer);
+    FramebufferSurface(HWComposer& hwc, DisplayId displayId,
+                       const sp<IGraphicBufferConsumer>& consumer);
 
     virtual status_t beginFrame(bool mustRecompose);
     virtual status_t prepareFrame(CompositionType compositionType);
@@ -63,8 +64,7 @@
     status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
             sp<Fence>& outFence, ui::Dataspace& outDataspace);
 
-    // mDisplayType must match one of the HWC display types
-    int mDisplayType;
+    const DisplayId mDisplayId;
 
     // mCurrentBufferIndex is the slot index of the current buffer or
     // INVALID_BUFFER_SLOT to indicate that either there is no current buffer
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 1a60c83..68e7876 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -124,6 +124,12 @@
     return mComposer->getMaxVirtualDisplayCount();
 }
 
+Error Device::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+                                           std::vector<uint8_t>* outData) const {
+    auto intError = mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
+    return static_cast<Error>(intError);
+}
+
 Error Device::createVirtualDisplay(uint32_t width, uint32_t height,
         PixelFormat* format, Display** outDisplay)
 {
@@ -137,8 +143,8 @@
         return error;
     }
 
-    auto display = std::make_unique<Display>(
-            *mComposer.get(), mPowerAdvisor, mCapabilities, displayId, DisplayType::Virtual);
+    auto display = std::make_unique<impl::Display>(*mComposer.get(), mPowerAdvisor, mCapabilities,
+                                                   displayId, DisplayType::Virtual);
     display->setConnected(true);
     *outDisplay = display.get();
     mDisplays.emplace(displayId, std::move(display));
@@ -176,8 +182,8 @@
             return;
         }
 
-        auto newDisplay = std::make_unique<Display>(
-                *mComposer.get(), mPowerAdvisor, mCapabilities, displayId, displayType);
+        auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mPowerAdvisor,
+                                                          mCapabilities, displayId, displayType);
         newDisplay->setConnected(true);
         mDisplays.emplace(displayId, std::move(newDisplay));
     } else if (connection == Connection::Disconnected) {
@@ -218,7 +224,36 @@
 }
 
 // Display methods
+Display::~Display() = default;
 
+Display::Config::Config(Display& display, hwc2_config_t id)
+      : mDisplay(display),
+        mId(id),
+        mWidth(-1),
+        mHeight(-1),
+        mVsyncPeriod(-1),
+        mDpiX(-1),
+        mDpiY(-1) {}
+
+Display::Config::Builder::Builder(Display& display, hwc2_config_t id)
+      : mConfig(new Config(display, id)) {}
+
+float Display::Config::Builder::getDefaultDensity() {
+    // Default density is based on TVs: 1080p displays get XHIGH density, lower-
+    // resolution displays get TV density. Maybe eventually we'll need to update
+    // it for 4k displays, though hopefully those will just report accurate DPI
+    // information to begin with. This is also used for virtual displays and
+    // older HWC implementations, so be careful about orientation.
+
+    auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight);
+    if (longDimension >= 1080) {
+        return ACONFIGURATION_DENSITY_XHIGH;
+    } else {
+        return ACONFIGURATION_DENSITY_TV;
+    }
+}
+
+namespace impl {
 Display::Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
                  const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
                  DisplayType type)
@@ -228,6 +263,22 @@
         mId(id),
         mIsConnected(false),
         mType(type) {
+    std::vector<Hwc2::DisplayCapability> tmpCapabilities;
+    auto error = static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
+    if (error == Error::None) {
+        for (auto capability : tmpCapabilities) {
+            mDisplayCapabilities.emplace(static_cast<DisplayCapability>(capability));
+        }
+    } else if (error == Error::Unsupported) {
+        if (capabilities.count(Capability::SkipClientColorTransform)) {
+            mDisplayCapabilities.emplace(DisplayCapability::SkipClientColorTransform);
+        }
+        bool dozeSupport = false;
+        error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport));
+        if (error == Error::None && dozeSupport) {
+            mDisplayCapabilities.emplace(DisplayCapability::Doze);
+        }
+    }
     ALOGV("Created display %" PRIu64, id);
 }
 
@@ -250,35 +301,7 @@
     }
 }
 
-Display::Config::Config(Display& display, hwc2_config_t id)
-  : mDisplay(display),
-    mId(id),
-    mWidth(-1),
-    mHeight(-1),
-    mVsyncPeriod(-1),
-    mDpiX(-1),
-    mDpiY(-1) {}
-
-Display::Config::Builder::Builder(Display& display, hwc2_config_t id)
-  : mConfig(new Config(display, id)) {}
-
-float Display::Config::Builder::getDefaultDensity() {
-    // Default density is based on TVs: 1080p displays get XHIGH density, lower-
-    // resolution displays get TV density. Maybe eventually we'll need to update
-    // it for 4k displays, though hopefully those will just report accurate DPI
-    // information to begin with. This is also used for virtual displays and
-    // older HWC implementations, so be careful about orientation.
-
-    auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight);
-    if (longDimension >= 1080) {
-        return ACONFIGURATION_DENSITY_XHIGH;
-    } else {
-        return ACONFIGURATION_DENSITY_TV;
-    }
-}
-
 // Required by HWC2 display
-
 Error Display::acceptChanges()
 {
     auto intError = mComposer.acceptDisplayChanges(mId);
@@ -403,21 +426,17 @@
     return static_cast<Error>(intError);
 }
 
-Error Display::getSupportedPerFrameMetadata(int32_t* outSupportedPerFrameMetadata) const
+int32_t Display::getSupportedPerFrameMetadata() const
 {
-    *outSupportedPerFrameMetadata = 0;
-    std::vector<Hwc2::PerFrameMetadataKey> tmpKeys;
-    auto intError = mComposer.getPerFrameMetadataKeys(mId, &tmpKeys);
-    auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
+    int32_t supportedPerFrameMetadata = 0;
+
+    std::vector<Hwc2::PerFrameMetadataKey> tmpKeys = mComposer.getPerFrameMetadataKeys(mId);
+    std::set<Hwc2::PerFrameMetadataKey> keys(tmpKeys.begin(), tmpKeys.end());
 
     // Check whether a specific metadata type is supported. A metadata type is considered
     // supported if and only if all required fields are supported.
 
     // SMPTE2086
-    std::set<Hwc2::PerFrameMetadataKey> keys(tmpKeys.begin(), tmpKeys.end());
     if (hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X) &&
         hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y) &&
         hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X) &&
@@ -428,15 +447,20 @@
         hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::WHITE_POINT_Y) &&
         hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MAX_LUMINANCE) &&
         hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MIN_LUMINANCE)) {
-        *outSupportedPerFrameMetadata |= HdrMetadata::Type::SMPTE2086;
+        supportedPerFrameMetadata |= HdrMetadata::Type::SMPTE2086;
     }
     // CTA861_3
     if (hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL) &&
         hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL)) {
-        *outSupportedPerFrameMetadata |= HdrMetadata::Type::CTA861_3;
+        supportedPerFrameMetadata |= HdrMetadata::Type::CTA861_3;
     }
 
-    return Error::None;
+    // HDR10PLUS
+    if (hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI)) {
+        supportedPerFrameMetadata |= HdrMetadata::Type::HDR10PLUS;
+    }
+
+    return supportedPerFrameMetadata;
 }
 
 Error Display::getRenderIntents(ColorMode colorMode,
@@ -505,15 +529,8 @@
     return Error::None;
 }
 
-Error Display::supportsDoze(bool* outSupport) const
-{
-    bool intSupport = false;
-    auto intError = mComposer.getDozeSupport(mId, &intSupport);
-    auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
-    *outSupport = static_cast<bool>(intSupport);
+Error Display::supportsDoze(bool* outSupport) const {
+    *outSupport = mDisplayCapabilities.count(DisplayCapability::Doze) > 0;
     return Error::None;
 }
 
@@ -536,6 +553,27 @@
     return Error::None;
 }
 
+Error Display::getDisplayedContentSamplingAttributes(PixelFormat* outFormat,
+                                                     Dataspace* outDataspace,
+                                                     uint8_t* outComponentMask) const {
+    auto intError = mComposer.getDisplayedContentSamplingAttributes(mId, outFormat, outDataspace,
+                                                                    outComponentMask);
+    return static_cast<Error>(intError);
+}
+
+Error Display::setDisplayContentSamplingEnabled(bool enabled, uint8_t componentMask,
+                                                uint64_t maxFrames) const {
+    auto intError =
+            mComposer.setDisplayContentSamplingEnabled(mId, enabled, componentMask, maxFrames);
+    return static_cast<Error>(intError);
+}
+
+Error Display::getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp,
+                                         android::DisplayedFrameStats* outStats) const {
+    auto intError = mComposer.getDisplayedContentSample(mId, maxFrames, timestamp, outStats);
+    return static_cast<Error>(intError);
+}
+
 Error Display::getReleaseFences(
         std::unordered_map<Layer*, sp<Fence>>* outFences) const
 {
@@ -757,6 +795,7 @@
 
     return mLayers.at(id).get();
 }
+} // namespace impl
 
 // Layer methods
 
@@ -765,7 +804,8 @@
   : mComposer(composer),
     mCapabilities(capabilities),
     mDisplayId(displayId),
-    mId(layerId)
+    mId(layerId),
+    mColorMatrix(android::mat4())
 {
     ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, displayId);
 }
@@ -872,37 +912,49 @@
     if (validTypes & HdrMetadata::SMPTE2086) {
         perFrameMetadatas.insert(perFrameMetadatas.end(),
                                  {{Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
-                                         mHdrMetadata.smpte2086.displayPrimaryRed.x},
-                                   {Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
-                                         mHdrMetadata.smpte2086.displayPrimaryRed.y},
-                                   {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
-                                         mHdrMetadata.smpte2086.displayPrimaryGreen.x},
-                                   {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
-                                         mHdrMetadata.smpte2086.displayPrimaryGreen.y},
-                                   {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
-                                         mHdrMetadata.smpte2086.displayPrimaryBlue.x},
-                                   {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
-                                         mHdrMetadata.smpte2086.displayPrimaryBlue.y},
-                                   {Hwc2::PerFrameMetadataKey::WHITE_POINT_X,
-                                         mHdrMetadata.smpte2086.whitePoint.x},
-                                   {Hwc2::PerFrameMetadataKey::WHITE_POINT_Y,
-                                         mHdrMetadata.smpte2086.whitePoint.y},
-                                   {Hwc2::PerFrameMetadataKey::MAX_LUMINANCE,
-                                         mHdrMetadata.smpte2086.maxLuminance},
-                                   {Hwc2::PerFrameMetadataKey::MIN_LUMINANCE,
-                                         mHdrMetadata.smpte2086.minLuminance}});
+                                   mHdrMetadata.smpte2086.displayPrimaryRed.x},
+                                  {Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
+                                   mHdrMetadata.smpte2086.displayPrimaryRed.y},
+                                  {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
+                                   mHdrMetadata.smpte2086.displayPrimaryGreen.x},
+                                  {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
+                                   mHdrMetadata.smpte2086.displayPrimaryGreen.y},
+                                  {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
+                                   mHdrMetadata.smpte2086.displayPrimaryBlue.x},
+                                  {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
+                                   mHdrMetadata.smpte2086.displayPrimaryBlue.y},
+                                  {Hwc2::PerFrameMetadataKey::WHITE_POINT_X,
+                                   mHdrMetadata.smpte2086.whitePoint.x},
+                                  {Hwc2::PerFrameMetadataKey::WHITE_POINT_Y,
+                                   mHdrMetadata.smpte2086.whitePoint.y},
+                                  {Hwc2::PerFrameMetadataKey::MAX_LUMINANCE,
+                                   mHdrMetadata.smpte2086.maxLuminance},
+                                  {Hwc2::PerFrameMetadataKey::MIN_LUMINANCE,
+                                   mHdrMetadata.smpte2086.minLuminance}});
     }
 
     if (validTypes & HdrMetadata::CTA861_3) {
         perFrameMetadatas.insert(perFrameMetadatas.end(),
                                  {{Hwc2::PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
-                                         mHdrMetadata.cta8613.maxContentLightLevel},
-                                   {Hwc2::PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
-                                         mHdrMetadata.cta8613.maxFrameAverageLightLevel}});
+                                   mHdrMetadata.cta8613.maxContentLightLevel},
+                                  {Hwc2::PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+                                   mHdrMetadata.cta8613.maxFrameAverageLightLevel}});
     }
 
-    auto intError = mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas);
-    return static_cast<Error>(intError);
+    Error error = static_cast<Error>(
+            mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas));
+
+    if (validTypes & HdrMetadata::HDR10PLUS) {
+        std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs;
+        perFrameMetadataBlobs.push_back(
+                {Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus});
+        Error setMetadataBlobsError = static_cast<Error>(
+                mComposer.setLayerPerFrameMetadataBlobs(mDisplayId, mId, perFrameMetadataBlobs));
+        if (error == Error::None) {
+            return setMetadataBlobsError;
+        }
+    }
+    return error;
 }
 
 Error Layer::setDisplayFrame(const Rect& frame)
@@ -972,4 +1024,13 @@
   return static_cast<Error>(intError);
 }
 
+// Composer HAL 2.3
+Error Layer::setColorTransform(const android::mat4& matrix) {
+    if (matrix == mColorMatrix) {
+        return Error::None;
+    }
+    mColorMatrix = matrix;
+    auto intError = mComposer.setLayerColorTransform(mDisplayId, mId, matrix.asArray());
+    return static_cast<Error>(intError);
+}
 } // namespace HWC2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index e423167..c1f481a 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -40,6 +40,7 @@
 #include "PowerAdvisor.h"
 
 namespace android {
+    struct DisplayedFrameStats;
     class Fence;
     class FloatRect;
     class GraphicBuffer;
@@ -95,6 +96,9 @@
     };
 
     uint32_t getMaxVirtualDisplayCount() const;
+    Error getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+                                       std::vector<uint8_t>* outData) const;
+
     Error createVirtualDisplay(uint32_t width, uint32_t height,
             android::ui::PixelFormat* format, Display** outDisplay);
     void destroyDisplay(hwc2_display_t displayId);
@@ -126,16 +130,11 @@
 };
 
 // Convenience C++ class to access hwc2_device_t Display functions directly.
-class Display
-{
+class Display {
 public:
-    Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
-            const std::unordered_set<Capability>& capabilities,
-            hwc2_display_t id, DisplayType type);
-    ~Display();
+    virtual ~Display();
 
-    class Config
-    {
+    class Config {
     public:
         class Builder
         {
@@ -203,71 +202,138 @@
         float mDpiY;
     };
 
-    // Required by HWC2
+    virtual hwc2_display_t getId() const = 0;
+    virtual bool isConnected() const = 0;
+    virtual void setConnected(bool connected) = 0; // For use by Device only
+    virtual const std::unordered_set<DisplayCapability>& getCapabilities() const = 0;
 
-    [[clang::warn_unused_result]] Error acceptChanges();
-    [[clang::warn_unused_result]] Error createLayer(Layer** outLayer);
-    [[clang::warn_unused_result]] Error destroyLayer(Layer* layer);
-    [[clang::warn_unused_result]] Error getActiveConfig(
-            std::shared_ptr<const Config>* outConfig) const;
-    [[clang::warn_unused_result]] Error getActiveConfigIndex(int* outIndex) const;
-    [[clang::warn_unused_result]] Error getChangedCompositionTypes(
-            std::unordered_map<Layer*, Composition>* outTypes);
-    [[clang::warn_unused_result]] Error getColorModes(
-            std::vector<android::ui::ColorMode>* outModes) const;
-    // outSupportedPerFrameMetadata is an opaque bitmask to the callers
-    // but contains HdrMetadata::Type::*.
-    [[clang::warn_unused_result]] Error getSupportedPerFrameMetadata(
-            int32_t* outSupportedPerFrameMetadata) const;
-    [[clang::warn_unused_result]] Error getRenderIntents(
+    [[clang::warn_unused_result]] virtual Error acceptChanges() = 0;
+    [[clang::warn_unused_result]] virtual Error createLayer(Layer** outLayer) = 0;
+    [[clang::warn_unused_result]] virtual Error destroyLayer(Layer* layer) = 0;
+    [[clang::warn_unused_result]] virtual Error getActiveConfig(
+            std::shared_ptr<const Config>* outConfig) const = 0;
+    [[clang::warn_unused_result]] virtual Error getActiveConfigIndex(int* outIndex) const = 0;
+    [[clang::warn_unused_result]] virtual Error getChangedCompositionTypes(
+            std::unordered_map<Layer*, Composition>* outTypes) = 0;
+    [[clang::warn_unused_result]] virtual Error getColorModes(
+            std::vector<android::ui::ColorMode>* outModes) const = 0;
+    // Returns a bitmask which contains HdrMetadata::Type::*.
+    [[clang::warn_unused_result]] virtual int32_t getSupportedPerFrameMetadata() const = 0;
+    [[clang::warn_unused_result]] virtual Error getRenderIntents(
             android::ui::ColorMode colorMode,
-            std::vector<android::ui::RenderIntent>* outRenderIntents) const;
-    [[clang::warn_unused_result]] Error getDataspaceSaturationMatrix(
-            android::ui::Dataspace dataspace, android::mat4* outMatrix);
+            std::vector<android::ui::RenderIntent>* outRenderIntents) const = 0;
+    [[clang::warn_unused_result]] virtual Error getDataspaceSaturationMatrix(
+            android::ui::Dataspace dataspace, android::mat4* outMatrix) = 0;
+
+    // Doesn't call into the HWC2 device, so no Errors are possible
+    virtual std::vector<std::shared_ptr<const Config>> getConfigs() const = 0;
+
+    [[clang::warn_unused_result]] virtual Error getName(std::string* outName) const = 0;
+    [[clang::warn_unused_result]] virtual Error getRequests(
+            DisplayRequest* outDisplayRequests,
+            std::unordered_map<Layer*, LayerRequest>* outLayerRequests) = 0;
+    [[clang::warn_unused_result]] virtual Error getType(DisplayType* outType) const = 0;
+    [[clang::warn_unused_result]] virtual Error supportsDoze(bool* outSupport) const = 0;
+    [[clang::warn_unused_result]] virtual Error getHdrCapabilities(
+            android::HdrCapabilities* outCapabilities) const = 0;
+    [[clang::warn_unused_result]] virtual Error getDisplayedContentSamplingAttributes(
+            android::ui::PixelFormat* outFormat, android::ui::Dataspace* outDataspace,
+            uint8_t* outComponentMask) const = 0;
+    [[clang::warn_unused_result]] virtual Error setDisplayContentSamplingEnabled(
+            bool enabled, uint8_t componentMask, uint64_t maxFrames) const = 0;
+    [[clang::warn_unused_result]] virtual Error getDisplayedContentSample(
+            uint64_t maxFrames, uint64_t timestamp,
+            android::DisplayedFrameStats* outStats) const = 0;
+    [[clang::warn_unused_result]] virtual Error getReleaseFences(
+            std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const = 0;
+    [[clang::warn_unused_result]] virtual Error present(
+            android::sp<android::Fence>* outPresentFence) = 0;
+    [[clang::warn_unused_result]] virtual Error setActiveConfig(
+            const std::shared_ptr<const Config>& config) = 0;
+    [[clang::warn_unused_result]] virtual Error setClientTarget(
+            uint32_t slot, const android::sp<android::GraphicBuffer>& target,
+            const android::sp<android::Fence>& acquireFence, android::ui::Dataspace dataspace) = 0;
+    [[clang::warn_unused_result]] virtual Error setColorMode(
+            android::ui::ColorMode mode, android::ui::RenderIntent renderIntent) = 0;
+    [[clang::warn_unused_result]] virtual Error setColorTransform(
+            const android::mat4& matrix, android_color_transform_t hint) = 0;
+    [[clang::warn_unused_result]] virtual Error setOutputBuffer(
+            const android::sp<android::GraphicBuffer>& buffer,
+            const android::sp<android::Fence>& releaseFence) = 0;
+    [[clang::warn_unused_result]] virtual Error setPowerMode(PowerMode mode) = 0;
+    [[clang::warn_unused_result]] virtual Error setVsyncEnabled(Vsync enabled) = 0;
+    [[clang::warn_unused_result]] virtual Error validate(uint32_t* outNumTypes,
+                                                         uint32_t* outNumRequests) = 0;
+    [[clang::warn_unused_result]] virtual Error presentOrValidate(
+            uint32_t* outNumTypes, uint32_t* outNumRequests,
+            android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
+};
+
+namespace impl {
+
+class Display : public HWC2::Display {
+public:
+    Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
+            const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
+            DisplayType type);
+    ~Display() override;
+
+    // Required by HWC2
+    Error acceptChanges() override;
+    Error createLayer(Layer** outLayer) override;
+    Error destroyLayer(Layer* layer) override;
+    Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override;
+    Error getActiveConfigIndex(int* outIndex) const override;
+    Error getChangedCompositionTypes(std::unordered_map<Layer*, Composition>* outTypes) override;
+    Error getColorModes(std::vector<android::ui::ColorMode>* outModes) const override;
+    // Returns a bitmask which contains HdrMetadata::Type::*.
+    int32_t getSupportedPerFrameMetadata() const override;
+    Error getRenderIntents(android::ui::ColorMode colorMode,
+                           std::vector<android::ui::RenderIntent>* outRenderIntents) const override;
+    Error getDataspaceSaturationMatrix(android::ui::Dataspace dataspace,
+                                       android::mat4* outMatrix) override;
 
     // Doesn't call into the HWC2 device, so no errors are possible
-    std::vector<std::shared_ptr<const Config>> getConfigs() const;
+    std::vector<std::shared_ptr<const Config>> getConfigs() const override;
 
-    [[clang::warn_unused_result]] Error getName(std::string* outName) const;
-    [[clang::warn_unused_result]] Error getRequests(
-            DisplayRequest* outDisplayRequests,
-            std::unordered_map<Layer*, LayerRequest>* outLayerRequests);
-    [[clang::warn_unused_result]] Error getType(DisplayType* outType) const;
-    [[clang::warn_unused_result]] Error supportsDoze(bool* outSupport) const;
-    [[clang::warn_unused_result]] Error getHdrCapabilities(
-            android::HdrCapabilities* outCapabilities) const;
-    [[clang::warn_unused_result]] Error getReleaseFences(
-            std::unordered_map<Layer*,
-                    android::sp<android::Fence>>* outFences) const;
-    [[clang::warn_unused_result]] Error present(
-            android::sp<android::Fence>* outPresentFence);
-    [[clang::warn_unused_result]] Error setActiveConfig(
-            const std::shared_ptr<const Config>& config);
-    [[clang::warn_unused_result]] Error setClientTarget(
-            uint32_t slot, const android::sp<android::GraphicBuffer>& target,
-            const android::sp<android::Fence>& acquireFence,
-            android::ui::Dataspace dataspace);
-    [[clang::warn_unused_result]] Error setColorMode(
-            android::ui::ColorMode mode,
-            android::ui::RenderIntent renderIntent);
-    [[clang::warn_unused_result]] Error setColorTransform(
-            const android::mat4& matrix, android_color_transform_t hint);
-    [[clang::warn_unused_result]] Error setOutputBuffer(
-            const android::sp<android::GraphicBuffer>& buffer,
-            const android::sp<android::Fence>& releaseFence);
-    [[clang::warn_unused_result]] Error setPowerMode(PowerMode mode);
-    [[clang::warn_unused_result]] Error setVsyncEnabled(Vsync enabled);
-    [[clang::warn_unused_result]] Error validate(uint32_t* outNumTypes,
-            uint32_t* outNumRequests);
-    [[clang::warn_unused_result]] Error presentOrValidate(uint32_t* outNumTypes,
-            uint32_t* outNumRequests,
-            android::sp<android::Fence>* outPresentFence, uint32_t* state);
+    Error getName(std::string* outName) const override;
+    Error getRequests(DisplayRequest* outDisplayRequests,
+                      std::unordered_map<Layer*, LayerRequest>* outLayerRequests) override;
+    Error getType(DisplayType* outType) const override;
+    Error supportsDoze(bool* outSupport) const override;
+    Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
+    Error getDisplayedContentSamplingAttributes(android::ui::PixelFormat* outFormat,
+                                                android::ui::Dataspace* outDataspace,
+                                                uint8_t* outComponentMask) const override;
+    Error setDisplayContentSamplingEnabled(bool enabled, uint8_t componentMask,
+                                           uint64_t maxFrames) const override;
+    Error getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp,
+                                    android::DisplayedFrameStats* outStats) const override;
+    Error getReleaseFences(
+            std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const override;
+    Error present(android::sp<android::Fence>* outPresentFence) override;
+    Error setActiveConfig(const std::shared_ptr<const HWC2::Display::Config>& config) override;
+    Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
+                          const android::sp<android::Fence>& acquireFence,
+                          android::ui::Dataspace dataspace) override;
+    Error setColorMode(android::ui::ColorMode mode,
+                       android::ui::RenderIntent renderIntent) override;
+    Error setColorTransform(const android::mat4& matrix, android_color_transform_t hint) override;
+    Error setOutputBuffer(const android::sp<android::GraphicBuffer>& buffer,
+                          const android::sp<android::Fence>& releaseFence) override;
+    Error setPowerMode(PowerMode mode) override;
+    Error setVsyncEnabled(Vsync enabled) override;
+    Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
+    Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
+                            android::sp<android::Fence>* outPresentFence, uint32_t* state) override;
 
     // Other Display methods
-
-    hwc2_display_t getId() const { return mId; }
-    bool isConnected() const { return mIsConnected; }
-    void setConnected(bool connected);  // For use by Device only
+    hwc2_display_t getId() const override { return mId; }
+    bool isConnected() const override { return mIsConnected; }
+    void setConnected(bool connected) override; // For use by Device only
+    const std::unordered_set<DisplayCapability>& getCapabilities() const override {
+        return mDisplayCapabilities;
+    };
 
 private:
     int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
@@ -294,7 +360,9 @@
     DisplayType mType;
     std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
     std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+    std::unordered_set<DisplayCapability> mDisplayCapabilities;
 };
+} // namespace impl
 
 // Convenience C++ class to access hwc2_device_t Layer functions directly.
 class Layer
@@ -340,6 +408,9 @@
     [[clang::warn_unused_result]] Error setZOrder(uint32_t z);
     [[clang::warn_unused_result]] Error setInfo(uint32_t type, uint32_t appId);
 
+    // Composer HAL 2.3
+    [[clang::warn_unused_result]] Error setColorTransform(const android::mat4& matrix);
+
 private:
     // These are references to data owned by HWC2::Device, which will outlive
     // this HWC2::Layer, so these references are guaranteed to be valid for
@@ -352,6 +423,7 @@
     android::ui::Dataspace mDataSpace = android::ui::Dataspace::UNKNOWN;
     android::HdrMetadata mHdrMetadata;
     std::function<void(Layer*)> mLayerDestroyedListener;
+    android::mat4 mColorMatrix;
 };
 
 } // namespace HWC2
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index f5f7a82..5b4d347 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -20,31 +20,12 @@
 #define LOG_TAG "HWComposer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <inttypes.h>
-#include <math.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
 #include <utils/Errors.h>
-#include <utils/misc.h>
-#include <utils/NativeHandle.h>
-#include <utils/String8.h>
-#include <utils/Thread.h>
 #include <utils/Trace.h>
-#include <utils/Vector.h>
 
 #include <ui/DebugUtils.h>
 #include <ui/GraphicBuffer.h>
 
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-
-#include <android/configuration.h>
-
-#include <cutils/properties.h>
 #include <log/log.h>
 
 #include "HWComposer.h"
@@ -54,16 +35,19 @@
 #include "../Layer.h"           // needed only for debugging
 #include "../SurfaceFlinger.h"
 
-#define LOG_DISPLAY_ERROR(displayId, msg) \
-    ALOGE("%s failed for display %d: %s", __FUNCTION__, displayId, msg)
+#define LOG_HWC_DISPLAY_ERROR(hwcDisplayId, msg) \
+    ALOGE("%s failed for HWC display %" PRIu64 ": %s", __FUNCTION__, hwcDisplayId, msg)
 
-#define LOG_HWC_ERROR(what, error, displayId)                                     \
-    ALOGE("%s: %s failed for display %d: %s (%d)", __FUNCTION__, what, displayId, \
-          to_string(error).c_str(), static_cast<int32_t>(error))
+#define LOG_DISPLAY_ERROR(displayId, msg) \
+    ALOGE("%s failed for display %s: %s", __FUNCTION__, to_string(displayId).c_str(), msg)
+
+#define LOG_HWC_ERROR(what, error, displayId)                          \
+    ALOGE("%s: %s failed for display %s: %s (%d)", __FUNCTION__, what, \
+          to_string(displayId).c_str(), to_string(error).c_str(), static_cast<int32_t>(error))
 
 #define RETURN_IF_INVALID_DISPLAY(displayId, ...)            \
     do {                                                     \
-        if (!isValidDisplay(displayId)) {                    \
+        if (mDisplayData.count(displayId) == 0) {            \
             LOG_DISPLAY_ERROR(displayId, "Invalid display"); \
             return __VA_ARGS__;                              \
         }                                                    \
@@ -82,28 +66,50 @@
 
 namespace android {
 
-#define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION
+HWComposer::~HWComposer() = default;
 
-// ---------------------------------------------------------------------------
+namespace impl {
 
-HWComposer::HWComposer(std::unique_ptr<android::Hwc2::Composer> composer)
+HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
       : mHwcDevice(std::make_unique<HWC2::Device>(std::move(composer))) {}
 
-HWComposer::~HWComposer() = default;
+HWComposer::~HWComposer() {
+    mDisplayData.clear();
+}
 
 void HWComposer::registerCallback(HWC2::ComposerCallback* callback,
                                   int32_t sequenceId) {
     mHwcDevice->registerCallback(callback, sequenceId);
 }
 
+bool HWComposer::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+                                              DisplayIdentificationData* outData) const {
+    const auto error = mHwcDevice->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
+    if (error != HWC2::Error::None) {
+        if (error != HWC2::Error::Unsupported) {
+            LOG_HWC_DISPLAY_ERROR(hwcDisplayId, to_string(error).c_str());
+        }
+        return false;
+    }
+    return true;
+}
+
 bool HWComposer::hasCapability(HWC2::Capability capability) const
 {
     return mHwcDevice->getCapabilities().count(capability) > 0;
 }
 
-bool HWComposer::isValidDisplay(int32_t displayId) const {
-    return static_cast<size_t>(displayId) < mDisplayData.size() &&
-            mDisplayData[displayId].hwcDisplay;
+bool HWComposer::hasDisplayCapability(const std::optional<DisplayId>& displayId,
+                                      HWC2::DisplayCapability capability) const {
+    if (!displayId) {
+        // Checkout global capabilities for displays without a corresponding HWC display.
+        if (capability == HWC2::DisplayCapability::SkipClientColorTransform) {
+            return hasCapability(HWC2::Capability::SkipClientColorTransform);
+        }
+        return false;
+    }
+    RETURN_IF_INVALID_DISPLAY(*displayId, false);
+    return mDisplayData.at(*displayId).hwcDisplay->getCapabilities().count(capability) > 0;
 }
 
 void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) {
@@ -131,126 +137,115 @@
     }
 }
 
-void HWComposer::onHotplug(hwc2_display_t displayId, int32_t displayType,
-                           HWC2::Connection connection) {
-    if (displayType >= HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
-        ALOGE("Invalid display type of %d", displayType);
-        return;
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hwc2_display_t hwcDisplayId,
+                                                               HWC2::Connection connection) {
+    std::optional<DisplayIdentificationInfo> info;
+
+    if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
+        info = DisplayIdentificationInfo{*displayId, std::string()};
+    } else {
+        if (connection == HWC2::Connection::Disconnected) {
+            ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+            return {};
+        }
+
+        info = onHotplugConnect(hwcDisplayId);
+        if (!info) return {};
     }
 
-    ALOGV("hotplug: %" PRIu64 ", %s %s", displayId,
-            displayType == DisplayDevice::DISPLAY_PRIMARY ? "primary" : "external",
-            to_string(connection).c_str());
-    mHwcDevice->onHotplug(displayId, connection);
+    ALOGV("%s: %s %s display %s with HWC ID %" PRIu64, __FUNCTION__, to_string(connection).c_str(),
+          hwcDisplayId == mInternalHwcDisplayId ? "internal" : "external",
+          to_string(info->id).c_str(), hwcDisplayId);
+
+    mHwcDevice->onHotplug(hwcDisplayId, connection);
+
     // Disconnect is handled through HWComposer::disconnectDisplay via
     // SurfaceFlinger's onHotplugReceived callback handling
     if (connection == HWC2::Connection::Connected) {
-        mDisplayData[displayType].hwcDisplay = mHwcDevice->getDisplayById(displayId);
-        mHwcDisplaySlots[displayId] = displayType;
+        mDisplayData[info->id].hwcDisplay = mHwcDevice->getDisplayById(hwcDisplayId);
+        mPhysicalDisplayIdMap[hwcDisplayId] = info->id;
     }
+
+    return info;
 }
 
-bool HWComposer::onVsync(hwc2_display_t displayId, int64_t timestamp,
-                         int32_t* outDisplay) {
-    auto display = mHwcDevice->getDisplayById(displayId);
-    if (!display) {
-        ALOGE("onVsync Failed to find display %" PRIu64, displayId);
-        return false;
-    }
-    auto displayType = HWC2::DisplayType::Invalid;
-    auto error = display->getType(&displayType);
-    if (error != HWC2::Error::None) {
-        ALOGE("onVsync: Failed to determine type of display %" PRIu64,
-                display->getId());
+bool HWComposer::onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) {
+    const auto displayId = toPhysicalDisplayId(hwcDisplayId);
+    if (!displayId) {
+        LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
         return false;
     }
 
-    if (displayType == HWC2::DisplayType::Virtual) {
-        ALOGE("Virtual display %" PRIu64 " passed to vsync callback",
-                display->getId());
+    RETURN_IF_INVALID_DISPLAY(*displayId, false);
+
+    auto& displayData = mDisplayData[*displayId];
+    if (displayData.isVirtual) {
+        LOG_DISPLAY_ERROR(*displayId, "Invalid operation on virtual display");
         return false;
     }
 
-    if (mHwcDisplaySlots.count(display->getId()) == 0) {
-        ALOGE("Unknown physical display %" PRIu64 " passed to vsync callback",
-                display->getId());
-        return false;
-    }
-
-    int32_t disp = mHwcDisplaySlots[display->getId()];
     {
-        Mutex::Autolock _l(mLock);
+        std::lock_guard lock(displayData.lastHwVsyncLock);
 
         // There have been reports of HWCs that signal several vsync events
         // with the same timestamp when turning the display off and on. This
         // is a bug in the HWC implementation, but filter the extra events
         // out here so they don't cause havoc downstream.
-        if (timestamp == mLastHwVSync[disp]) {
-            ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")",
-                    timestamp);
+        if (timestamp == displayData.lastHwVsync) {
+            ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")",
+                  to_string(*displayId).c_str(), timestamp);
             return false;
         }
 
-        mLastHwVSync[disp] = timestamp;
+        displayData.lastHwVsync = timestamp;
     }
 
-    if (outDisplay) {
-        *outDisplay = disp;
-    }
-
-    char tag[16];
-    snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
-    ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
+    const auto tag = "HW_VSYNC_" + to_string(*displayId);
+    ATRACE_INT(tag.c_str(), displayData.vsyncTraceToggle);
+    displayData.vsyncTraceToggle = !displayData.vsyncTraceToggle;
 
     return true;
 }
 
-status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height,
-        ui::PixelFormat* format, int32_t *outId) {
+std::optional<DisplayId> HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height,
+                                                            ui::PixelFormat* format) {
     if (mRemainingHwcVirtualDisplays == 0) {
-        ALOGE("allocateVirtualDisplay: No remaining virtual displays");
-        return NO_MEMORY;
+        ALOGE("%s: No remaining virtual displays", __FUNCTION__);
+        return {};
     }
 
     if (SurfaceFlinger::maxVirtualDisplaySize != 0 &&
         (width > SurfaceFlinger::maxVirtualDisplaySize ||
          height > SurfaceFlinger::maxVirtualDisplaySize)) {
-        ALOGE("createVirtualDisplay: Can't create a virtual display with"
-                      " a dimension > %" PRIu64 " (tried %u x %u)",
-              SurfaceFlinger::maxVirtualDisplaySize, width, height);
-        return INVALID_OPERATION;
+        ALOGE("%s: Display size %ux%u exceeds maximum dimension of %" PRIu64, __FUNCTION__, width,
+              height, SurfaceFlinger::maxVirtualDisplaySize);
+        return {};
     }
-
     HWC2::Display* display;
     auto error = mHwcDevice->createVirtualDisplay(width, height, format,
             &display);
     if (error != HWC2::Error::None) {
-        ALOGE("allocateVirtualDisplay: Failed to create HWC virtual display");
-        return NO_MEMORY;
+        ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
+        return {};
     }
 
-    size_t displaySlot = 0;
-    if (!mFreeDisplaySlots.empty()) {
-        displaySlot = *mFreeDisplaySlots.begin();
-        mFreeDisplaySlots.erase(displaySlot);
-    } else if (mDisplayData.size() < INT32_MAX) {
-        // Don't bother allocating a slot larger than we can return
-        displaySlot = mDisplayData.size();
-        mDisplayData.resize(displaySlot + 1);
+    DisplayId displayId;
+    if (mFreeVirtualDisplayIds.empty()) {
+        displayId = getVirtualDisplayId(mNextVirtualDisplayId++);
     } else {
-        ALOGE("allocateVirtualDisplay: Unable to allocate a display slot");
-        return NO_MEMORY;
+        displayId = *mFreeVirtualDisplayIds.begin();
+        mFreeVirtualDisplayIds.erase(displayId);
     }
 
-    mDisplayData[displaySlot].hwcDisplay = display;
+    auto& displayData = mDisplayData[displayId];
+    displayData.hwcDisplay = display;
+    displayData.isVirtual = true;
 
     --mRemainingHwcVirtualDisplays;
-    *outId = static_cast<int32_t>(displaySlot);
-
-    return NO_ERROR;
+    return displayId;
 }
 
-HWC2::Layer* HWComposer::createLayer(int32_t displayId) {
+HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
     RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
 
     auto display = mDisplayData[displayId].hwcDisplay;
@@ -260,7 +255,7 @@
     return layer;
 }
 
-void HWComposer::destroyLayer(int32_t displayId, HWC2::Layer* layer) {
+void HWComposer::destroyLayer(DisplayId displayId, HWC2::Layer* layer) {
     RETURN_IF_INVALID_DISPLAY(displayId);
 
     auto display = mDisplayData[displayId].hwcDisplay;
@@ -268,27 +263,29 @@
     RETURN_IF_HWC_ERROR(error, displayId);
 }
 
-nsecs_t HWComposer::getRefreshTimestamp(int32_t displayId) const {
+nsecs_t HWComposer::getRefreshTimestamp(DisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, 0);
+    const auto& displayData = mDisplayData.at(displayId);
     // this returns the last refresh timestamp.
     // if the last one is not available, we estimate it based on
     // the refresh period and whatever closest timestamp we have.
-    Mutex::Autolock _l(mLock);
+    std::lock_guard lock(displayData.lastHwVsyncLock);
     nsecs_t now = systemTime(CLOCK_MONOTONIC);
     auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
-    return now - ((now - mLastHwVSync[displayId]) % vsyncPeriod);
+    return now - ((now - displayData.lastHwVsync) % vsyncPeriod);
 }
 
-bool HWComposer::isConnected(int32_t displayId) const {
+bool HWComposer::isConnected(DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
-    return mDisplayData[displayId].hwcDisplay->isConnected();
+    return mDisplayData.at(displayId).hwcDisplay->isConnected();
 }
 
-std::vector<std::shared_ptr<const HWC2::Display::Config>>
-        HWComposer::getConfigs(int32_t displayId) const {
+std::vector<std::shared_ptr<const HWC2::Display::Config>> HWComposer::getConfigs(
+        DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
-    auto& displayData = mDisplayData[displayId];
-    auto configs = mDisplayData[displayId].hwcDisplay->getConfigs();
+    const auto& displayData = mDisplayData.at(displayId);
+    auto configs = displayData.hwcDisplay->getConfigs();
     if (displayData.configMap.empty()) {
         for (size_t i = 0; i < configs.size(); ++i) {
             displayData.configMap[i] = configs[i];
@@ -297,12 +294,12 @@
     return configs;
 }
 
-std::shared_ptr<const HWC2::Display::Config>
-        HWComposer::getActiveConfig(int32_t displayId) const {
+std::shared_ptr<const HWC2::Display::Config> HWComposer::getActiveConfig(
+        DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
 
     std::shared_ptr<const HWC2::Display::Config> config;
-    auto error = mDisplayData[displayId].hwcDisplay->getActiveConfig(&config);
+    auto error = mDisplayData.at(displayId).hwcDisplay->getActiveConfig(&config);
     if (error == HWC2::Error::BadConfig) {
         LOG_DISPLAY_ERROR(displayId, "No active config");
         return nullptr;
@@ -318,39 +315,37 @@
     return config;
 }
 
-int HWComposer::getActiveConfigIndex(int32_t displayId) const {
-    if (!isValidDisplay(displayId)) {
-        ALOGV("getActiveConfigIndex: Attempted to access invalid display %d", displayId);
+int HWComposer::getActiveConfigIndex(DisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, -1);
+
+    int index;
+    auto error = mDisplayData.at(displayId).hwcDisplay->getActiveConfigIndex(&index);
+    if (error == HWC2::Error::BadConfig) {
+        LOG_DISPLAY_ERROR(displayId, "No active config");
         return -1;
     }
-    int index;
-    auto error = mDisplayData[displayId].hwcDisplay->getActiveConfigIndex(&index);
-    if (error == HWC2::Error::BadConfig) {
-        ALOGE("getActiveConfigIndex: No config active, returning -1");
-        return -1;
-    } else if (error != HWC2::Error::None) {
-        ALOGE("getActiveConfigIndex failed for display %d: %s (%d)", displayId,
-              to_string(error).c_str(), static_cast<int32_t>(error));
-        return -1;
-    } else if (index < 0) {
-        ALOGE("getActiveConfigIndex returned an unknown config for display %d", displayId);
+
+    RETURN_IF_HWC_ERROR(error, displayId, -1);
+
+    if (index < 0) {
+        LOG_DISPLAY_ERROR(displayId, "Unknown config");
         return -1;
     }
 
     return index;
 }
 
-std::vector<ui::ColorMode> HWComposer::getColorModes(int32_t displayId) const {
+std::vector<ui::ColorMode> HWComposer::getColorModes(DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
     std::vector<ui::ColorMode> modes;
-    auto error = mDisplayData[displayId].hwcDisplay->getColorModes(&modes);
+    auto error = mDisplayData.at(displayId).hwcDisplay->getColorModes(&modes);
     RETURN_IF_HWC_ERROR(error, displayId, {});
     return modes;
 }
 
-status_t HWComposer::setActiveColorMode(int32_t displayId, ui::ColorMode mode,
-        ui::RenderIntent renderIntent) {
+status_t HWComposer::setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
+                                        ui::RenderIntent renderIntent) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& displayData = mDisplayData[displayId];
@@ -363,56 +358,49 @@
     return NO_ERROR;
 }
 
+void HWComposer::setVsyncEnabled(DisplayId displayId, HWC2::Vsync enabled) {
+    RETURN_IF_INVALID_DISPLAY(displayId);
+    auto& displayData = mDisplayData[displayId];
 
-void HWComposer::setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled) {
-    if (displayId < 0 || displayId >= HWC_DISPLAY_VIRTUAL) {
-        ALOGD("setVsyncEnabled: Ignoring for virtual display %d", displayId);
+    if (displayData.isVirtual) {
+        LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
         return;
     }
 
-    RETURN_IF_INVALID_DISPLAY(displayId);
-
     // NOTE: we use our own internal lock here because we have to call
     // into the HWC with the lock held, and we want to make sure
     // that even if HWC blocks (which it shouldn't), it won't
     // affect other threads.
-    Mutex::Autolock _l(mVsyncLock);
-    auto& displayData = mDisplayData[displayId];
-    if (enabled != displayData.vsyncEnabled) {
-        ATRACE_CALL();
-        auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
-        RETURN_IF_HWC_ERROR(error, displayId);
-
-        displayData.vsyncEnabled = enabled;
-
-        char tag[16];
-        snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", displayId);
-        ATRACE_INT(tag, enabled == HWC2::Vsync::Enable ? 1 : 0);
+    std::lock_guard lock(displayData.vsyncEnabledLock);
+    if (enabled == displayData.vsyncEnabled) {
+        return;
     }
+
+    ATRACE_CALL();
+    auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
+    RETURN_IF_HWC_ERROR(error, displayId);
+
+    displayData.vsyncEnabled = enabled;
+
+    const auto tag = "HW_VSYNC_ON_" + to_string(displayId);
+    ATRACE_INT(tag.c_str(), enabled == HWC2::Vsync::Enable ? 1 : 0);
 }
 
-status_t HWComposer::setClientTarget(int32_t displayId, uint32_t slot,
-        const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
-        ui::Dataspace dataspace) {
+status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot,
+                                     const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
+                                     ui::Dataspace dataspace) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
-    ALOGV("setClientTarget for display %d", displayId);
+    ALOGV("%s for display %s", __FUNCTION__, to_string(displayId).c_str());
     auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
     auto error = hwcDisplay->setClientTarget(slot, target, acquireFence, dataspace);
     RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
     return NO_ERROR;
 }
 
-status_t HWComposer::prepare(DisplayDevice& displayDevice) {
+status_t HWComposer::prepare(DisplayId displayId, std::vector<CompositionInfo>& compositionData) {
     ATRACE_CALL();
 
-    Mutex::Autolock _l(mDisplayLock);
-    auto displayId = displayDevice.getHwcDisplayId();
-    if (displayId == DisplayDevice::DISPLAY_ID_INVALID) {
-        ALOGV("Skipping HWComposer prepare for non-HWC display");
-        return NO_ERROR;
-    }
-
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& displayData = mDisplayData[displayId];
@@ -436,7 +424,7 @@
     // back to validate when there is any client layer.
     displayData.validateWasSkipped = false;
     if (!displayData.hasClientComposition) {
-        sp<android::Fence> outPresentFence;
+        sp<Fence> outPresentFence;
         uint32_t state = UINT32_MAX;
         error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
         if (error != HWC2::Error::HasChanges) {
@@ -474,18 +462,21 @@
 
     displayData.hasClientComposition = false;
     displayData.hasDeviceComposition = false;
-    for (auto& layer : displayDevice.getVisibleLayersSortedByZ()) {
-        auto hwcLayer = layer->getHwcLayer(displayId);
+    for (auto& compositionInfo : compositionData) {
+        auto hwcLayer = compositionInfo.hwc.hwcLayer;
 
-        if (changedTypes.count(hwcLayer) != 0) {
+        if (changedTypes.count(&*hwcLayer) != 0) {
             // We pass false so we only update our state and don't call back
             // into the HWC device
-            validateChange(layer->getCompositionType(displayId),
-                    changedTypes[hwcLayer]);
-            layer->setCompositionType(displayId, changedTypes[hwcLayer], false);
+            validateChange(compositionInfo.compositionType,
+                    changedTypes[&*hwcLayer]);
+            compositionInfo.compositionType = changedTypes[&*hwcLayer];
+            compositionInfo.layer->mLayer->setCompositionType(displayId,
+                                                              compositionInfo.compositionType,
+                                                              false);
         }
 
-        switch (layer->getCompositionType(displayId)) {
+        switch (compositionInfo.compositionType) {
             case HWC2::Composition::Client:
                 displayData.hasClientComposition = true;
                 break;
@@ -499,17 +490,19 @@
                 break;
         }
 
-        if (layerRequests.count(hwcLayer) != 0 &&
-                layerRequests[hwcLayer] ==
+        if (layerRequests.count(&*hwcLayer) != 0 &&
+                layerRequests[&*hwcLayer] ==
                         HWC2::LayerRequest::ClearClientTarget) {
-            layer->setClearClientTarget(displayId, true);
+            compositionInfo.hwc.clearClientTarget = true;
+            compositionInfo.layer->mLayer->setClearClientTarget(displayId, true);
         } else {
-            if (layerRequests.count(hwcLayer) != 0) {
+            if (layerRequests.count(&*hwcLayer) != 0) {
                 LOG_DISPLAY_ERROR(displayId,
-                                  ("Unknown layer request " + to_string(layerRequests[hwcLayer]))
+                                  ("Unknown layer request " + to_string(layerRequests[&*hwcLayer]))
                                           .c_str());
             }
-            layer->setClearClientTarget(displayId, false);
+            compositionInfo.hwc.clearClientTarget = false;
+            compositionInfo.layer->mLayer->setClearClientTarget(displayId, false);
         }
     }
 
@@ -519,49 +512,48 @@
     return NO_ERROR;
 }
 
-bool HWComposer::hasDeviceComposition(int32_t displayId) const {
-    if (displayId == DisplayDevice::DISPLAY_ID_INVALID) {
+bool HWComposer::hasDeviceComposition(const std::optional<DisplayId>& displayId) const {
+    if (!displayId) {
         // Displays without a corresponding HWC display are never composed by
         // the device
         return false;
     }
 
-    RETURN_IF_INVALID_DISPLAY(displayId, false);
-    return mDisplayData[displayId].hasDeviceComposition;
+    RETURN_IF_INVALID_DISPLAY(*displayId, false);
+    return mDisplayData.at(*displayId).hasDeviceComposition;
 }
 
-bool HWComposer::hasFlipClientTargetRequest(int32_t displayId) const {
-    if (displayId == DisplayDevice::DISPLAY_ID_INVALID) {
+bool HWComposer::hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const {
+    if (!displayId) {
         // Displays without a corresponding HWC display are never composed by
         // the device
         return false;
     }
 
-    RETURN_IF_INVALID_DISPLAY(displayId, false);
-    return ((static_cast<uint32_t>(mDisplayData[displayId].displayRequests) &
+    RETURN_IF_INVALID_DISPLAY(*displayId, false);
+    return ((static_cast<uint32_t>(mDisplayData.at(*displayId).displayRequests) &
              static_cast<uint32_t>(HWC2::DisplayRequest::FlipClientTarget)) != 0);
 }
 
-bool HWComposer::hasClientComposition(int32_t displayId) const {
-    if (displayId == DisplayDevice::DISPLAY_ID_INVALID) {
+bool HWComposer::hasClientComposition(const std::optional<DisplayId>& displayId) const {
+    if (!displayId) {
         // Displays without a corresponding HWC display are always composed by
         // the client
         return true;
     }
 
-    RETURN_IF_INVALID_DISPLAY(displayId, true);
-    return mDisplayData[displayId].hasClientComposition;
+    RETURN_IF_INVALID_DISPLAY(*displayId, true);
+    return mDisplayData.at(*displayId).hasClientComposition;
 }
 
-sp<Fence> HWComposer::getPresentFence(int32_t displayId) const {
+sp<Fence> HWComposer::getPresentFence(DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
-    return mDisplayData[displayId].lastPresentFence;
+    return mDisplayData.at(displayId).lastPresentFence;
 }
 
-sp<Fence> HWComposer::getLayerReleaseFence(int32_t displayId,
-        HWC2::Layer* layer) const {
+sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
-    auto displayFences = mDisplayData[displayId].releaseFences;
+    auto displayFences = mDisplayData.at(displayId).releaseFences;
     if (displayFences.count(layer) == 0) {
         ALOGV("getLayerReleaseFence: Release fence not found");
         return Fence::NO_FENCE;
@@ -569,7 +561,7 @@
     return displayFences[layer];
 }
 
-status_t HWComposer::presentAndGetReleaseFences(int32_t displayId) {
+status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {
     ATRACE_CALL();
 
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -597,11 +589,11 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setPowerMode(int32_t displayId, int32_t intMode) {
-    ALOGV("setPowerMode(%d, %d)", displayId, intMode);
+status_t HWComposer::setPowerMode(DisplayId displayId, int32_t intMode) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
-    if (displayId >= VIRTUAL_DISPLAY_ID_BASE) {
+    const auto& displayData = mDisplayData[displayId];
+    if (displayData.isVirtual) {
         LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
         return INVALID_OPERATION;
     }
@@ -611,7 +603,7 @@
         setVsyncEnabled(displayId, HWC2::Vsync::Disable);
     }
 
-    auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
+    auto& hwcDisplay = displayData.hwcDisplay;
     switch (mode) {
         case HWC2::PowerMode::Off:
         case HWC2::PowerMode::On:
@@ -653,7 +645,7 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setActiveConfig(int32_t displayId, size_t configId) {
+status_t HWComposer::setActiveConfig(DisplayId displayId, size_t configId) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& displayData = mDisplayData[displayId];
@@ -667,8 +659,7 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setColorTransform(int32_t displayId,
-        const mat4& transform) {
+status_t HWComposer::setColorTransform(DisplayId displayId, const mat4& transform) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& displayData = mDisplayData[displayId];
@@ -680,54 +671,52 @@
     return NO_ERROR;
 }
 
-void HWComposer::disconnectDisplay(int displayId) {
-    LOG_ALWAYS_FATAL_IF(displayId < 0);
+void HWComposer::disconnectDisplay(DisplayId displayId) {
+    RETURN_IF_INVALID_DISPLAY(displayId);
     auto& displayData = mDisplayData[displayId];
 
-    auto displayType = HWC2::DisplayType::Invalid;
-    auto error = displayData.hwcDisplay->getType(&displayType);
-    RETURN_IF_HWC_ERROR_FOR("getType", error, displayId);
-
     // If this was a virtual display, add its slot back for reuse by future
     // virtual displays
-    if (displayType == HWC2::DisplayType::Virtual) {
-        mFreeDisplaySlots.insert(displayId);
+    if (displayData.isVirtual) {
+        mFreeVirtualDisplayIds.insert(displayId);
         ++mRemainingHwcVirtualDisplays;
     }
 
-    auto hwcId = displayData.hwcDisplay->getId();
-    mHwcDisplaySlots.erase(hwcId);
-    displayData.reset();
+    const auto hwcDisplayId = displayData.hwcDisplay->getId();
+    mPhysicalDisplayIdMap.erase(hwcDisplayId);
+    mDisplayData.erase(displayId);
 
-    mHwcDevice->destroyDisplay(hwcId);
+    // TODO(b/74619554): Select internal/external display from remaining displays.
+    if (hwcDisplayId == mInternalHwcDisplayId) {
+        mInternalHwcDisplayId.reset();
+    } else if (hwcDisplayId == mExternalHwcDisplayId) {
+        mExternalHwcDisplayId.reset();
+    }
+
+    mHwcDevice->destroyDisplay(hwcDisplayId);
 }
 
-status_t HWComposer::setOutputBuffer(int32_t displayId,
-        const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) {
+status_t HWComposer::setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+                                     const sp<GraphicBuffer>& buffer) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto& displayData = mDisplayData[displayId];
 
-    auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
-    auto displayType = HWC2::DisplayType::Invalid;
-    auto error = hwcDisplay->getType(&displayType);
-    RETURN_IF_HWC_ERROR_FOR("getType", error, displayId, NAME_NOT_FOUND);
-
-    if (displayType != HWC2::DisplayType::Virtual) {
+    if (!displayData.isVirtual) {
         LOG_DISPLAY_ERROR(displayId, "Invalid operation on physical display");
         return INVALID_OPERATION;
     }
 
-    error = hwcDisplay->setOutputBuffer(buffer, acquireFence);
+    auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
 }
 
-void HWComposer::clearReleaseFences(int32_t displayId) {
+void HWComposer::clearReleaseFences(DisplayId displayId) {
     RETURN_IF_INVALID_DISPLAY(displayId);
     mDisplayData[displayId].releaseFences.clear();
 }
 
-status_t HWComposer::getHdrCapabilities(
-        int32_t displayId, HdrCapabilities* outCapabilities) {
+status_t HWComposer::getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
@@ -736,27 +725,22 @@
     return NO_ERROR;
 }
 
-int32_t HWComposer::getSupportedPerFrameMetadata(int32_t displayId) const {
+int32_t HWComposer::getSupportedPerFrameMetadata(DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, 0);
-
-    int32_t supportedMetadata;
-    auto error = mDisplayData[displayId].hwcDisplay->getSupportedPerFrameMetadata(
-            &supportedMetadata);
-    RETURN_IF_HWC_ERROR(error, displayId, 0);
-    return supportedMetadata;
+    return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata();
 }
 
-std::vector<ui::RenderIntent> HWComposer::getRenderIntents(int32_t displayId,
-        ui::ColorMode colorMode) const {
+std::vector<ui::RenderIntent> HWComposer::getRenderIntents(DisplayId displayId,
+                                                           ui::ColorMode colorMode) const {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
     std::vector<ui::RenderIntent> renderIntents;
-    auto error = mDisplayData[displayId].hwcDisplay->getRenderIntents(colorMode, &renderIntents);
+    auto error = mDisplayData.at(displayId).hwcDisplay->getRenderIntents(colorMode, &renderIntents);
     RETURN_IF_HWC_ERROR(error, displayId, {});
     return renderIntents;
 }
 
-mat4 HWComposer::getDataspaceSaturationMatrix(int32_t displayId, ui::Dataspace dataspace) {
+mat4 HWComposer::getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
     mat4 matrix;
@@ -766,65 +750,117 @@
     return matrix;
 }
 
-// Converts a PixelFormat to a human-readable string.  Max 11 chars.
-// (Could use a table of prefab String8 objects.)
-/*
-static String8 getFormatStr(PixelFormat format) {
-    switch (format) {
-    case PIXEL_FORMAT_RGBA_8888:    return String8("RGBA_8888");
-    case PIXEL_FORMAT_RGBX_8888:    return String8("RGBx_8888");
-    case PIXEL_FORMAT_RGB_888:      return String8("RGB_888");
-    case PIXEL_FORMAT_RGB_565:      return String8("RGB_565");
-    case PIXEL_FORMAT_BGRA_8888:    return String8("BGRA_8888");
-    case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
-                                    return String8("ImplDef");
-    default:
-        String8 result;
-        result.appendFormat("? %08x", format);
-        return result;
-    }
+status_t HWComposer::getDisplayedContentSamplingAttributes(DisplayId displayId,
+                                                           ui::PixelFormat* outFormat,
+                                                           ui::Dataspace* outDataspace,
+                                                           uint8_t* outComponentMask) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error =
+            mDisplayData[displayId]
+                    .hwcDisplay->getDisplayedContentSamplingAttributes(outFormat, outDataspace,
+                                                                       outComponentMask);
+    if (error == HWC2::Error::Unsupported) RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+    return NO_ERROR;
 }
-*/
+
+status_t HWComposer::setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+                                                      uint8_t componentMask, uint64_t maxFrames) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error =
+            mDisplayData[displayId].hwcDisplay->setDisplayContentSamplingEnabled(enabled,
+                                                                                 componentMask,
+                                                                                 maxFrames);
+
+    if (error == HWC2::Error::Unsupported) RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+    if (error == HWC2::Error::BadParameter) RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+    return NO_ERROR;
+}
+
+status_t HWComposer::getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
+                                               uint64_t timestamp, DisplayedFrameStats* outStats) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error =
+            mDisplayData[displayId].hwcDisplay->getDisplayedContentSample(maxFrames, timestamp,
+                                                                          outStats);
+    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+    return NO_ERROR;
+}
 
 bool HWComposer::isUsingVrComposer() const {
     return getComposer()->isUsingVrComposer();
 }
 
-void HWComposer::dump(String8& result) const {
+void HWComposer::dump(std::string& result) const {
     // TODO: In order to provide a dump equivalent to HWC1, we need to shadow
     // all the state going into the layers. This is probably better done in
     // Layer itself, but it's going to take a bit of work to get there.
-    result.append(mHwcDevice->dump().c_str());
+    result.append(mHwcDevice->dump());
 }
 
-std::optional<hwc2_display_t>
-HWComposer::getHwcDisplayId(int32_t displayId) const {
-    if (!isValidDisplay(displayId)) {
+std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const {
+    if (const auto it = mPhysicalDisplayIdMap.find(hwcDisplayId);
+        it != mPhysicalDisplayIdMap.end()) {
+        return it->second;
+    }
+    return {};
+}
+
+std::optional<hwc2_display_t> HWComposer::fromPhysicalDisplayId(DisplayId displayId) const {
+    if (const auto it = mDisplayData.find(displayId);
+        it != mDisplayData.end() && !it->second.isVirtual) {
+        return it->second.hwcDisplay->getId();
+    }
+    return {};
+}
+
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(hwc2_display_t hwcDisplayId) {
+    if (isUsingVrComposer() && mInternalHwcDisplayId) {
+        ALOGE("Ignoring connection of external display %" PRIu64 " in VR mode", hwcDisplayId);
         return {};
     }
-    return mDisplayData[displayId].hwcDisplay->getId();
+
+    uint8_t port;
+    DisplayIdentificationData data;
+    const bool hasMultiDisplaySupport = getDisplayIdentificationData(hwcDisplayId, &port, &data);
+
+    if (mPhysicalDisplayIdMap.empty()) {
+        mHasMultiDisplaySupport = hasMultiDisplaySupport;
+        ALOGI("Switching to %s multi-display mode",
+              hasMultiDisplaySupport ? "generalized" : "legacy");
+    } else if (mHasMultiDisplaySupport && !hasMultiDisplaySupport) {
+        ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
+              hwcDisplayId);
+        return {};
+    }
+
+    std::optional<DisplayIdentificationInfo> info;
+
+    if (mHasMultiDisplaySupport) {
+        info = parseDisplayIdentificationData(port, data);
+        ALOGE_IF(!info, "Failed to parse identification data for display %" PRIu64, hwcDisplayId);
+    } else if (mInternalHwcDisplayId && mExternalHwcDisplayId) {
+        ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId);
+        return {};
+    } else {
+        ALOGW_IF(hasMultiDisplaySupport, "Ignoring identification data for display %" PRIu64,
+                 hwcDisplayId);
+        port = mInternalHwcDisplayId ? HWC_DISPLAY_EXTERNAL : HWC_DISPLAY_PRIMARY;
+    }
+
+    if (!mInternalHwcDisplayId) {
+        mInternalHwcDisplayId = hwcDisplayId;
+    } else if (!mExternalHwcDisplayId) {
+        mExternalHwcDisplayId = hwcDisplayId;
+    }
+
+    if (info) return info;
+
+    return DisplayIdentificationInfo{getFallbackDisplayId(port),
+                                     hwcDisplayId == mInternalHwcDisplayId ? "Internal display"
+                                                                           : "External display"};
 }
 
-// ---------------------------------------------------------------------------
-
-HWComposer::DisplayData::DisplayData()
-  : hasClientComposition(false),
-    hasDeviceComposition(false),
-    hwcDisplay(nullptr),
-    lastPresentFence(Fence::NO_FENCE),
-    outbufHandle(nullptr),
-    outbufAcquireFence(Fence::NO_FENCE),
-    vsyncEnabled(HWC2::Vsync::Disable) {
-    ALOGV("Created new DisplayData");
-}
-
-HWComposer::DisplayData::~DisplayData() {
-}
-
-void HWComposer::DisplayData::reset() {
-    ALOGV("DisplayData reset");
-    *this = DisplayData();
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index f968948..f42f860 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -17,223 +17,352 @@
 #ifndef ANDROID_SF_HWCOMPOSER_H
 #define ANDROID_SF_HWCOMPOSER_H
 
-#include "HWC2.h"
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/Fence.h>
-#include <ui/GraphicTypes.h>
-#include <utils/BitSet.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-#include <utils/StrongPointer.h>
-#include <utils/Thread.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
+#include <cstdint>
 #include <memory>
+#include <mutex>
 #include <optional>
-#include <set>
+#include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
-extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
-                           const struct timespec *request,
-                           struct timespec *remain);
+#include <android-base/thread_annotations.h>
+#include <ui/Fence.h>
+#include <ui/GraphicTypes.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
 
-struct framebuffer_device_t;
-
-namespace HWC2 {
-    class Device;
-    class Display;
-}
+#include "DisplayIdentification.h"
+#include "HWC2.h"
 
 namespace android {
-// ---------------------------------------------------------------------------
 
-class DisplayDevice;
-class Fence;
-class FloatRect;
+struct DisplayedFrameStats;
 class GraphicBuffer;
-class NativeHandle;
-class Region;
-class String8;
 class TestableSurfaceFlinger;
+struct CompositionInfo;
 
 namespace Hwc2 {
 class Composer;
 } // namespace Hwc2
 
-class HWComposer
-{
+class HWComposer {
 public:
-    explicit HWComposer(std::unique_ptr<android::Hwc2::Composer> composer);
+    virtual ~HWComposer();
 
-    ~HWComposer();
+    virtual void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
 
-    void registerCallback(HWC2::ComposerCallback* callback,
-                          int32_t sequenceId);
+    virtual bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+                                              DisplayIdentificationData* outData) const = 0;
 
-    bool hasCapability(HWC2::Capability capability) const;
+    virtual bool hasCapability(HWC2::Capability capability) const = 0;
+    virtual bool hasDisplayCapability(const std::optional<DisplayId>& displayId,
+                                      HWC2::DisplayCapability capability) const = 0;
 
-    // Attempts to allocate a virtual display. If the virtual display is created
-    // on the HWC device, outId will contain its HWC ID.
-    status_t allocateVirtualDisplay(uint32_t width, uint32_t height,
-            ui::PixelFormat* format, int32_t* outId);
+    // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
+    virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
+                                                            ui::PixelFormat* format) = 0;
 
     // Attempts to create a new layer on this display
-    HWC2::Layer* createLayer(int32_t displayId);
+    virtual HWC2::Layer* createLayer(DisplayId displayId) = 0;
     // Destroy a previously created layer
-    void destroyLayer(int32_t displayId, HWC2::Layer* layer);
+    virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0;
 
     // Asks the HAL what it can do
-    status_t prepare(DisplayDevice& displayDevice);
+    virtual status_t prepare(DisplayId displayId,
+                             std::vector<CompositionInfo>& compositionData) = 0;
 
-    status_t setClientTarget(int32_t displayId, uint32_t slot,
-            const sp<Fence>& acquireFence,
-            const sp<GraphicBuffer>& target, ui::Dataspace dataspace);
+    virtual status_t setClientTarget(DisplayId displayId, uint32_t slot,
+                                     const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
+                                     ui::Dataspace dataspace) = 0;
 
     // Present layers to the display and read releaseFences.
-    status_t presentAndGetReleaseFences(int32_t displayId);
+    virtual status_t presentAndGetReleaseFences(DisplayId displayId) = 0;
 
     // set power mode
-    status_t setPowerMode(int32_t displayId, int mode);
+    virtual status_t setPowerMode(DisplayId displayId, int mode) = 0;
 
     // set active config
-    status_t setActiveConfig(int32_t displayId, size_t configId);
+    virtual status_t setActiveConfig(DisplayId displayId, size_t configId) = 0;
 
     // Sets a color transform to be applied to the result of composition
-    status_t setColorTransform(int32_t displayId, const mat4& transform);
+    virtual status_t setColorTransform(DisplayId displayId, const mat4& transform) = 0;
 
     // reset state when an external, non-virtual display is disconnected
-    void disconnectDisplay(int32_t displayId);
+    virtual void disconnectDisplay(DisplayId displayId) = 0;
 
     // does this display have layers handled by HWC
-    bool hasDeviceComposition(int32_t displayId) const;
+    virtual bool hasDeviceComposition(const std::optional<DisplayId>& displayId) const = 0;
 
     // does this display have pending request to flip client target
-    bool hasFlipClientTargetRequest(int32_t displayId) const;
+    virtual bool hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const = 0;
 
     // does this display have layers handled by GLES
-    bool hasClientComposition(int32_t displayId) const;
+    virtual bool hasClientComposition(const std::optional<DisplayId>& displayId) const = 0;
 
     // get the present fence received from the last call to present.
-    sp<Fence> getPresentFence(int32_t displayId) const;
+    virtual sp<Fence> getPresentFence(DisplayId displayId) const = 0;
 
     // Get last release fence for the given layer
-    sp<Fence> getLayerReleaseFence(int32_t displayId,
-            HWC2::Layer* layer) const;
+    virtual sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const = 0;
 
     // Set the output buffer and acquire fence for a virtual display.
     // Returns INVALID_OPERATION if displayId is not a virtual display.
-    status_t setOutputBuffer(int32_t displayId, const sp<Fence>& acquireFence,
-            const sp<GraphicBuffer>& buf);
+    virtual status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+                                     const sp<GraphicBuffer>& buffer) = 0;
 
     // After SurfaceFlinger has retrieved the release fences for all the frames,
     // it can call this to clear the shared pointers in the release fence map
-    void clearReleaseFences(int32_t displayId);
+    virtual void clearReleaseFences(DisplayId displayId) = 0;
 
     // Fetches the HDR capabilities of the given display
-    status_t getHdrCapabilities(int32_t displayId, HdrCapabilities* outCapabilities);
+    virtual status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) = 0;
 
-    int32_t getSupportedPerFrameMetadata(int32_t displayId) const;
+    virtual int32_t getSupportedPerFrameMetadata(DisplayId displayId) const = 0;
 
     // Returns the available RenderIntent of the given display.
-    std::vector<ui::RenderIntent> getRenderIntents(int32_t displayId, ui::ColorMode colorMode) const;
+    virtual std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId,
+                                                           ui::ColorMode colorMode) const = 0;
 
-    mat4 getDataspaceSaturationMatrix(int32_t displayId, ui::Dataspace dataspace);
+    virtual mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) = 0;
+
+    // Returns the attributes of the color sampling engine.
+    virtual status_t getDisplayedContentSamplingAttributes(DisplayId displayId,
+                                                           ui::PixelFormat* outFormat,
+                                                           ui::Dataspace* outDataspace,
+                                                           uint8_t* outComponentMask) = 0;
+    virtual status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+                                                      uint8_t componentMask,
+                                                      uint64_t maxFrames) = 0;
+    virtual status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
+                                               uint64_t timestamp,
+                                               DisplayedFrameStats* outStats) = 0;
 
     // Events handling ---------------------------------------------------------
 
-    // Returns true if successful, false otherwise. The
-    // DisplayDevice::DisplayType of the display is returned as an output param.
-    bool onVsync(hwc2_display_t displayId, int64_t timestamp,
-                 int32_t* outDisplay);
-    void onHotplug(hwc2_display_t displayId, int32_t displayType, HWC2::Connection connection);
+    // Returns stable display ID (and display name on connection of new or previously disconnected
+    // display), or std::nullopt if hotplug event was ignored.
+    virtual std::optional<DisplayIdentificationInfo> onHotplug(hwc2_display_t hwcDisplayId,
+                                                               HWC2::Connection connection) = 0;
 
-    void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled);
+    virtual bool onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) = 0;
+    virtual void setVsyncEnabled(DisplayId displayId, HWC2::Vsync enabled) = 0;
 
-    // Query display parameters.  Pass in a display index (e.g.
-    // HWC_DISPLAY_PRIMARY).
-    nsecs_t getRefreshTimestamp(int32_t displayId) const;
-    bool isConnected(int32_t displayId) const;
+    virtual nsecs_t getRefreshTimestamp(DisplayId displayId) const = 0;
+    virtual bool isConnected(DisplayId displayId) const = 0;
 
     // Non-const because it can update configMap inside of mDisplayData
-    std::vector<std::shared_ptr<const HWC2::Display::Config>>
-            getConfigs(int32_t displayId) const;
+    virtual std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
+            DisplayId displayId) const = 0;
 
-    std::shared_ptr<const HWC2::Display::Config>
-            getActiveConfig(int32_t displayId) const;
-    int getActiveConfigIndex(int32_t displayId) const;
+    virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
+            DisplayId displayId) const = 0;
+    virtual int getActiveConfigIndex(DisplayId displayId) const = 0;
 
-    std::vector<ui::ColorMode> getColorModes(int32_t displayId) const;
+    virtual std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const = 0;
 
-    status_t setActiveColorMode(int32_t displayId, ui::ColorMode mode,
-            ui::RenderIntent renderIntent);
+    virtual status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
+                                        ui::RenderIntent renderIntent) = 0;
 
-    bool isUsingVrComposer() const;
+    virtual bool isUsingVrComposer() const = 0;
 
     // for debugging ----------------------------------------------------------
-    void dump(String8& out) const;
+    virtual void dump(std::string& out) const = 0;
 
-    android::Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); }
+    virtual Hwc2::Composer* getComposer() const = 0;
 
-    std::optional<hwc2_display_t> getHwcDisplayId(int32_t displayId) const;
+    // TODO(b/74619554): Remove special cases for internal/external display.
+    virtual std::optional<hwc2_display_t> getInternalHwcDisplayId() const = 0;
+    virtual std::optional<hwc2_display_t> getExternalHwcDisplayId() const = 0;
+
+    virtual std::optional<DisplayId> toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const = 0;
+    virtual std::optional<hwc2_display_t> fromPhysicalDisplayId(DisplayId displayId) const = 0;
+};
+
+namespace impl {
+
+class HWComposer final : public android::HWComposer {
+public:
+    explicit HWComposer(std::unique_ptr<Hwc2::Composer> composer);
+
+    ~HWComposer() override;
+
+    void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
+
+    bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+                                      DisplayIdentificationData* outData) const override;
+
+    bool hasCapability(HWC2::Capability capability) const override;
+    bool hasDisplayCapability(const std::optional<DisplayId>& displayId,
+                              HWC2::DisplayCapability capability) const override;
+
+    // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
+    std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
+                                                    ui::PixelFormat* format) override;
+
+    // Attempts to create a new layer on this display
+    HWC2::Layer* createLayer(DisplayId displayId) override;
+    // Destroy a previously created layer
+    void destroyLayer(DisplayId displayId, HWC2::Layer* layer) override;
+
+    // Asks the HAL what it can do
+    status_t prepare(DisplayId displayId, std::vector<CompositionInfo>& compositionData) override;
+
+    status_t setClientTarget(DisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence,
+                             const sp<GraphicBuffer>& target, ui::Dataspace dataspace) override;
+
+    // Present layers to the display and read releaseFences.
+    status_t presentAndGetReleaseFences(DisplayId displayId) override;
+
+    // set power mode
+    status_t setPowerMode(DisplayId displayId, int mode) override;
+
+    // set active config
+    status_t setActiveConfig(DisplayId displayId, size_t configId) override;
+
+    // Sets a color transform to be applied to the result of composition
+    status_t setColorTransform(DisplayId displayId, const mat4& transform) override;
+
+    // reset state when an external, non-virtual display is disconnected
+    void disconnectDisplay(DisplayId displayId) override;
+
+    // does this display have layers handled by HWC
+    bool hasDeviceComposition(const std::optional<DisplayId>& displayId) const override;
+
+    // does this display have pending request to flip client target
+    bool hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const override;
+
+    // does this display have layers handled by GLES
+    bool hasClientComposition(const std::optional<DisplayId>& displayId) const override;
+
+    // get the present fence received from the last call to present.
+    sp<Fence> getPresentFence(DisplayId displayId) const override;
+
+    // Get last release fence for the given layer
+    sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const override;
+
+    // Set the output buffer and acquire fence for a virtual display.
+    // Returns INVALID_OPERATION if displayId is not a virtual display.
+    status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+                             const sp<GraphicBuffer>& buffer) override;
+
+    // After SurfaceFlinger has retrieved the release fences for all the frames,
+    // it can call this to clear the shared pointers in the release fence map
+    void clearReleaseFences(DisplayId displayId) override;
+
+    // Fetches the HDR capabilities of the given display
+    status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) override;
+
+    int32_t getSupportedPerFrameMetadata(DisplayId displayId) const override;
+
+    // Returns the available RenderIntent of the given display.
+    std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId,
+                                                   ui::ColorMode colorMode) const override;
+
+    mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) override;
+
+    // Returns the attributes of the color sampling engine.
+    status_t getDisplayedContentSamplingAttributes(DisplayId displayId, ui::PixelFormat* outFormat,
+                                                   ui::Dataspace* outDataspace,
+                                                   uint8_t* outComponentMask) override;
+    status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+                                              uint8_t componentMask, uint64_t maxFrames) override;
+    status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, uint64_t timestamp,
+                                       DisplayedFrameStats* outStats) override;
+
+    // Events handling ---------------------------------------------------------
+
+    // Returns stable display ID (and display name on connection of new or previously disconnected
+    // display), or std::nullopt if hotplug event was ignored.
+    std::optional<DisplayIdentificationInfo> onHotplug(hwc2_display_t hwcDisplayId,
+                                                       HWC2::Connection connection) override;
+
+    bool onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) override;
+    void setVsyncEnabled(DisplayId displayId, HWC2::Vsync enabled) override;
+
+    nsecs_t getRefreshTimestamp(DisplayId displayId) const override;
+    bool isConnected(DisplayId displayId) const override;
+
+    // Non-const because it can update configMap inside of mDisplayData
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
+            DisplayId displayId) const override;
+
+    std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
+            DisplayId displayId) const override;
+    int getActiveConfigIndex(DisplayId displayId) const override;
+
+    std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const override;
+
+    status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
+                                ui::RenderIntent renderIntent) override;
+
+    bool isUsingVrComposer() const override;
+
+    // for debugging ----------------------------------------------------------
+    void dump(std::string& out) const override;
+
+    Hwc2::Composer* getComposer() const override { return mHwcDevice->getComposer(); }
+
+    // TODO(b/74619554): Remove special cases for internal/external display.
+    std::optional<hwc2_display_t> getInternalHwcDisplayId() const override {
+        return mInternalHwcDisplayId;
+    }
+    std::optional<hwc2_display_t> getExternalHwcDisplayId() const override {
+        return mExternalHwcDisplayId;
+    }
+
+    std::optional<DisplayId> toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const override;
+    std::optional<hwc2_display_t> fromPhysicalDisplayId(DisplayId displayId) const override;
+
 private:
     // For unit tests
     friend TestableSurfaceFlinger;
 
-    static const int32_t VIRTUAL_DISPLAY_ID_BASE = 2;
+    std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId);
 
-    bool isValidDisplay(int32_t displayId) const;
     static void validateChange(HWC2::Composition from, HWC2::Composition to);
 
-    struct cb_context;
-
     struct DisplayData {
-        DisplayData();
-        ~DisplayData();
-        void reset();
-
-        bool hasClientComposition;
-        bool hasDeviceComposition;
-        HWC2::Display* hwcDisplay;
+        bool isVirtual = false;
+        bool hasClientComposition = false;
+        bool hasDeviceComposition = false;
+        HWC2::Display* hwcDisplay = nullptr;
         HWC2::DisplayRequest displayRequests;
-        sp<Fence> lastPresentFence;  // signals when the last set op retires
+        sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
         std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
-        buffer_handle_t outbufHandle;
-        sp<Fence> outbufAcquireFence;
+        buffer_handle_t outbufHandle = nullptr;
+        sp<Fence> outbufAcquireFence = Fence::NO_FENCE;
         mutable std::unordered_map<int32_t,
                 std::shared_ptr<const HWC2::Display::Config>> configMap;
 
-        // protected by mVsyncLock
-        HWC2::Vsync vsyncEnabled;
-
         bool validateWasSkipped;
         HWC2::Error presentError;
+
+        bool vsyncTraceToggle = false;
+
+        std::mutex vsyncEnabledLock;
+        HWC2::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = HWC2::Vsync::Disable;
+
+        mutable std::mutex lastHwVsyncLock;
+        nsecs_t lastHwVsync GUARDED_BY(lastHwVsyncLock) = 0;
     };
 
-    std::unique_ptr<HWC2::Device>   mHwcDevice;
-    std::vector<DisplayData> mDisplayData{HWC_NUM_PHYSICAL_DISPLAY_TYPES};
-    std::set<size_t>                mFreeDisplaySlots;
-    std::unordered_map<hwc2_display_t, int32_t> mHwcDisplaySlots;
-    // protect mDisplayData from races between prepare and dump
-    mutable Mutex mDisplayLock;
+    std::unordered_map<DisplayId, DisplayData> mDisplayData;
 
-    cb_context* mCBContext = nullptr;
-    size_t mVSyncCounts[HWC_NUM_PHYSICAL_DISPLAY_TYPES]{0, 0};
+    // This must be destroyed before mDisplayData, because destructor may call back into HWComposer
+    // and look up DisplayData.
+    std::unique_ptr<HWC2::Device> mHwcDevice;
+
+    std::unordered_map<hwc2_display_t, DisplayId> mPhysicalDisplayIdMap;
+    std::optional<hwc2_display_t> mInternalHwcDisplayId;
+    std::optional<hwc2_display_t> mExternalHwcDisplayId;
+    bool mHasMultiDisplaySupport = false;
+
+    std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
+    uint32_t mNextVirtualDisplayId = 0;
     uint32_t mRemainingHwcVirtualDisplays{mHwcDevice->getMaxVirtualDisplayCount()};
-
-    // protected by mLock
-    mutable Mutex mLock;
-    mutable std::unordered_map<int32_t, nsecs_t> mLastHwVSync{
-            {{HWC_DISPLAY_PRIMARY, 0}, {HWC_DISPLAY_EXTERNAL, 0}}};
-
-    // thread-safe
-    mutable Mutex mVsyncLock;
 };
 
-// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace impl
+} // namespace android
 
 #endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
deleted file mode 100644
index fe7944f..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * Copyright (C) 2010 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_HWCOMPOSER_HWC1_H
-#define ANDROID_SF_HWCOMPOSER_HWC1_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <hardware/hwcomposer_defs.h>
-
-#include <system/graphics.h>
-
-#include <ui/Fence.h>
-
-#include <utils/BitSet.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-#include <utils/StrongPointer.h>
-#include <utils/Thread.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
-extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
-                           const struct timespec *request,
-                           struct timespec *remain);
-
-struct hwc_composer_device_1;
-struct hwc_display_contents_1;
-struct hwc_layer_1;
-struct hwc_procs;
-struct framebuffer_device_t;
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-class Fence;
-class FloatRect;
-class GraphicBuffer;
-class NativeHandle;
-class Region;
-class String8;
-class SurfaceFlinger;
-
-class HWComposer
-{
-public:
-    class EventHandler {
-        friend class HWComposer;
-        virtual void onVSyncReceived(
-            HWComposer* composer, int32_t disp, nsecs_t timestamp) = 0;
-        virtual void onHotplugReceived(HWComposer* composer, int disp, bool connected) = 0;
-        virtual void onInvalidateReceived(HWComposer* composer) = 0;
-    protected:
-        virtual ~EventHandler() {}
-    };
-
-    enum {
-        NUM_BUILTIN_DISPLAYS = HWC_NUM_PHYSICAL_DISPLAY_TYPES,
-        MAX_HWC_DISPLAYS = HWC_NUM_DISPLAY_TYPES,
-        VIRTUAL_DISPLAY_ID_BASE = HWC_DISPLAY_VIRTUAL,
-    };
-
-    HWComposer(
-            const sp<SurfaceFlinger>& flinger,
-            EventHandler& handler);
-
-    ~HWComposer();
-
-    status_t initCheck() const;
-
-    // Returns a display ID starting at VIRTUAL_DISPLAY_ID_BASE, this ID is to
-    // be used with createWorkList (and all other methods requiring an ID
-    // below).
-    // IDs below NUM_BUILTIN_DISPLAYS are pre-defined and therefore are
-    // always valid.
-    // Returns -1 if an ID cannot be allocated
-    int32_t allocateDisplayId();
-
-    // Recycles the given virtual display ID and frees the associated worklist.
-    // IDs below NUM_BUILTIN_DISPLAYS are not recycled.
-    status_t freeDisplayId(int32_t id);
-
-
-    // Asks the HAL what it can do
-    status_t prepare();
-
-    // commits the list
-    status_t commit();
-
-    // set power mode
-    status_t setPowerMode(int disp, int mode);
-
-    // set active config
-    status_t setActiveConfig(int disp, int mode);
-
-    // reset state when an external, non-virtual display is disconnected
-    void disconnectDisplay(int disp);
-
-    // create a work list for numLayers layer. sets HWC_GEOMETRY_CHANGED.
-    status_t createWorkList(int32_t id, size_t numLayers);
-
-    bool supportsFramebufferTarget() const;
-
-    // does this display have layers handled by HWC
-    bool hasHwcComposition(int32_t id) const;
-
-    // does this display have layers handled by GLES
-    bool hasGlesComposition(int32_t id) const;
-
-    // get the releaseFence file descriptor for a display's framebuffer layer.
-    // the release fence is only valid after commit()
-    sp<Fence> getAndResetReleaseFence(int32_t id);
-
-    // needed forward declarations
-    class LayerListIterator;
-
-    // return the visual id to be used to find a suitable EGLConfig for
-    // *ALL* displays.
-    int getVisualID() const;
-
-    // Forwarding to FB HAL for pre-HWC-1.1 code (see FramebufferSurface).
-    int fbPost(int32_t id, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf);
-    int fbCompositionComplete();
-    void fbDump(String8& result);
-
-    // Set the output buffer and acquire fence for a virtual display.
-    // Returns INVALID_OPERATION if id is not a virtual display.
-    status_t setOutputBuffer(int32_t id, const sp<Fence>& acquireFence,
-            const sp<GraphicBuffer>& buf);
-
-    // Get the retire fence for the last committed frame. This fence will
-    // signal when the h/w composer is completely finished with the frame.
-    // For physical displays, it is no longer being displayed. For virtual
-    // displays, writes to the output buffer are complete.
-    sp<Fence> getLastRetireFence(int32_t id) const;
-
-    status_t setCursorPositionAsync(int32_t id, const Rect &pos);
-
-    /*
-     * Interface to hardware composer's layers functionality.
-     * This abstracts the HAL interface to layers which can evolve in
-     * incompatible ways from one release to another.
-     * The idea is that we could extend this interface as we add
-     * features to h/w composer.
-     */
-    class HWCLayerInterface {
-    protected:
-        virtual ~HWCLayerInterface() { }
-    public:
-        virtual int32_t getCompositionType() const = 0;
-        virtual uint32_t getHints() const = 0;
-        virtual sp<Fence> getAndResetReleaseFence() = 0;
-        virtual void setDefaultState() = 0;
-        virtual void setSkip(bool skip) = 0;
-        virtual void setIsCursorLayerHint(bool isCursor = true) = 0;
-        virtual void setBlending(uint32_t blending) = 0;
-        virtual void setTransform(uint32_t transform) = 0;
-        virtual void setFrame(const Rect& frame) = 0;
-        virtual void setCrop(const FloatRect& crop) = 0;
-        virtual void setVisibleRegionScreen(const Region& reg) = 0;
-        virtual void setSurfaceDamage(const Region& reg) = 0;
-        virtual void setSidebandStream(const sp<NativeHandle>& stream) = 0;
-        virtual void setBuffer(const sp<GraphicBuffer>& buffer) = 0;
-        virtual void setAcquireFenceFd(int fenceFd) = 0;
-        virtual void setPlaneAlpha(uint8_t alpha) = 0;
-        virtual void onDisplayed() = 0;
-    };
-
-    /*
-     * Interface used to implement an iterator to a list
-     * of HWCLayer.
-     */
-    class HWCLayer : public HWCLayerInterface {
-        friend class LayerListIterator;
-        // select the layer at the given index
-        virtual status_t setLayer(size_t index) = 0;
-        virtual HWCLayer* dup() = 0;
-        static HWCLayer* copy(HWCLayer *rhs) {
-            return rhs ? rhs->dup() : nullptr;
-        }
-    protected:
-        virtual ~HWCLayer() { }
-    };
-
-    /*
-     * Iterator through a HWCLayer list.
-     * This behaves more or less like a forward iterator.
-     */
-    class LayerListIterator {
-        friend class HWComposer;
-        HWCLayer* const mLayerList;
-        size_t mIndex;
-
-        LayerListIterator() : mLayerList(nullptr), mIndex(0) { }
-
-        LayerListIterator(HWCLayer* layer, size_t index)
-            : mLayerList(layer), mIndex(index) { }
-
-        // we don't allow assignment, because we don't need it for now
-        LayerListIterator& operator = (const LayerListIterator& rhs);
-
-    public:
-        // copy operators
-        LayerListIterator(const LayerListIterator& rhs)
-            : mLayerList(HWCLayer::copy(rhs.mLayerList)), mIndex(rhs.mIndex) {
-        }
-
-        ~LayerListIterator() { delete mLayerList; }
-
-        // pre-increment
-        LayerListIterator& operator++() {
-            mLayerList->setLayer(++mIndex);
-            return *this;
-        }
-
-        // dereference
-        HWCLayerInterface& operator * () { return *mLayerList; }
-        HWCLayerInterface* operator -> () { return mLayerList; }
-
-        // comparison
-        bool operator == (const LayerListIterator& rhs) const {
-            return mIndex == rhs.mIndex;
-        }
-        bool operator != (const LayerListIterator& rhs) const {
-            return !operator==(rhs);
-        }
-    };
-
-    // Returns an iterator to the beginning of the layer list
-    LayerListIterator begin(int32_t id);
-
-    // Returns an iterator to the end of the layer list
-    LayerListIterator end(int32_t id);
-
-
-    // Events handling ---------------------------------------------------------
-
-    enum {
-        EVENT_VSYNC = HWC_EVENT_VSYNC
-    };
-
-    void eventControl(int disp, int event, int enabled);
-
-    struct DisplayConfig {
-        uint32_t width;
-        uint32_t height;
-        float xdpi;
-        float ydpi;
-        nsecs_t refresh;
-        android_color_mode_t colorMode;
-        bool operator==(const DisplayConfig& rhs) const {
-            return width == rhs.width &&
-                    height == rhs.height &&
-                    xdpi == rhs.xdpi &&
-                    ydpi == rhs.ydpi &&
-                    refresh == rhs.refresh &&
-                    colorMode == rhs.colorMode;
-        }
-    };
-
-    // Query display parameters.  Pass in a display index (e.g.
-    // HWC_DISPLAY_PRIMARY).
-    nsecs_t getRefreshTimestamp(int disp) const;
-    sp<Fence> getDisplayFence(int disp) const;
-    uint32_t getFormat(int disp) const;
-    bool isConnected(int disp) const;
-
-    // These return the values for the current config of a given display index.
-    // To get the values for all configs, use getConfigs below.
-    uint32_t getWidth(int disp) const;
-    uint32_t getHeight(int disp) const;
-    float getDpiX(int disp) const;
-    float getDpiY(int disp) const;
-    nsecs_t getRefreshPeriod(int disp) const;
-    android_color_mode_t getColorMode(int disp) const;
-
-    const Vector<DisplayConfig>& getConfigs(int disp) const;
-    size_t getCurrentConfig(int disp) const;
-
-    status_t setVirtualDisplayProperties(int32_t id, uint32_t w, uint32_t h,
-            uint32_t format);
-
-    // this class is only used to fake the VSync event on systems that don't
-    // have it.
-    class VSyncThread : public Thread {
-        HWComposer& mHwc;
-        mutable Mutex mLock;
-        Condition mCondition;
-        bool mEnabled;
-        mutable nsecs_t mNextFakeVSync;
-        nsecs_t mRefreshPeriod;
-        virtual void onFirstRef();
-        virtual bool threadLoop();
-    public:
-        VSyncThread(HWComposer& hwc);
-        void setEnabled(bool enabled);
-    };
-
-    friend class VSyncThread;
-
-    // for debugging ----------------------------------------------------------
-    void dump(String8& out) const;
-
-private:
-    void loadHwcModule();
-    int loadFbHalModule();
-
-    LayerListIterator getLayerIterator(int32_t id, size_t index);
-
-    struct cb_context;
-
-    static void hook_invalidate(const struct hwc_procs* procs);
-    static void hook_vsync(const struct hwc_procs* procs, int disp,
-            int64_t timestamp);
-    static void hook_hotplug(const struct hwc_procs* procs, int disp,
-            int connected);
-
-    inline void invalidate();
-    inline void vsync(int disp, int64_t timestamp);
-    inline void hotplug(int disp, int connected);
-
-    status_t queryDisplayProperties(int disp);
-
-    status_t setFramebufferTarget(int32_t id,
-            const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf);
-
-    struct DisplayData {
-        DisplayData();
-        ~DisplayData();
-        Vector<DisplayConfig> configs;
-        size_t currentConfig;
-        uint32_t format;    // pixel format from FB hal, for pre-hwc-1.1
-        bool connected;
-        bool hasFbComp;
-        bool hasOvComp;
-        size_t capacity;
-        hwc_display_contents_1* list;
-        hwc_layer_1* framebufferTarget;
-        buffer_handle_t fbTargetHandle;
-        sp<Fence> lastRetireFence;  // signals when the last set op retires
-        sp<Fence> lastDisplayFence; // signals when the last set op takes
-                                    // effect on screen
-        buffer_handle_t outbufHandle;
-        sp<Fence> outbufAcquireFence;
-
-        // protected by mEventControlLock
-        int32_t events;
-
-        // We need to hold "copies" of these for memory management purposes. The
-        // actual hwc_layer_1_t holds pointers to the memory within. Vector<>
-        // internally doesn't copy the memory unless one of the copies is
-        // modified.
-        Vector<Region> visibleRegions;
-        Vector<Region> surfaceDamageRegions;
-    };
-
-    sp<SurfaceFlinger>              mFlinger;
-    framebuffer_device_t*           mFbDev;
-    struct hwc_composer_device_1*   mHwc;
-    // invariant: mLists[0] != nullptr iff mHwc != nullptr
-    // mLists[i>0] can be nullptr. that display is to be ignored
-    struct hwc_display_contents_1*  mLists[MAX_HWC_DISPLAYS];
-    DisplayData                     mDisplayData[MAX_HWC_DISPLAYS];
-    // protect mDisplayData from races between prepare and dump
-    mutable Mutex mDisplayLock;
-    size_t                          mNumDisplays;
-
-    cb_context*                     mCBContext;
-    EventHandler&                   mEventHandler;
-    size_t                          mVSyncCounts[HWC_NUM_PHYSICAL_DISPLAY_TYPES];
-    sp<VSyncThread>                 mVSyncThread;
-    bool                            mDebugForceFakeVSync;
-    BitSet32                        mAllocatedDisplayIDs;
-
-    // protected by mLock
-    mutable Mutex mLock;
-    mutable nsecs_t mLastHwVSync[HWC_NUM_PHYSICAL_DISPLAY_TYPES];
-
-    // thread-safe
-    mutable Mutex mEventControlLock;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 9a2817d..1c2853a 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -32,50 +32,54 @@
 // ---------------------------------------------------------------------------
 
 #define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \
-        mDisplayName.string(), ##__VA_ARGS__)
+        mDisplayName.c_str(), ##__VA_ARGS__)
 #define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \
-        mDisplayName.string(), ##__VA_ARGS__)
+        mDisplayName.c_str(), ##__VA_ARGS__)
 #define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \
-        mDisplayName.string(), ##__VA_ARGS__)
+        mDisplayName.c_str(), ##__VA_ARGS__)
 
-static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) {
+static const char* dbgCompositionTypeStr(compositionengine::DisplaySurface::CompositionType type) {
     switch (type) {
-        case DisplaySurface::COMPOSITION_UNKNOWN: return "UNKNOWN";
-        case DisplaySurface::COMPOSITION_GLES:    return "GLES";
-        case DisplaySurface::COMPOSITION_HWC:     return "HWC";
-        case DisplaySurface::COMPOSITION_MIXED:   return "MIXED";
+        case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN:
+            return "UNKNOWN";
+        case compositionengine::DisplaySurface::COMPOSITION_GLES:
+            return "GLES";
+        case compositionengine::DisplaySurface::COMPOSITION_HWC:
+            return "HWC";
+        case compositionengine::DisplaySurface::COMPOSITION_MIXED:
+            return "MIXED";
         default:                                  return "<INVALID>";
     }
 }
 
-VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t dispId,
-        const sp<IGraphicBufferProducer>& sink,
-        const sp<IGraphicBufferProducer>& bqProducer,
-        const sp<IGraphicBufferConsumer>& bqConsumer,
-        const String8& name)
-:   ConsumerBase(bqConsumer),
-    mHwc(hwc),
-    mDisplayId(dispId),
-    mDisplayName(name),
-    mSource{},
-    mDefaultOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
-    mOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
-    mOutputUsage(GRALLOC_USAGE_HW_COMPOSER),
-    mProducerSlotSource(0),
-    mProducerBuffers(),
-    mQueueBufferOutput(),
-    mSinkBufferWidth(0),
-    mSinkBufferHeight(0),
-    mCompositionType(COMPOSITION_UNKNOWN),
-    mFbFence(Fence::NO_FENCE),
-    mOutputFence(Fence::NO_FENCE),
-    mFbProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
-    mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
-    mDbgState(DBG_STATE_IDLE),
-    mDbgLastCompositionType(COMPOSITION_UNKNOWN),
-    mMustRecompose(false),
-    mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv)
-{
+VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc,
+                                             const std::optional<DisplayId>& displayId,
+                                             const sp<IGraphicBufferProducer>& sink,
+                                             const sp<IGraphicBufferProducer>& bqProducer,
+                                             const sp<IGraphicBufferConsumer>& bqConsumer,
+                                             const std::string& name)
+      : ConsumerBase(bqConsumer),
+        mHwc(hwc),
+        mDisplayId(displayId),
+        mDisplayName(name),
+        mSource{},
+        mDefaultOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
+        mOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
+        mOutputUsage(GRALLOC_USAGE_HW_COMPOSER),
+        mProducerSlotSource(0),
+        mProducerBuffers(),
+        mQueueBufferOutput(),
+        mSinkBufferWidth(0),
+        mSinkBufferHeight(0),
+        mCompositionType(COMPOSITION_UNKNOWN),
+        mFbFence(Fence::NO_FENCE),
+        mOutputFence(Fence::NO_FENCE),
+        mFbProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
+        mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
+        mDbgState(DBG_STATE_IDLE),
+        mDbgLastCompositionType(COMPOSITION_UNKNOWN),
+        mMustRecompose(false),
+        mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv) {
     mSource[SOURCE_SINK] = sink;
     mSource[SOURCE_SCRATCH] = bqProducer;
 
@@ -102,7 +106,7 @@
     }
     mOutputFormat = mDefaultOutputFormat;
 
-    ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.string());
+    ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.c_str());
     mConsumer->setConsumerName(ConsumerBase::mName);
     mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
     mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight);
@@ -116,8 +120,9 @@
 }
 
 status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
-    if (mDisplayId < 0)
+    if (!mDisplayId) {
         return NO_ERROR;
+    }
 
     mMustRecompose = mustRecompose;
 
@@ -129,8 +134,9 @@
 }
 
 status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
-    if (mDisplayId < 0)
+    if (!mDisplayId) {
         return NO_ERROR;
+    }
 
     VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN,
             "Unexpected prepareFrame() in %s state", dbgStateStr());
@@ -177,8 +183,9 @@
 }
 
 status_t VirtualDisplaySurface::advanceFrame() {
-    if (mDisplayId < 0)
+    if (!mDisplayId) {
         return NO_ERROR;
+    }
 
     if (mCompositionType == COMPOSITION_HWC) {
         VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
@@ -211,7 +218,7 @@
 
     // At this point we know the output buffer acquire fence,
     // so update HWC state with it.
-    mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer);
+    mHwc.setOutputBuffer(*mDisplayId, mOutputFence, outBuffer);
 
     status_t result = NO_ERROR;
     if (fbBuffer != nullptr) {
@@ -221,22 +228,23 @@
                 &hwcSlot, &hwcBuffer);
 
         // TODO: Correctly propagate the dataspace from GL composition
-        result = mHwc.setClientTarget(mDisplayId, hwcSlot, mFbFence,
-                hwcBuffer, ui::Dataspace::UNKNOWN);
+        result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer,
+                                      ui::Dataspace::UNKNOWN);
     }
 
     return result;
 }
 
 void VirtualDisplaySurface::onFrameCommitted() {
-    if (mDisplayId < 0)
+    if (!mDisplayId) {
         return;
+    }
 
     VDS_LOGW_IF(mDbgState != DBG_STATE_HWC,
             "Unexpected onFrameCommitted() in %s state", dbgStateStr());
     mDbgState = DBG_STATE_IDLE;
 
-    sp<Fence> retireFence = mHwc.getPresentFence(mDisplayId);
+    sp<Fence> retireFence = mHwc.getPresentFence(*mDisplayId);
     if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
         // release the scratch buffer back to the pool
         Mutex::Autolock lock(mMutex);
@@ -291,8 +299,9 @@
 
 status_t VirtualDisplaySurface::requestBuffer(int pslot,
         sp<GraphicBuffer>* outBuf) {
-    if (mDisplayId < 0)
+    if (!mDisplayId) {
         return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
+    }
 
     VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
             "Unexpected requestBuffer pslot=%d in %s state",
@@ -313,7 +322,7 @@
 
 status_t VirtualDisplaySurface::dequeueBuffer(Source source,
         PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
-    LOG_FATAL_IF(mDisplayId < 0, "mDisplayId=%d but should not be < 0.", mDisplayId);
+    LOG_FATAL_IF(!mDisplayId);
 
     status_t result =
             mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight,
@@ -359,7 +368,7 @@
                                               PixelFormat format, uint64_t usage,
                                               uint64_t* outBufferAge,
                                               FrameEventHistoryDelta* outTimestamps) {
-    if (mDisplayId < 0) {
+    if (!mDisplayId) {
         return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge,
                                                    outTimestamps);
     }
@@ -446,8 +455,9 @@
 
 status_t VirtualDisplaySurface::queueBuffer(int pslot,
         const QueueBufferInput& input, QueueBufferOutput* output) {
-    if (mDisplayId < 0)
+    if (!mDisplayId) {
         return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
+    }
 
     VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
             "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
@@ -504,8 +514,9 @@
 
 status_t VirtualDisplaySurface::cancelBuffer(int pslot,
         const sp<Fence>& fence) {
-    if (mDisplayId < 0)
+    if (!mDisplayId) {
         return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
+    }
 
     VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
             "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
@@ -616,6 +627,8 @@
 }
 
 status_t VirtualDisplaySurface::refreshOutputBuffer() {
+    LOG_FATAL_IF(!mDisplayId);
+
     if (mOutputProducerSlot >= 0) {
         mSource[SOURCE_SINK]->cancelBuffer(
                 mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot),
@@ -633,8 +646,8 @@
     // until after GLES calls queueBuffer(). So here we just set the buffer
     // (for use in HWC prepare) but not the fence; we'll call this again with
     // the proper fence once we have it.
-    result = mHwc.setOutputBuffer(mDisplayId, Fence::NO_FENCE,
-            mProducerBuffers[mOutputProducerSlot]);
+    result = mHwc.setOutputBuffer(*mDisplayId, Fence::NO_FENCE,
+                                  mProducerBuffers[mOutputProducerSlot]);
 
     return result;
 }
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 5c8acea..87ae7dd 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -17,12 +17,16 @@
 #ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
 #define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
 
-#include "DisplaySurface.h"
-#include "HWComposerBufferCache.h"
+#include <optional>
+#include <string>
 
+#include <compositionengine/DisplaySurface.h>
 #include <gui/ConsumerBase.h>
 #include <gui/IGraphicBufferProducer.h>
 
+#include "DisplayIdentification.h"
+#include "HWComposerBufferCache.h"
+
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -69,15 +73,14 @@
  * the HWC output buffer. When HWC composition is complete, the scratch buffer
  * is released and the output buffer is queued to the sink.
  */
-class VirtualDisplaySurface : public DisplaySurface,
+class VirtualDisplaySurface : public compositionengine::DisplaySurface,
                               public BnGraphicBufferProducer,
                               private ConsumerBase {
 public:
-    VirtualDisplaySurface(HWComposer& hwc, int32_t dispId,
-            const sp<IGraphicBufferProducer>& sink,
-            const sp<IGraphicBufferProducer>& bqProducer,
-            const sp<IGraphicBufferConsumer>& bqConsumer,
-            const String8& name);
+    VirtualDisplaySurface(HWComposer& hwc, const std::optional<DisplayId>& displayId,
+                          const sp<IGraphicBufferProducer>& sink,
+                          const sp<IGraphicBufferProducer>& bqProducer,
+                          const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name);
 
     //
     // DisplaySurface interface
@@ -152,8 +155,8 @@
     // Immutable after construction
     //
     HWComposer& mHwc;
-    const int32_t mDisplayId;
-    const String8 mDisplayName;
+    const std::optional<DisplayId> mDisplayId;
+    const std::string mDisplayName;
     sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
     uint32_t mDefaultOutputFormat;
 
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
deleted file mode 100644
index bc271c8..0000000
--- a/services/surfaceflinger/EventThread.cpp
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * Copyright (C) 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <pthread.h>
-#include <sched.h>
-#include <sys/types.h>
-#include <chrono>
-#include <cstdint>
-
-#include <cutils/compiler.h>
-#include <cutils/sched_policy.h>
-
-#include <gui/DisplayEventReceiver.h>
-
-#include <utils/Errors.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-#include "EventThread.h"
-
-using namespace std::chrono_literals;
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-EventThread::~EventThread() = default;
-
-namespace impl {
-
-EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
-                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
-      : mVSyncSource(src),
-        mResyncWithRateLimitCallback(resyncWithRateLimitCallback),
-        mInterceptVSyncsCallback(interceptVSyncsCallback) {
-    for (auto& event : mVSyncEvent) {
-        event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
-        event.header.id = 0;
-        event.header.timestamp = 0;
-        event.vsync.count = 0;
-    }
-
-    mThread = std::thread(&EventThread::threadMain, this);
-
-    pthread_setname_np(mThread.native_handle(), threadName);
-
-    pid_t tid = pthread_gettid_np(mThread.native_handle());
-
-    // Use SCHED_FIFO to minimize jitter
-    constexpr int EVENT_THREAD_PRIORITY = 2;
-    struct sched_param param = {0};
-    param.sched_priority = EVENT_THREAD_PRIORITY;
-    if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, &param) != 0) {
-        ALOGE("Couldn't set SCHED_FIFO for EventThread");
-    }
-
-    set_sched_policy(tid, SP_FOREGROUND);
-}
-
-EventThread::~EventThread() {
-    {
-        std::lock_guard<std::mutex> lock(mMutex);
-        mKeepRunning = false;
-        mCondition.notify_all();
-    }
-    mThread.join();
-}
-
-void EventThread::setPhaseOffset(nsecs_t phaseOffset) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mVSyncSource->setPhaseOffset(phaseOffset);
-}
-
-sp<BnDisplayEventConnection> EventThread::createEventConnection() const {
-    return new Connection(const_cast<EventThread*>(this));
-}
-
-status_t EventThread::registerDisplayEventConnection(
-        const sp<EventThread::Connection>& connection) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mDisplayEventConnections.add(connection);
-    mCondition.notify_all();
-    return NO_ERROR;
-}
-
-void EventThread::removeDisplayEventConnectionLocked(const wp<EventThread::Connection>& connection) {
-    mDisplayEventConnections.remove(connection);
-}
-
-void EventThread::setVsyncRate(uint32_t count, const sp<EventThread::Connection>& connection) {
-    if (int32_t(count) >= 0) { // server must protect against bad params
-        std::lock_guard<std::mutex> lock(mMutex);
-        const int32_t new_count = (count == 0) ? -1 : count;
-        if (connection->count != new_count) {
-            connection->count = new_count;
-            mCondition.notify_all();
-        }
-    }
-}
-
-void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) {
-    std::lock_guard<std::mutex> lock(mMutex);
-
-    if (mResyncWithRateLimitCallback) {
-        mResyncWithRateLimitCallback();
-    }
-
-    if (connection->count < 0) {
-        connection->count = 0;
-        mCondition.notify_all();
-    }
-}
-
-void EventThread::onScreenReleased() {
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (!mUseSoftwareVSync) {
-        // disable reliance on h/w vsync
-        mUseSoftwareVSync = true;
-        mCondition.notify_all();
-    }
-}
-
-void EventThread::onScreenAcquired() {
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (mUseSoftwareVSync) {
-        // resume use of h/w vsync
-        mUseSoftwareVSync = false;
-        mCondition.notify_all();
-    }
-}
-
-void EventThread::onVSyncEvent(nsecs_t timestamp) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
-    mVSyncEvent[0].header.id = 0;
-    mVSyncEvent[0].header.timestamp = timestamp;
-    mVSyncEvent[0].vsync.count++;
-    mCondition.notify_all();
-}
-
-void EventThread::onHotplugReceived(int type, bool connected) {
-    ALOGE_IF(type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES,
-             "received hotplug event for an invalid display (id=%d)", type);
-
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
-        DisplayEventReceiver::Event event;
-        event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG;
-        event.header.id = type;
-        event.header.timestamp = systemTime();
-        event.hotplug.connected = connected;
-        mPendingEvents.add(event);
-        mCondition.notify_all();
-    }
-}
-
-void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
-    std::unique_lock<std::mutex> lock(mMutex);
-    while (mKeepRunning) {
-        DisplayEventReceiver::Event event;
-        Vector<sp<EventThread::Connection> > signalConnections;
-        signalConnections = waitForEventLocked(&lock, &event);
-
-        // dispatch events to listeners...
-        const size_t count = signalConnections.size();
-        for (size_t i = 0; i < count; i++) {
-            const sp<Connection>& conn(signalConnections[i]);
-            // now see if we still need to report this event
-            status_t err = conn->postEvent(event);
-            if (err == -EAGAIN || err == -EWOULDBLOCK) {
-                // The destination doesn't accept events anymore, it's probably
-                // full. For now, we just drop the events on the floor.
-                // FIXME: Note that some events cannot be dropped and would have
-                // to be re-sent later.
-                // Right-now we don't have the ability to do this.
-                ALOGW("EventThread: dropping event (%08x) for connection %p", event.header.type,
-                      conn.get());
-            } else if (err < 0) {
-                // handle any other error on the pipe as fatal. the only
-                // reasonable thing to do is to clean-up this connection.
-                // The most common error we'll get here is -EPIPE.
-                removeDisplayEventConnectionLocked(signalConnections[i]);
-            }
-        }
-    }
-}
-
-// This will return when (1) a vsync event has been received, and (2) there was
-// at least one connection interested in receiving it when we started waiting.
-Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked(
-        std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* event) {
-    Vector<sp<EventThread::Connection> > signalConnections;
-
-    while (signalConnections.isEmpty() && mKeepRunning) {
-        bool eventPending = false;
-        bool waitForVSync = false;
-
-        size_t vsyncCount = 0;
-        nsecs_t timestamp = 0;
-        for (int32_t i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; i++) {
-            timestamp = mVSyncEvent[i].header.timestamp;
-            if (timestamp) {
-                // we have a vsync event to dispatch
-                if (mInterceptVSyncsCallback) {
-                    mInterceptVSyncsCallback(timestamp);
-                }
-                *event = mVSyncEvent[i];
-                mVSyncEvent[i].header.timestamp = 0;
-                vsyncCount = mVSyncEvent[i].vsync.count;
-                break;
-            }
-        }
-
-        if (!timestamp) {
-            // no vsync event, see if there are some other event
-            eventPending = !mPendingEvents.isEmpty();
-            if (eventPending) {
-                // we have some other event to dispatch
-                *event = mPendingEvents[0];
-                mPendingEvents.removeAt(0);
-            }
-        }
-
-        // find out connections waiting for events
-        size_t count = mDisplayEventConnections.size();
-        for (size_t i = 0; i < count;) {
-            sp<Connection> connection(mDisplayEventConnections[i].promote());
-            if (connection != nullptr) {
-                bool added = false;
-                if (connection->count >= 0) {
-                    // we need vsync events because at least
-                    // one connection is waiting for it
-                    waitForVSync = true;
-                    if (timestamp) {
-                        // we consume the event only if it's time
-                        // (ie: we received a vsync event)
-                        if (connection->count == 0) {
-                            // fired this time around
-                            connection->count = -1;
-                            signalConnections.add(connection);
-                            added = true;
-                        } else if (connection->count == 1 ||
-                                   (vsyncCount % connection->count) == 0) {
-                            // continuous event, and time to report it
-                            signalConnections.add(connection);
-                            added = true;
-                        }
-                    }
-                }
-
-                if (eventPending && !timestamp && !added) {
-                    // we don't have a vsync event to process
-                    // (timestamp==0), but we have some pending
-                    // messages.
-                    signalConnections.add(connection);
-                }
-                ++i;
-            } else {
-                // we couldn't promote this reference, the connection has
-                // died, so clean-up!
-                mDisplayEventConnections.removeAt(i);
-                --count;
-            }
-        }
-
-        // Here we figure out if we need to enable or disable vsyncs
-        if (timestamp && !waitForVSync) {
-            // we received a VSYNC but we have no clients
-            // don't report it, and disable VSYNC events
-            disableVSyncLocked();
-        } else if (!timestamp && waitForVSync) {
-            // we have at least one client, so we want vsync enabled
-            // (TODO: this function is called right after we finish
-            // notifying clients of a vsync, so this call will be made
-            // at the vsync rate, e.g. 60fps.  If we can accurately
-            // track the current state we could avoid making this call
-            // so often.)
-            enableVSyncLocked();
-        }
-
-        // note: !timestamp implies signalConnections.isEmpty(), because we
-        // don't populate signalConnections if there's no vsync pending
-        if (!timestamp && !eventPending) {
-            // wait for something to happen
-            if (waitForVSync) {
-                // This is where we spend most of our time, waiting
-                // for vsync events and new client registrations.
-                //
-                // If the screen is off, we can't use h/w vsync, so we
-                // use a 16ms timeout instead.  It doesn't need to be
-                // precise, we just need to keep feeding our clients.
-                //
-                // We don't want to stall if there's a driver bug, so we
-                // use a (long) timeout when waiting for h/w vsync, and
-                // generate fake events when necessary.
-                bool softwareSync = mUseSoftwareVSync;
-                auto timeout = softwareSync ? 16ms : 1000ms;
-                if (mCondition.wait_for(*lock, timeout) == std::cv_status::timeout) {
-                    if (!softwareSync) {
-                        ALOGW("Timed out waiting for hw vsync; faking it");
-                    }
-                    // FIXME: how do we decide which display id the fake
-                    // vsync came from ?
-                    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
-                    mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY;
-                    mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
-                    mVSyncEvent[0].vsync.count++;
-                }
-            } else {
-                // Nobody is interested in vsync, so we just want to sleep.
-                // h/w vsync should be disabled, so this will wait until we
-                // get a new connection, or an existing connection becomes
-                // interested in receiving vsync again.
-                mCondition.wait(*lock);
-            }
-        }
-    }
-
-    // here we're guaranteed to have a timestamp and some connections to signal
-    // (The connections might have dropped out of mDisplayEventConnections
-    // while we were asleep, but we'll still have strong references to them.)
-    return signalConnections;
-}
-
-void EventThread::enableVSyncLocked() {
-    if (!mUseSoftwareVSync) {
-        // never enable h/w VSYNC when screen is off
-        if (!mVsyncEnabled) {
-            mVsyncEnabled = true;
-            mVSyncSource->setCallback(this);
-            mVSyncSource->setVSyncEnabled(true);
-        }
-    }
-    mDebugVsyncEnabled = true;
-}
-
-void EventThread::disableVSyncLocked() {
-    if (mVsyncEnabled) {
-        mVsyncEnabled = false;
-        mVSyncSource->setVSyncEnabled(false);
-        mDebugVsyncEnabled = false;
-    }
-}
-
-void EventThread::dump(String8& result) const {
-    std::lock_guard<std::mutex> lock(mMutex);
-    result.appendFormat("VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled");
-    result.appendFormat("  soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled");
-    result.appendFormat("  numListeners=%zu,\n  events-delivered: %u\n",
-                        mDisplayEventConnections.size(),
-                        mVSyncEvent[DisplayDevice::DISPLAY_PRIMARY].vsync.count);
-    for (size_t i = 0; i < mDisplayEventConnections.size(); i++) {
-        sp<Connection> connection = mDisplayEventConnections.itemAt(i).promote();
-        result.appendFormat("    %p: count=%d\n", connection.get(),
-                            connection != nullptr ? connection->count : 0);
-    }
-}
-
-// ---------------------------------------------------------------------------
-
-EventThread::Connection::Connection(EventThread* eventThread)
-      : count(-1), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {}
-
-EventThread::Connection::~Connection() {
-    // do nothing here -- clean-up will happen automatically
-    // when the main thread wakes up
-}
-
-void EventThread::Connection::onFirstRef() {
-    // NOTE: mEventThread doesn't hold a strong reference on us
-    mEventThread->registerDisplayEventConnection(this);
-}
-
-status_t EventThread::Connection::stealReceiveChannel(gui::BitTube* outChannel) {
-    outChannel->setReceiveFd(mChannel.moveReceiveFd());
-    return NO_ERROR;
-}
-
-status_t EventThread::Connection::setVsyncRate(uint32_t count) {
-    mEventThread->setVsyncRate(count, this);
-    return NO_ERROR;
-}
-
-void EventThread::Connection::requestNextVsync() {
-    mEventThread->requestNextVsync(this);
-}
-
-status_t EventThread::Connection::postEvent(const DisplayEventReceiver::Event& event) {
-    ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
-    return size < 0 ? status_t(size) : status_t(NO_ERROR);
-}
-
-// ---------------------------------------------------------------------------
-
-} // namespace impl
-} // namespace android
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
deleted file mode 100644
index 9c13ed2..0000000
--- a/services/surfaceflinger/EventThread.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <condition_variable>
-#include <mutex>
-#include <thread>
-
-#include <android-base/thread_annotations.h>
-
-#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
-#include <private/gui/BitTube.h>
-
-#include <utils/Errors.h>
-#include <utils/SortedVector.h>
-
-#include "DisplayDevice.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class EventThreadTest;
-class SurfaceFlinger;
-class String8;
-
-// ---------------------------------------------------------------------------
-
-class VSyncSource {
-public:
-    class Callback {
-    public:
-        virtual ~Callback() {}
-        virtual void onVSyncEvent(nsecs_t when) = 0;
-    };
-
-    virtual ~VSyncSource() {}
-    virtual void setVSyncEnabled(bool enable) = 0;
-    virtual void setCallback(Callback* callback) = 0;
-    virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
-};
-
-class EventThread {
-public:
-    virtual ~EventThread();
-
-    virtual sp<BnDisplayEventConnection> createEventConnection() const = 0;
-
-    // called before the screen is turned off from main thread
-    virtual void onScreenReleased() = 0;
-
-    // called after the screen is turned on from main thread
-    virtual void onScreenAcquired() = 0;
-
-    // called when receiving a hotplug event
-    virtual void onHotplugReceived(int type, bool connected) = 0;
-
-    virtual void dump(String8& result) const = 0;
-
-    virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
-};
-
-namespace impl {
-
-class EventThread : public android::EventThread, private VSyncSource::Callback {
-    class Connection : public BnDisplayEventConnection {
-    public:
-        explicit Connection(EventThread* eventThread);
-        virtual ~Connection();
-
-        virtual status_t postEvent(const DisplayEventReceiver::Event& event);
-
-        // count >= 1 : continuous event. count is the vsync rate
-        // count == 0 : one-shot event that has not fired
-        // count ==-1 : one-shot event that fired this round / disabled
-        int32_t count;
-
-    private:
-        virtual void onFirstRef();
-        status_t stealReceiveChannel(gui::BitTube* outChannel) override;
-        status_t setVsyncRate(uint32_t count) override;
-        void requestNextVsync() override; // asynchronous
-        EventThread* const mEventThread;
-        gui::BitTube mChannel;
-    };
-
-public:
-    using ResyncWithRateLimitCallback = std::function<void()>;
-    using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
-
-    EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
-                InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
-    ~EventThread();
-
-    sp<BnDisplayEventConnection> createEventConnection() const override;
-    status_t registerDisplayEventConnection(const sp<Connection>& connection);
-
-    void setVsyncRate(uint32_t count, const sp<Connection>& connection);
-    void requestNextVsync(const sp<Connection>& connection);
-
-    // called before the screen is turned off from main thread
-    void onScreenReleased() override;
-
-    // called after the screen is turned on from main thread
-    void onScreenAcquired() override;
-
-    // called when receiving a hotplug event
-    void onHotplugReceived(int type, bool connected) override;
-
-    void dump(String8& result) const override;
-
-    void setPhaseOffset(nsecs_t phaseOffset) override;
-
-private:
-    friend EventThreadTest;
-
-    void threadMain();
-    Vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock,
-                                                           DisplayEventReceiver::Event* event)
-            REQUIRES(mMutex);
-
-    void removeDisplayEventConnectionLocked(const wp<Connection>& connection) REQUIRES(mMutex);
-    void enableVSyncLocked() REQUIRES(mMutex);
-    void disableVSyncLocked() REQUIRES(mMutex);
-
-    // Implements VSyncSource::Callback
-    void onVSyncEvent(nsecs_t timestamp) override;
-
-    // constants
-    VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr;
-    const ResyncWithRateLimitCallback mResyncWithRateLimitCallback;
-    const InterceptVSyncsCallback mInterceptVSyncsCallback;
-
-    std::thread mThread;
-    mutable std::mutex mMutex;
-    mutable std::condition_variable mCondition;
-
-    // protected by mLock
-    SortedVector<wp<Connection>> mDisplayEventConnections GUARDED_BY(mMutex);
-    Vector<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
-    DisplayEventReceiver::Event mVSyncEvent[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES] GUARDED_BY(
-            mMutex);
-    bool mUseSoftwareVSync GUARDED_BY(mMutex) = false;
-    bool mVsyncEnabled GUARDED_BY(mMutex) = false;
-    bool mKeepRunning GUARDED_BY(mMutex) = true;
-
-    // for debugging
-    bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false;
-};
-
-// ---------------------------------------------------------------------------
-
-} // namespace impl
-} // namespace android
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index 1539873..f4cc49b 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -19,6 +19,7 @@
 
 #include <inttypes.h>
 
+#include <android-base/stringprintf.h>
 #include <android/log.h>
 #include <utils/String8.h>
 
@@ -230,17 +231,17 @@
             mFrameRecords[idx].actualPresentTime < INT64_MAX;
 }
 
-void FrameTracker::dumpStats(String8& result) const {
+void FrameTracker::dumpStats(std::string& result) const {
     Mutex::Autolock lock(mMutex);
     processFencesLocked();
 
     const size_t o = mOffset;
     for (size_t i = 1; i < NUM_FRAME_RECORDS; i++) {
         const size_t index = (o+i) % NUM_FRAME_RECORDS;
-        result.appendFormat("%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n",
-            mFrameRecords[index].desiredPresentTime,
-            mFrameRecords[index].actualPresentTime,
-            mFrameRecords[index].frameReadyTime);
+        base::StringAppendF(&result, "%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n",
+                            mFrameRecords[index].desiredPresentTime,
+                            mFrameRecords[index].actualPresentTime,
+                            mFrameRecords[index].frameReadyTime);
     }
     result.append("\n");
 }
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index b4a9fd6..555dcc1 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -90,7 +90,7 @@
     void logAndResetStats(const String8& name);
 
     // dumpStats dump appends the current frame display time history to the result string.
-    void dumpStats(String8& result) const;
+    void dumpStats(std::string& result) const;
 
 private:
     struct FrameRecord {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0caac9b..619964f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -25,6 +25,8 @@
 #include <sys/types.h>
 #include <algorithm>
 
+#include <android-base/stringprintf.h>
+
 #include <cutils/compiler.h>
 #include <cutils/native_handle.h>
 #include <cutils/properties.h>
@@ -50,11 +52,11 @@
 #include "LayerRejecter.h"
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
-#include "clz.h"
 
 #include "DisplayHardware/HWComposer.h"
+#include "TimeStats/TimeStats.h"
 
-#include "RenderEngine/RenderEngine.h"
+#include <renderengine/RenderEngine.h>
 
 #include <mutex>
 #include "LayerProtoHelper.h"
@@ -63,77 +65,59 @@
 
 namespace android {
 
-LayerBE::LayerBE()
-      : mMesh(Mesh::TRIANGLE_FAN, 4, 2, 2) {
-}
+using base::StringAppendF;
 
+std::atomic<int32_t> Layer::sSequence{1};
 
-int32_t Layer::sSequence = 1;
-
-Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w,
-             uint32_t h, uint32_t flags)
-      : contentDirty(false),
-        sequence(uint32_t(android_atomic_inc(&sSequence))),
-        mFlinger(flinger),
-        mPremultipliedAlpha(true),
-        mName(name),
-        mTransactionFlags(0),
-        mPendingStateMutex(),
-        mPendingStates(),
-        mQueuedFrames(0),
-        mSidebandStreamChanged(false),
-        mActiveBufferSlot(BufferQueue::INVALID_BUFFER_SLOT),
-        mCurrentTransform(0),
-        mOverrideScalingMode(-1),
-        mCurrentOpacity(true),
-        mCurrentFrameNumber(0),
-        mFrameLatencyNeeded(false),
-        mFiltering(false),
-        mNeedsFiltering(false),
-        mProtectedByApp(false),
-        mClientRef(client),
-        mPotentialCursor(false),
-        mQueueItemLock(),
-        mQueueItemCondition(),
-        mQueueItems(),
-        mLastFrameNumberReceived(0),
-        mAutoRefresh(false),
-        mFreezeGeometryUpdates(false),
-        mCurrentChildren(LayerVector::StateSet::Current),
-        mDrawingChildren(LayerVector::StateSet::Drawing) {
+Layer::Layer(const LayerCreationArgs& args)
+      : mFlinger(args.flinger),
+        mName(args.name),
+        mClientRef(args.client),
+        mBE{this, args.name.string()} {
     mCurrentCrop.makeInvalid();
 
     uint32_t layerFlags = 0;
-    if (flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
-    if (flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
-    if (flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
+    if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
+    if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
+    if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
 
-    mName = name;
     mTransactionName = String8("TX - ") + mName;
 
-    mCurrentState.active.w = w;
-    mCurrentState.active.h = h;
+    mCurrentState.active_legacy.w = args.w;
+    mCurrentState.active_legacy.h = args.h;
     mCurrentState.flags = layerFlags;
-    mCurrentState.active.transform.set(0, 0);
-    mCurrentState.crop.makeInvalid();
-    mCurrentState.finalCrop.makeInvalid();
-    mCurrentState.requestedFinalCrop = mCurrentState.finalCrop;
-    mCurrentState.requestedCrop = mCurrentState.crop;
+    mCurrentState.active_legacy.transform.set(0, 0);
+    mCurrentState.crop_legacy.makeInvalid();
+    mCurrentState.requestedCrop_legacy = mCurrentState.crop_legacy;
     mCurrentState.z = 0;
     mCurrentState.color.a = 1.0f;
     mCurrentState.layerStack = 0;
     mCurrentState.sequence = 0;
-    mCurrentState.requested = mCurrentState.active;
-    mCurrentState.appId = 0;
-    mCurrentState.type = 0;
+    mCurrentState.requested_legacy = mCurrentState.active_legacy;
+    mCurrentState.active.w = UINT32_MAX;
+    mCurrentState.active.h = UINT32_MAX;
+    mCurrentState.active.transform.set(0, 0);
+    mCurrentState.transform = 0;
+    mCurrentState.transformToDisplayInverse = false;
+    mCurrentState.crop.makeInvalid();
+    mCurrentState.acquireFence = new Fence(-1);
+    mCurrentState.dataspace = ui::Dataspace::UNKNOWN;
+    mCurrentState.hdrMetadata.validTypes = 0;
+    mCurrentState.surfaceDamageRegion.clear();
+    mCurrentState.cornerRadius = 0.0f;
+    mCurrentState.api = -1;
+    mCurrentState.hasColorTransform = false;
+    mCurrentState.colorDataspace = ui::Dataspace::UNKNOWN;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
 
     CompositorTiming compositorTiming;
-    flinger->getCompositorTiming(&compositorTiming);
+    args.flinger->getCompositorTiming(&compositorTiming);
     mFrameEventHistory.initializeCompositorTiming(compositorTiming);
     mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval);
+
+    mFlinger->onLayerCreated();
 }
 
 Layer::~Layer() {
@@ -142,13 +126,11 @@
         c->detachLayer(this);
     }
 
-    for (auto& point : mRemoteSyncPoints) {
-        point->setTransactionApplied();
-    }
-    for (auto& point : mLocalSyncPoints) {
-        point->setFrameAvailable();
-    }
     mFrameTracker.logAndResetStats(mName);
+
+    destroyAllHwcLayersPlusChildren();
+
+    mFlinger->onLayerDestroyed();
 }
 
 // ---------------------------------------------------------------------------
@@ -163,10 +145,9 @@
 void Layer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {}
 
 void Layer::onRemovedFromCurrentState() {
+    mRemovedFromCurrentState = true;
+
     // the layer is removed from SF mCurrentState to mLayersPendingRemoval
-
-    mPendingRemoval = true;
-
     if (mCurrentState.zOrderRelativeOf != nullptr) {
         sp<Layer> strongRelative = mCurrentState.zOrderRelativeOf.promote();
         if (strongRelative != nullptr) {
@@ -176,19 +157,36 @@
         mCurrentState.zOrderRelativeOf = nullptr;
     }
 
+    // Since we are no longer reachable from CurrentState SurfaceFlinger
+    // will no longer invoke doTransaction for us, and so we will
+    // never finish applying transactions. We signal the sync point
+    // now so that another layer will not become indefinitely
+    // blocked.
+    for (auto& point: mRemoteSyncPoints) {
+        point->setTransactionApplied();
+    }
+    mRemoteSyncPoints.clear();
+
+    {
+    Mutex::Autolock syncLock(mLocalSyncPointMutex);
+    for (auto& point : mLocalSyncPoints) {
+        point->setFrameAvailable();
+    }
+    mLocalSyncPoints.clear();
+    }
+
     for (const auto& child : mCurrentChildren) {
         child->onRemovedFromCurrentState();
     }
+
+    mFlinger->markLayerPendingRemovalLocked(this);
 }
 
-void Layer::onRemoved() {
-    // the layer is removed from SF mLayersPendingRemoval
-    abandon();
-
-    destroyAllHwcLayers();
+void Layer::addToCurrentState() {
+    mRemovedFromCurrentState = false;
 
     for (const auto& child : mCurrentChildren) {
-        child->onRemoved();
+        child->addToCurrentState();
     }
 }
 
@@ -213,44 +211,52 @@
 // h/w composer set-up
 // ---------------------------------------------------------------------------
 
-bool Layer::createHwcLayer(HWComposer* hwc, int32_t hwcId) {
-    LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.count(hwcId) != 0,
-                        "Already have a layer for hwcId %d", hwcId);
-    HWC2::Layer* layer = hwc->createLayer(hwcId);
+bool Layer::createHwcLayer(HWComposer* hwc, DisplayId displayId) {
+    LOG_ALWAYS_FATAL_IF(hasHwcLayer(displayId), "Already have a layer for display %s",
+                        to_string(displayId).c_str());
+    auto layer = std::shared_ptr<HWC2::Layer>(
+            hwc->createLayer(displayId),
+            [hwc, displayId](HWC2::Layer* layer) {
+               hwc->destroyLayer(displayId, layer); });
     if (!layer) {
         return false;
     }
-    LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers[hwcId];
+    LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers[displayId];
     hwcInfo.hwc = hwc;
     hwcInfo.layer = layer;
     layer->setLayerDestroyedListener(
-            [this, hwcId](HWC2::Layer* /*layer*/) { getBE().mHwcLayers.erase(hwcId); });
+            [this, displayId](HWC2::Layer* /*layer*/) { getBE().mHwcLayers.erase(displayId); });
     return true;
 }
 
-bool Layer::destroyHwcLayer(int32_t hwcId) {
-    if (getBE().mHwcLayers.count(hwcId) == 0) {
+bool Layer::destroyHwcLayer(DisplayId displayId) {
+    if (!hasHwcLayer(displayId)) {
         return false;
     }
-    auto& hwcInfo = getBE().mHwcLayers[hwcId];
+    auto& hwcInfo = getBE().mHwcLayers[displayId];
     LOG_ALWAYS_FATAL_IF(hwcInfo.layer == nullptr, "Attempt to destroy null layer");
     LOG_ALWAYS_FATAL_IF(hwcInfo.hwc == nullptr, "Missing HWComposer");
-    hwcInfo.hwc->destroyLayer(hwcId, hwcInfo.layer);
-    // The layer destroyed listener should have cleared the entry from
-    // mHwcLayers. Verify that.
-    LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.count(hwcId) != 0,
-                        "Stale layer entry in getBE().mHwcLayers");
+    hwcInfo.layer = nullptr;
+
     return true;
 }
 
-void Layer::destroyAllHwcLayers() {
+void Layer::destroyHwcLayersForAllDisplays() {
     size_t numLayers = getBE().mHwcLayers.size();
     for (size_t i = 0; i < numLayers; ++i) {
         LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.empty(), "destroyAllHwcLayers failed");
         destroyHwcLayer(getBE().mHwcLayers.begin()->first);
     }
+}
+
+void Layer::destroyAllHwcLayersPlusChildren() {
+    destroyHwcLayersForAllDisplays();
     LOG_ALWAYS_FATAL_IF(!getBE().mHwcLayers.empty(),
                         "All hardware composer layers should have been destroyed");
+
+    for (const sp<Layer>& child : mDrawingChildren) {
+        child->destroyAllHwcLayersPlusChildren();
+    }
 }
 
 Rect Layer::getContentCrop() const {
@@ -289,83 +295,91 @@
 }
 
 Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const {
-    const Layer::State& s(getDrawingState());
-    Rect win(s.active.w, s.active.h);
-
-    if (!s.crop.isEmpty()) {
-        win.intersect(s.crop, &win);
-    }
-
-    Transform t = getTransform();
-    win = t.transform(win);
-
-    if (!s.finalCrop.isEmpty()) {
-        win.intersect(s.finalCrop, &win);
-    }
-
-    const sp<Layer>& p = mDrawingParent.promote();
-    // Now we need to calculate the parent bounds, so we can clip ourselves to those.
-    // When calculating the parent bounds for purposes of clipping,
-    // we don't need to constrain the parent to its transparent region.
-    // The transparent region is an optimization based on the
-    // buffer contents of the layer, but does not affect the space allocated to
-    // it by policy, and thus children should be allowed to extend into the
-    // parent's transparent region. In fact one of the main uses, is to reduce
-    // buffer allocation size in cases where a child window sits behind a main window
-    // (by marking the hole in the parent window as a transparent region)
-    if (p != nullptr) {
-        Rect bounds = p->computeScreenBounds(false);
-        bounds.intersect(win, &win);
-    }
-
-    if (reduceTransparentRegion) {
-        auto const screenTransparentRegion = t.transform(s.activeTransparentRegion);
-        win = reduce(win, screenTransparentRegion);
-    }
-
-    return win;
+    const State& s(getDrawingState());
+    Region transparentRegion = reduceTransparentRegion ? getActiveTransparentRegion(s) : Region();
+    FloatRect bounds = computeBounds(transparentRegion);
+    ui::Transform t = getTransform();
+    // Transform to screen space.
+    bounds = t.transform(bounds);
+    return Rect{bounds};
 }
 
 FloatRect Layer::computeBounds() const {
-    const Layer::State& s(getDrawingState());
-    return computeBounds(s.activeTransparentRegion);
+    const State& s(getDrawingState());
+    return computeBounds(getActiveTransparentRegion(s));
 }
 
 FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const {
-    const Layer::State& s(getDrawingState());
-    Rect win(s.active.w, s.active.h);
+    const State& s(getDrawingState());
+    Rect bounds = getCroppedBufferSize(s);
+    FloatRect floatBounds = bounds.toFloatRect();
+    if (bounds.isValid()) {
+        // Layer has bounds. Pass in our bounds as a special case. Then pass on to our parents so
+        // that they can clip it.
+        floatBounds = cropChildBounds(floatBounds);
+    } else {
+        // Layer does not have bounds, so we fill to our parent bounds. This is done by getting our
+        // parent bounds and inverting the transform to get the maximum bounds we can have that
+        // will fit within our parent bounds.
+        const auto& p = mDrawingParent.promote();
+        if (p != nullptr) {
+            ui::Transform t = s.active_legacy.transform;
+            // When calculating the parent bounds for purposes of clipping, we don't need to
+            // constrain the parent to its transparent region. The transparent region is an
+            // optimization based on the buffer contents of the layer, but does not affect the
+            // space allocated to it by policy, and thus children should be allowed to extend into
+            // the parent's transparent region.
+            // One of the main uses is a parent window with a child sitting behind the parent
+            // window, marked by a transparent region. When computing the parent bounds from the
+            // parent's perspective we pass in the transparent region to reduce buffer allocation
+            // size. When computing the parent bounds from the child's perspective, we pass in an
+            // empty transparent region in order to extend into the the parent bounds.
+            floatBounds = p->computeBounds(Region());
+            // Transform back to layer space.
+            floatBounds = t.inverse().transform(floatBounds);
+        }
+    }
 
-    if (!s.crop.isEmpty()) {
-        win.intersect(s.crop, &win);
+    // Subtract the transparent region and snap to the bounds.
+    return reduce(floatBounds, activeTransparentRegion);
+}
+
+FloatRect Layer::cropChildBounds(const FloatRect& childBounds) const {
+    const State& s(getDrawingState());
+    Rect bounds = getCroppedBufferSize(s);
+    FloatRect croppedBounds = childBounds;
+
+    // If the layer has bounds, then crop the passed in child bounds and pass
+    // it to our parents so they can crop it as well. If the layer has no bounds,
+    // then pass on the child bounds.
+    if (bounds.isValid()) {
+        croppedBounds = croppedBounds.intersect(bounds.toFloatRect());
     }
 
     const auto& p = mDrawingParent.promote();
-    FloatRect floatWin = win.toFloatRect();
-    FloatRect parentBounds = floatWin;
     if (p != nullptr) {
-        // We pass an empty Region here for reasons mirroring that of the case described in
-        // the computeScreenBounds reduceTransparentRegion=false case.
-        parentBounds = p->computeBounds(Region());
+        // Transform to parent space and allow parent layer to crop the
+        // child bounds as well.
+        ui::Transform t = s.active_legacy.transform;
+        croppedBounds = t.transform(croppedBounds);
+        croppedBounds = p->cropChildBounds(croppedBounds);
+        croppedBounds = t.inverse().transform(croppedBounds);
     }
-
-    Transform t = s.active.transform;
-
-
-    if (p != nullptr || !s.finalCrop.isEmpty()) {
-        floatWin = t.transform(floatWin);
-        floatWin = floatWin.intersect(parentBounds);
-
-        if (!s.finalCrop.isEmpty()) {
-            floatWin = floatWin.intersect(s.finalCrop.toFloatRect());
-        }
-        floatWin = t.inverse().transform(floatWin);
-    }
-
-    // subtract the transparent region and snap to the bounds
-    return reduce(floatWin, activeTransparentRegion);
+    return croppedBounds;
 }
 
-Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& hw) const {
+Rect Layer::getCroppedBufferSize(const State& s) const {
+    Rect size = getBufferSize(s);
+    Rect crop = getCrop(s);
+    if (!crop.isEmpty() && size.isValid()) {
+        size.intersect(crop, &size);
+    } else if (!crop.isEmpty()) {
+        size = crop;
+    }
+    return size;
+}
+
+Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& display) const {
     // the crop is the area of the window that gets cropped, but not
     // scaled in any ways.
     const State& s(getDrawingState());
@@ -374,35 +388,44 @@
     // layerstack space, and convert-back to layer space.
     // if there are no window scaling involved, this operation will map to full
     // pixels in the buffer.
-    // FIXME: the 3 lines below can produce slightly incorrect clipping when we have
-    // a viewport clipping and a window transform. we should use floating point to fix this.
 
-    Rect activeCrop(s.active.w, s.active.h);
-    if (!s.crop.isEmpty()) {
-        activeCrop.intersect(s.crop, &activeCrop);
-    }
-
-    Transform t = getTransform();
-    activeCrop = t.transform(activeCrop);
-    if (!activeCrop.intersect(hw->getViewport(), &activeCrop)) {
+    FloatRect activeCropFloat = computeBounds();
+    ui::Transform t = getTransform();
+    // Transform to screen space.
+    activeCropFloat = t.transform(activeCropFloat);
+    activeCropFloat = activeCropFloat.intersect(display->getViewport().toFloatRect());
+    // Back to layer space to work with the content crop.
+    activeCropFloat = t.inverse().transform(activeCropFloat);
+    // This needs to be here as transform.transform(Rect) computes the
+    // transformed rect and then takes the bounding box of the result before
+    // returning. This means
+    // transform.inverse().transform(transform.transform(Rect)) != Rect
+    // in which case we need to make sure the final rect is clipped to the
+    // display bounds.
+    Rect activeCrop{activeCropFloat};
+    if (!activeCrop.intersect(getBufferSize(s), &activeCrop)) {
         activeCrop.clear();
     }
-    if (!s.finalCrop.isEmpty()) {
-        if (!activeCrop.intersect(s.finalCrop, &activeCrop)) {
-            activeCrop.clear();
-        }
-    }
-
-    const auto& p = mDrawingParent.promote();
-    if (p != nullptr) {
-        auto parentCrop = p->computeInitialCrop(hw);
-        activeCrop.intersect(parentCrop, &activeCrop);
-    }
-
     return activeCrop;
 }
 
-FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const {
+void Layer::setupRoundedCornersCropCoordinates(Rect win,
+                                               const FloatRect& roundedCornersCrop) const {
+    // Translate win by the rounded corners rect coordinates, to have all values in
+    // layer coordinate space.
+    win.left -= roundedCornersCrop.left;
+    win.right -= roundedCornersCrop.left;
+    win.top -= roundedCornersCrop.top;
+    win.bottom -= roundedCornersCrop.top;
+
+    renderengine::Mesh::VertexArray<vec2> cropCoords(getBE().mMesh.getCropCoordArray<vec2>());
+    cropCoords[0] = vec2(win.left, win.top);
+    cropCoords[1] = vec2(win.left, win.top + win.getHeight());
+    cropCoords[2] = vec2(win.right, win.top + win.getHeight());
+    cropCoords[3] = vec2(win.right, win.top);
+}
+
+FloatRect Layer::computeCrop(const sp<const DisplayDevice>& display) const {
     // the content crop is the area of the content that gets scaled to the
     // layer's size. This is in buffer space.
     FloatRect crop = getContentCrop().toFloatRect();
@@ -410,24 +433,8 @@
     // In addition there is a WM-specified crop we pull from our drawing state.
     const State& s(getDrawingState());
 
-    // Screen space to make reduction to parent crop clearer.
-    Rect activeCrop = computeInitialCrop(hw);
-    Transform t = getTransform();
-    // Back to layer space to work with the content crop.
-    activeCrop = t.inverse().transform(activeCrop);
-
-    // This needs to be here as transform.transform(Rect) computes the
-    // transformed rect and then takes the bounding box of the result before
-    // returning. This means
-    // transform.inverse().transform(transform.transform(Rect)) != Rect
-    // in which case we need to make sure the final rect is clipped to the
-    // display bounds.
-    if (!activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop)) {
-        activeCrop.clear();
-    }
-
-    // subtract the transparent region and snap to the bounds
-    activeCrop = reduce(activeCrop, s.activeTransparentRegion);
+    Rect activeCrop = computeInitialCrop(display);
+    Rect bufferSize = getBufferSize(s);
 
     // Transform the window crop to match the buffer coordinate system,
     // which means using the inverse of the current transform set on the
@@ -444,11 +451,12 @@
             invTransformOrient ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H;
         }
         // and apply to the current transform
-        invTransform = (Transform(invTransformOrient) * Transform(invTransform)).getOrientation();
+        invTransform = (ui::Transform(invTransformOrient) *
+                        ui::Transform(invTransform)).getOrientation();
     }
 
-    int winWidth = s.active.w;
-    int winHeight = s.active.h;
+    int winWidth = bufferSize.getWidth();
+    int winHeight = bufferSize.getHeight();
     if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
         // If the activeCrop has been rotate the ends are rotated but not
         // the space itself so when transforming ends back we can't rely on
@@ -460,10 +468,10 @@
         if (is_h_flipped == is_v_flipped) {
             invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H;
         }
-        winWidth = s.active.h;
-        winHeight = s.active.w;
+        std::swap(winWidth, winHeight);
     }
-    const Rect winCrop = activeCrop.transform(invTransform, s.active.w, s.active.h);
+    const Rect winCrop =
+            activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight());
 
     // below, crop is intersected with winCrop expressed in crop's coordinate space
     float xScale = crop.getWidth() / float(winWidth);
@@ -482,18 +490,16 @@
     return crop;
 }
 
-void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z)
-{
-    const auto hwcId = displayDevice->getHwcDisplayId();
-    if (!hasHwcLayer(hwcId)) {
-        return;
-    }
-    auto& hwcInfo = getBE().mHwcLayers[hwcId];
+void Layer::setGeometry(const sp<const DisplayDevice>& display, uint32_t z) {
+    const auto displayId = display->getId();
+    LOG_ALWAYS_FATAL_IF(!displayId);
+    RETURN_IF_NO_HWC_LAYER(*displayId);
+    auto& hwcInfo = getBE().mHwcLayers[*displayId];
 
     // enable this layer
     hwcInfo.forceClientComposition = false;
 
-    if (isSecure() && !displayDevice->isSecure()) {
+    if (isSecure() && !display->isSecure()) {
         hwcInfo.forceClientComposition = true;
     }
 
@@ -501,6 +507,7 @@
 
     // this gives us only the "orientation" component of the transform
     const State& s(getDrawingState());
+    const Rect bufferSize = getBufferSize(s);
     auto blendMode = HWC2::BlendMode::None;
     if (!isOpaque(s) || getAlpha() != 1.0f) {
         blendMode =
@@ -512,15 +519,16 @@
              " %s (%d)",
              mName.string(), to_string(blendMode).c_str(), to_string(error).c_str(),
              static_cast<int32_t>(error));
+    getBE().compositionInfo.hwc.blendMode = blendMode;
 
     // apply the layer's transform, followed by the display's global transform
     // here we're guaranteed that the layer's transform preserves rects
-    Region activeTransparentRegion(s.activeTransparentRegion);
-    Transform t = getTransform();
-    if (!s.crop.isEmpty()) {
-        Rect activeCrop(s.crop);
+    Region activeTransparentRegion(getActiveTransparentRegion(s));
+    ui::Transform t = getTransform();
+    Rect activeCrop = getCrop(s);
+    if (!activeCrop.isEmpty() && bufferSize.isValid()) {
         activeCrop = t.transform(activeCrop);
-        if (!activeCrop.intersect(displayDevice->getViewport(), &activeCrop)) {
+        if (!activeCrop.intersect(display->getViewport(), &activeCrop)) {
             activeCrop.clear();
         }
         activeCrop = t.inverse().transform(activeCrop, true);
@@ -530,29 +538,25 @@
         // transform.inverse().transform(transform.transform(Rect)) != Rect
         // in which case we need to make sure the final rect is clipped to the
         // display bounds.
-        if (!activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop)) {
+        if (!activeCrop.intersect(bufferSize, &activeCrop)) {
             activeCrop.clear();
         }
         // mark regions outside the crop as transparent
-        activeTransparentRegion.orSelf(Rect(0, 0, s.active.w, activeCrop.top));
-        activeTransparentRegion.orSelf(Rect(0, activeCrop.bottom, s.active.w, s.active.h));
+        activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top));
+        activeTransparentRegion.orSelf(
+                Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight()));
         activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom));
         activeTransparentRegion.orSelf(
-                Rect(activeCrop.right, activeCrop.top, s.active.w, activeCrop.bottom));
+                Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom));
     }
 
     // computeBounds returns a FloatRect to provide more accuracy during the
     // transformation. We then round upon constructing 'frame'.
     Rect frame{t.transform(computeBounds(activeTransparentRegion))};
-    if (!s.finalCrop.isEmpty()) {
-        if (!frame.intersect(s.finalCrop, &frame)) {
-            frame.clear();
-        }
-    }
-    if (!frame.intersect(displayDevice->getViewport(), &frame)) {
+    if (!frame.intersect(display->getViewport(), &frame)) {
         frame.clear();
     }
-    const Transform& tr(displayDevice->getTransform());
+    const ui::Transform& tr = display->getTransform();
     Rect transformedFrame = tr.transform(frame);
     error = hwcLayer->setDisplayFrame(transformedFrame);
     if (error != HWC2::Error::None) {
@@ -562,8 +566,9 @@
     } else {
         hwcInfo.displayFrame = transformedFrame;
     }
+    getBE().compositionInfo.hwc.displayFrame = transformedFrame;
 
-    FloatRect sourceCrop = computeCrop(displayDevice);
+    FloatRect sourceCrop = computeCrop(display);
     error = hwcLayer->setSourceCrop(sourceCrop);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
@@ -573,6 +578,7 @@
     } else {
         hwcInfo.sourceCrop = sourceCrop;
     }
+    getBE().compositionInfo.hwc.sourceCrop = sourceCrop;
 
     float alpha = static_cast<float>(getAlpha());
     error = hwcLayer->setPlaneAlpha(alpha);
@@ -580,19 +586,23 @@
              "[%s] Failed to set plane alpha %.3f: "
              "%s (%d)",
              mName.string(), alpha, to_string(error).c_str(), static_cast<int32_t>(error));
+    getBE().compositionInfo.hwc.alpha = alpha;
 
     error = hwcLayer->setZOrder(z);
     ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)", mName.string(), z,
              to_string(error).c_str(), static_cast<int32_t>(error));
+    getBE().compositionInfo.hwc.z = z;
 
-    int type = s.type;
-    int appId = s.appId;
+    int type = s.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+    int appId = s.metadata.getInt32(METADATA_OWNER_UID, 0);
     sp<Layer> parent = mDrawingParent.promote();
     if (parent.get()) {
         auto& parentState = parent->getDrawingState();
-        if (parentState.type >= 0 || parentState.appId >= 0) {
-            type = parentState.type;
-            appId = parentState.appId;
+        const int parentType = parentState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+        const int parentAppId = parentState.metadata.getInt32(METADATA_OWNER_UID, 0);
+        if (parentType >= 0 || parentAppId >= 0) {
+            type = parentType;
+            appId = parentAppId;
         }
     }
 
@@ -600,6 +610,9 @@
     ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set info (%d)", mName.string(),
              static_cast<int32_t>(error));
 
+    getBE().compositionInfo.hwc.type = type;
+    getBE().compositionInfo.hwc.appId = appId;
+
     /*
      * Transformations are applied in this order:
      * 1) buffer orientation/flip/mirror
@@ -608,8 +621,8 @@
      * (NOTE: the matrices are multiplied in reverse order)
      */
 
-    const Transform bufferOrientation(mCurrentTransform);
-    Transform transform(tr * t * bufferOrientation);
+    const ui::Transform bufferOrientation(mCurrentTransform);
+    ui::Transform transform(tr * t * bufferOrientation);
 
     if (getTransformToDisplayInverse()) {
         /*
@@ -626,16 +639,17 @@
          * Here we cancel out the orientation component of the WM transform.
          * The scaling and translate components are already included in our bounds
          * computation so it's enough to just omit it in the composition.
-         * See comment in onDraw with ref to b/36727915 for why.
+         * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why.
          */
-        transform = Transform(invTransform) * tr * bufferOrientation;
+        transform = ui::Transform(invTransform) * tr * bufferOrientation;
     }
 
     // this gives us only the "orientation" component of the transform
     const uint32_t orientation = transform.getOrientation();
-    if (orientation & Transform::ROT_INVALID) {
+    if (orientation & ui::Transform::ROT_INVALID) {
         // we can only handle simple transformation
         hwcInfo.forceClientComposition = true;
+        getBE().mHwcLayers[*displayId].compositionType = HWC2::Composition::Client;
     } else {
         auto transform = static_cast<HWC2::Transform>(orientation);
         hwcInfo.transform = transform;
@@ -645,31 +659,24 @@
                  "%s (%d)",
                  mName.string(), to_string(transform).c_str(), to_string(error).c_str(),
                  static_cast<int32_t>(error));
+        getBE().compositionInfo.hwc.transform = transform;
     }
 }
 
-void Layer::forceClientComposition(int32_t hwcId) {
-    if (getBE().mHwcLayers.count(hwcId) == 0) {
-        ALOGE("forceClientComposition: no HWC layer found (%d)", hwcId);
-        return;
-    }
-
-    getBE().mHwcLayers[hwcId].forceClientComposition = true;
+void Layer::forceClientComposition(DisplayId displayId) {
+    RETURN_IF_NO_HWC_LAYER(displayId);
+    getBE().mHwcLayers[displayId].forceClientComposition = true;
 }
 
-bool Layer::getForceClientComposition(int32_t hwcId) {
-    if (getBE().mHwcLayers.count(hwcId) == 0) {
-        ALOGE("getForceClientComposition: no HWC layer found (%d)", hwcId);
-        return false;
-    }
-
-    return getBE().mHwcLayers[hwcId].forceClientComposition;
+bool Layer::getForceClientComposition(DisplayId displayId) {
+    RETURN_IF_NO_HWC_LAYER(displayId, false);
+    return getBE().mHwcLayers[displayId].forceClientComposition;
 }
 
-void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) {
-    auto hwcId = displayDevice->getHwcDisplayId();
-    if (getBE().mHwcLayers.count(hwcId) == 0 ||
-        getCompositionType(hwcId) != HWC2::Composition::Cursor) {
+void Layer::updateCursorPosition(const sp<const DisplayDevice>& display) {
+    const auto displayId = display->getId();
+    LOG_ALWAYS_FATAL_IF(!displayId);
+    if (!hasHwcLayer(*displayId) || getCompositionType(displayId) != HWC2::Composition::Cursor) {
         return;
     }
 
@@ -678,22 +685,17 @@
 
     // Apply the layer's transform, followed by the display's global transform
     // Here we're guaranteed that the layer's transform preserves rects
-    Rect win(s.active.w, s.active.h);
-    if (!s.crop.isEmpty()) {
-        win.intersect(s.crop, &win);
-    }
+    Rect win = getCroppedBufferSize(s);
     // Subtract the transparent region and snap to the bounds
-    Rect bounds = reduce(win, s.activeTransparentRegion);
+    Rect bounds = reduce(win, getActiveTransparentRegion(s));
     Rect frame(getTransform().transform(bounds));
-    frame.intersect(displayDevice->getViewport(), &frame);
-    if (!s.finalCrop.isEmpty()) {
-        frame.intersect(s.finalCrop, &frame);
-    }
-    auto& displayTransform(displayDevice->getTransform());
+    frame.intersect(display->getViewport(), &frame);
+    auto& displayTransform = display->getTransform();
     auto position = displayTransform.transform(frame);
 
-    auto error = getBE().mHwcLayers[hwcId].layer->setCursorPosition(position.left,
-                                                                              position.top);
+    auto error =
+            getBE().mHwcLayers[*displayId].layer->setCursorPosition(position.left, position.top);
+
     ALOGE_IF(error != HWC2::Error::None,
              "[%s] Failed to set cursor position "
              "to (%d, %d): %s (%d)",
@@ -705,16 +707,51 @@
 // drawing...
 // ---------------------------------------------------------------------------
 
-void Layer::draw(const RenderArea& renderArea, const Region& clip) const {
-    onDraw(renderArea, clip, false);
+bool Layer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                               Region& clearRegion, renderengine::LayerSettings& layer) {
+    return prepareClientLayer(renderArea, clip, false, clearRegion, layer);
 }
 
-void Layer::draw(const RenderArea& renderArea, bool useIdentityTransform) const {
-    onDraw(renderArea, Region(renderArea.getBounds()), useIdentityTransform);
+bool Layer::prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
+                               Region& clearRegion, renderengine::LayerSettings& layer) {
+    return prepareClientLayer(renderArea, Region(renderArea.getBounds()), useIdentityTransform,
+                              clearRegion, layer);
 }
 
-void Layer::draw(const RenderArea& renderArea) const {
-    onDraw(renderArea, Region(renderArea.getBounds()), false);
+bool Layer::prepareClientLayer(const RenderArea& /*renderArea*/, const Region& /*clip*/,
+                               bool useIdentityTransform, Region& /*clearRegion*/,
+                               renderengine::LayerSettings& layer) {
+    FloatRect bounds = computeBounds();
+    half alpha = getAlpha();
+    layer.geometry.boundaries = bounds;
+    if (useIdentityTransform) {
+        layer.geometry.positionTransform = mat4();
+    } else {
+        const ui::Transform transform = getTransform();
+        mat4 m;
+        m[0][0] = transform[0][0];
+        m[0][1] = transform[0][1];
+        m[0][3] = transform[0][2];
+        m[1][0] = transform[1][0];
+        m[1][1] = transform[1][1];
+        m[1][3] = transform[1][2];
+        m[3][0] = transform[2][0];
+        m[3][1] = transform[2][1];
+        m[3][3] = transform[2][2];
+        layer.geometry.positionTransform = m;
+    }
+
+    if (hasColorTransform()) {
+        layer.colorTransform = getColorTransform();
+    }
+
+    const auto roundedCornerState = getRoundedCornerState();
+    layer.geometry.roundedCornersRadius = roundedCornerState.radius;
+    layer.geometry.roundedCornersCrop = roundedCornerState.cropRect;
+
+    layer.alpha = alpha;
+    layer.sourceDataspace = mCurrentDataSpace;
+    return true;
 }
 
 void Layer::clearWithOpenGL(const RenderArea& renderArea, float red, float green, float blue,
@@ -729,20 +766,20 @@
     clearWithOpenGL(renderArea, 0, 0, 0, 0);
 }
 
-void Layer::setCompositionType(int32_t hwcId, HWC2::Composition type, bool callIntoHwc) {
-    if (getBE().mHwcLayers.count(hwcId) == 0) {
+void Layer::setCompositionType(DisplayId displayId, HWC2::Composition type, bool callIntoHwc) {
+    if (getBE().mHwcLayers.count(displayId) == 0) {
         ALOGE("setCompositionType called without a valid HWC layer");
         return;
     }
-    auto& hwcInfo = getBE().mHwcLayers[hwcId];
+    auto& hwcInfo = getBE().mHwcLayers[displayId];
     auto& hwcLayer = hwcInfo.layer;
-    ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", hwcLayer->getId(), to_string(type).c_str(),
+    ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", (hwcLayer)->getId(), to_string(type).c_str(),
           static_cast<int>(callIntoHwc));
     if (hwcInfo.compositionType != type) {
         ALOGV("    actually setting");
         hwcInfo.compositionType = type;
         if (callIntoHwc) {
-            auto error = hwcLayer->setCompositionType(type);
+            auto error = (hwcLayer)->setCompositionType(type);
             ALOGE_IF(error != HWC2::Error::None,
                      "[%s] Failed to set "
                      "composition type %s: %s (%d)",
@@ -752,33 +789,33 @@
     }
 }
 
-HWC2::Composition Layer::getCompositionType(int32_t hwcId) const {
-    if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) {
+HWC2::Composition Layer::getCompositionType(const std::optional<DisplayId>& displayId) const {
+    if (!displayId) {
         // If we're querying the composition type for a display that does not
         // have a HWC counterpart, then it will always be Client
         return HWC2::Composition::Client;
     }
-    if (getBE().mHwcLayers.count(hwcId) == 0) {
+    if (getBE().mHwcLayers.count(*displayId) == 0) {
         ALOGE("getCompositionType called with an invalid HWC layer");
         return HWC2::Composition::Invalid;
     }
-    return getBE().mHwcLayers.at(hwcId).compositionType;
+    return getBE().mHwcLayers.at(*displayId).compositionType;
 }
 
-void Layer::setClearClientTarget(int32_t hwcId, bool clear) {
-    if (getBE().mHwcLayers.count(hwcId) == 0) {
+void Layer::setClearClientTarget(DisplayId displayId, bool clear) {
+    if (getBE().mHwcLayers.count(displayId) == 0) {
         ALOGE("setClearClientTarget called without a valid HWC layer");
         return;
     }
-    getBE().mHwcLayers[hwcId].clearClientTarget = clear;
+    getBE().mHwcLayers[displayId].clearClientTarget = clear;
 }
 
-bool Layer::getClearClientTarget(int32_t hwcId) const {
-    if (getBE().mHwcLayers.count(hwcId) == 0) {
+bool Layer::getClearClientTarget(DisplayId displayId) const {
+    if (getBE().mHwcLayers.count(displayId) == 0) {
         ALOGE("getClearClientTarget called without a valid HWC layer");
         return false;
     }
-    return getBE().mHwcLayers.at(hwcId).clearClientTarget;
+    return getBE().mHwcLayers.at(displayId).clearClientTarget;
 }
 
 bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) {
@@ -787,44 +824,23 @@
         // relevant frame
         return false;
     }
+    if (isRemovedFromCurrentState()) {
+        return false;
+    }
 
     Mutex::Autolock lock(mLocalSyncPointMutex);
     mLocalSyncPoints.push_back(point);
     return true;
 }
 
-void Layer::setFiltering(bool filtering) {
-    mFiltering = filtering;
-}
-
-bool Layer::getFiltering() const {
-    return mFiltering;
-}
-
 // ----------------------------------------------------------------------------
 // local state
 // ----------------------------------------------------------------------------
 
-static void boundPoint(vec2* point, const Rect& crop) {
-    if (point->x < crop.left) {
-        point->x = crop.left;
-    }
-    if (point->x > crop.right) {
-        point->x = crop.right;
-    }
-    if (point->y < crop.top) {
-        point->y = crop.top;
-    }
-    if (point->y > crop.bottom) {
-        point->y = crop.bottom;
-    }
-}
-
-void Layer::computeGeometry(const RenderArea& renderArea, Mesh& mesh,
+void Layer::computeGeometry(const RenderArea& renderArea,
+                            renderengine::Mesh& mesh,
                             bool useIdentityTransform) const {
-    const Layer::State& s(getDrawingState());
-    const Transform renderAreaTransform(renderArea.getTransform());
-    const uint32_t height = renderArea.getHeight();
+    const ui::Transform renderAreaTransform(renderArea.getTransform());
     FloatRect win = computeBounds();
 
     vec2 lt = vec2(win.left, win.top);
@@ -832,7 +848,7 @@
     vec2 rb = vec2(win.right, win.bottom);
     vec2 rt = vec2(win.right, win.top);
 
-    Transform layerTransform = getTransform();
+    ui::Transform layerTransform = getTransform();
     if (!useIdentityTransform) {
         lt = layerTransform.transform(lt);
         lb = layerTransform.transform(lb);
@@ -840,25 +856,15 @@
         rt = layerTransform.transform(rt);
     }
 
-    if (!s.finalCrop.isEmpty()) {
-        boundPoint(&lt, s.finalCrop);
-        boundPoint(&lb, s.finalCrop);
-        boundPoint(&rb, s.finalCrop);
-        boundPoint(&rt, s.finalCrop);
-    }
-
-    Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+    renderengine::Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
     position[0] = renderAreaTransform.transform(lt);
     position[1] = renderAreaTransform.transform(lb);
     position[2] = renderAreaTransform.transform(rb);
     position[3] = renderAreaTransform.transform(rt);
-    for (size_t i = 0; i < 4; i++) {
-        position[i].y = height - position[i].y;
-    }
 }
 
 bool Layer::isSecure() const {
-    const Layer::State& s(mDrawingState);
+    const State& s(mDrawingState);
     return (s.flags & layer_state_t::eLayerSecure);
 }
 
@@ -894,22 +900,24 @@
 
     // If this transaction is waiting on the receipt of a frame, generate a sync
     // point and send it to the remote layer.
-    if (mCurrentState.barrierLayer != nullptr) {
-        sp<Layer> barrierLayer = mCurrentState.barrierLayer.promote();
+    // We don't allow installing sync points after we are removed from the current state
+    // as we won't be able to signal our end.
+    if (mCurrentState.barrierLayer_legacy != nullptr && !isRemovedFromCurrentState()) {
+        sp<Layer> barrierLayer = mCurrentState.barrierLayer_legacy.promote();
         if (barrierLayer == nullptr) {
             ALOGE("[%s] Unable to promote barrier Layer.", mName.string());
             // If we can't promote the layer we are intended to wait on,
             // then it is expired or otherwise invalid. Allow this transaction
             // to be applied as per normal (no synchronization).
-            mCurrentState.barrierLayer = nullptr;
+            mCurrentState.barrierLayer_legacy = nullptr;
         } else {
-            auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber);
+            auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber_legacy);
             if (barrierLayer->addSyncPoint(syncPoint)) {
                 mRemoteSyncPoints.push_back(std::move(syncPoint));
             } else {
                 // We already missed the frame we're supposed to synchronize
                 // on, so go ahead and apply the state update
-                mCurrentState.barrierLayer = nullptr;
+                mCurrentState.barrierLayer_legacy = nullptr;
             }
         }
 
@@ -931,7 +939,7 @@
 bool Layer::applyPendingStates(State* stateToCommit) {
     bool stateUpdateAvailable = false;
     while (!mPendingStates.empty()) {
-        if (mPendingStates[0].barrierLayer != nullptr) {
+        if (mPendingStates[0].barrierLayer_legacy != nullptr) {
             if (mRemoteSyncPoints.empty()) {
                 // If we don't have a sync point for this, apply it anyway. It
                 // will be visually wrong, but it should keep us from getting
@@ -942,7 +950,8 @@
                 continue;
             }
 
-            if (mRemoteSyncPoints.front()->getFrameNumber() != mPendingStates[0].frameNumber) {
+            if (mRemoteSyncPoints.front()->getFrameNumber() !=
+                mPendingStates[0].frameNumber_legacy) {
                 ALOGE("[%s] Unexpected sync point frame number found", mName.string());
 
                 // Signal our end of the sync point and then dispose of it
@@ -979,18 +988,11 @@
     return stateUpdateAvailable;
 }
 
-uint32_t Layer::doTransaction(uint32_t flags) {
-    ATRACE_CALL();
+uint32_t Layer::doTransactionResize(uint32_t flags, State* stateToCommit) {
+    const State& s(getDrawingState());
 
-    pushPendingState();
-    Layer::State c = getCurrentState();
-    if (!applyPendingStates(&c)) {
-        return 0;
-    }
-
-    const Layer::State& s(getDrawingState());
-
-    const bool sizeChanged = (c.requested.w != s.requested.w) || (c.requested.h != s.requested.h);
+    const bool sizeChanged = (stateToCommit->requested_legacy.w != s.requested_legacy.w) ||
+            (stateToCommit->requested_legacy.h != s.requested_legacy.h);
 
     if (sizeChanged) {
         // the size changed, we need to ask our client to request a new buffer
@@ -1000,16 +1002,15 @@
                  "            requested={ wh={%4u,%4u} }}\n"
                  "  drawing={ active   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n"
                  "            requested={ wh={%4u,%4u} }}\n",
-                 this, getName().string(), mCurrentTransform,
-                 getEffectiveScalingMode(), c.active.w, c.active.h, c.crop.left, c.crop.top,
-                 c.crop.right, c.crop.bottom, c.crop.getWidth(), c.crop.getHeight(), c.requested.w,
-                 c.requested.h, s.active.w, s.active.h, s.crop.left, s.crop.top, s.crop.right,
-                 s.crop.bottom, s.crop.getWidth(), s.crop.getHeight(), s.requested.w,
-                 s.requested.h);
-
-        // record the new size, form this point on, when the client request
-        // a buffer, it'll get the new size.
-        setDefaultBufferSize(c.requested.w, c.requested.h);
+                 this, getName().string(), mCurrentTransform, getEffectiveScalingMode(),
+                 stateToCommit->active_legacy.w, stateToCommit->active_legacy.h,
+                 stateToCommit->crop_legacy.left, stateToCommit->crop_legacy.top,
+                 stateToCommit->crop_legacy.right, stateToCommit->crop_legacy.bottom,
+                 stateToCommit->crop_legacy.getWidth(), stateToCommit->crop_legacy.getHeight(),
+                 stateToCommit->requested_legacy.w, stateToCommit->requested_legacy.h,
+                 s.active_legacy.w, s.active_legacy.h, s.crop_legacy.left, s.crop_legacy.top,
+                 s.crop_legacy.right, s.crop_legacy.bottom, s.crop_legacy.getWidth(),
+                 s.crop_legacy.getHeight(), s.requested_legacy.w, s.requested_legacy.h);
     }
 
     // Don't let Layer::doTransaction update the drawing state
@@ -1030,7 +1031,9 @@
     // resizePending state is to avoid applying the state of the new buffer
     // to the old buffer. However in the state where we don't have an old buffer
     // there is no such concern but we may still be being used as a parent layer.
-    const bool resizePending = ((c.requested.w != c.active.w) || (c.requested.h != c.active.h)) &&
+    const bool resizePending =
+            ((stateToCommit->requested_legacy.w != stateToCommit->active_legacy.w) ||
+             (stateToCommit->requested_legacy.h != stateToCommit->active_legacy.h)) &&
             (getBE().compositionInfo.mBuffer != nullptr);
     if (!isFixedSize()) {
         if (resizePending && getBE().compositionInfo.hwc.sidebandStream == nullptr) {
@@ -1042,7 +1045,7 @@
     // latching configuration. See Layer.h for a detailed discussion of
     // how geometry latching is controlled.
     if (!(flags & eDontUpdateGeometryState)) {
-        Layer::State& editCurrentState(getCurrentState());
+        State& editCurrentState(getCurrentState());
 
         // If mFreezeGeometryUpdates is true we are in the setGeometryAppliesWithResize
         // mode, which causes attributes which normally latch regardless of scaling mode,
@@ -1054,21 +1057,41 @@
         // being stored in the same data structure while having different latching rules.
         // b/38182305
         //
-        // Careful that "c" and editCurrentState may not begin as equivalent due to
+        // Careful that "stateToCommit" and editCurrentState may not begin as equivalent due to
         // applyPendingStates in the presence of deferred transactions.
         if (mFreezeGeometryUpdates) {
-            float tx = c.active.transform.tx();
-            float ty = c.active.transform.ty();
-            c.active = c.requested;
-            c.active.transform.set(tx, ty);
-            editCurrentState.active = c.active;
+            float tx = stateToCommit->active_legacy.transform.tx();
+            float ty = stateToCommit->active_legacy.transform.ty();
+            stateToCommit->active_legacy = stateToCommit->requested_legacy;
+            stateToCommit->active_legacy.transform.set(tx, ty);
+            editCurrentState.active_legacy = stateToCommit->active_legacy;
         } else {
-            editCurrentState.active = editCurrentState.requested;
-            c.active = c.requested;
+            editCurrentState.active_legacy = editCurrentState.requested_legacy;
+            stateToCommit->active_legacy = stateToCommit->requested_legacy;
         }
     }
 
-    if (s.active != c.active) {
+    return flags;
+}
+
+uint32_t Layer::doTransaction(uint32_t flags) {
+    ATRACE_CALL();
+
+    if (mLayerDetached) {
+        return 0;
+    }
+
+    pushPendingState();
+    State c = getCurrentState();
+    if (!applyPendingStates(&c)) {
+        return 0;
+    }
+
+    flags = doTransactionResize(flags, &c);
+
+    const State& s(getDrawingState());
+
+    if (getActiveGeometry(c) != getActiveGeometry(s)) {
         // invalidate and recompute the visible regions if needed
         flags |= Layer::eVisibleRegion;
     }
@@ -1079,8 +1102,8 @@
         this->contentDirty = true;
 
         // we may use linear filtering, if the matrix scales us
-        const uint8_t type = c.active.transform.getType();
-        mNeedsFiltering = (!c.active.transform.preserveRects() || (type >= Transform::SCALE));
+        const uint8_t type = getActiveTransform(c).getType();
+        mNeedsFiltering = (!getActiveTransform(c).preserveRects() || type >= ui::Transform::SCALE);
     }
 
     // If the layer is hidden, signal and clear out all local sync points so
@@ -1090,8 +1113,14 @@
         clearSyncPoints();
     }
 
+    if (mCurrentState.inputInfoChanged) {
+        flags |= eInputInfoChanged;
+        mCurrentState.inputInfoChanged = false;
+    }
+
     // Commit the transaction
     commitTransaction(c);
+    mCurrentState.callbackHandles = {};
     return flags;
 }
 
@@ -1100,28 +1129,29 @@
 }
 
 uint32_t Layer::getTransactionFlags(uint32_t flags) {
-    return android_atomic_and(~flags, &mTransactionFlags) & flags;
+    return mTransactionFlags.fetch_and(~flags) & flags;
 }
 
 uint32_t Layer::setTransactionFlags(uint32_t flags) {
-    return android_atomic_or(flags, &mTransactionFlags);
+    return mTransactionFlags.fetch_or(flags);
 }
 
 bool Layer::setPosition(float x, float y, bool immediate) {
-    if (mCurrentState.requested.transform.tx() == x && mCurrentState.requested.transform.ty() == y)
+    if (mCurrentState.requested_legacy.transform.tx() == x &&
+        mCurrentState.requested_legacy.transform.ty() == y)
         return false;
     mCurrentState.sequence++;
 
     // We update the requested and active position simultaneously because
     // we want to apply the position portion of the transform matrix immediately,
     // but still delay scaling when resizing a SCALING_MODE_FREEZE layer.
-    mCurrentState.requested.transform.set(x, y);
+    mCurrentState.requested_legacy.transform.set(x, y);
     if (immediate && !mFreezeGeometryUpdates) {
         // Here we directly update the active state
         // unlike other setters, because we store it within
         // the transform, but use different latching rules.
         // b/38182305
-        mCurrentState.active.transform.set(x, y);
+        mCurrentState.active_legacy.transform.set(x, y);
     }
     mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
 
@@ -1200,7 +1230,7 @@
     }
 
     if (mCurrentState.z == relativeZ && usingRelativeZ(LayerVector::StateSet::Current) &&
-            mCurrentState.zOrderRelativeOf == relative) {
+        mCurrentState.zOrderRelativeOf == relative) {
         return false;
     }
 
@@ -1221,11 +1251,16 @@
 }
 
 bool Layer::setSize(uint32_t w, uint32_t h) {
-    if (mCurrentState.requested.w == w && mCurrentState.requested.h == h) return false;
-    mCurrentState.requested.w = w;
-    mCurrentState.requested.h = h;
+    if (mCurrentState.requested_legacy.w == w && mCurrentState.requested_legacy.h == h)
+        return false;
+    mCurrentState.requested_legacy.w = w;
+    mCurrentState.requested_legacy.h = h;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
+
+    // record the new size, from this point on, when the client request
+    // a buffer, it'll get the new size.
+    setDefaultBufferSize(mCurrentState.requested_legacy.w, mCurrentState.requested_legacy.h);
     return true;
 }
 bool Layer::setAlpha(float alpha) {
@@ -1251,9 +1286,19 @@
     return true;
 }
 
+bool Layer::setCornerRadius(float cornerRadius) {
+    if (mCurrentState.cornerRadius == cornerRadius) return false;
+
+    mCurrentState.sequence++;
+    mCurrentState.cornerRadius = cornerRadius;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
         bool allowNonRectPreservingTransforms) {
-    Transform t;
+    ui::Transform t;
     t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
 
     if (!allowNonRectPreservingTransforms && !t.preserveRects()) {
@@ -1261,13 +1306,15 @@
         return false;
     }
     mCurrentState.sequence++;
-    mCurrentState.requested.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+    mCurrentState.requested_legacy.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx,
+                                                 matrix.dsdy);
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
+
 bool Layer::setTransparentRegionHint(const Region& transparent) {
-    mCurrentState.requestedTransparentRegion = transparent;
+    mCurrentState.requestedTransparentRegion_legacy = transparent;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -1282,26 +1329,12 @@
     return true;
 }
 
-bool Layer::setCrop(const Rect& crop, bool immediate) {
-    if (mCurrentState.requestedCrop == crop) return false;
+bool Layer::setCrop_legacy(const Rect& crop, bool immediate) {
+    if (mCurrentState.requestedCrop_legacy == crop) return false;
     mCurrentState.sequence++;
-    mCurrentState.requestedCrop = crop;
+    mCurrentState.requestedCrop_legacy = crop;
     if (immediate && !mFreezeGeometryUpdates) {
-        mCurrentState.crop = crop;
-    }
-    mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
-
-    mCurrentState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool Layer::setFinalCrop(const Rect& crop, bool immediate) {
-    if (mCurrentState.requestedFinalCrop == crop) return false;
-    mCurrentState.sequence++;
-    mCurrentState.requestedFinalCrop = crop;
-    if (immediate && !mFreezeGeometryUpdates) {
-        mCurrentState.finalCrop = crop;
+        mCurrentState.crop_legacy = crop;
     }
     mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
 
@@ -1317,11 +1350,14 @@
     return true;
 }
 
-void Layer::setInfo(int32_t type, int32_t appId) {
-    mCurrentState.appId = appId;
-    mCurrentState.type = type;
+bool Layer::setMetadata(LayerMetadata data) {
+    bool changed = data.mMap != mCurrentState.metadata.mMap;
+    if (!changed) return false;
+    mCurrentState.metadata = std::move(data);
+    mCurrentState.sequence++;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
+    return true;
 }
 
 bool Layer::setLayerStack(uint32_t layerStack) {
@@ -1341,30 +1377,29 @@
     return p->getLayerStack();
 }
 
-void Layer::deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
-    mCurrentState.barrierLayer = barrierLayer;
-    mCurrentState.frameNumber = frameNumber;
+void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
+    mCurrentState.barrierLayer_legacy = barrierLayer;
+    mCurrentState.frameNumber_legacy = frameNumber;
     // We don't set eTransactionNeeded, because just receiving a deferral
     // request without any other state updates shouldn't actually induce a delay
     mCurrentState.modified = true;
     pushPendingState();
-    mCurrentState.barrierLayer = nullptr;
-    mCurrentState.frameNumber = 0;
+    mCurrentState.barrierLayer_legacy = nullptr;
+    mCurrentState.frameNumber_legacy = 0;
     mCurrentState.modified = false;
 }
 
-void Layer::deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber) {
+void Layer::deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle, uint64_t frameNumber) {
     sp<Handle> handle = static_cast<Handle*>(barrierHandle.get());
-    deferTransactionUntil(handle->owner.promote(), frameNumber);
+    deferTransactionUntil_legacy(handle->owner.promote(), frameNumber);
 }
 
-
 // ----------------------------------------------------------------------------
 // pageflip handling...
 // ----------------------------------------------------------------------------
 
 bool Layer::isHiddenByPolicy() const {
-    const Layer::State& s(mDrawingState);
+    const State& s(mDrawingState);
     const auto& parent = mDrawingParent.promote();
     if (parent != nullptr && parent->isHiddenByPolicy()) {
         return true;
@@ -1385,15 +1420,18 @@
     return usage;
 }
 
-void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const {
+void Layer::updateTransformHint(const sp<const DisplayDevice>& display) const {
     uint32_t orientation = 0;
-    if (!mFlinger->mDebugDisableTransformHint) {
+    // Disable setting transform hint if the debug flag is set or if the
+    // getTransformToDisplayInverse flag is set and the client wants to submit buffers
+    // in one orientation.
+    if (!mFlinger->mDebugDisableTransformHint && !getTransformToDisplayInverse()) {
         // The transform hint is used to improve performance, but we can
         // only have a single transform hint, it cannot
         // apply to all displays.
-        const Transform& planeTransform(hw->getTransform());
+        const ui::Transform& planeTransform = display->getTransform();
         orientation = planeTransform.getOrientation();
-        if (orientation & Transform::ROT_INVALID) {
+        if (orientation & ui::Transform::ROT_INVALID) {
             orientation = 0;
         }
     }
@@ -1404,34 +1442,34 @@
 // debugging
 // ----------------------------------------------------------------------------
 
+// TODO(marissaw): add new layer state info to layer debugging
 LayerDebugInfo Layer::getLayerDebugInfo() const {
     LayerDebugInfo info;
-    const Layer::State& ds = getDrawingState();
+    const State& ds = getDrawingState();
     info.mName = getName();
     sp<Layer> parent = getParent();
     info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string());
-    info.mType = String8(getTypeId());
-    info.mTransparentRegion = ds.activeTransparentRegion;
+    info.mType = std::string(getTypeId());
+    info.mTransparentRegion = ds.activeTransparentRegion_legacy;
     info.mVisibleRegion = visibleRegion;
     info.mSurfaceDamageRegion = surfaceDamageRegion;
     info.mLayerStack = getLayerStack();
-    info.mX = ds.active.transform.tx();
-    info.mY = ds.active.transform.ty();
+    info.mX = ds.active_legacy.transform.tx();
+    info.mY = ds.active_legacy.transform.ty();
     info.mZ = ds.z;
-    info.mWidth = ds.active.w;
-    info.mHeight = ds.active.h;
-    info.mCrop = ds.crop;
-    info.mFinalCrop = ds.finalCrop;
+    info.mWidth = ds.active_legacy.w;
+    info.mHeight = ds.active_legacy.h;
+    info.mCrop = ds.crop_legacy;
     info.mColor = ds.color;
     info.mFlags = ds.flags;
     info.mPixelFormat = getPixelFormat();
     info.mDataSpace = static_cast<android_dataspace>(mCurrentDataSpace);
-    info.mMatrix[0][0] = ds.active.transform[0][0];
-    info.mMatrix[0][1] = ds.active.transform[0][1];
-    info.mMatrix[1][0] = ds.active.transform[1][0];
-    info.mMatrix[1][1] = ds.active.transform[1][1];
+    info.mMatrix[0][0] = ds.active_legacy.transform[0][0];
+    info.mMatrix[0][1] = ds.active_legacy.transform[0][1];
+    info.mMatrix[1][0] = ds.active_legacy.transform[1][0];
+    info.mMatrix[1][1] = ds.active_legacy.transform[1][1];
     {
-        sp<const GraphicBuffer> buffer = getBE().compositionInfo.mBuffer;
+        sp<const GraphicBuffer> buffer = mActiveBuffer;
         if (buffer != 0) {
             info.mActiveBufferWidth = buffer->getWidth();
             info.mActiveBufferHeight = buffer->getHeight();
@@ -1451,54 +1489,66 @@
     return info;
 }
 
-void Layer::miniDumpHeader(String8& result) {
-    result.append("----------------------------------------");
-    result.append("---------------------------------------\n");
+void Layer::miniDumpHeader(std::string& result) {
+    result.append("-------------------------------");
+    result.append("-------------------------------");
+    result.append("-----------------------------\n");
     result.append(" Layer name\n");
     result.append("           Z | ");
     result.append(" Comp Type | ");
+    result.append(" Transform | ");
     result.append("  Disp Frame (LTRB) | ");
     result.append("         Source Crop (LTRB)\n");
-    result.append("----------------------------------------");
-    result.append("---------------------------------------\n");
+    result.append("-------------------------------");
+    result.append("-------------------------------");
+    result.append("-----------------------------\n");
 }
 
-void Layer::miniDump(String8& result, int32_t hwcId) const {
-    if (getBE().mHwcLayers.count(hwcId) == 0) {
+void Layer::miniDump(std::string& result, DisplayId displayId) const {
+    if (!hasHwcLayer(displayId)) {
         return;
     }
 
-    String8 name;
+    std::string name;
     if (mName.length() > 77) {
         std::string shortened;
         shortened.append(mName.string(), 36);
         shortened.append("[...]");
         shortened.append(mName.string() + (mName.length() - 36), 36);
-        name = shortened.c_str();
+        name = shortened;
     } else {
-        name = mName;
+        name = std::string(mName.string(), mName.size());
     }
 
-    result.appendFormat(" %s\n", name.string());
+    StringAppendF(&result, " %s\n", name.c_str());
 
-    const Layer::State& layerState(getDrawingState());
-    const LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers.at(hwcId);
+    const State& layerState(getDrawingState());
+    const LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers.at(displayId);
     if (layerState.zOrderRelativeOf != nullptr || mDrawingParent != nullptr) {
-        result.appendFormat("  rel %6d | ", layerState.z);
+        StringAppendF(&result, "  rel %6d | ", layerState.z);
     } else {
-        result.appendFormat("  %10d | ", layerState.z);
+        StringAppendF(&result, "  %10d | ", layerState.z);
     }
-    result.appendFormat("%10s | ", to_string(getCompositionType(hwcId)).c_str());
+    StringAppendF(&result, "%10s | ", to_string(getCompositionType(displayId)).c_str());
+    StringAppendF(&result, "%10s | ", to_string(hwcInfo.transform).c_str());
     const Rect& frame = hwcInfo.displayFrame;
-    result.appendFormat("%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
+    StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
     const FloatRect& crop = hwcInfo.sourceCrop;
-    result.appendFormat("%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top, crop.right, crop.bottom);
+    StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top, crop.right,
+                  crop.bottom);
 
-    result.append("- - - - - - - - - - - - - - - - - - - - ");
-    result.append("- - - - - - - - - - - - - - - - - - - -\n");
+    result.append("- - - - - - - - - - - - - - - -\n");
+
+    std::string compositionInfoStr;
+    getBE().compositionInfo.dump(compositionInfoStr, "compositionInfo");
+    result.append(compositionInfoStr);
+
+    result.append("- - - - - - - - - - - - - - - -");
+    result.append("- - - - - - - - - - - - - - - -");
+    result.append("- - - - - - - - - - - - - - -\n");
 }
 
-void Layer::dumpFrameStats(String8& result) const {
+void Layer::dumpFrameStats(std::string& result) const {
     mFrameTracker.dumpStats(result);
 }
 
@@ -1514,8 +1564,8 @@
     mFrameTracker.getStats(outStats);
 }
 
-void Layer::dumpFrameEvents(String8& result) {
-    result.appendFormat("- Layer %s (%s, %p)\n", getName().string(), getTypeId(), this);
+void Layer::dumpFrameEvents(std::string& result) {
+    StringAppendF(&result, "- Layer %s (%s, %p)\n", getName().string(), getTypeId(), this);
     Mutex::Autolock lock(mFrameEventHistoryMutex);
     mFrameEventHistory.checkFencesForCompletion();
     mFrameEventHistory.dump(result);
@@ -1524,14 +1574,14 @@
 void Layer::onDisconnect() {
     Mutex::Autolock lock(mFrameEventHistoryMutex);
     mFrameEventHistory.onDisconnect();
-    mTimeStats.onDisconnect(getName().c_str());
+    mFlinger->mTimeStats->onDestroy(getSequence());
 }
 
 void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
                                      FrameEventHistoryDelta* outDelta) {
     if (newTimestamps) {
-        mTimeStats.setPostTime(getName().c_str(), newTimestamps->frameNumber,
-                               newTimestamps->postedTime);
+        mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
+                                          getName().c_str(), newTimestamps->postedTime);
     }
 
     Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -1566,6 +1616,7 @@
 
 ssize_t Layer::removeChild(const sp<Layer>& layer) {
     layer->setParent(nullptr);
+
     return mCurrentChildren.remove(layer);
 }
 
@@ -1582,13 +1633,11 @@
         return false;
     }
 
+    if (attachChildren()) {
+        setTransactionFlags(eTransactionNeeded);
+    }
     for (const sp<Layer>& child : mCurrentChildren) {
         newParent->addChild(child);
-
-        sp<Client> client(child->mClientRef.promote());
-        if (client != nullptr) {
-            client->updateParent(newParent);
-        }
     }
     mCurrentChildren.clear();
 
@@ -1602,30 +1651,55 @@
 }
 
 bool Layer::reparent(const sp<IBinder>& newParentHandle) {
-    if (newParentHandle == nullptr) {
+    bool callSetTransactionFlags = false;
+
+    // While layers are detached, we allow most operations
+    // and simply halt performing the actual transaction. However
+    // for reparent != null we would enter the mRemovedFromCurrentState
+    // state, regardless of whether doTransaction was called, and
+    // so we need to prevent the update here.
+    if (mLayerDetached && newParentHandle == nullptr) {
         return false;
     }
 
-    auto handle = static_cast<Handle*>(newParentHandle.get());
-    sp<Layer> newParent = handle->owner.promote();
-    if (newParent == nullptr) {
-        ALOGE("Unable to promote Layer handle");
-        return false;
+    sp<Layer> newParent;
+    if (newParentHandle != nullptr) {
+        auto handle = static_cast<Handle*>(newParentHandle.get());
+        newParent = handle->owner.promote();
+        if (newParent == nullptr) {
+            ALOGE("Unable to promote Layer handle");
+            return false;
+        }
+        if (newParent == this) {
+            ALOGE("Invalid attempt to reparent Layer (%s) to itself", getName().c_str());
+            return false;
+        }
     }
 
     sp<Layer> parent = getParent();
     if (parent != nullptr) {
         parent->removeChild(this);
     }
-    newParent->addChild(this);
 
-    sp<Client> client(mClientRef.promote());
-    sp<Client> newParentClient(newParent->mClientRef.promote());
+    if (newParentHandle != nullptr) {
+        newParent->addChild(this);
+        if (!newParent->isRemovedFromCurrentState()) {
+            addToCurrentState();
+        } else {
+            onRemovedFromCurrentState();
+        }
 
-    if (client != newParentClient) {
-        client->updateParent(newParent);
+        if (mLayerDetached) {
+            mLayerDetached = false;
+            callSetTransactionFlags = true;
+        }
+    } else {
+        onRemovedFromCurrentState();
     }
 
+    if (callSetTransactionFlags || attachChildren()) {
+        setTransactionFlags(eTransactionNeeded);
+    }
     return true;
 }
 
@@ -1634,7 +1708,7 @@
         sp<Client> parentClient = mClientRef.promote();
         sp<Client> client(child->mClientRef.promote());
         if (client != nullptr && parentClient != client) {
-            client->detachLayer(child.get());
+            child->mLayerDetached = true;
             child->detachChildren();
         }
     }
@@ -1642,6 +1716,53 @@
     return true;
 }
 
+bool Layer::attachChildren() {
+    bool changed = false;
+    for (const sp<Layer>& child : mCurrentChildren) {
+        sp<Client> parentClient = mClientRef.promote();
+        sp<Client> client(child->mClientRef.promote());
+        if (client != nullptr && parentClient != client) {
+            if (child->mLayerDetached) {
+                child->mLayerDetached = false;
+                changed = true;
+            }
+            changed |= child->attachChildren();
+        }
+    }
+
+    return changed;
+}
+
+bool Layer::setColorTransform(const mat4& matrix) {
+    static const mat4 identityMatrix = mat4();
+
+    if (mCurrentState.colorTransform == matrix) {
+        return false;
+    }
+    ++mCurrentState.sequence;
+    mCurrentState.colorTransform = matrix;
+    mCurrentState.hasColorTransform = matrix != identityMatrix;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+mat4 Layer::getColorTransform() const {
+    mat4 colorTransform = mat4(getDrawingState().colorTransform);
+    if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
+        colorTransform = parent->getColorTransform() * colorTransform;
+    }
+    return colorTransform;
+}
+
+bool Layer::hasColorTransform() const {
+    bool hasColorTransform = getDrawingState().hasColorTransform;
+    if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
+        hasColorTransform = hasColorTransform || parent->hasColorTransform();
+    }
+    return hasColorTransform;
+}
+
 bool Layer::isLegacyDataSpace() const {
     // return true when no higher bits are set
     return !(mCurrentDataSpace & (ui::Dataspace::STANDARD_MASK |
@@ -1852,8 +1973,8 @@
     traverseChildrenInZOrderInner(layersInTree, stateSet, visitor);
 }
 
-Transform Layer::getTransform() const {
-    Transform t;
+ui::Transform Layer::getTransform() const {
+    ui::Transform t;
     const auto& p = mDrawingParent.promote();
     if (p != nullptr) {
         t = p->getTransform();
@@ -1873,14 +1994,14 @@
                 bufferHeight = p->getBE().compositionInfo.mBuffer->getWidth();
                 bufferWidth = p->getBE().compositionInfo.mBuffer->getHeight();
             }
-            float sx = p->getDrawingState().active.w / static_cast<float>(bufferWidth);
-            float sy = p->getDrawingState().active.h / static_cast<float>(bufferHeight);
-            Transform extraParentScaling;
+            float sx = p->getActiveWidth(p->getDrawingState()) / static_cast<float>(bufferWidth);
+            float sy = p->getActiveHeight(p->getDrawingState()) / static_cast<float>(bufferHeight);
+            ui::Transform extraParentScaling;
             extraParentScaling.set(sx, 0, 0, sy);
             t = t * extraParentScaling;
         }
     }
-    return t * getDrawingState().active.transform;
+    return t * getActiveTransform(getDrawingState());
 }
 
 half Layer::getAlpha() const {
@@ -1895,6 +2016,26 @@
     return half4(color.r, color.g, color.b, getAlpha());
 }
 
+Layer::RoundedCornerState Layer::getRoundedCornerState() const {
+    const auto& p = mDrawingParent.promote();
+    if (p != nullptr) {
+        RoundedCornerState parentState = p->getRoundedCornerState();
+        if (parentState.radius > 0) {
+            ui::Transform t = getActiveTransform(getDrawingState());
+            t = t.inverse();
+            parentState.cropRect = t.transform(parentState.cropRect);
+            // The rounded corners shader only accepts 1 corner radius for performance reasons,
+            // but a transform matrix can define horizontal and vertical scales.
+            // Let's take the average between both of them and pass into the shader, practically we
+            // never do this type of transformation on windows anyway.
+            parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
+            return parentState;
+        }
+    }
+    const float radius = getDrawingState().cornerRadius;
+    return radius > 0 ? RoundedCornerState(computeBounds(), radius) : RoundedCornerState();
+}
+
 void Layer::commitChildList() {
     for (size_t i = 0; i < mCurrentChildren.size(); i++) {
         const auto& child = mCurrentChildren[i];
@@ -1904,13 +2045,20 @@
     mDrawingParent = mCurrentParent;
 }
 
+void Layer::setInputInfo(const InputWindowInfo& info) {
+    mCurrentState.inputInfo = info;
+    mCurrentState.modified = true;
+    mCurrentState.inputInfoChanged = true;
+    setTransactionFlags(eTransactionNeeded);
+}
+
 void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) {
     const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
     const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
     const State& state = useDrawing ? mDrawingState : mCurrentState;
 
-    Transform requestedTransform = state.active.transform;
-    Transform transform = getTransform();
+    ui::Transform requestedTransform = state.active_legacy.transform;
+    ui::Transform transform = getTransform();
 
     layerInfo->set_id(sequence);
     layerInfo->set_name(getName().c_str());
@@ -1927,7 +2075,7 @@
         }
     }
 
-    LayerProtoHelper::writeToProto(state.activeTransparentRegion,
+    LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy,
                                    layerInfo->mutable_transparent_region());
     LayerProtoHelper::writeToProto(visibleRegion, layerInfo->mutable_visible_region());
     LayerProtoHelper::writeToProto(surfaceDamageRegion, layerInfo->mutable_damage_region());
@@ -1944,11 +2092,11 @@
     requestedPosition->set_y(requestedTransform.ty());
 
     SizeProto* size = layerInfo->mutable_size();
-    size->set_w(state.active.w);
-    size->set_h(state.active.h);
+    size->set_w(state.active_legacy.w);
+    size->set_h(state.active_legacy.h);
 
-    LayerProtoHelper::writeToProto(state.crop, layerInfo->mutable_crop());
-    LayerProtoHelper::writeToProto(state.finalCrop, layerInfo->mutable_final_crop());
+    LayerProtoHelper::writeToProto(state.crop_legacy, layerInfo->mutable_crop());
+    layerInfo->set_corner_radius(getRoundedCornerState().radius);
 
     layerInfo->set_is_opaque(isOpaque(state));
     layerInfo->set_invalidate(contentDirty);
@@ -1978,31 +2126,38 @@
     auto buffer = getBE().compositionInfo.mBuffer;
     if (buffer != nullptr) {
         LayerProtoHelper::writeToProto(buffer, layerInfo->mutable_active_buffer());
+        LayerProtoHelper::writeToProto(ui::Transform(mCurrentTransform),
+                                       layerInfo->mutable_buffer_transform());
     }
 
     layerInfo->set_queued_frames(getQueuedFrameCount());
     layerInfo->set_refresh_pending(isBufferLatched());
-    layerInfo->set_window_type(state.type);
-    layerInfo->set_app_id(state.appId);
     layerInfo->set_curr_frame(mCurrentFrameNumber);
+    layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
 
     for (const auto& pendingState : mPendingStates) {
-        auto barrierLayer = pendingState.barrierLayer.promote();
+        auto barrierLayer = pendingState.barrierLayer_legacy.promote();
         if (barrierLayer != nullptr) {
             BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer();
             barrierLayerProto->set_id(barrierLayer->sequence);
-            barrierLayerProto->set_frame_number(pendingState.frameNumber);
+            barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy);
         }
     }
+
+    auto protoMap = layerInfo->mutable_metadata();
+    for (const auto& entry : state.metadata.mMap) {
+        (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
+    }
 }
 
-void Layer::writeToProto(LayerProto* layerInfo, int32_t hwcId) {
-    if (!hasHwcLayer(hwcId)) {
+void Layer::writeToProto(LayerProto* layerInfo, DisplayId displayId) {
+    if (!hasHwcLayer(displayId)) {
         return;
     }
+
     writeToProto(layerInfo, LayerVector::StateSet::Drawing);
 
-    const auto& hwcInfo = getBE().mHwcLayers.at(hwcId);
+    const auto& hwcInfo = getBE().mHwcLayers.at(displayId);
 
     const Rect& frame = hwcInfo.displayFrame;
     LayerProtoHelper::writeToProto(frame, layerInfo->mutable_hwc_frame());
@@ -2024,6 +2179,54 @@
     }
 }
 
+bool Layer::isRemovedFromCurrentState() const  {
+    return mRemovedFromCurrentState;
+}
+
+InputWindowInfo Layer::fillInputInfo() {
+    InputWindowInfo info = mDrawingState.inputInfo;
+
+    if (info.displayId == ADISPLAY_ID_NONE) {
+        info.displayId = mDrawingState.layerStack;
+    }
+
+    ui::Transform t = getTransform();
+    const float xScale = t.sx();
+    const float yScale = t.sy();
+    if (xScale != 1.0f || yScale != 1.0f) {
+        info.windowXScale *= 1.0f / xScale;
+        info.windowYScale *= 1.0f / yScale;
+        info.touchableRegion.scaleSelf(xScale, yScale);
+    }
+
+    // Transform layer size to screen space and inset it by surface insets.
+    // If this is a portal window, set the touchableRegion to the layerBounds.
+    Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
+            ? getBufferSize(getDrawingState())
+            : info.touchableRegion.getBounds();
+    if (!layerBounds.isValid()) {
+        layerBounds = getCroppedBufferSize(getDrawingState());
+    }
+    layerBounds = t.transform(layerBounds);
+    layerBounds.inset(info.surfaceInset, info.surfaceInset, info.surfaceInset, info.surfaceInset);
+
+    // Input coordinate should match the layer bounds.
+    info.frameLeft = layerBounds.left;
+    info.frameTop = layerBounds.top;
+    info.frameRight = layerBounds.right;
+    info.frameBottom = layerBounds.bottom;
+
+    // Position the touchable region relative to frame screen location and restrict it to frame
+    // bounds.
+    info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
+    info.visible = isVisible();
+    return info;
+}
+
+bool Layer::hasInput() const {
+    return mDrawingState.inputInfo.token != nullptr;
+}
+
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 301f190..00e1a6f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -19,40 +19,40 @@
 
 #include <sys/types.h>
 
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-
+#include <gui/BufferQueue.h>
+#include <gui/ISurfaceComposerClient.h>
+#include <gui/LayerState.h>
+#include <input/InputWindow.h>
+#include <layerproto/LayerProtoHeader.h>
+#include <math/vec4.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/Texture.h>
 #include <ui/FloatRect.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
 #include <ui/Region.h>
+#include <ui/Transform.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
 
-#include <gui/ISurfaceComposerClient.h>
-#include <gui/LayerState.h>
-#include <gui/BufferQueue.h>
-
-#include <list>
 #include <cstdint>
+#include <list>
+#include <optional>
+#include <vector>
 
 #include "Client.h"
 #include "FrameTracker.h"
+#include "LayerBE.h"
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
-#include "TimeStats/TimeStats.h"
-#include "Transform.h"
+#include "TransactionCompletedThread.h"
 
-#include <layerproto/LayerProtoHeader.h>
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/HWComposerBufferCache.h"
 #include "RenderArea.h"
-#include "RenderEngine/Mesh.h"
-#include "RenderEngine/Texture.h"
-
-#include <math/vec4.h>
-#include <vector>
 
 using namespace android::surfaceflinger;
 
@@ -74,77 +74,27 @@
 
 // ---------------------------------------------------------------------------
 
-struct CompositionInfo {
-    HWC2::Composition compositionType;
-    sp<GraphicBuffer> mBuffer = nullptr;
-    int mBufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
-    struct {
-        HWComposer* hwc;
-        sp<Fence> fence;
-        HWC2::BlendMode blendMode;
-        Rect displayFrame;
-        float alpha;
-        FloatRect sourceCrop;
-        HWC2::Transform transform;
-        int z;
-        int type;
-        int appId;
-        Region visibleRegion;
-        Region surfaceDamage;
-        sp<NativeHandle> sidebandStream;
-        android_dataspace dataspace;
-        hwc_color_t color;
-    } hwc;
-    struct {
-        RE::RenderEngine* renderEngine;
-        Mesh* mesh;
-    } renderEngine;
-};
+struct LayerCreationArgs {
+    LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name,
+                      uint32_t w, uint32_t h, uint32_t flags)
+          : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags) {}
 
-class LayerBE {
-public:
-    LayerBE();
-
-    // The mesh used to draw the layer in GLES composition mode
-    Mesh mMesh;
-
-    // HWC items, accessed from the main thread
-    struct HWCInfo {
-        HWCInfo()
-              : hwc(nullptr),
-                layer(nullptr),
-                forceClientComposition(false),
-                compositionType(HWC2::Composition::Invalid),
-                clearClientTarget(false),
-                transform(HWC2::Transform::None) {}
-
-        HWComposer* hwc;
-        HWC2::Layer* layer;
-        bool forceClientComposition;
-        HWC2::Composition compositionType;
-        bool clearClientTarget;
-        Rect displayFrame;
-        FloatRect sourceCrop;
-        HWComposerBufferCache bufferCache;
-        HWC2::Transform transform;
-    };
-
-    // A layer can be attached to multiple displays when operating in mirror mode
-    // (a.k.a: when several displays are attached with equal layerStack). In this
-    // 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;
-
-    CompositionInfo compositionInfo;
+    SurfaceFlinger* flinger;
+    const sp<Client>& client;
+    const String8& name;
+    uint32_t w;
+    uint32_t h;
+    uint32_t flags;
 };
 
 class Layer : public virtual RefBase {
-    static int32_t sSequence;
+    static std::atomic<int32_t> sSequence;
 
 public:
+    friend class LayerBE;
     LayerBE& getBE() { return mBE; }
     LayerBE& getBE() const { return mBE; }
-    mutable bool contentDirty;
+    mutable bool contentDirty{false};
     // regions below are in window-manager space
     Region visibleRegion;
     Region coveredRegion;
@@ -154,17 +104,18 @@
     // Layer serial number.  This gives layers an explicit ordering, so we
     // have a stable sort order when their layer stack and Z-order are
     // the same.
-    int32_t sequence;
+    int32_t sequence{sSequence++};
 
     enum { // flags for doTransaction()
         eDontUpdateGeometryState = 0x00000001,
         eVisibleRegion = 0x00000002,
+        eInputInfoChanged = 0x00000004
     };
 
     struct Geometry {
         uint32_t w;
         uint32_t h;
-        Transform transform;
+        ui::Transform transform;
 
         inline bool operator==(const Geometry& rhs) const {
             return (w == rhs.w && h == rhs.h) && (transform.tx() == rhs.transform.tx()) &&
@@ -173,9 +124,20 @@
         inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); }
     };
 
+    struct RoundedCornerState {
+        RoundedCornerState() = default;
+        RoundedCornerState(FloatRect cropRect, float radius)
+              : cropRect(cropRect), radius(radius) {}
+
+        // Rounded rectangle in local layer coordinate space.
+        FloatRect cropRect = FloatRect();
+        // Radius of the rounded rectangle.
+        float radius = 0.0f;
+    };
+
     struct State {
-        Geometry active;
-        Geometry requested;
+        Geometry active_legacy;
+        Geometry requested_legacy;
         int32_t z;
 
         // The identifier of the layer stack this layer belongs to. A layer can
@@ -191,26 +153,21 @@
         bool modified;
 
         // Crop is expressed in layer space coordinate.
-        Rect crop;
-        Rect requestedCrop;
-
-        // finalCrop is expressed in display space coordinate.
-        Rect finalCrop;
-        Rect requestedFinalCrop;
+        Rect crop_legacy;
+        Rect requestedCrop_legacy;
 
         // If set, defers this state update until the identified Layer
         // receives a frame with the given frameNumber
-        wp<Layer> barrierLayer;
-        uint64_t frameNumber;
+        wp<Layer> barrierLayer_legacy;
+        uint64_t frameNumber_legacy;
 
         // the transparentRegion hint is a bit special, it's latched only
         // when we receive a buffer -- this is because it's "content"
         // dependent.
-        Region activeTransparentRegion;
-        Region requestedTransparentRegion;
+        Region activeTransparentRegion_legacy;
+        Region requestedTransparentRegion_legacy;
 
-        int32_t appId;
-        int32_t type;
+        LayerMetadata metadata;
 
         // If non-null, a Surface this Surface's Z-order is interpreted relative to.
         wp<Layer> zOrderRelativeOf;
@@ -219,13 +176,47 @@
         SortedVector<wp<Layer>> zOrderRelatives;
 
         half4 color;
+        float cornerRadius;
+
+        bool inputInfoChanged;
+        InputWindowInfo inputInfo;
+
+        // The fields below this point are only used by BufferStateLayer
+        Geometry active;
+
+        uint32_t transform;
+        bool transformToDisplayInverse;
+
+        Rect crop;
+        Region transparentRegionHint;
+
+        sp<GraphicBuffer> buffer;
+        sp<Fence> acquireFence;
+        ui::Dataspace dataspace;
+        HdrMetadata hdrMetadata;
+        Region surfaceDamageRegion;
+        int32_t api;
+
+        sp<NativeHandle> sidebandStream;
+        mat4 colorTransform;
+        bool hasColorTransform;
+
+        // pointer to background color layer that, if set, appears below the buffer state layer
+        // and the buffer state layer's children.  Z order will be set to
+        // INT_MIN
+        sp<Layer> bgColorLayer;
+        ui::Dataspace colorDataspace; // The dataspace of the background color layer
+
+        // The deque of callback handles for this frame. The back of the deque contains the most
+        // recent callback handle.
+        std::deque<sp<CallbackHandle>> callbackHandles;
     };
 
-    Layer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w,
-          uint32_t h, uint32_t flags);
+    explicit Layer(const LayerCreationArgs& args);
     virtual ~Layer();
 
     void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
+    bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
 
     // ------------------------------------------------------------------------
     // Geometry setting functions.
@@ -254,11 +245,12 @@
     // also the rendered size of the layer prior to any transformations. Parent
     // or local matrix transformations will not affect the size of the buffer,
     // but may affect it's on-screen size or clipping.
-    bool setSize(uint32_t w, uint32_t h);
+    virtual bool setSize(uint32_t w, uint32_t h);
     // Set a 2x2 transformation matrix on the layer. This transform
     // will be applied after parent transforms, but before any final
     // producer specified transform.
-    bool setMatrix(const layer_state_t::matrix22_t& matrix, bool allowNonRectPreservingTransforms);
+    virtual bool setMatrix(const layer_state_t::matrix22_t& matrix,
+                           bool allowNonRectPreservingTransforms);
 
     // This second set of geometry attributes are controlled by
     // setGeometryAppliesWithResize, and their default mode is to be
@@ -268,32 +260,62 @@
 
     // setPosition operates in parent buffer space (pre parent-transform) or display
     // space for top-level layers.
-    bool setPosition(float x, float y, bool immediate);
+    virtual bool setPosition(float x, float y, bool immediate);
     // Buffer space
-    bool setCrop(const Rect& crop, bool immediate);
-    // Parent buffer space/display space
-    bool setFinalCrop(const Rect& crop, bool immediate);
+    virtual bool setCrop_legacy(const Rect& crop, bool immediate);
 
     // TODO(b/38182121): Could we eliminate the various latching modes by
     // using the layer hierarchy?
     // -----------------------------------------------------------------------
-    bool setLayer(int32_t z);
-    bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
+    virtual bool setLayer(int32_t z);
+    virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
 
-    bool setAlpha(float alpha);
-    bool setColor(const half3& color);
-    bool setTransparentRegionHint(const Region& transparent);
-    bool setFlags(uint8_t flags, uint8_t mask);
-    bool setLayerStack(uint32_t layerStack);
-    uint32_t getLayerStack() const;
-    void deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber);
-    void deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber);
-    bool setOverrideScalingMode(int32_t overrideScalingMode);
-    void setInfo(int32_t type, int32_t appId);
-    bool reparentChildren(const sp<IBinder>& layer);
-    void setChildrenDrawingParent(const sp<Layer>& layer);
-    bool reparent(const sp<IBinder>& newParentHandle);
-    bool detachChildren();
+    virtual bool setAlpha(float alpha);
+    virtual bool setColor(const half3& color);
+
+    // Set rounded corner radius for this layer and its children.
+    //
+    // We only support 1 radius per layer in the hierarchy, where parent layers have precedence.
+    // The shape of the rounded corner rectangle is specified by the crop rectangle of the layer
+    // from which we inferred the rounded corner radius.
+    virtual bool setCornerRadius(float cornerRadius);
+    virtual bool setTransparentRegionHint(const Region& transparent);
+    virtual bool setFlags(uint8_t flags, uint8_t mask);
+    virtual bool setLayerStack(uint32_t layerStack);
+    virtual uint32_t getLayerStack() const;
+    virtual void deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle,
+                                              uint64_t frameNumber);
+    virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
+    virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
+    virtual bool setMetadata(LayerMetadata data);
+    virtual bool reparentChildren(const sp<IBinder>& layer);
+    virtual void setChildrenDrawingParent(const sp<Layer>& layer);
+    virtual bool reparent(const sp<IBinder>& newParentHandle);
+    virtual bool detachChildren();
+    bool attachChildren();
+    bool isLayerDetached() const { return mLayerDetached; }
+    virtual bool setColorTransform(const mat4& matrix);
+    virtual mat4 getColorTransform() const;
+    virtual bool hasColorTransform() const;
+
+    // Used only to set BufferStateLayer state
+    virtual bool setTransform(uint32_t /*transform*/) { return false; };
+    virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
+    virtual bool setCrop(const Rect& /*crop*/) { return false; };
+    virtual bool setFrame(const Rect& /*frame*/) { return false; };
+    virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/) { return false; };
+    virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
+    virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; };
+    virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; };
+    virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; };
+    virtual bool setApi(int32_t /*api*/) { return false; };
+    virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
+    virtual bool setTransactionCompletedListeners(
+            const std::vector<sp<CallbackHandle>>& /*handles*/) {
+        return false;
+    };
+    virtual bool setColorAlpha(float /*alpha*/) { return false; };
+    virtual bool setColorDataspace(ui::Dataspace /*dataspace*/) { return false; };
 
     ui::Dataspace getDataSpace() const { return mCurrentDataSpace; }
 
@@ -310,14 +332,19 @@
     virtual void useSurfaceDamage() {}
     virtual void useEmptyDamage() {}
 
+    uint32_t getTransactionFlags() const { return mTransactionFlags; }
     uint32_t getTransactionFlags(uint32_t flags);
     uint32_t setTransactionFlags(uint32_t flags);
 
+    // Deprecated, please use compositionengine::Output::belongsInOutput()
+    // instead.
+    // TODO(lpique): Move the remaining callers (screencap) to the new function.
     bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const {
         return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
     }
 
-    void computeGeometry(const RenderArea& renderArea, Mesh& mesh, bool useIdentityTransform) const;
+    void computeGeometry(const RenderArea& renderArea, renderengine::Mesh& mesh,
+                         bool useIdentityTransform) const;
     FloatRect computeBounds(const Region& activeTransparentRegion) const;
     FloatRect computeBounds() const;
 
@@ -356,6 +383,12 @@
     bool isHiddenByPolicy() const;
 
     /*
+     * isProtected - true if the layer may contain protected content in the
+     * GRALLOC_USAGE_PROTECTED sense.
+     */
+    virtual bool isProtected() const { return false; }
+
+    /*
      * isFixedSize - true if content has a fixed size
      */
     virtual bool isFixedSize() const { return true; }
@@ -365,60 +398,68 @@
     // to avoid grabbing the lock again to avoid deadlock
     virtual bool isCreatedFromMainThread() const { return false; }
 
-
-    bool isPendingRemoval() const { return mPendingRemoval; }
+    bool isRemovedFromCurrentState() const;
 
     void writeToProto(LayerProto* layerInfo,
                       LayerVector::StateSet stateSet = LayerVector::StateSet::Drawing);
 
-    void writeToProto(LayerProto* layerInfo, int32_t hwcId);
+    void writeToProto(LayerProto* layerInfo, DisplayId displayId);
+
+    virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
+    virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; }
+    virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; }
+    virtual ui::Transform getActiveTransform(const Layer::State& s) const {
+        return s.active_legacy.transform;
+    }
+    virtual Region getActiveTransparentRegion(const Layer::State& s) const {
+        return s.activeTransparentRegion_legacy;
+    }
+    virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
 
 protected:
-    /*
-     * onDraw - draws the surface.
-     */
-    virtual void onDraw(const RenderArea& renderArea, const Region& clip,
-                        bool useIdentityTransform) const = 0;
+    virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+                                    bool useIdentityTransform, Region& clearRegion,
+                                    renderengine::LayerSettings& layer) = 0;
 
 public:
     virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
 
     virtual bool isHdrY410() const { return false; }
 
-    void setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z);
-    void forceClientComposition(int32_t hwcId);
-    bool getForceClientComposition(int32_t hwcId);
-    virtual void setPerFrameData(const sp<const DisplayDevice>& displayDevice) = 0;
+    void setGeometry(const sp<const DisplayDevice>& display, uint32_t z);
+    void forceClientComposition(DisplayId displayId);
+    bool getForceClientComposition(DisplayId displayId);
+    virtual void setPerFrameData(DisplayId displayId, const ui::Transform& transform,
+                                 const Rect& viewport, int32_t supportedPerFrameMetadata) = 0;
 
     // callIntoHwc exists so we can update our local state and call
     // acceptDisplayChanges without unnecessarily updating the device's state
-    void setCompositionType(int32_t hwcId, HWC2::Composition type, bool callIntoHwc = true);
-    HWC2::Composition getCompositionType(int32_t hwcId) const;
-    void setClearClientTarget(int32_t hwcId, bool clear);
-    bool getClearClientTarget(int32_t hwcId) const;
-    void updateCursorPosition(const sp<const DisplayDevice>& hw);
+    void setCompositionType(DisplayId displayId, HWC2::Composition type, bool callIntoHwc = true);
+    HWC2::Composition getCompositionType(const std::optional<DisplayId>& displayId) const;
+    void setClearClientTarget(DisplayId displayId, bool clear);
+    bool getClearClientTarget(DisplayId displayId) const;
+    void updateCursorPosition(const sp<const DisplayDevice>& display);
 
     /*
      * called after page-flip
      */
     virtual void onLayerDisplayed(const sp<Fence>& releaseFence);
 
-    virtual void abandon() {}
-
-    virtual bool shouldPresentNow(const DispSync& /*dispSync*/) const { return false; }
+    virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
     virtual void setTransformHint(uint32_t /*orientation*/) const { }
 
     /*
      * called before composition.
      * returns true if the layer has pending updates.
      */
-    virtual bool onPreComposition(nsecs_t /*refreshStartTime*/) { return true; }
+    virtual bool onPreComposition(nsecs_t refreshStartTime) = 0;
 
     /*
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
-    virtual bool onPostComposition(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+    virtual bool onPostComposition(const std::optional<DisplayId>& /*displayId*/,
+                                   const std::shared_ptr<FenceTime>& /*glDoneFence*/,
                                    const std::shared_ptr<FenceTime>& /*presentFence*/,
                                    const CompositorTiming& /*compositorTiming*/) {
         return false;
@@ -427,14 +468,15 @@
     // If a buffer was replaced this frame, release the former buffer
     virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
 
-
     /*
-     * draw - performs some global clipping optimizations
-     * and calls onDraw().
+     * prepareClientLayer - populates a renderengine::LayerSettings to passed to
+     * RenderEngine::drawLayers. Returns true if the layer can be used, and
+     * false otherwise.
      */
-    void draw(const RenderArea& renderArea, const Region& clip) const;
-    void draw(const RenderArea& renderArea, bool useIdentityTransform) const;
-    void draw(const RenderArea& renderArea) const;
+    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, Region& clearRegion,
+                            renderengine::LayerSettings& layer);
+    bool prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
+                            Region& clearRegion, renderengine::LayerSettings& layer);
 
     /*
      * doTransaction - process the transaction. This is a good place to figure
@@ -472,13 +514,13 @@
      * operation, so this should be set only if needed). Typically this is used
      * to figure out if the content or size of a surface has changed.
      */
-    virtual Region latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/) {
+    virtual Region latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
+                               const sp<Fence>& /*releaseFence*/) {
         return {};
     }
 
     virtual bool isBufferLatched() const { return false; }
 
-    bool isPotentialCursor() const { return mPotentialCursor; }
     /*
      * called with the state lock from a binder thread when the layer is
      * removed from the current list to the pending removal list
@@ -486,14 +528,13 @@
     void onRemovedFromCurrentState();
 
     /*
-     * called with the state lock from the main thread when the layer is
-     * removed from the pending removal list
+     * Called when the layer is added back to the current state list.
      */
-    void onRemoved();
+    void addToCurrentState();
 
     // Updates the transform hint in our SurfaceFlingerConsumer to match
     // the current orientation of the display device.
-    void updateTransformHint(const sp<const DisplayDevice>& hw) const;
+    void updateTransformHint(const sp<const DisplayDevice>& display) const;
 
     /*
      * returns the rectangle that crops the content of the layer and scales it
@@ -502,37 +543,38 @@
     Rect getContentCrop() const;
 
     /*
-     * Returns if a frame is queued.
+     * Returns if a frame is ready
      */
-    bool hasQueuedFrame() const {
-        return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh;
-    }
+    virtual bool hasReadyFrame() const { return false; }
 
-    int32_t getQueuedFrameCount() const { return mQueuedFrames; }
+    virtual int32_t getQueuedFrameCount() const { return 0; }
 
     // -----------------------------------------------------------------------
 
-    bool createHwcLayer(HWComposer* hwc, int32_t hwcId);
-    bool destroyHwcLayer(int32_t hwcId);
-    void destroyAllHwcLayers();
+    bool createHwcLayer(HWComposer* hwc, DisplayId displayId);
+    bool destroyHwcLayer(DisplayId displayId);
+    void destroyHwcLayersForAllDisplays();
+    void destroyAllHwcLayersPlusChildren();
 
-    bool hasHwcLayer(int32_t hwcId) {
-        return getBE().mHwcLayers.count(hwcId) > 0;
-    }
+    bool hasHwcLayer(DisplayId displayId) const { return getBE().mHwcLayers.count(displayId) > 0; }
 
-    HWC2::Layer* getHwcLayer(int32_t hwcId) {
-        if (getBE().mHwcLayers.count(hwcId) == 0) {
+    HWC2::Layer* getHwcLayer(DisplayId displayId) {
+        if (!hasHwcLayer(displayId)) {
             return nullptr;
         }
-        return getBE().mHwcLayers[hwcId].layer;
+        return getBE().mHwcLayers[displayId].layer.get();
+    }
+
+    bool setHwcLayer(DisplayId displayId) {
+        if (!hasHwcLayer(displayId)) {
+            return false;
+        }
+        getBE().compositionInfo.hwc.hwcLayer = getBE().mHwcLayers[displayId].layer;
+        return true;
     }
 
     // -----------------------------------------------------------------------
-
     void clearWithOpenGL(const RenderArea& renderArea) const;
-    void setFiltering(bool filtering);
-    bool getFiltering() const;
-
 
     inline const State& getDrawingState() const { return mDrawingState; }
     inline const State& getCurrentState() const { return mCurrentState; }
@@ -541,10 +583,10 @@
     LayerDebugInfo getLayerDebugInfo() const;
 
     /* always call base class first */
-    static void miniDumpHeader(String8& result);
-    void miniDump(String8& result, int32_t hwcId) const;
-    void dumpFrameStats(String8& result) const;
-    void dumpFrameEvents(String8& result);
+    static void miniDumpHeader(std::string& result);
+    void miniDump(std::string& result, DisplayId displayId) const;
+    void dumpFrameStats(std::string& result) const;
+    void dumpFrameEvents(std::string& result);
     void clearFrameStats();
     void logFrameStats();
     void getFrameStats(FrameStats* outStats) const;
@@ -559,7 +601,7 @@
 
     virtual bool getTransformToDisplayInverse() const { return false; }
 
-    Transform getTransform() const;
+    ui::Transform getTransform() const;
 
     // Returns the Alpha of the Surface, accounting for the Alpha
     // of parent Surfaces in the hierarchy (alpha's will be multiplied
@@ -567,6 +609,13 @@
     half getAlpha() const;
     half4 getColor() const;
 
+    // Returns how rounded corners should be drawn for this layer.
+    // This will traverse the hierarchy until it reaches its root, finding topmost rounded
+    // corner definition and converting it into current layer's coordinates.
+    // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
+    // ignored.
+    RoundedCornerState getRoundedCornerState() const;
+
     void traverseInReverseZOrder(LayerVector::StateSet stateSet,
                                  const LayerVector::Visitor& visitor);
     void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
@@ -594,7 +643,13 @@
     // SurfaceFlinger to complete a transaction.
     void commitChildList();
     int32_t getZ() const;
-    void pushPendingState();
+    virtual void pushPendingState();
+
+    /**
+     * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
+     * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
+     */
+    virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
 
 protected:
     // constant
@@ -605,12 +660,12 @@
      */
     class LayerCleaner {
         sp<SurfaceFlinger> mFlinger;
-        wp<Layer> mLayer;
+        sp<Layer> mLayer;
 
     protected:
         ~LayerCleaner() {
             // destroy client resources
-            mFlinger->onLayerDestroyed(mLayer);
+            mFlinger->onHandleDestroyed(mLayer);
         }
 
     public:
@@ -620,21 +675,28 @@
 
     friend class impl::SurfaceInterceptor;
 
+    // For unit tests
+    friend class TestableSurfaceFlinger;
+
     void commitTransaction(const State& stateToCommit);
 
     uint32_t getEffectiveUsage(uint32_t usage) const;
 
-    FloatRect computeCrop(const sp<const DisplayDevice>& hw) const;
+    virtual FloatRect computeCrop(const sp<const DisplayDevice>& display) const;
     // Compute the initial crop as specified by parent layers and the
     // SurfaceControl for this layer. Does not include buffer crop from the
     // IGraphicBufferProducer client, as that should not affect child clipping.
     // Returns in screen space.
-    Rect computeInitialCrop(const sp<const DisplayDevice>& hw) const;
+    Rect computeInitialCrop(const sp<const DisplayDevice>& display) const;
+    /**
+     * Setup rounded corners coordinates of this layer, taking into account the layer bounds and
+     * crop coordinates, transforming them into layer space.
+     */
+    void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
 
     // drawing
     void clearWithOpenGL(const RenderArea& renderArea, float r, float g, float b,
                          float alpha) const;
-
     void setParent(const sp<Layer>& layer);
 
     LayerVector makeTraversalList(LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers);
@@ -676,7 +738,8 @@
     bool addSyncPoint(const std::shared_ptr<SyncPoint>& point);
 
     void popPendingState(State* stateToCommit);
-    bool applyPendingStates(State* stateToCommit);
+    virtual bool applyPendingStates(State* stateToCommit);
+    virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
 
     void clearSyncPoints();
 
@@ -708,11 +771,17 @@
     virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
     bool getPremultipledAlpha() const;
 
+    bool mPendingHWCDestroy{false};
+    void setInputInfo(const InputWindowInfo& info);
+
+    InputWindowInfo fillInputInfo();
+    bool hasInput() const;
+
 protected:
     // -----------------------------------------------------------------------
     bool usingRelativeZ(LayerVector::StateSet stateSet);
 
-    bool mPremultipliedAlpha;
+    bool mPremultipliedAlpha{true};
     String8 mName;
     String8 mTransactionName; // A cached version of "TX - " + mName for systraces
 
@@ -721,16 +790,12 @@
     // these are protected by an external lock
     State mCurrentState;
     State mDrawingState;
-    volatile int32_t mTransactionFlags;
+    std::atomic<uint32_t> mTransactionFlags{0};
 
     // Accessed from main thread and binder threads
     Mutex mPendingStateMutex;
     Vector<State> mPendingStates;
 
-    // thread-safe
-    volatile int32_t mQueuedFrames;
-    volatile int32_t mSidebandStreamChanged; // used like an atomic boolean
-
     // Timestamp history for UIAutomation. Thread safe.
     FrameTracker mFrameTracker;
 
@@ -741,29 +806,28 @@
     FenceTimeline mAcquireTimeline;
     FenceTimeline mReleaseTimeline;
 
-    TimeStats& mTimeStats = TimeStats::getInstance();
-
     // main thread
-    int mActiveBufferSlot;
+    // Active buffer fields
     sp<GraphicBuffer> mActiveBuffer;
-    sp<NativeHandle> mSidebandStream;
+    sp<Fence> mActiveBufferFence;
+    // False if the buffer and its contents have been previously used for GPU
+    // composition, true otherwise.
+    bool mIsActiveBufferUpdatedForGpu = true;
+
     ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN;
     Rect mCurrentCrop;
-    uint32_t mCurrentTransform;
+    uint32_t mCurrentTransform{0};
     // We encode unset as -1.
-    int32_t mOverrideScalingMode;
-    bool mCurrentOpacity;
-    std::atomic<uint64_t> mCurrentFrameNumber;
-    bool mFrameLatencyNeeded;
-    // Whether filtering is forced on or not
-    bool mFiltering;
+    int32_t mOverrideScalingMode{-1};
+    std::atomic<uint64_t> mCurrentFrameNumber{0};
+    bool mFrameLatencyNeeded{false};
     // Whether filtering is needed b/c of the drawingstate
-    bool mNeedsFiltering;
+    bool mNeedsFiltering{false};
 
-    bool mPendingRemoval = false;
+    std::atomic<bool> mRemovedFromCurrentState{false};
 
     // page-flip thread (currently main thread)
-    bool mProtectedByApp; // application requires protected path to external sink
+    bool mProtectedByApp{false}; // application requires protected path to external sink
 
     // protected by mLock
     mutable Mutex mLock;
@@ -771,26 +835,23 @@
     const wp<Client> mClientRef;
 
     // This layer can be a cursor on some displays.
-    bool mPotentialCursor;
+    bool mPotentialCursor{false};
 
-    // Local copy of the queued contents of the incoming BufferQueue
-    mutable Mutex mQueueItemLock;
-    Condition mQueueItemCondition;
-    Vector<BufferItem> mQueueItems;
-    std::atomic<uint64_t> mLastFrameNumberReceived;
-    bool mAutoRefresh;
-    bool mFreezeGeometryUpdates;
+    bool mFreezeGeometryUpdates{false};
 
     // Child list about to be committed/used for editing.
-    LayerVector mCurrentChildren;
+    LayerVector mCurrentChildren{LayerVector::StateSet::Current};
     // Child list used for rendering.
-    LayerVector mDrawingChildren;
+    LayerVector mDrawingChildren{LayerVector::StateSet::Drawing};
 
     wp<Layer> mCurrentParent;
     wp<Layer> mDrawingParent;
 
     mutable LayerBE mBE;
 
+    // Can only be accessed with the SF state lock held.
+    bool mLayerDetached{false};
+
 private:
     /**
      * Returns an unsorted vector of all layers that are part of this tree.
@@ -806,10 +867,31 @@
                                        const LayerVector::Visitor& visitor);
     LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                           const std::vector<Layer*>& layersInTree);
+    /**
+     * Retuns the child bounds in layer space cropped to its bounds as well all its parent bounds.
+     * The cropped bounds must be transformed back from parent layer space to child layer space by
+     * applying the inverse of the child's transformation.
+     */
+    FloatRect cropChildBounds(const FloatRect& childBounds) const;
+
+    /**
+     * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
+     * INVALID_RECT if the layer has no buffer and no crop.
+     * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
+     * bounds are constrained by its parent bounds.
+     */
+    Rect getCroppedBufferSize(const Layer::State& s) const;
 };
 
-// ---------------------------------------------------------------------------
+} // namespace android
 
-}; // namespace android
+#define RETURN_IF_NO_HWC_LAYER(displayId, ...)                                         \
+    do {                                                                               \
+        if (!hasHwcLayer(displayId)) {                                                 \
+            ALOGE("[%s] %s failed: no HWC layer found for display %s", mName.string(), \
+                  __FUNCTION__, to_string(displayId).c_str());                         \
+            return __VA_ARGS__;                                                        \
+        }                                                                              \
+    } while (false)
 
 #endif // ANDROID_LAYER_H
diff --git a/services/surfaceflinger/LayerBE.cpp b/services/surfaceflinger/LayerBE.cpp
new file mode 100644
index 0000000..e39babe
--- /dev/null
+++ b/services/surfaceflinger/LayerBE.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "LayerBE"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "Layer.h"
+
+#include <android-base/stringprintf.h>
+#include <renderengine/RenderEngine.h>
+
+#include <string>
+
+namespace {
+
+const char* getCompositionName(HWC2::Composition compositionType) {
+    switch (compositionType) {
+        case HWC2::Composition::Invalid:
+            return "Invalid";
+        case HWC2::Composition::Client:
+            return "Client";
+        case HWC2::Composition::Device:
+            return "Device";
+        case HWC2::Composition::SolidColor:
+            return "Solid Color";
+        case HWC2::Composition::Cursor:
+            return "Cursor";
+        case HWC2::Composition::Sideband:
+            return "Sideband";
+    }
+    return "Invalid";
+}
+
+}  // namespace anonymous
+
+namespace android {
+
+LayerBE::LayerBE(Layer* layer, std::string layerName)
+      : mLayer(layer),
+        mMesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2, 2) {
+    compositionInfo.layer = std::make_shared<LayerBE>(*this);
+    compositionInfo.layerName = layerName;
+}
+
+LayerBE::LayerBE(const LayerBE& layer)
+      : mLayer(layer.mLayer),
+        mMesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2, 2) {
+    compositionInfo.layer = layer.compositionInfo.layer;
+    compositionInfo.layerName = layer.mLayer->getName().string();
+}
+
+void LayerBE::onLayerDisplayed(const sp<Fence>& releaseFence) {
+    mLayer->onLayerDisplayed(releaseFence);
+}
+
+void LayerBE::clear(renderengine::RenderEngine& engine) {
+    engine.setupFillWithColor(0, 0, 0, 0);
+    engine.drawMesh(mMesh);
+}
+
+void CompositionInfo::dump(const char* tag) const
+{
+    std::string logString;
+    dump(logString, tag);
+    ALOGV("%s", logString.c_str());
+}
+
+void CompositionInfo::dumpHwc(std::string& result, const char* tag) const {
+    if (tag == nullptr) {
+        result += base::StringPrintf("HWC parameters\n");
+    } else {
+        result += base::StringPrintf("[%s]HWC parameters\n", tag);
+    }
+
+    result += base::StringPrintf("\thwcLayer=%p\n", static_cast<HWC2::Layer*>(&*hwc.hwcLayer));
+    result += base::StringPrintf("\tfence=%p\n", hwc.fence.get());
+    result += base::StringPrintf("\tblendMode=%d\n", hwc.blendMode);
+    result += base::StringPrintf("\ttransform=%d\n", hwc.transform);
+    result += base::StringPrintf("\tz=%d\n", hwc.z);
+    result += base::StringPrintf("\ttype=%d\n", hwc.type);
+    result += base::StringPrintf("\tappId=%d\n", hwc.appId);
+    result += base::StringPrintf("\tdisplayFrame=%4d %4d %4d %4d\n", hwc.displayFrame.left,
+                                 hwc.displayFrame.top, hwc.displayFrame.right,
+                                 hwc.displayFrame.bottom);
+    result += base::StringPrintf("\talpha=%.3f", hwc.alpha);
+    result += base::StringPrintf("\tsourceCrop=%6.1f %6.1f %6.1f %6.1f\n", hwc.sourceCrop.left,
+                                 hwc.sourceCrop.top, hwc.sourceCrop.right, hwc.sourceCrop.bottom);
+
+    hwc.visibleRegion.dump(result, "visibleRegion");
+    hwc.surfaceDamage.dump(result, "surfaceDamage");
+
+    result += base::StringPrintf("\tcolor transform matrix:\n"
+                                 "\t\t[%f, %f, %f, %f,\n"
+                                 "\t\t %f, %f, %f, %f,\n"
+                                 "\t\t %f, %f, %f, %f,\n"
+                                 "\t\t %f, %f, %f, %f]\n",
+                                 hwc.colorTransform[0][0], hwc.colorTransform[1][0],
+                                 hwc.colorTransform[2][0], hwc.colorTransform[3][0],
+                                 hwc.colorTransform[0][1], hwc.colorTransform[1][1],
+                                 hwc.colorTransform[2][1], hwc.colorTransform[3][1],
+                                 hwc.colorTransform[0][2], hwc.colorTransform[1][2],
+                                 hwc.colorTransform[2][2], hwc.colorTransform[3][2],
+                                 hwc.colorTransform[0][3], hwc.colorTransform[1][3],
+                                 hwc.colorTransform[2][3], hwc.colorTransform[3][3]);
+}
+
+void CompositionInfo::dumpRe(std::string& result, const char* tag) const {
+    if (tag == nullptr) {
+        result += base::StringPrintf("RenderEngine parameters:\n");
+    } else {
+        result += base::StringPrintf("[%s]RenderEngine parameters:\n", tag);
+    }
+
+    result += base::StringPrintf("\tblackoutLayer=%d\n", re.blackoutLayer);
+    result += base::StringPrintf("\tclearArea=%d\n", re.clearArea);
+    result += base::StringPrintf("\tpreMultipliedAlpha=%d\n", re.preMultipliedAlpha);
+    result += base::StringPrintf("\topaque=%d\n", re.opaque);
+    result += base::StringPrintf("\tdisableTexture=%d\n", re.disableTexture);
+    result += base::StringPrintf("\tuseIdentityTransform=%d\n", re.useIdentityTransform);
+}
+
+void CompositionInfo::dump(std::string& result, const char* tag) const
+{
+    if (tag == nullptr) {
+        result += base::StringPrintf("CompositionInfo\n");
+    } else {
+        result += base::StringPrintf("[%s]CompositionInfo\n", tag);
+    }
+    result += base::StringPrintf("\tLayerName: %s\n", layerName.c_str());
+    result += base::StringPrintf("\tCompositionType: %s\n",
+                                 getCompositionName(compositionType));
+    result += base::StringPrintf("\tmBuffer = %p\n", mBuffer.get());
+    result += base::StringPrintf("\tmBufferSlot=%d\n", mBufferSlot);
+    result += base::StringPrintf("\tdisplayFrame=%4d %4d %4d %4d\n", hwc.displayFrame.left,
+                                 hwc.displayFrame.top, hwc.displayFrame.right,
+                                 hwc.displayFrame.bottom);
+    result += base::StringPrintf("\talpha=%f\n", hwc.alpha);
+    result += base::StringPrintf("\tsourceCrop=%6.1f %6.1f %6.1f %6.1f\n", hwc.sourceCrop.left,
+                                 hwc.sourceCrop.top, hwc.sourceCrop.right, hwc.sourceCrop.bottom);
+
+    switch (compositionType) {
+        case HWC2::Composition::Device:
+            dumpHwc(result, tag);
+            break;
+        case HWC2::Composition::Client:
+            dumpRe(result, tag);
+            break;
+        default:
+            break;
+    }
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/LayerBE.h b/services/surfaceflinger/LayerBE.h
new file mode 100644
index 0000000..3f5134e
--- /dev/null
+++ b/services/surfaceflinger/LayerBE.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <renderengine/Mesh.h>
+#include <renderengine/RenderEngine.h>
+#include <renderengine/Texture.h>
+#include <ui/Region.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+#include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/HWComposerBufferCache.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+class LayerBE;
+
+struct CompositionInfo {
+    std::string layerName;
+    HWC2::Composition compositionType;
+    bool firstClear = false;
+    sp<GraphicBuffer> mBuffer = nullptr;
+    int mBufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
+    std::shared_ptr<LayerBE> layer;
+    struct {
+        std::shared_ptr<HWC2::Layer> hwcLayer;
+        DisplayId displayId;
+        sp<Fence> fence;
+        HWC2::BlendMode blendMode = HWC2::BlendMode::Invalid;
+        Rect displayFrame;
+        float alpha;
+        FloatRect sourceCrop;
+        HWC2::Transform transform = HWC2::Transform::None;
+        int z;
+        int type;
+        int appId;
+        Region visibleRegion;
+        Region surfaceDamage;
+        sp<NativeHandle> sidebandStream;
+        ui::Dataspace dataspace;
+        hwc_color_t color;
+        bool clearClientTarget = false;
+        bool supportedPerFrameMetadata = false;
+        HdrMetadata hdrMetadata;
+        mat4 colorTransform;
+    } hwc;
+    struct {
+        bool blackoutLayer = false;
+        bool clearArea = false;
+        bool preMultipliedAlpha = false;
+        bool opaque = false;
+        bool disableTexture = false;
+        half4 color;
+        bool useIdentityTransform = false;
+        bool Y410BT2020 = false;
+    } re;
+
+    void dump(const char* tag) const;
+    void dump(std::string& result, const char* tag = nullptr) const;
+    void dumpHwc(std::string& result, const char* tag = nullptr) const;
+    void dumpRe(std::string& result, const char* tag = nullptr) const;
+};
+
+class LayerBE {
+public:
+    friend class Layer;
+    friend class BufferLayer;
+    friend class BufferQueueLayer;
+    friend class BufferStateLayer;
+    friend class ColorLayer;
+    friend class SurfaceFlinger;
+
+    // For unit tests
+    friend class TestableSurfaceFlinger;
+
+    LayerBE(Layer* layer, std::string layerName);
+    explicit LayerBE(const LayerBE& layer);
+
+    void onLayerDisplayed(const sp<Fence>& releaseFence);
+    void clear(renderengine::RenderEngine& renderEngine);
+    renderengine::Mesh& getMesh() { return mMesh; }
+
+    Layer*const mLayer;
+private:
+    // The mesh used to draw the layer in GLES composition mode
+    renderengine::Mesh mMesh;
+
+    // HWC items, accessed from the main thread
+    struct HWCInfo {
+        HWCInfo()
+              : hwc(nullptr),
+                layer(nullptr),
+                forceClientComposition(false),
+                compositionType(HWC2::Composition::Invalid),
+                clearClientTarget(false),
+                transform(HWC2::Transform::None) {}
+
+        HWComposer* hwc;
+        std::shared_ptr<HWC2::Layer> layer;
+        bool forceClientComposition;
+        HWC2::Composition compositionType;
+        bool clearClientTarget;
+        Rect displayFrame;
+        FloatRect sourceCrop;
+        HWComposerBufferCache bufferCache;
+        HWC2::Transform transform;
+    };
+
+    // A layer can be attached to multiple displays when operating in mirror mode
+    // (a.k.a: when several displays are attached with equal layerStack). In this
+    // case we need to keep track. In non-mirror mode, a layer will have only one
+    // HWCInfo.
+    std::unordered_map<DisplayId, HWCInfo> mHwcLayers;
+
+    CompositionInfo compositionInfo;
+};
+
+}; // namespace android
+
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index cc39550..3289e8f 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -51,7 +51,8 @@
     colorProto->set_a(color.a);
 }
 
-void LayerProtoHelper::writeToProto(const Transform& transform, TransformProto* transformProto) {
+void LayerProtoHelper::writeToProto(const ui::Transform& transform,
+                                    TransformProto* transformProto) {
     transformProto->set_dsdx(transform[0][0]);
     transformProto->set_dtdx(transform[0][1]);
     transformProto->set_dsdy(transform[1][0]);
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 860da63..6df5aea 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -16,13 +16,11 @@
 
 #include <layerproto/LayerProtoHeader.h>
 
+#include <math/vec4.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
-
-#include <Transform.h>
-
-#include <math/vec4.h>
+#include <ui/Transform.h>
 
 namespace android {
 namespace surfaceflinger {
@@ -32,7 +30,7 @@
     static void writeToProto(const FloatRect& rect, FloatRectProto* rectProto);
     static void writeToProto(const Region& region, RegionProto* regionProto);
     static void writeToProto(const half4 color, ColorProto* colorProto);
-    static void writeToProto(const Transform& transform, TransformProto* transformProto);
+    static void writeToProto(const ui::Transform& transform, TransformProto* transformProto);
     static void writeToProto(const sp<GraphicBuffer>& buffer, ActiveBufferProto* activeBufferProto);
 };
 
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index a5f0b98..72abea8 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -19,8 +19,6 @@
 #include <gui/BufferItem.h>
 #include <system/window.h>
 
-#include "clz.h"
-
 #define DEBUG_RESIZE 0
 
 namespace android {
@@ -31,6 +29,7 @@
                              bool stickySet,
                              const char* name,
                              int32_t overrideScalingMode,
+                             bool transformToDisplayInverse,
                              bool& freezePositionUpdates)
   : mFront(front),
     mCurrent(current),
@@ -38,6 +37,7 @@
     mStickyTransformSet(stickySet),
     mName(name),
     mOverrideScalingMode(overrideScalingMode),
+    mTransformToDisplayInverse(transformToDisplayInverse),
     mFreezeGeometryUpdates(freezePositionUpdates) {}
 
 bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
@@ -50,26 +50,34 @@
 
     // check that we received a buffer of the right size
     // (Take the buffer's orientation into account)
-    if (item.mTransform & Transform::ROT_90) {
-        swap(bufWidth, bufHeight);
+    if (item.mTransform & ui::Transform::ROT_90) {
+        std::swap(bufWidth, bufHeight);
+    }
+
+    if (mTransformToDisplayInverse) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufWidth, bufHeight);
+        }
     }
 
     int actualScalingMode = mOverrideScalingMode >= 0 ? mOverrideScalingMode : item.mScalingMode;
     bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
-    if (mFront.active != mFront.requested) {
-        if (isFixedSize || (bufWidth == mFront.requested.w && bufHeight == mFront.requested.h)) {
+    if (mFront.active_legacy != mFront.requested_legacy) {
+        if (isFixedSize ||
+            (bufWidth == mFront.requested_legacy.w && bufHeight == mFront.requested_legacy.h)) {
             // Here we pretend the transaction happened by updating the
             // current and drawing states. Drawing state is only accessed
             // in this thread, no need to have it locked
-            mFront.active = mFront.requested;
+            mFront.active_legacy = mFront.requested_legacy;
 
             // We also need to update the current state so that
             // we don't end-up overwriting the drawing state with
             // this stale current state during the next transaction
             //
             // NOTE: We don't need to hold the transaction lock here
-            // because State::active is only accessed from this thread.
-            mCurrent.active = mFront.active;
+            // because State::active_legacy is only accessed from this thread.
+            mCurrent.active_legacy = mFront.active_legacy;
             mCurrent.modified = true;
 
             // recompute visible region
@@ -77,35 +85,32 @@
 
             mFreezeGeometryUpdates = false;
 
-            if (mFront.crop != mFront.requestedCrop) {
-                mFront.crop = mFront.requestedCrop;
-                mCurrent.crop = mFront.requestedCrop;
-                mRecomputeVisibleRegions = true;
-            }
-            if (mFront.finalCrop != mFront.requestedFinalCrop) {
-                mFront.finalCrop = mFront.requestedFinalCrop;
-                mCurrent.finalCrop = mFront.requestedFinalCrop;
+            if (mFront.crop_legacy != mFront.requestedCrop_legacy) {
+                mFront.crop_legacy = mFront.requestedCrop_legacy;
+                mCurrent.crop_legacy = mFront.requestedCrop_legacy;
                 mRecomputeVisibleRegions = true;
             }
         }
 
         ALOGD_IF(DEBUG_RESIZE,
                  "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
-                 "  drawing={ active   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) "
+                 "  drawing={ active_legacy   ={ wh={%4u,%4u} crop_legacy={%4d,%4d,%4d,%4d} "
+                 "(%4d,%4d) "
                  "}\n"
-                 "            requested={ wh={%4u,%4u} }}\n",
-                 mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode, mFront.active.w,
-                 mFront.active.h, mFront.crop.left, mFront.crop.top, mFront.crop.right,
-                 mFront.crop.bottom, mFront.crop.getWidth(), mFront.crop.getHeight(),
-                 mFront.requested.w, mFront.requested.h);
+                 "            requested_legacy={ wh={%4u,%4u} }}\n",
+                 mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode,
+                 mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop_legacy.left,
+                 mFront.crop_legacy.top, mFront.crop_legacy.right, mFront.crop_legacy.bottom,
+                 mFront.crop_legacy.getWidth(), mFront.crop_legacy.getHeight(),
+                 mFront.requested_legacy.w, mFront.requested_legacy.h);
     }
 
     if (!isFixedSize && !mStickyTransformSet) {
-        if (mFront.active.w != bufWidth || mFront.active.h != bufHeight) {
+        if (mFront.active_legacy.w != bufWidth || mFront.active_legacy.h != bufHeight) {
             // reject this buffer
             ALOGE("[%s] rejecting buffer: "
-                  "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
-                  mName, bufWidth, bufHeight, mFront.active.w, mFront.active.h);
+                  "bufWidth=%d, bufHeight=%d, front.active_legacy.{w=%d, h=%d}",
+                  mName, bufWidth, bufHeight, mFront.active_legacy.w, mFront.active_legacy.h);
             return true;
         }
     }
@@ -118,16 +123,17 @@
     // We latch the transparent region here, instead of above where we latch
     // the rest of the geometry because it is only content but not necessarily
     // resize dependent.
-    if (!mFront.activeTransparentRegion.isTriviallyEqual(mFront.requestedTransparentRegion)) {
-        mFront.activeTransparentRegion = mFront.requestedTransparentRegion;
+    if (!mFront.activeTransparentRegion_legacy.isTriviallyEqual(
+                mFront.requestedTransparentRegion_legacy)) {
+        mFront.activeTransparentRegion_legacy = mFront.requestedTransparentRegion_legacy;
 
         // We also need to update the current state so that
         // we don't end-up overwriting the drawing state with
         // this stale current state during the next transaction
         //
         // NOTE: We don't need to hold the transaction lock here
-        // because State::active is only accessed from this thread.
-        mCurrent.activeTransparentRegion = mFront.activeTransparentRegion;
+        // because State::active_legacy is only accessed from this thread.
+        mCurrent.activeTransparentRegion_legacy = mFront.activeTransparentRegion_legacy;
 
         // recompute visible region
         mRecomputeVisibleRegions = true;
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
index 40972aa..63d51de 100644
--- a/services/surfaceflinger/LayerRejecter.h
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -29,6 +29,7 @@
                       bool stickySet,
                       const char *name,
                       int32_t overrideScalingMode,
+                      bool transformToDisplayInverse,
                       bool &freezePositionUpdates);
 
         virtual bool reject(const sp<GraphicBuffer> &buf, const BufferItem &item);
@@ -40,6 +41,7 @@
         bool mStickyTransformSet;
         const char *mName;
         int32_t mOverrideScalingMode;
+        bool mTransformToDisplayInverse;
         bool &mFreezeGeometryUpdates;
     };
 }  // namespace android
diff --git a/services/surfaceflinger/LayerStats.cpp b/services/surfaceflinger/LayerStats.cpp
index 2a67955..a2d1feb 100644
--- a/services/surfaceflinger/LayerStats.cpp
+++ b/services/surfaceflinger/LayerStats.cpp
@@ -23,11 +23,13 @@
 
 #include <android-base/stringprintf.h>
 #include <log/log.h>
-#include <utils/String8.h>
 #include <utils/Trace.h>
 
 namespace android {
 
+using base::StringAppendF;
+using base::StringPrintf;
+
 void LayerStats::enable() {
     ATRACE_CALL();
     std::lock_guard<std::mutex> lock(mMutex);
@@ -57,33 +59,31 @@
 }
 
 void LayerStats::traverseLayerTreeStatsLocked(
-        const std::vector<std::unique_ptr<LayerProtoParser::Layer>>& layerTree,
+        const std::vector<LayerProtoParser::Layer*>& layerTree,
         const LayerProtoParser::LayerGlobal& layerGlobal,
         std::vector<std::string>* const outLayerShapeVec) {
     for (const auto& layer : layerTree) {
         if (!layer) continue;
         traverseLayerTreeStatsLocked(layer->children, layerGlobal, outLayerShapeVec);
         std::string key = "";
-        base::StringAppendF(&key, ",%s", layer->type.c_str());
-        base::StringAppendF(&key, ",%s", layerCompositionType(layer->hwcCompositionType));
-        base::StringAppendF(&key, ",%d", layer->isProtected);
-        base::StringAppendF(&key, ",%s", layerTransform(layer->hwcTransform));
-        base::StringAppendF(&key, ",%s", layerPixelFormat(layer->activeBuffer.format).c_str());
-        base::StringAppendF(&key, ",%s", layer->dataspace.c_str());
-        base::StringAppendF(&key, ",%s",
-                            destinationLocation(layer->hwcFrame.left, layerGlobal.resolution[0],
-                                                true));
-        base::StringAppendF(&key, ",%s",
-                            destinationLocation(layer->hwcFrame.top, layerGlobal.resolution[1],
-                                                false));
-        base::StringAppendF(&key, ",%s",
-                            destinationSize(layer->hwcFrame.right - layer->hwcFrame.left,
-                                            layerGlobal.resolution[0], true));
-        base::StringAppendF(&key, ",%s",
-                            destinationSize(layer->hwcFrame.bottom - layer->hwcFrame.top,
-                                            layerGlobal.resolution[1], false));
-        base::StringAppendF(&key, ",%s", scaleRatioWH(layer.get()).c_str());
-        base::StringAppendF(&key, ",%s", alpha(static_cast<float>(layer->color.a)));
+        StringAppendF(&key, ",%s", layer->type.c_str());
+        StringAppendF(&key, ",%s", layerCompositionType(layer->hwcCompositionType));
+        StringAppendF(&key, ",%d", layer->isProtected);
+        StringAppendF(&key, ",%s", layerTransform(layer->hwcTransform));
+        StringAppendF(&key, ",%s", layerPixelFormat(layer->activeBuffer.format).c_str());
+        StringAppendF(&key, ",%s", layer->dataspace.c_str());
+        StringAppendF(&key, ",%s",
+                      destinationLocation(layer->hwcFrame.left, layerGlobal.resolution[0], true));
+        StringAppendF(&key, ",%s",
+                      destinationLocation(layer->hwcFrame.top, layerGlobal.resolution[1], false));
+        StringAppendF(&key, ",%s",
+                      destinationSize(layer->hwcFrame.right - layer->hwcFrame.left,
+                                      layerGlobal.resolution[0], true));
+        StringAppendF(&key, ",%s",
+                      destinationSize(layer->hwcFrame.bottom - layer->hwcFrame.top,
+                                      layerGlobal.resolution[1], false));
+        StringAppendF(&key, ",%s", scaleRatioWH(layer).c_str());
+        StringAppendF(&key, ",%s", alpha(static_cast<float>(layer->color.a)));
 
         outLayerShapeVec->push_back(key);
         ALOGV("%s", key.c_str());
@@ -98,12 +98,12 @@
     std::vector<std::string> layerShapeVec;
 
     std::lock_guard<std::mutex> lock(mMutex);
-    traverseLayerTreeStatsLocked(layerTree, layerGlobal, &layerShapeVec);
+    traverseLayerTreeStatsLocked(layerTree.topLevelLayers, layerGlobal, &layerShapeVec);
 
     std::string layerShapeKey =
-            base::StringPrintf("%d,%s,%s,%s", static_cast<int32_t>(layerShapeVec.size()),
-                               layerGlobal.colorMode.c_str(), layerGlobal.colorTransform.c_str(),
-                               layerTransform(layerGlobal.globalTransform));
+            StringPrintf("%d,%s,%s,%s", static_cast<int32_t>(layerShapeVec.size()),
+                         layerGlobal.colorMode.c_str(), layerGlobal.colorTransform.c_str(),
+                         layerTransform(layerGlobal.globalTransform));
     ALOGV("%s", layerShapeKey.c_str());
 
     std::sort(layerShapeVec.begin(), layerShapeVec.end(), std::greater<std::string>());
@@ -114,7 +114,7 @@
     mLayerShapeStatsMap[layerShapeKey]++;
 }
 
-void LayerStats::dump(String8& result) {
+void LayerStats::dump(std::string& result) {
     ATRACE_CALL();
     ALOGD("Dumping");
     std::lock_guard<std::mutex> lock(mMutex);
@@ -122,7 +122,7 @@
     result.append("LayerType,CompositionType,IsProtected,Transform,PixelFormat,Dataspace,");
     result.append("DstX,DstY,DstWidth,DstHeight,WScale,HScale,Alpha\n");
     for (auto& u : mLayerShapeStatsMap) {
-        result.appendFormat("%u,%s\n", u.second, u.first.c_str());
+        StringAppendF(&result, "%u,%s\n", u.second, u.first.c_str());
     }
 }
 
diff --git a/services/surfaceflinger/LayerStats.h b/services/surfaceflinger/LayerStats.h
index bd17d82..62b2688 100644
--- a/services/surfaceflinger/LayerStats.h
+++ b/services/surfaceflinger/LayerStats.h
@@ -24,7 +24,6 @@
 using namespace android::surfaceflinger;
 
 namespace android {
-class String8;
 
 class LayerStats {
 public:
@@ -33,12 +32,12 @@
     void clear();
     bool isEnabled();
     void logLayerStats(const LayersProto& layersProto);
-    void dump(String8& result);
+    void dump(std::string& result);
 
 private:
     // Traverse layer tree to get all visible layers' stats
     void traverseLayerTreeStatsLocked(
-            const std::vector<std::unique_ptr<LayerProtoParser::Layer>>& layerTree,
+            const std::vector<LayerProtoParser::Layer*>& layerTree,
             const LayerProtoParser::LayerGlobal& layerGlobal,
             std::vector<std::string>* const outLayerShapeVec);
     // Convert layer's top-left position into 8x8 percentage of the display
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 389fbd2..06e3d9c 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#include "MessageQueue.h"
 #include "MonitoredProducer.h"
-#include "SurfaceFlinger.h"
 #include "Layer.h"
+#include "SurfaceFlinger.h"
+
+#include "Scheduler/MessageQueue.h"
 
 namespace android {
 
diff --git a/services/surfaceflinger/NativeWindowSurface.cpp b/services/surfaceflinger/NativeWindowSurface.cpp
new file mode 100644
index 0000000..3fff928
--- /dev/null
+++ b/services/surfaceflinger/NativeWindowSurface.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeWindowSurface.h"
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+
+namespace android::surfaceflinger {
+
+NativeWindowSurface::~NativeWindowSurface() = default;
+
+namespace impl {
+
+std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+        const sp<IGraphicBufferProducer>& producer) {
+    class NativeWindowSurface final : public surfaceflinger::NativeWindowSurface {
+    public:
+        explicit NativeWindowSurface(const sp<IGraphicBufferProducer>& producer)
+              : mSurface(new Surface(producer, /* controlledByApp */ false)) {}
+
+        ~NativeWindowSurface() override = default;
+
+        sp<ANativeWindow> getNativeWindow() const override { return mSurface; }
+
+        void preallocateBuffers() override { mSurface->allocateBuffers(); }
+
+    private:
+        sp<Surface> mSurface;
+    };
+
+    return std::make_unique<NativeWindowSurface>(producer);
+}
+
+} // namespace impl
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/NativeWindowSurface.h b/services/surfaceflinger/NativeWindowSurface.h
new file mode 100644
index 0000000..f34a45a
--- /dev/null
+++ b/services/surfaceflinger/NativeWindowSurface.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <utils/StrongPointer.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+class IGraphicBufferProducer;
+
+namespace surfaceflinger {
+
+// A thin interface to abstract creating instances of Surface (gui/Surface.h) to
+// use as a NativeWindow.
+class NativeWindowSurface {
+public:
+    virtual ~NativeWindowSurface();
+
+    // Gets the NativeWindow to use for the surface.
+    virtual sp<ANativeWindow> getNativeWindow() const = 0;
+
+    // Indicates that the surface should allocate its buffers now.
+    virtual void preallocateBuffers() = 0;
+};
+
+namespace impl {
+
+std::unique_ptr<NativeWindowSurface> createNativeWindowSurface(const sp<IGraphicBufferProducer>&);
+
+} // namespace impl
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp
index 1a8edf3..93759e8 100644
--- a/services/surfaceflinger/RenderArea.cpp
+++ b/services/surfaceflinger/RenderArea.cpp
@@ -1,7 +1,5 @@
 #include "RenderArea.h"
 
-#include <gui/LayerState.h>
-
 namespace android {
 
 float RenderArea::getCaptureFillValue(CaptureFill captureFill) {
@@ -13,37 +11,5 @@
             return 1.0f;
     }
 }
-/*
- * Checks that the requested width and height are valid and updates them to the render area
- * dimensions if they are set to 0
- */
-status_t RenderArea::updateDimensions(int displayRotation) {
-    // get screen geometry
-
-    uint32_t width = getWidth();
-    uint32_t height = getHeight();
-
-    if (mRotationFlags & Transform::ROT_90) {
-        std::swap(width, height);
-    }
-
-    if (displayRotation & DisplayState::eOrientationSwapMask) {
-        std::swap(width, height);
-    }
-
-    if ((mReqWidth > width) || (mReqHeight > height)) {
-        ALOGE("size mismatch (%d, %d) > (%d, %d)", mReqWidth, mReqHeight, width, height);
-        return BAD_VALUE;
-    }
-
-    if (mReqWidth == 0) {
-        mReqWidth = width;
-    }
-    if (mReqHeight == 0) {
-        mReqHeight = height;
-    }
-
-    return NO_ERROR;
-}
 
 } // namespace android
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 96e4b5f..9bad6de 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -1,50 +1,87 @@
 #pragma once
 
 #include <ui/GraphicTypes.h>
-
-#include "Transform.h"
+#include <ui/Transform.h>
 
 #include <functional>
 
 namespace android {
 
+// RenderArea describes a rectangular area that layers can be rendered to.
+//
+// There is a logical render area and a physical render area.  When a layer is
+// rendered to the render area, it is first transformed and clipped to the logical
+// render area.  The transformed and clipped layer is then projected onto the
+// physical render area.
 class RenderArea {
-
 public:
     enum class CaptureFill {CLEAR, OPAQUE};
 
     static float getCaptureFillValue(CaptureFill captureFill);
 
-    RenderArea(uint32_t reqHeight, uint32_t reqWidth, CaptureFill captureFill,
-               ISurfaceComposer::Rotation rotation = ISurfaceComposer::eRotateNone)
-          : mReqHeight(reqHeight), mReqWidth(reqWidth), mCaptureFill(captureFill) {
-        mRotationFlags = Transform::fromRotation(rotation);
-    }
+    RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
+               ui::Dataspace reqDataSpace,
+               ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
+          : mReqWidth(reqWidth),
+            mReqHeight(reqHeight),
+            mReqDataSpace(reqDataSpace),
+            mCaptureFill(captureFill),
+            mRotationFlags(rotation) {}
 
     virtual ~RenderArea() = default;
 
-    virtual const Transform& getTransform() const = 0;
-    virtual Rect getBounds() const = 0;
-    virtual int getHeight() const = 0;
-    virtual int getWidth() const = 0;
-    virtual bool isSecure() const = 0;
-    virtual bool needsFiltering() const = 0;
-    virtual Rect getSourceCrop() const = 0;
-
+    // Invoke drawLayers to render layers into the render area.
     virtual void render(std::function<void()> drawLayers) { drawLayers(); }
 
-    int getReqHeight() const { return mReqHeight; };
-    int getReqWidth() const { return mReqWidth; };
-    Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
-    status_t updateDimensions(int displayRotation);
+    // Returns true if the render area is secure.  A secure layer should be
+    // blacked out / skipped when rendered to an insecure render area.
+    virtual bool isSecure() const = 0;
 
+    // Returns true if the otherwise disabled layer filtering should be
+    // enabled when rendering to this render area.
+    virtual bool needsFiltering() const = 0;
+
+    // Returns the transform to be applied on layers to transform them into
+    // the logical render area.
+    virtual const ui::Transform& getTransform() const = 0;
+
+    // Returns the size of the logical render area.  Layers are clipped to the
+    // logical render area.
+    virtual int getWidth() const = 0;
+    virtual int getHeight() const = 0;
+    virtual Rect getBounds() const = 0;
+
+    // Returns the source crop of the render area.  The source crop defines
+    // how layers are projected from the logical render area onto the physical
+    // render area.  It can be larger than the logical render area.  It can
+    // also be optionally rotated.
+    //
+    // Layers are first clipped to the source crop (in addition to being
+    // clipped to the logical render area already).  The source crop and the
+    // layers are then rotated around the center of the source crop, and
+    // scaled to the physical render area linearly.
+    virtual Rect getSourceCrop() const = 0;
+
+    // Returns the rotation of the source crop and the layers.
+    ui::Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
+
+    // Returns the size of the physical render area.
+    int getReqWidth() const { return mReqWidth; };
+    int getReqHeight() const { return mReqHeight; };
+
+    // Returns the composition data space of the render area.
+    ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
+
+    // Returns the fill color of the physical render area.  Regions not
+    // covered by any rendered layer should be filled with this color.
     CaptureFill getCaptureFill() const { return mCaptureFill; };
 
 private:
-    uint32_t mReqHeight;
-    uint32_t mReqWidth;
-    Transform::orientation_flags mRotationFlags;
-    CaptureFill mCaptureFill;
+    const uint32_t mReqWidth;
+    const uint32_t mReqHeight;
+    const ui::Dataspace mReqDataSpace;
+    const CaptureFill mCaptureFill;
+    const ui::Transform::orientation_flags mRotationFlags;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp
deleted file mode 100644
index c218e4d..0000000
--- a/services/surfaceflinger/RenderEngine/Description.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2013 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 <stdint.h>
-#include <string.h>
-
-#include <utils/TypeHelpers.h>
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include "Description.h"
-
-namespace android {
-
-void Description::setPremultipliedAlpha(bool premultipliedAlpha) {
-    mPremultipliedAlpha = premultipliedAlpha;
-}
-
-void Description::setOpaque(bool opaque) {
-    mOpaque = opaque;
-}
-
-void Description::setTexture(const Texture& texture) {
-    mTexture = texture;
-    mTextureEnabled = true;
-}
-
-void Description::disableTexture() {
-    mTextureEnabled = false;
-}
-
-void Description::setColor(const half4& color) {
-    mColor = color;
-}
-
-void Description::setProjectionMatrix(const mat4& mtx) {
-    mProjectionMatrix = mtx;
-}
-
-void Description::setColorMatrix(const mat4& mtx) {
-    mColorMatrix = mtx;
-}
-
-void Description::setInputTransformMatrix(const mat3& matrix) {
-    mInputTransformMatrix = matrix;
-}
-
-void Description::setOutputTransformMatrix(const mat4& matrix) {
-    mOutputTransformMatrix = matrix;
-}
-
-bool Description::hasInputTransformMatrix() const {
-    const mat3 identity;
-    return mInputTransformMatrix != identity;
-}
-
-bool Description::hasOutputTransformMatrix() const {
-    const mat4 identity;
-    return mOutputTransformMatrix != identity;
-}
-
-bool Description::hasColorMatrix() const {
-    const mat4 identity;
-    return mColorMatrix != identity;
-}
-
-const mat4& Description::getColorMatrix() const {
-    return mColorMatrix;
-}
-
-void Description::setY410BT2020(bool enable) {
-    mY410BT2020 = enable;
-}
-
-void Description::setInputTransferFunction(TransferFunction transferFunction) {
-    mInputTransferFunction = transferFunction;
-}
-
-void Description::setOutputTransferFunction(TransferFunction transferFunction) {
-    mOutputTransferFunction = transferFunction;
-}
-
-void Description::setDisplayMaxLuminance(const float maxLuminance) {
-    mDisplayMaxLuminance = maxLuminance;
-}
-
-} /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h
deleted file mode 100644
index 6ebb340..0000000
--- a/services/surfaceflinger/RenderEngine/Description.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2013 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 <GLES2/gl2.h>
-#include "Texture.h"
-
-#ifndef SF_RENDER_ENGINE_DESCRIPTION_H_
-#define SF_RENDER_ENGINE_DESCRIPTION_H_
-
-namespace android {
-
-class Program;
-
-/*
- * This holds the state of the rendering engine. This class is used
- * to generate a corresponding GLSL program and set the appropriate
- * uniform.
- *
- * Program and ProgramCache are friends and access the state directly
- */
-class Description {
-public:
-    Description() = default;
-    ~Description() = default;
-
-    void setPremultipliedAlpha(bool premultipliedAlpha);
-    void setOpaque(bool opaque);
-    void setTexture(const Texture& texture);
-    void disableTexture();
-    void setColor(const half4& color);
-    void setProjectionMatrix(const mat4& mtx);
-    void setColorMatrix(const mat4& mtx);
-    void setInputTransformMatrix(const mat3& matrix);
-    void setOutputTransformMatrix(const mat4& matrix);
-    bool hasInputTransformMatrix() const;
-    bool hasOutputTransformMatrix() const;
-    bool hasColorMatrix() const;
-    const mat4& getColorMatrix() const;
-
-    void setY410BT2020(bool enable);
-
-    enum class TransferFunction : int {
-        LINEAR,
-        SRGB,
-        ST2084,
-        HLG,  // Hybrid Log-Gamma for HDR.
-    };
-    void setInputTransferFunction(TransferFunction transferFunction);
-    void setOutputTransferFunction(TransferFunction transferFunction);
-    void setDisplayMaxLuminance(const float maxLuminance);
-
-private:
-    friend class Program;
-    friend class ProgramCache;
-
-    // whether textures are premultiplied
-    bool mPremultipliedAlpha = false;
-    // whether this layer is marked as opaque
-    bool mOpaque = true;
-
-    // Texture this layer uses
-    Texture mTexture;
-    bool mTextureEnabled = false;
-
-    // color used when texturing is disabled or when setting alpha.
-    half4 mColor;
-
-    // true if the sampled pixel values are in Y410/BT2020 rather than RGBA
-    bool mY410BT2020 = false;
-
-    // transfer functions for the input/output
-    TransferFunction mInputTransferFunction = TransferFunction::LINEAR;
-    TransferFunction mOutputTransferFunction = TransferFunction::LINEAR;
-
-    float mDisplayMaxLuminance;
-
-    // projection matrix
-    mat4 mProjectionMatrix;
-    mat4 mColorMatrix;
-    mat3 mInputTransformMatrix;
-    mat4 mOutputTransformMatrix;
-};
-
-} /* namespace android */
-
-#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
deleted file mode 100644
index 744a70c..0000000
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright 2013 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
-#undef LOG_TAG
-#define LOG_TAG "RenderEngine"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <ui/ColorSpace.h>
-#include <ui/DebugUtils.h>
-#include <ui/Rect.h>
-
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-#include <cutils/compiler.h>
-#include <gui/ISurfaceComposer.h>
-#include <math.h>
-
-#include "Description.h"
-#include "GLES20RenderEngine.h"
-#include "Mesh.h"
-#include "Program.h"
-#include "ProgramCache.h"
-#include "Texture.h"
-
-#include <fstream>
-#include <sstream>
-
-// ---------------------------------------------------------------------------
-bool checkGlError(const char* op, int lineNumber) {
-    bool errorFound = false;
-    GLint error = glGetError();
-    while (error != GL_NO_ERROR) {
-        errorFound = true;
-        error = glGetError();
-        ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
-    }
-    return errorFound;
-}
-
-static constexpr bool outputDebugPPMs = false;
-
-void writePPM(const char* basename, GLuint width, GLuint height) {
-    ALOGV("writePPM #%s: %d x %d", basename, width, height);
-
-    std::vector<GLubyte> pixels(width * height * 4);
-    std::vector<GLubyte> outBuffer(width * height * 3);
-
-    // TODO(courtneygo): We can now have float formats, need
-    // to remove this code or update to support.
-    // Make returned pixels fit in uint32_t, one byte per component
-    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
-    if (checkGlError(__FUNCTION__, __LINE__)) {
-        return;
-    }
-
-    std::string filename(basename);
-    filename.append(".ppm");
-    std::ofstream file(filename.c_str(), std::ios::binary);
-    if (!file.is_open()) {
-        ALOGE("Unable to open file: %s", filename.c_str());
-        ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
-              "surfaceflinger to write debug images");
-        return;
-    }
-
-    file << "P6\n";
-    file << width << "\n";
-    file << height << "\n";
-    file << 255 << "\n";
-
-    auto ptr = reinterpret_cast<char*>(pixels.data());
-    auto outPtr = reinterpret_cast<char*>(outBuffer.data());
-    for (int y = height - 1; y >= 0; y--) {
-        char* data = ptr + y * width * sizeof(uint32_t);
-
-        for (GLuint x = 0; x < width; x++) {
-            // Only copy R, G and B components
-            outPtr[0] = data[0];
-            outPtr[1] = data[1];
-            outPtr[2] = data[2];
-            data += sizeof(uint32_t);
-            outPtr += 3;
-        }
-    }
-    file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
-}
-
-// ---------------------------------------------------------------------------
-namespace android {
-namespace RE {
-namespace impl {
-// ---------------------------------------------------------------------------
-
-using ui::Dataspace;
-
-GLES20RenderEngine::GLES20RenderEngine(uint32_t featureFlags)
-      : RenderEngine(featureFlags),
-        mVpWidth(0),
-        mVpHeight(0),
-        mPlatformHasWideColor((featureFlags & WIDE_COLOR_SUPPORT) != 0) {
-    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
-    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
-
-    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-    glPixelStorei(GL_PACK_ALIGNMENT, 4);
-
-    const uint16_t protTexData[] = {0};
-    glGenTextures(1, &mProtectedTexName);
-    glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
-
-    // mColorBlindnessCorrection = M;
-
-    if (mPlatformHasWideColor) {
-        ColorSpace srgb(ColorSpace::sRGB());
-        ColorSpace displayP3(ColorSpace::DisplayP3());
-        ColorSpace bt2020(ColorSpace::BT2020());
-
-        // Compute sRGB to Display P3 transform matrix.
-        // NOTE: For now, we are limiting output wide color space support to
-        // Display-P3 only.
-        mSrgbToDisplayP3 = mat4(ColorSpaceConnector(srgb, displayP3).getTransform());
-
-        // Compute Display P3 to sRGB transform matrix.
-        mDisplayP3ToSrgb = mat4(ColorSpaceConnector(displayP3, srgb).getTransform());
-
-        // no chromatic adaptation needed since all color spaces use D65 for their white points.
-        mSrgbToXyz = srgb.getRGBtoXYZ();
-        mDisplayP3ToXyz = displayP3.getRGBtoXYZ();
-        mBt2020ToXyz = bt2020.getRGBtoXYZ();
-        mXyzToSrgb = mat4(srgb.getXYZtoRGB());
-        mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB());
-        mXyzToBt2020 = mat4(bt2020.getXYZtoRGB());
-    }
-}
-
-GLES20RenderEngine::~GLES20RenderEngine() {}
-
-size_t GLES20RenderEngine::getMaxTextureSize() const {
-    return mMaxTextureSize;
-}
-
-size_t GLES20RenderEngine::getMaxViewportDims() const {
-    return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1];
-}
-
-void GLES20RenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
-                                                  size_t hwh, bool yswap,
-                                                  Transform::orientation_flags rotation) {
-    int32_t l = sourceCrop.left;
-    int32_t r = sourceCrop.right;
-
-    // In GL, (0, 0) is the bottom-left corner, so flip y coordinates
-    int32_t t = hwh - sourceCrop.top;
-    int32_t b = hwh - sourceCrop.bottom;
-
-    mat4 m;
-    if (yswap) {
-        m = mat4::ortho(l, r, t, b, 0, 1);
-    } else {
-        m = mat4::ortho(l, r, b, t, 0, 1);
-    }
-
-    // Apply custom rotation to the projection.
-    float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
-    switch (rotation) {
-        case Transform::ROT_0:
-            break;
-        case Transform::ROT_90:
-            m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m;
-            break;
-        case Transform::ROT_180:
-            m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m;
-            break;
-        case Transform::ROT_270:
-            m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m;
-            break;
-        default:
-            break;
-    }
-
-    glViewport(0, 0, vpw, vph);
-    mState.setProjectionMatrix(m);
-    mVpWidth = vpw;
-    mVpHeight = vph;
-}
-
-void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque,
-                                            bool disableTexture, const half4& color) {
-    mState.setPremultipliedAlpha(premultipliedAlpha);
-    mState.setOpaque(opaque);
-    mState.setColor(color);
-
-    if (disableTexture) {
-        mState.disableTexture();
-    }
-
-    if (color.a < 1.0f || !opaque) {
-        glEnable(GL_BLEND);
-        glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    } else {
-        glDisable(GL_BLEND);
-    }
-}
-
-void GLES20RenderEngine::setSourceY410BT2020(bool enable) {
-    mState.setY410BT2020(enable);
-}
-
-void GLES20RenderEngine::setSourceDataSpace(Dataspace source) {
-    mDataSpace = source;
-}
-
-void GLES20RenderEngine::setOutputDataSpace(Dataspace dataspace) {
-    mOutputDataSpace = dataspace;
-}
-
-void GLES20RenderEngine::setDisplayMaxLuminance(const float maxLuminance) {
-    mState.setDisplayMaxLuminance(maxLuminance);
-}
-
-void GLES20RenderEngine::setupLayerTexturing(const Texture& texture) {
-    GLuint target = texture.getTextureTarget();
-    glBindTexture(target, texture.getTextureName());
-    GLenum filter = GL_NEAREST;
-    if (texture.getFiltering()) {
-        filter = GL_LINEAR;
-    }
-    glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
-    glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
-
-    mState.setTexture(texture);
-}
-
-void GLES20RenderEngine::setupLayerBlackedOut() {
-    glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
-    Texture texture(Texture::TEXTURE_2D, mProtectedTexName);
-    texture.setDimensions(1, 1); // FIXME: we should get that from somewhere
-    mState.setTexture(texture);
-}
-
-void GLES20RenderEngine::setupColorTransform(const mat4& colorTransform) {
-    mState.setColorMatrix(colorTransform);
-}
-
-void GLES20RenderEngine::disableTexturing() {
-    mState.disableTexture();
-}
-
-void GLES20RenderEngine::disableBlending() {
-    glDisable(GL_BLEND);
-}
-
-void GLES20RenderEngine::bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName,
-                                                uint32_t* fbName, uint32_t* status) {
-    GLuint tname, name;
-    // turn our EGLImage into a texture
-    glGenTextures(1, &tname);
-    glBindTexture(GL_TEXTURE_2D, tname);
-    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
-
-    // create a Framebuffer Object to render into
-    glGenFramebuffers(1, &name);
-    glBindFramebuffer(GL_FRAMEBUFFER, name);
-    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tname, 0);
-
-    *status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-    *texName = tname;
-    *fbName = name;
-}
-
-void GLES20RenderEngine::unbindFramebuffer(uint32_t texName, uint32_t fbName) {
-    glBindFramebuffer(GL_FRAMEBUFFER, 0);
-    glDeleteFramebuffers(1, &fbName);
-    glDeleteTextures(1, &texName);
-}
-
-void GLES20RenderEngine::setupFillWithColor(float r, float g, float b, float a) {
-    mState.setPremultipliedAlpha(true);
-    mState.setOpaque(false);
-    mState.setColor(half4(r, g, b, a));
-    mState.disableTexture();
-    glDisable(GL_BLEND);
-}
-
-void GLES20RenderEngine::drawMesh(const Mesh& mesh) {
-    ATRACE_CALL();
-    if (mesh.getTexCoordsSize()) {
-        glEnableVertexAttribArray(Program::texCoords);
-        glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE,
-                              mesh.getByteStride(), mesh.getTexCoords());
-    }
-
-    glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
-                          mesh.getByteStride(), mesh.getPositions());
-
-    // By default, DISPLAY_P3 is the only supported wide color output. However,
-    // when HDR content is present, hardware composer may be able to handle
-    // BT2020 data space, in that case, the output data space is set to be
-    // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
-    // to respect this and convert non-HDR content to HDR format.
-    if (mPlatformHasWideColor) {
-        Description wideColorState = mState;
-        Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
-        Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
-        Dataspace outputStandard = static_cast<Dataspace>(mOutputDataSpace &
-                                                          Dataspace::STANDARD_MASK);
-        Dataspace outputTransfer = static_cast<Dataspace>(mOutputDataSpace &
-                                                          Dataspace::TRANSFER_MASK);
-        bool needsXYZConversion = needsXYZTransformMatrix();
-
-        if (needsXYZConversion) {
-            // The supported input color spaces are standard RGB, Display P3 and BT2020.
-            switch (inputStandard) {
-                case Dataspace::STANDARD_DCI_P3:
-                    wideColorState.setInputTransformMatrix(mDisplayP3ToXyz);
-                    break;
-                case Dataspace::STANDARD_BT2020:
-                    wideColorState.setInputTransformMatrix(mBt2020ToXyz);
-                    break;
-                default:
-                    wideColorState.setInputTransformMatrix(mSrgbToXyz);
-                    break;
-            }
-
-            // The supported output color spaces are BT2020, Display P3 and standard RGB.
-            switch (outputStandard) {
-                case Dataspace::STANDARD_BT2020:
-                    wideColorState.setOutputTransformMatrix(mXyzToBt2020);
-                    break;
-                case Dataspace::STANDARD_DCI_P3:
-                    wideColorState.setOutputTransformMatrix(mXyzToDisplayP3);
-                    break;
-                default:
-                    wideColorState.setOutputTransformMatrix(mXyzToSrgb);
-                    break;
-            }
-        } else if (inputStandard != outputStandard) {
-            // At this point, the input data space and output data space could be both
-            // HDR data spaces, but they match each other, we do nothing in this case.
-            // In addition to the case above, the input data space could be
-            // - scRGB linear
-            // - scRGB non-linear
-            // - sRGB
-            // - Display P3
-            // The output data spaces could be
-            // - sRGB
-            // - Display P3
-            if (outputStandard == Dataspace::STANDARD_BT709) {
-                wideColorState.setOutputTransformMatrix(mDisplayP3ToSrgb);
-            } else if (outputStandard == Dataspace::STANDARD_DCI_P3) {
-                wideColorState.setOutputTransformMatrix(mSrgbToDisplayP3);
-            }
-        }
-
-        // we need to convert the RGB value to linear space and convert it back when:
-        // - there is a color matrix that is not an identity matrix, or
-        // - there is an output transform matrix that is not an identity matrix, or
-        // - the input transfer function doesn't match the output transfer function.
-        if (wideColorState.hasColorMatrix() || wideColorState.hasOutputTransformMatrix() ||
-            inputTransfer != outputTransfer) {
-            switch (inputTransfer) {
-                case Dataspace::TRANSFER_ST2084:
-                    wideColorState.setInputTransferFunction(Description::TransferFunction::ST2084);
-                    break;
-                case Dataspace::TRANSFER_HLG:
-                    wideColorState.setInputTransferFunction(Description::TransferFunction::HLG);
-                    break;
-                case Dataspace::TRANSFER_LINEAR:
-                    wideColorState.setInputTransferFunction(Description::TransferFunction::LINEAR);
-                    break;
-                default:
-                    wideColorState.setInputTransferFunction(Description::TransferFunction::SRGB);
-                    break;
-            }
-
-            switch (outputTransfer) {
-                case Dataspace::TRANSFER_ST2084:
-                    wideColorState.setOutputTransferFunction(Description::TransferFunction::ST2084);
-                    break;
-                case Dataspace::TRANSFER_HLG:
-                    wideColorState.setOutputTransferFunction(Description::TransferFunction::HLG);
-                    break;
-                default:
-                    wideColorState.setOutputTransferFunction(Description::TransferFunction::SRGB);
-                    break;
-            }
-        }
-
-        ProgramCache::getInstance().useProgram(wideColorState);
-
-        glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
-
-        if (outputDebugPPMs) {
-            static uint64_t wideColorFrameCount = 0;
-            std::ostringstream out;
-            out << "/data/texture_out" << wideColorFrameCount++;
-            writePPM(out.str().c_str(), mVpWidth, mVpHeight);
-        }
-    } else {
-        ProgramCache::getInstance().useProgram(mState);
-
-        glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
-    }
-
-    if (mesh.getTexCoordsSize()) {
-        glDisableVertexAttribArray(Program::texCoords);
-    }
-}
-
-void GLES20RenderEngine::dump(String8& result) {
-    RenderEngine::dump(result);
-    result.appendFormat("RenderEngine last dataspace conversion: (%s) to (%s)\n",
-                        dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
-                        dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
-}
-
-bool GLES20RenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
-    const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK);
-    const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK);
-    return standard == Dataspace::STANDARD_BT2020 &&
-        (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG);
-}
-
-// For convenience, we want to convert the input color space to XYZ color space first,
-// and then convert from XYZ color space to output color space when
-// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or
-//   HDR content will be tone-mapped to SDR; Or,
-// - there are HDR PQ and HLG contents presented at the same time, where we want to convert
-//   HLG content to PQ content.
-// In either case above, we need to operate the Y value in XYZ color space. Thus, when either
-// input data space or output data space is HDR data space, and the input transfer function
-// doesn't match the output transfer function, we would enable an intermediate transfrom to
-// XYZ color space.
-bool GLES20RenderEngine::needsXYZTransformMatrix() const {
-    const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace);
-    const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace);
-    const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
-    const Dataspace outputTransfer = static_cast<Dataspace>(mOutputDataSpace &
-                                                            Dataspace::TRANSFER_MASK);
-
-    return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
-}
-
-// ---------------------------------------------------------------------------
-} // namespace impl
-} // namespace RE
-} // namespace android
-// ---------------------------------------------------------------------------
-
-#if defined(__gl_h_)
-#error "don't include gl/gl.h in this file"
-#endif
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
deleted file mode 100644
index cc8eb1d..0000000
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2013 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 SF_GLES20RENDERENGINE_H_
-#define SF_GLES20RENDERENGINE_H_
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <GLES2/gl2.h>
-#include <Transform.h>
-
-#include "Description.h"
-#include "ProgramCache.h"
-#include "RenderEngine.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class String8;
-class Mesh;
-class Texture;
-
-namespace RE {
-namespace impl {
-
-class GLES20RenderEngine : public RenderEngine {
-    GLuint mProtectedTexName;
-    GLint mMaxViewportDims[2];
-    GLint mMaxTextureSize;
-    GLuint mVpWidth;
-    GLuint mVpHeight;
-
-    struct Group {
-        GLuint texture;
-        GLuint fbo;
-        GLuint width;
-        GLuint height;
-        mat4 colorTransform;
-    };
-
-    Description mState;
-    Vector<Group> mGroupStack;
-
-    virtual void bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName, uint32_t* fbName,
-                                        uint32_t* status);
-    virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName);
-
-public:
-    GLES20RenderEngine(uint32_t featureFlags); // See RenderEngine::FeatureFlag
-    virtual ~GLES20RenderEngine();
-
-protected:
-    virtual void dump(String8& result);
-    virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, size_t hwh,
-                                          bool yswap, Transform::orientation_flags rotation);
-    virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
-                                    const half4& color) override;
-
-    // Color management related functions and state
-    void setSourceY410BT2020(bool enable) override;
-    void setSourceDataSpace(ui::Dataspace source) override;
-    void setOutputDataSpace(ui::Dataspace dataspace) override;
-    void setDisplayMaxLuminance(const float maxLuminance) override;
-
-    virtual void setupLayerTexturing(const Texture& texture);
-    virtual void setupLayerBlackedOut();
-    virtual void setupFillWithColor(float r, float g, float b, float a);
-    virtual void setupColorTransform(const mat4& colorTransform);
-    virtual void disableTexturing();
-    virtual void disableBlending();
-
-    virtual void drawMesh(const Mesh& mesh);
-
-    virtual size_t getMaxTextureSize() const;
-    virtual size_t getMaxViewportDims() const;
-
-    // Current dataspace of layer being rendered
-    ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN;
-
-    // Current output dataspace of the render engine
-    ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN;
-
-    // Currently only supporting sRGB, BT2020 and DisplayP3 color spaces
-    const bool mPlatformHasWideColor = false;
-    mat4 mSrgbToDisplayP3;
-    mat4 mDisplayP3ToSrgb;
-    mat3 mSrgbToXyz;
-    mat3 mBt2020ToXyz;
-    mat3 mDisplayP3ToXyz;
-    mat4 mXyzToSrgb;
-    mat4 mXyzToDisplayP3;
-    mat4 mXyzToBt2020;
-
-private:
-    // A data space is considered HDR data space if it has BT2020 color space
-    // with PQ or HLG transfer function.
-    bool isHdrDataSpace(const ui::Dataspace dataSpace) const;
-    bool needsXYZTransformMatrix() const;
-};
-
-// ---------------------------------------------------------------------------
-} // namespace impl
-} // namespace RE
-} // namespace android
-// ---------------------------------------------------------------------------
-
-#endif /* SF_GLES20RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/Image.h b/services/surfaceflinger/RenderEngine/Image.h
deleted file mode 100644
index 1ae7e09..0000000
--- a/services/surfaceflinger/RenderEngine/Image.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-struct ANativeWindowBuffer;
-
-namespace android {
-namespace RE {
-
-class Image {
-public:
-    virtual ~Image() = 0;
-    virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected,
-                                       int32_t cropWidth, int32_t cropHeight) = 0;
-};
-
-namespace impl {
-
-class RenderEngine;
-
-class Image : public RE::Image {
-public:
-    explicit Image(const RenderEngine& engine);
-    ~Image() override;
-
-    Image(const Image&) = delete;
-    Image& operator=(const Image&) = delete;
-
-    bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth,
-                               int32_t cropHeight) override;
-
-private:
-    // methods internal to RenderEngine
-    friend class RenderEngine;
-    EGLSurface getEGLImage() const { return mEGLImage; }
-
-    EGLDisplay mEGLDisplay;
-    EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR;
-};
-
-} // namespace impl
-} // namespace RE
-} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
deleted file mode 100644
index d745770..0000000
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
- * Copyright 2013 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 <log/log.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
-#include "GLES20RenderEngine.h"
-#include "GLExtensions.h"
-#include "Image.h"
-#include "Mesh.h"
-#include "RenderEngine.h"
-
-#include <SurfaceFlinger.h>
-#include <vector>
-
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
-
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
-// ---------------------------------------------------------------------------
-namespace android {
-namespace RE {
-// ---------------------------------------------------------------------------
-
-RenderEngine::~RenderEngine() = default;
-
-namespace impl {
-
-std::unique_ptr<RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags) {
-    // initialize EGL for the default display
-    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (!eglInitialize(display, nullptr, nullptr)) {
-        LOG_ALWAYS_FATAL("failed to initialize EGL");
-    }
-
-    GLExtensions& extensions = GLExtensions::getInstance();
-    extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION),
-                                  eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS));
-
-    // The code assumes that ES2 or later is available if this extension is
-    // supported.
-    EGLConfig config = EGL_NO_CONFIG;
-    if (!extensions.hasNoConfigContext()) {
-        config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
-    }
-
-    EGLint renderableType = 0;
-    if (config == EGL_NO_CONFIG) {
-        renderableType = EGL_OPENGL_ES2_BIT;
-    } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
-        LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
-    }
-    EGLint contextClientVersion = 0;
-    if (renderableType & EGL_OPENGL_ES2_BIT) {
-        contextClientVersion = 2;
-    } else if (renderableType & EGL_OPENGL_ES_BIT) {
-        contextClientVersion = 1;
-    } else {
-        LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
-    }
-
-    std::vector<EGLint> contextAttributes;
-    contextAttributes.reserve(6);
-    contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
-    contextAttributes.push_back(contextClientVersion);
-    bool useContextPriority = overrideUseContextPriorityFromConfig(extensions.hasContextPriority());
-    if (useContextPriority) {
-        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
-        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
-    }
-    contextAttributes.push_back(EGL_NONE);
-
-    EGLContext ctxt = eglCreateContext(display, config, nullptr, contextAttributes.data());
-
-    // if can't create a GL context, we can only abort.
-    LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
-
-    // now figure out what version of GL did we actually get
-    // NOTE: a dummy surface is not needed if KHR_create_context is supported
-
-    EGLConfig dummyConfig = config;
-    if (dummyConfig == EGL_NO_CONFIG) {
-        dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
-    }
-    EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
-    EGLSurface dummy = eglCreatePbufferSurface(display, dummyConfig, attribs);
-    LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
-    EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
-    LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
-
-    extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
-                                 glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
-
-    GlesVersion version = parseGlesVersion(extensions.getVersion());
-
-    // initialize the renderer while GL is current
-
-    std::unique_ptr<RenderEngine> engine;
-    switch (version) {
-        case GLES_VERSION_1_0:
-        case GLES_VERSION_1_1:
-            LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
-            break;
-        case GLES_VERSION_2_0:
-        case GLES_VERSION_3_0:
-            engine = std::make_unique<GLES20RenderEngine>(featureFlags);
-            break;
-    }
-    engine->setEGLHandles(display, config, ctxt);
-
-    ALOGI("OpenGL ES informations:");
-    ALOGI("vendor    : %s", extensions.getVendor());
-    ALOGI("renderer  : %s", extensions.getRenderer());
-    ALOGI("version   : %s", extensions.getVersion());
-    ALOGI("extensions: %s", extensions.getExtensions());
-    ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
-    ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
-
-    eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    eglDestroySurface(display, dummy);
-
-    return engine;
-}
-
-bool RenderEngine::overrideUseContextPriorityFromConfig(bool useContextPriority) {
-    OptionalBool ret;
-    ISurfaceFlingerConfigs::getService()->useContextPriority([&ret](OptionalBool b) { ret = b; });
-    if (ret.specified) {
-        return ret.value;
-    } else {
-        return useContextPriority;
-    }
-}
-
-RenderEngine::RenderEngine(uint32_t featureFlags)
-      : mEGLDisplay(EGL_NO_DISPLAY),
-        mEGLConfig(nullptr),
-        mEGLContext(EGL_NO_CONTEXT),
-        mFeatureFlags(featureFlags) {}
-
-RenderEngine::~RenderEngine() {
-    eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    eglTerminate(mEGLDisplay);
-}
-
-void RenderEngine::setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt) {
-    mEGLDisplay = display;
-    mEGLConfig = config;
-    mEGLContext = ctxt;
-}
-
-EGLDisplay RenderEngine::getEGLDisplay() const {
-    return mEGLDisplay;
-}
-
-EGLConfig RenderEngine::getEGLConfig() const {
-    return mEGLConfig;
-}
-
-bool RenderEngine::supportsImageCrop() const {
-    return GLExtensions::getInstance().hasImageCrop();
-}
-
-bool RenderEngine::isCurrent() const {
-    return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext();
-}
-
-std::unique_ptr<RE::Surface> RenderEngine::createSurface() {
-    return std::make_unique<Surface>(*this);
-}
-
-std::unique_ptr<RE::Image> RenderEngine::createImage() {
-    return std::make_unique<Image>(*this);
-}
-
-bool RenderEngine::setCurrentSurface(const android::RE::Surface& surface) {
-    // Note: RE::Surface is an abstract interface. This implementation only ever
-    // creates RE::impl::Surface's, so it is safe to just cast to the actual
-    // type.
-    return setCurrentSurface(static_cast<const android::RE::impl::Surface&>(surface));
-}
-
-bool RenderEngine::setCurrentSurface(const android::RE::impl::Surface& surface) {
-    bool success = true;
-    EGLSurface eglSurface = surface.getEGLSurface();
-    if (eglSurface != eglGetCurrentSurface(EGL_DRAW)) {
-        success = eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext) == EGL_TRUE;
-        if (success && surface.getAsync()) {
-            eglSwapInterval(mEGLDisplay, 0);
-        }
-    }
-
-    return success;
-}
-
-void RenderEngine::resetCurrentSurface() {
-    eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-}
-
-base::unique_fd RenderEngine::flush() {
-    if (!GLExtensions::getInstance().hasNativeFenceSync()) {
-        return base::unique_fd();
-    }
-
-    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
-    if (sync == EGL_NO_SYNC_KHR) {
-        ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
-        return base::unique_fd();
-    }
-
-    // native fence fd will not be populated until flush() is done.
-    glFlush();
-
-    // get the fence fd
-    base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
-    eglDestroySyncKHR(mEGLDisplay, sync);
-    if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
-        ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
-    }
-
-    return fenceFd;
-}
-
-bool RenderEngine::finish() {
-    if (!GLExtensions::getInstance().hasFenceSync()) {
-        ALOGW("no synchronization support");
-        return false;
-    }
-
-    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr);
-    if (sync == EGL_NO_SYNC_KHR) {
-        ALOGW("failed to create EGL fence sync: %#x", eglGetError());
-        return false;
-    }
-
-    EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
-                                         2000000000 /*2 sec*/);
-    EGLint error = eglGetError();
-    eglDestroySyncKHR(mEGLDisplay, sync);
-    if (result != EGL_CONDITION_SATISFIED_KHR) {
-        if (result == EGL_TIMEOUT_EXPIRED_KHR) {
-            ALOGW("fence wait timed out");
-        } else {
-            ALOGW("error waiting on EGL fence: %#x", error);
-        }
-        return false;
-    }
-
-    return true;
-}
-
-bool RenderEngine::waitFence(base::unique_fd fenceFd) {
-    if (!GLExtensions::getInstance().hasNativeFenceSync() ||
-        !GLExtensions::getInstance().hasWaitSync()) {
-        return false;
-    }
-
-    EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
-    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
-    if (sync == EGL_NO_SYNC_KHR) {
-        ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
-        return false;
-    }
-
-    // fenceFd is now owned by EGLSync
-    (void)fenceFd.release();
-
-    // XXX: The spec draft is inconsistent as to whether this should return an
-    // EGLint or void.  Ignore the return value for now, as it's not strictly
-    // needed.
-    eglWaitSyncKHR(mEGLDisplay, sync, 0);
-    EGLint error = eglGetError();
-    eglDestroySyncKHR(mEGLDisplay, sync);
-    if (error != EGL_SUCCESS) {
-        ALOGE("failed to wait for EGL native fence sync: %#x", error);
-        return false;
-    }
-
-    return true;
-}
-
-void RenderEngine::checkErrors() const {
-    do {
-        // there could be more than one error flag
-        GLenum error = glGetError();
-        if (error == GL_NO_ERROR) break;
-        ALOGE("GL error 0x%04x", int(error));
-    } while (true);
-}
-
-RenderEngine::GlesVersion RenderEngine::parseGlesVersion(const char* str) {
-    int major, minor;
-    if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
-        if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) {
-            ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
-            return GLES_VERSION_1_0;
-        }
-    }
-
-    if (major == 1 && minor == 0) return GLES_VERSION_1_0;
-    if (major == 1 && minor >= 1) return GLES_VERSION_1_1;
-    if (major == 2 && minor >= 0) return GLES_VERSION_2_0;
-    if (major == 3 && minor >= 0) return GLES_VERSION_3_0;
-
-    ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor);
-    return GLES_VERSION_1_0;
-}
-
-void RenderEngine::fillRegionWithColor(const Region& region, uint32_t height, float red,
-                                       float green, float blue, float alpha) {
-    size_t c;
-    Rect const* r = region.getArray(&c);
-    Mesh mesh(Mesh::TRIANGLES, c * 6, 2);
-    Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
-    for (size_t i = 0; i < c; i++, r++) {
-        position[i * 6 + 0].x = r->left;
-        position[i * 6 + 0].y = height - r->top;
-        position[i * 6 + 1].x = r->left;
-        position[i * 6 + 1].y = height - r->bottom;
-        position[i * 6 + 2].x = r->right;
-        position[i * 6 + 2].y = height - r->bottom;
-        position[i * 6 + 3].x = r->left;
-        position[i * 6 + 3].y = height - r->top;
-        position[i * 6 + 4].x = r->right;
-        position[i * 6 + 4].y = height - r->bottom;
-        position[i * 6 + 5].x = r->right;
-        position[i * 6 + 5].y = height - r->top;
-    }
-    setupFillWithColor(red, green, blue, alpha);
-    drawMesh(mesh);
-}
-
-void RenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
-    glClearColor(red, green, blue, alpha);
-    glClear(GL_COLOR_BUFFER_BIT);
-}
-
-void RenderEngine::setScissor(uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) {
-    glScissor(left, bottom, right, top);
-    glEnable(GL_SCISSOR_TEST);
-}
-
-void RenderEngine::disableScissor() {
-    glDisable(GL_SCISSOR_TEST);
-}
-
-void RenderEngine::genTextures(size_t count, uint32_t* names) {
-    glGenTextures(count, names);
-}
-
-void RenderEngine::deleteTextures(size_t count, uint32_t const* names) {
-    glDeleteTextures(count, names);
-}
-
-void RenderEngine::bindExternalTextureImage(uint32_t texName, const android::RE::Image& image) {
-    // Note: RE::Image is an abstract interface. This implementation only ever
-    // creates RE::impl::Image's, so it is safe to just cast to the actual type.
-    return bindExternalTextureImage(texName, static_cast<const android::RE::impl::Image&>(image));
-}
-
-void RenderEngine::bindExternalTextureImage(uint32_t texName,
-                                            const android::RE::impl::Image& image) {
-    const GLenum target = GL_TEXTURE_EXTERNAL_OES;
-
-    glBindTexture(target, texName);
-    if (image.getEGLImage() != EGL_NO_IMAGE_KHR) {
-        glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(image.getEGLImage()));
-    }
-}
-
-void RenderEngine::readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) {
-    glReadPixels(l, b, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
-}
-
-void RenderEngine::dump(String8& result) {
-    const GLExtensions& extensions = GLExtensions::getInstance();
-
-    result.appendFormat("EGL implementation : %s\n", extensions.getEGLVersion());
-    result.appendFormat("%s\n", extensions.getEGLExtensions());
-
-    result.appendFormat("GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
-                        extensions.getVersion());
-    result.appendFormat("%s\n", extensions.getExtensions());
-}
-
-// ---------------------------------------------------------------------------
-
-void RenderEngine::bindNativeBufferAsFrameBuffer(ANativeWindowBuffer* buffer,
-                                                 RE::BindNativeBufferAsFramebuffer* bindHelper) {
-    bindHelper->mImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
-                                           buffer, nullptr);
-    if (bindHelper->mImage == EGL_NO_IMAGE_KHR) {
-        bindHelper->mStatus = NO_MEMORY;
-        return;
-    }
-
-    uint32_t glStatus;
-    bindImageAsFramebuffer(bindHelper->mImage, &bindHelper->mTexName, &bindHelper->mFbName,
-                           &glStatus);
-
-    ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
-             glStatus);
-
-    bindHelper->mStatus = glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
-}
-
-void RenderEngine::unbindNativeBufferAsFrameBuffer(RE::BindNativeBufferAsFramebuffer* bindHelper) {
-    if (bindHelper->mImage == EGL_NO_IMAGE_KHR) {
-        return;
-    }
-
-    // back to main framebuffer
-    unbindFramebuffer(bindHelper->mTexName, bindHelper->mFbName);
-    eglDestroyImageKHR(mEGLDisplay, bindHelper->mImage);
-
-    // Workaround for b/77935566 to force the EGL driver to release the
-    // screenshot buffer
-    setScissor(0, 0, 0, 0);
-    clearWithColor(0.0, 0.0, 0.0, 0.0);
-    disableScissor();
-}
-
-// ---------------------------------------------------------------------------
-
-static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
-                                         EGLint wanted, EGLConfig* outConfig) {
-    EGLint numConfigs = -1, n = 0;
-    eglGetConfigs(dpy, nullptr, 0, &numConfigs);
-    EGLConfig* const configs = new EGLConfig[numConfigs];
-    eglChooseConfig(dpy, attrs, configs, numConfigs, &n);
-
-    if (n) {
-        if (attribute != EGL_NONE) {
-            for (int i = 0; i < n; i++) {
-                EGLint value = 0;
-                eglGetConfigAttrib(dpy, configs[i], attribute, &value);
-                if (wanted == value) {
-                    *outConfig = configs[i];
-                    delete[] configs;
-                    return NO_ERROR;
-                }
-            }
-        } else {
-            // just pick the first one
-            *outConfig = configs[0];
-            delete[] configs;
-            return NO_ERROR;
-        }
-    }
-    delete[] configs;
-    return NAME_NOT_FOUND;
-}
-
-class EGLAttributeVector {
-    struct Attribute;
-    class Adder;
-    friend class Adder;
-    KeyedVector<Attribute, EGLint> mList;
-    struct Attribute {
-        Attribute() : v(0){};
-        explicit Attribute(EGLint v) : v(v) {}
-        EGLint v;
-        bool operator<(const Attribute& other) const {
-            // this places EGL_NONE at the end
-            EGLint lhs(v);
-            EGLint rhs(other.v);
-            if (lhs == EGL_NONE) lhs = 0x7FFFFFFF;
-            if (rhs == EGL_NONE) rhs = 0x7FFFFFFF;
-            return lhs < rhs;
-        }
-    };
-    class Adder {
-        friend class EGLAttributeVector;
-        EGLAttributeVector& v;
-        EGLint attribute;
-        Adder(EGLAttributeVector& v, EGLint attribute) : v(v), attribute(attribute) {}
-
-    public:
-        void operator=(EGLint value) {
-            if (attribute != EGL_NONE) {
-                v.mList.add(Attribute(attribute), value);
-            }
-        }
-        operator EGLint() const { return v.mList[attribute]; }
-    };
-
-public:
-    EGLAttributeVector() { mList.add(Attribute(EGL_NONE), EGL_NONE); }
-    void remove(EGLint attribute) {
-        if (attribute != EGL_NONE) {
-            mList.removeItem(Attribute(attribute));
-        }
-    }
-    Adder operator[](EGLint attribute) { return Adder(*this, attribute); }
-    EGLint operator[](EGLint attribute) const { return mList[attribute]; }
-    // cast-operator to (EGLint const*)
-    operator EGLint const*() const { return &mList.keyAt(0).v; }
-};
-
-static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
-                                EGLConfig* config) {
-    // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
-    // it is to be used with WIFI displays
-    status_t err;
-    EGLint wantedAttribute;
-    EGLint wantedAttributeValue;
-
-    EGLAttributeVector attribs;
-    if (renderableType) {
-        attribs[EGL_RENDERABLE_TYPE] = renderableType;
-        attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
-        attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
-        attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
-        attribs[EGL_RED_SIZE] = 8;
-        attribs[EGL_GREEN_SIZE] = 8;
-        attribs[EGL_BLUE_SIZE] = 8;
-        attribs[EGL_ALPHA_SIZE] = 8;
-        wantedAttribute = EGL_NONE;
-        wantedAttributeValue = EGL_NONE;
-    } else {
-        // if no renderable type specified, fallback to a simplified query
-        wantedAttribute = EGL_NATIVE_VISUAL_ID;
-        wantedAttributeValue = format;
-    }
-
-    err = selectConfigForAttribute(display, attribs, wantedAttribute, wantedAttributeValue, config);
-    if (err == NO_ERROR) {
-        EGLint caveat;
-        if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
-            ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
-    }
-
-    return err;
-}
-
-EGLConfig RenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
-    status_t err;
-    EGLConfig config;
-
-    // First try to get an ES2 config
-    err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
-    if (err != NO_ERROR) {
-        // If ES2 fails, try ES1
-        err = selectEGLConfig(display, format, EGL_OPENGL_ES_BIT, &config);
-        if (err != NO_ERROR) {
-            // still didn't work, probably because we're on the emulator...
-            // try a simplified query
-            ALOGW("no suitable EGLConfig found, trying a simpler query");
-            err = selectEGLConfig(display, format, 0, &config);
-            if (err != NO_ERROR) {
-                // this EGL is too lame for android
-                LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
-            }
-        }
-    }
-
-    if (logConfig) {
-        // print some debugging info
-        EGLint r, g, b, a;
-        eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
-        eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
-        eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
-        eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
-        ALOGI("EGL information:");
-        ALOGI("vendor    : %s", eglQueryString(display, EGL_VENDOR));
-        ALOGI("version   : %s", eglQueryString(display, EGL_VERSION));
-        ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
-        ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
-        ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
-    }
-
-    return config;
-}
-
-void RenderEngine::primeCache() const {
-    ProgramCache::getInstance().primeCache(mFeatureFlags & WIDE_COLOR_SUPPORT);
-}
-
-// ---------------------------------------------------------------------------
-
-} // namespace impl
-} // namespace RE
-} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
deleted file mode 100644
index 1786155..0000000
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright 2013 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 SF_RENDERENGINE_H_
-#define SF_RENDERENGINE_H_
-
-#include <memory>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <Transform.h>
-#include <android-base/unique_fd.h>
-#include <gui/SurfaceControl.h>
-#include <math/mat4.h>
-
-#define EGL_NO_CONFIG ((EGLConfig)0)
-
-struct ANativeWindowBuffer;
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class String8;
-class Rect;
-class Region;
-class Mesh;
-class Texture;
-
-namespace RE {
-
-class Image;
-class Surface;
-class BindNativeBufferAsFramebuffer;
-
-namespace impl {
-class RenderEngine;
-}
-
-class RenderEngine {
-public:
-    enum FeatureFlag {
-        WIDE_COLOR_SUPPORT = 1 << 0 // Platform has a wide color display
-    };
-
-    virtual ~RenderEngine() = 0;
-
-    virtual std::unique_ptr<RE::Surface> createSurface() = 0;
-    virtual std::unique_ptr<RE::Image> createImage() = 0;
-
-    virtual void primeCache() const = 0;
-
-    // dump the extension strings. always call the base class.
-    virtual void dump(String8& result) = 0;
-
-    virtual bool supportsImageCrop() const = 0;
-
-    virtual bool isCurrent() const = 0;
-    virtual bool setCurrentSurface(const RE::Surface& surface) = 0;
-    virtual void resetCurrentSurface() = 0;
-
-    // helpers
-    // flush submits RenderEngine command stream for execution and returns a
-    // native fence fd that is signaled when the execution has completed.  It
-    // returns -1 on errors.
-    virtual base::unique_fd flush() = 0;
-    // finish waits until RenderEngine command stream has been executed.  It
-    // returns false on errors.
-    virtual bool finish() = 0;
-    // waitFence inserts a wait on an external fence fd to RenderEngine
-    // command stream.  It returns false on errors.
-    virtual bool waitFence(base::unique_fd fenceFd) = 0;
-
-    virtual void clearWithColor(float red, float green, float blue, float alpha) = 0;
-    virtual void fillRegionWithColor(const Region& region, uint32_t height, float red, float green,
-                                     float blue, float alpha) = 0;
-
-    // common to all GL versions
-    virtual void setScissor(uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) = 0;
-    virtual void disableScissor() = 0;
-    virtual void genTextures(size_t count, uint32_t* names) = 0;
-    virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
-    virtual void bindExternalTextureImage(uint32_t texName, const RE::Image& image) = 0;
-    virtual void readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) = 0;
-    virtual void bindNativeBufferAsFrameBuffer(ANativeWindowBuffer* buffer,
-                                               RE::BindNativeBufferAsFramebuffer* bindHelper) = 0;
-    virtual void unbindNativeBufferAsFrameBuffer(RE::BindNativeBufferAsFramebuffer* bindHelper) = 0;
-
-    // set-up
-    virtual void checkErrors() const;
-    virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, size_t hwh,
-                                          bool yswap, Transform::orientation_flags rotation) = 0;
-    virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
-                                    const half4& color) = 0;
-    virtual void setupLayerTexturing(const Texture& texture) = 0;
-    virtual void setupLayerBlackedOut() = 0;
-    virtual void setupFillWithColor(float r, float g, float b, float a) = 0;
-
-    virtual void setupColorTransform(const mat4& /* colorTransform */) = 0;
-
-    virtual void disableTexturing() = 0;
-    virtual void disableBlending() = 0;
-
-    // HDR and wide color gamut support
-    virtual void setSourceY410BT2020(bool enable) = 0;
-    virtual void setSourceDataSpace(ui::Dataspace source) = 0;
-    virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0;
-    virtual void setDisplayMaxLuminance(const float maxLuminance) = 0;
-
-    // drawing
-    virtual void drawMesh(const Mesh& mesh) = 0;
-
-    // queries
-    virtual size_t getMaxTextureSize() const = 0;
-    virtual size_t getMaxViewportDims() const = 0;
-};
-
-class BindNativeBufferAsFramebuffer {
-public:
-    BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer)
-          : mEngine(engine) {
-        mEngine.bindNativeBufferAsFrameBuffer(buffer, this);
-    }
-    ~BindNativeBufferAsFramebuffer() { mEngine.unbindNativeBufferAsFrameBuffer(this); }
-    status_t getStatus() const { return mStatus; }
-
-protected:
-    friend impl::RenderEngine;
-
-    RenderEngine& mEngine;
-    EGLImageKHR mImage;
-    uint32_t mTexName, mFbName;
-    status_t mStatus;
-};
-
-namespace impl {
-
-class Image;
-class Surface;
-
-class RenderEngine : public RE::RenderEngine {
-    enum GlesVersion {
-        GLES_VERSION_1_0 = 0x10000,
-        GLES_VERSION_1_1 = 0x10001,
-        GLES_VERSION_2_0 = 0x20000,
-        GLES_VERSION_3_0 = 0x30000,
-    };
-    static GlesVersion parseGlesVersion(const char* str);
-
-    EGLDisplay mEGLDisplay;
-    EGLConfig mEGLConfig;
-    EGLContext mEGLContext;
-    void setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt);
-
-    static bool overrideUseContextPriorityFromConfig(bool useContextPriority);
-
-protected:
-    RenderEngine(uint32_t featureFlags);
-
-    const uint32_t mFeatureFlags;
-
-public:
-    virtual ~RenderEngine() = 0;
-
-    static std::unique_ptr<RenderEngine> create(int hwcFormat, uint32_t featureFlags);
-
-    static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
-
-    // RenderEngine interface implementation
-
-    std::unique_ptr<RE::Surface> createSurface() override;
-    std::unique_ptr<RE::Image> createImage() override;
-
-    void primeCache() const override;
-
-    // dump the extension strings. always call the base class.
-    void dump(String8& result) override;
-
-    bool supportsImageCrop() const override;
-
-    bool isCurrent() const;
-    bool setCurrentSurface(const RE::Surface& surface) override;
-    void resetCurrentSurface() override;
-
-    // synchronization
-
-    // flush submits RenderEngine command stream for execution and returns a
-    // native fence fd that is signaled when the execution has completed.  It
-    // returns -1 on errors.
-    base::unique_fd flush() override;
-    // finish waits until RenderEngine command stream has been executed.  It
-    // returns false on errors.
-    bool finish() override;
-    // waitFence inserts a wait on an external fence fd to RenderEngine
-    // command stream.  It returns false on errors.
-    bool waitFence(base::unique_fd fenceFd) override;
-
-    // helpers
-    void clearWithColor(float red, float green, float blue, float alpha) override;
-    void fillRegionWithColor(const Region& region, uint32_t height, float red, float green,
-                             float blue, float alpha) override;
-
-    // common to all GL versions
-    void setScissor(uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) override;
-    void disableScissor() override;
-    void genTextures(size_t count, uint32_t* names) override;
-    void deleteTextures(size_t count, uint32_t const* names) override;
-    void bindExternalTextureImage(uint32_t texName, const RE::Image& image) override;
-    void readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) override;
-
-    void checkErrors() const override;
-
-    void setupColorTransform(const mat4& /* colorTransform */) override {}
-
-    // internal to RenderEngine
-    EGLDisplay getEGLDisplay() const;
-    EGLConfig getEGLConfig() const;
-
-    // Common implementation
-    bool setCurrentSurface(const RE::impl::Surface& surface);
-    void bindExternalTextureImage(uint32_t texName, const RE::impl::Image& image);
-
-    void bindNativeBufferAsFrameBuffer(ANativeWindowBuffer* buffer,
-                                       RE::BindNativeBufferAsFramebuffer* bindHelper) override;
-    void unbindNativeBufferAsFrameBuffer(RE::BindNativeBufferAsFramebuffer* bindHelper) override;
-
-    // Overriden by each specialization
-    virtual void bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName, uint32_t* fbName,
-                                        uint32_t* status) = 0;
-    virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName) = 0;
-};
-
-} // namespace impl
-} // namespace RE
-} // namespace android
-
-#endif /* SF_RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/Surface.cpp b/services/surfaceflinger/RenderEngine/Surface.cpp
deleted file mode 100644
index 0d20f1f..0000000
--- a/services/surfaceflinger/RenderEngine/Surface.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.
- */
-
-#include "Surface.h"
-
-#include "RenderEngine.h"
-
-#include <log/log.h>
-
-namespace android {
-namespace RE {
-
-Surface::~Surface() = default;
-
-namespace impl {
-
-Surface::Surface(const RenderEngine& engine)
-      : mEGLDisplay(engine.getEGLDisplay()), mEGLConfig(engine.getEGLConfig()) {
-    // RE does not assume any config when EGL_KHR_no_config_context is supported
-    if (mEGLConfig == EGL_NO_CONFIG_KHR) {
-        mEGLConfig = RenderEngine::chooseEglConfig(mEGLDisplay, PIXEL_FORMAT_RGBA_8888, false);
-    }
-}
-
-Surface::~Surface() {
-    setNativeWindow(nullptr);
-}
-
-void Surface::setNativeWindow(ANativeWindow* window) {
-    if (mEGLSurface != EGL_NO_SURFACE) {
-        eglDestroySurface(mEGLDisplay, mEGLSurface);
-        mEGLSurface = EGL_NO_SURFACE;
-    }
-
-    mWindow = window;
-    if (mWindow) {
-        mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mWindow, nullptr);
-    }
-}
-
-void Surface::swapBuffers() const {
-    if (!eglSwapBuffers(mEGLDisplay, mEGLSurface)) {
-        EGLint error = eglGetError();
-
-        const char format[] = "eglSwapBuffers(%p, %p) failed with 0x%08x";
-        if (mCritical || error == EGL_CONTEXT_LOST) {
-            LOG_ALWAYS_FATAL(format, mEGLDisplay, mEGLSurface, error);
-        } else {
-            ALOGE(format, mEGLDisplay, mEGLSurface, error);
-        }
-    }
-}
-
-EGLint Surface::queryConfig(EGLint attrib) const {
-    EGLint value;
-    if (!eglGetConfigAttrib(mEGLDisplay, mEGLConfig, attrib, &value)) {
-        value = 0;
-    }
-
-    return value;
-}
-
-EGLint Surface::querySurface(EGLint attrib) const {
-    EGLint value;
-    if (!eglQuerySurface(mEGLDisplay, mEGLSurface, attrib, &value)) {
-        value = 0;
-    }
-
-    return value;
-}
-
-int32_t Surface::queryRedSize() const {
-    return queryConfig(EGL_RED_SIZE);
-}
-
-int32_t Surface::queryGreenSize() const {
-    return queryConfig(EGL_GREEN_SIZE);
-}
-
-int32_t Surface::queryBlueSize() const {
-    return queryConfig(EGL_BLUE_SIZE);
-}
-
-int32_t Surface::queryAlphaSize() const {
-    return queryConfig(EGL_ALPHA_SIZE);
-}
-
-int32_t Surface::queryWidth() const {
-    return querySurface(EGL_WIDTH);
-}
-
-int32_t Surface::queryHeight() const {
-    return querySurface(EGL_HEIGHT);
-}
-
-} // namespace impl
-} // namespace RE
-} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Surface.h b/services/surfaceflinger/RenderEngine/Surface.h
deleted file mode 100644
index d4d3d8c..0000000
--- a/services/surfaceflinger/RenderEngine/Surface.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <EGL/egl.h>
-
-struct ANativeWindow;
-
-namespace android {
-namespace RE {
-
-class Surface {
-public:
-    virtual ~Surface() = 0;
-
-    virtual void setCritical(bool enable) = 0;
-    virtual void setAsync(bool enable) = 0;
-
-    virtual void setNativeWindow(ANativeWindow* window) = 0;
-    virtual void swapBuffers() const = 0;
-
-    virtual int32_t queryRedSize() const = 0;
-    virtual int32_t queryGreenSize() const = 0;
-    virtual int32_t queryBlueSize() const = 0;
-    virtual int32_t queryAlphaSize() const = 0;
-
-    virtual int32_t queryWidth() const = 0;
-    virtual int32_t queryHeight() const = 0;
-};
-
-namespace impl {
-
-class RenderEngine;
-
-class Surface final : public RE::Surface {
-public:
-    Surface(const RenderEngine& engine);
-    ~Surface();
-
-    Surface(const Surface&) = delete;
-    Surface& operator=(const Surface&) = delete;
-
-    // RE::Surface implementation
-    void setCritical(bool enable) override { mCritical = enable; }
-    void setAsync(bool enable) override { mAsync = enable; }
-
-    void setNativeWindow(ANativeWindow* window) override;
-    void swapBuffers() const override;
-
-    int32_t queryRedSize() const override;
-    int32_t queryGreenSize() const override;
-    int32_t queryBlueSize() const override;
-    int32_t queryAlphaSize() const override;
-
-    int32_t queryWidth() const override;
-    int32_t queryHeight() const override;
-
-private:
-    EGLint queryConfig(EGLint attrib) const;
-    EGLint querySurface(EGLint attrib) const;
-
-    // methods internal to RenderEngine
-    friend class RenderEngine;
-    bool getAsync() const { return mAsync; }
-    EGLSurface getEGLSurface() const { return mEGLSurface; }
-
-    EGLDisplay mEGLDisplay;
-    EGLConfig mEGLConfig;
-
-    bool mCritical = false;
-    bool mAsync = false;
-
-    ANativeWindow* mWindow = nullptr;
-    EGLSurface mEGLSurface = EGL_NO_SURFACE;
-};
-
-} // namespace impl
-} // namespace RE
-} // namespace android
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
similarity index 73%
rename from services/surfaceflinger/DispSync.cpp
rename to services/surfaceflinger/Scheduler/DispSync.cpp
index 37dc27d..075e238 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -24,11 +24,11 @@
 
 #include <algorithm>
 
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
 #include <log/log.h>
-#include <utils/String8.h>
 #include <utils/Thread.h>
 #include <utils/Trace.h>
-#include <utils/Vector.h>
 
 #include <ui/FenceTime.h>
 
@@ -36,14 +36,15 @@
 #include "EventLog/EventLog.h"
 #include "SurfaceFlinger.h"
 
+using android::base::StringAppendF;
 using std::max;
 using std::min;
 
 namespace android {
 
-// Setting this to true enables verbose tracing that can be used to debug
-// vsync event model or phase issues.
-static const bool kTraceDetailedInfo = false;
+DispSync::~DispSync() = default;
+
+namespace impl {
 
 // Setting this to true adds a zero-phase tracer for correlating with hardware
 // vsync events
@@ -59,19 +60,20 @@
 #define LOG_TAG "DispSyncThread"
 class DispSyncThread : public Thread {
 public:
-    explicit DispSyncThread(const char* name)
+    DispSyncThread(const char* name, bool showTraceDetailedInfo)
           : mName(name),
             mStop(false),
             mPeriod(0),
             mPhase(0),
             mReferenceTime(0),
             mWakeupLatency(0),
-            mFrameNumber(0) {}
+            mFrameNumber(0),
+            mTraceDetailedInfo(showTraceDetailedInfo) {}
 
     virtual ~DispSyncThread() {}
 
     void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
-        if (kTraceDetailedInfo) ATRACE_CALL();
+        if (mTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
         mPeriod = period;
         mPhase = phase;
@@ -83,7 +85,7 @@
     }
 
     void stop() {
-        if (kTraceDetailedInfo) ATRACE_CALL();
+        if (mTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
         mStop = true;
         mCond.signal();
@@ -94,14 +96,14 @@
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
 
         while (true) {
-            Vector<CallbackInvocation> callbackInvocations;
+            std::vector<CallbackInvocation> callbackInvocations;
 
             nsecs_t targetTime = 0;
 
             { // Scope for lock
                 Mutex::Autolock lock(mMutex);
 
-                if (kTraceDetailedInfo) {
+                if (mTraceDetailedInfo) {
                     ATRACE_INT64("DispSync:Frame", mFrameNumber);
                 }
                 ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber);
@@ -125,7 +127,7 @@
                 bool isWakeup = false;
 
                 if (now < targetTime) {
-                    if (kTraceDetailedInfo) ATRACE_NAME("DispSync waiting");
+                    if (mTraceDetailedInfo) ATRACE_NAME("DispSync waiting");
 
                     if (targetTime == INT64_MAX) {
                         ALOGV("[%s] Waiting forever", mName);
@@ -151,7 +153,7 @@
                 if (isWakeup) {
                     mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64;
                     mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency);
-                    if (kTraceDetailedInfo) {
+                    if (mTraceDetailedInfo) {
                         ATRACE_INT64("DispSync:WakeupLat", now - targetTime);
                         ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
                     }
@@ -169,7 +171,7 @@
     }
 
     status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback) {
-        if (kTraceDetailedInfo) ATRACE_CALL();
+        if (mTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
 
         for (size_t i = 0; i < mEventListeners.size(); i++) {
@@ -187,7 +189,7 @@
         // allowing any past events to fire
         listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency;
 
-        mEventListeners.push(listener);
+        mEventListeners.push_back(listener);
 
         mCond.signal();
 
@@ -195,12 +197,13 @@
     }
 
     status_t removeEventListener(DispSync::Callback* callback) {
-        if (kTraceDetailedInfo) ATRACE_CALL();
+        if (mTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
 
-        for (size_t i = 0; i < mEventListeners.size(); i++) {
-            if (mEventListeners[i].mCallback == callback) {
-                mEventListeners.removeAt(i);
+        for (std::vector<EventListener>::iterator it = mEventListeners.begin();
+             it != mEventListeners.end(); ++it) {
+            if (it->mCallback == callback) {
+                mEventListeners.erase(it);
                 mCond.signal();
                 return NO_ERROR;
             }
@@ -210,14 +213,13 @@
     }
 
     status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
-        if (kTraceDetailedInfo) ATRACE_CALL();
+        if (mTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
 
-        for (size_t i = 0; i < mEventListeners.size(); i++) {
-            if (mEventListeners[i].mCallback == callback) {
-                EventListener& listener = mEventListeners.editItemAt(i);
-                const nsecs_t oldPhase = listener.mPhase;
-                listener.mPhase = phase;
+        for (auto& eventListener : mEventListeners) {
+            if (eventListener.mCallback == callback) {
+                const nsecs_t oldPhase = eventListener.mPhase;
+                eventListener.mPhase = phase;
 
                 // Pretend that the last time this event was handled at the same frame but with the
                 // new offset to allow for a seamless offset change without double-firing or
@@ -228,7 +230,7 @@
                 } else if (diff < -mPeriod / 2) {
                     diff += mPeriod;
                 }
-                listener.mLastEventTime -= diff;
+                eventListener.mLastEventTime -= diff;
                 mCond.signal();
                 return NO_ERROR;
             }
@@ -237,14 +239,6 @@
         return BAD_VALUE;
     }
 
-    // This method is only here to handle the !SurfaceFlinger::hasSyncFramework
-    // case.
-    bool hasAnyEventListeners() {
-        if (kTraceDetailedInfo) ATRACE_CALL();
-        Mutex::Autolock lock(mMutex);
-        return !mEventListeners.empty();
-    }
-
 private:
     struct EventListener {
         const char* mName;
@@ -259,7 +253,7 @@
     };
 
     nsecs_t computeNextEventTimeLocked(nsecs_t now) {
-        if (kTraceDetailedInfo) ATRACE_CALL();
+        if (mTraceDetailedInfo) ATRACE_CALL();
         ALOGV("[%s] computeNextEventTimeLocked", mName);
         nsecs_t nextEventTime = INT64_MAX;
         for (size_t i = 0; i < mEventListeners.size(); i++) {
@@ -274,23 +268,23 @@
         return nextEventTime;
     }
 
-    Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
-        if (kTraceDetailedInfo) ATRACE_CALL();
+    std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
+        if (mTraceDetailedInfo) ATRACE_CALL();
         ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now));
 
-        Vector<CallbackInvocation> callbackInvocations;
+        std::vector<CallbackInvocation> callbackInvocations;
         nsecs_t onePeriodAgo = now - mPeriod;
 
-        for (size_t i = 0; i < mEventListeners.size(); i++) {
-            nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], onePeriodAgo);
+        for (auto& eventListener : mEventListeners) {
+            nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo);
 
             if (t < now) {
                 CallbackInvocation ci;
-                ci.mCallback = mEventListeners[i].mCallback;
+                ci.mCallback = eventListener.mCallback;
                 ci.mEventTime = t;
-                ALOGV("[%s] [%s] Preparing to fire", mName, mEventListeners[i].mName);
-                callbackInvocations.push(ci);
-                mEventListeners.editItemAt(i).mLastEventTime = t;
+                ALOGV("[%s] [%s] Preparing to fire", mName, eventListener.mName);
+                callbackInvocations.push_back(ci);
+                eventListener.mLastEventTime = t;
             }
         }
 
@@ -298,7 +292,7 @@
     }
 
     nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, nsecs_t baseTime) {
-        if (kTraceDetailedInfo) ATRACE_CALL();
+        if (mTraceDetailedInfo) ATRACE_CALL();
         ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")", mName, listener.mName,
               ns2us(baseTime));
 
@@ -348,8 +342,8 @@
         return t;
     }
 
-    void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
-        if (kTraceDetailedInfo) ATRACE_CALL();
+    void fireCallbackInvocations(const std::vector<CallbackInvocation>& callbacks) {
+        if (mTraceDetailedInfo) ATRACE_CALL();
         for (size_t i = 0; i < callbacks.size(); i++) {
             callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
         }
@@ -366,10 +360,13 @@
 
     int64_t mFrameNumber;
 
-    Vector<EventListener> mEventListeners;
+    std::vector<EventListener> mEventListeners;
 
     Mutex mMutex;
     Condition mCond;
+
+    // Flag to turn on logging in systrace.
+    const bool mTraceDetailedInfo;
 };
 
 #undef LOG_TAG
@@ -388,8 +385,13 @@
     bool mParity;
 };
 
-DispSync::DispSync(const char* name)
-      : mName(name), mRefreshSkipCount(0), mThread(new DispSyncThread(name)) {}
+DispSync::DispSync(const char* name) : mName(name), mRefreshSkipCount(0) {
+    // This flag offers the ability to turn on systrace logging from the shell.
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.sf.dispsync_trace_detailed_info", value, "0");
+    mTraceDetailedInfo = atoi(value);
+    mThread = new DispSyncThread(name, mTraceDetailedInfo);
+}
 
 DispSync::~DispSync() {}
 
@@ -408,22 +410,18 @@
     reset();
     beginResync();
 
-    if (kTraceDetailedInfo) {
-        // If we're not getting present fences then the ZeroPhaseTracer
-        // would prevent HW vsync event from ever being turned off.
-        // Even if we're just ignoring the fences, the zero-phase tracing is
-        // not needed because any time there is an event registered we will
-        // turn on the HW vsync events.
-        if (!mIgnorePresentFences && kEnableZeroPhaseTracer) {
-            mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
-            addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get());
-        }
+    if (mTraceDetailedInfo && kEnableZeroPhaseTracer) {
+        mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
+        addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get());
     }
 }
 
 void DispSync::reset() {
     Mutex::Autolock lock(mMutex);
+    resetLocked();
+}
 
+void DispSync::resetLocked() {
     mPhase = 0;
     mReferenceTime = 0;
     mModelUpdated = false;
@@ -436,6 +434,10 @@
 bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
     Mutex::Autolock lock(mMutex);
 
+    if (mIgnorePresentFences) {
+        return true;
+    }
+
     mPresentFences[mPresentSampleOffset] = fenceTime;
     mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
     mNumResyncSamplesSincePresent = 0;
@@ -481,12 +483,10 @@
     }
 
     if (mIgnorePresentFences) {
-        // If we don't have the sync framework we will never have
-        // addPresentFence called.  This means we have no way to know whether
+        // If we're ignoring the present fences we have no way to know whether
         // or not we're synchronized with the HW vsyncs, so we just request
-        // that the HW vsync events be turned on whenever we need to generate
-        // SW vsync events.
-        return mThread->hasAnyEventListeners();
+        // that the HW vsync events be turned on.
+        return true;
     }
 
     // Check against kErrorThreshold / 2 to add some hysteresis before having to
@@ -580,7 +580,7 @@
             ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase));
         }
 
-        if (kTraceDetailedInfo) {
+        if (mTraceDetailedInfo) {
             ATRACE_INT64("DispSync:Period", mPeriod);
             ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
         }
@@ -639,7 +639,7 @@
                  "No present times for model error.");
     }
 
-    if (kTraceDetailedInfo) {
+    if (mTraceDetailedInfo) {
         ATRACE_INT64("DispSync:Error", mError);
     }
 }
@@ -657,57 +657,109 @@
     Mutex::Autolock lock(mMutex);
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     nsecs_t phase = mReferenceTime + mPhase;
+    if (mPeriod == 0) {
+        return 0;
+    }
     return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
 }
 
-void DispSync::dump(String8& result) const {
+void DispSync::setIgnorePresentFences(bool ignore) {
     Mutex::Autolock lock(mMutex);
-    result.appendFormat("present fences are %s\n", mIgnorePresentFences ? "ignored" : "used");
-    result.appendFormat("mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n", mPeriod,
-                        1000000000.0 / mPeriod, mRefreshSkipCount);
-    result.appendFormat("mPhase: %" PRId64 " ns\n", mPhase);
-    result.appendFormat("mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError));
-    result.appendFormat("mNumResyncSamplesSincePresent: %d (limit %d)\n",
-                        mNumResyncSamplesSincePresent, MAX_RESYNC_SAMPLES_WITHOUT_PRESENT);
-    result.appendFormat("mNumResyncSamples: %zd (max %d)\n", mNumResyncSamples, MAX_RESYNC_SAMPLES);
+    if (mIgnorePresentFences != ignore) {
+        mIgnorePresentFences = ignore;
+        resetLocked();
+    }
+}
 
-    result.appendFormat("mResyncSamples:\n");
+void DispSync::dump(std::string& result) const {
+    Mutex::Autolock lock(mMutex);
+    StringAppendF(&result, "present fences are %s\n", mIgnorePresentFences ? "ignored" : "used");
+    StringAppendF(&result, "mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n", mPeriod,
+                  1000000000.0 / mPeriod, mRefreshSkipCount);
+    StringAppendF(&result, "mPhase: %" PRId64 " ns\n", mPhase);
+    StringAppendF(&result, "mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError));
+    StringAppendF(&result, "mNumResyncSamplesSincePresent: %d (limit %d)\n",
+                  mNumResyncSamplesSincePresent, MAX_RESYNC_SAMPLES_WITHOUT_PRESENT);
+    StringAppendF(&result, "mNumResyncSamples: %zd (max %d)\n", mNumResyncSamples,
+                  MAX_RESYNC_SAMPLES);
+
+    result.append("mResyncSamples:\n");
     nsecs_t previous = -1;
     for (size_t i = 0; i < mNumResyncSamples; i++) {
         size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
         nsecs_t sampleTime = mResyncSamples[idx];
         if (i == 0) {
-            result.appendFormat("  %" PRId64 "\n", sampleTime);
+            StringAppendF(&result, "  %" PRId64 "\n", sampleTime);
         } else {
-            result.appendFormat("  %" PRId64 " (+%" PRId64 ")\n", sampleTime,
-                                sampleTime - previous);
+            StringAppendF(&result, "  %" PRId64 " (+%" PRId64 ")\n", sampleTime,
+                          sampleTime - previous);
         }
         previous = sampleTime;
     }
 
-    result.appendFormat("mPresentFences [%d]:\n", NUM_PRESENT_SAMPLES);
+    StringAppendF(&result, "mPresentFences [%d]:\n", NUM_PRESENT_SAMPLES);
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     previous = Fence::SIGNAL_TIME_INVALID;
     for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
         size_t idx = (i + mPresentSampleOffset) % NUM_PRESENT_SAMPLES;
         nsecs_t presentTime = mPresentFences[idx]->getSignalTime();
         if (presentTime == Fence::SIGNAL_TIME_PENDING) {
-            result.appendFormat("  [unsignaled fence]\n");
+            StringAppendF(&result, "  [unsignaled fence]\n");
         } else if (presentTime == Fence::SIGNAL_TIME_INVALID) {
-            result.appendFormat("  [invalid fence]\n");
+            StringAppendF(&result, "  [invalid fence]\n");
         } else if (previous == Fence::SIGNAL_TIME_PENDING ||
                    previous == Fence::SIGNAL_TIME_INVALID) {
-            result.appendFormat("  %" PRId64 "  (%.3f ms ago)\n", presentTime,
-                                (now - presentTime) / 1000000.0);
+            StringAppendF(&result, "  %" PRId64 "  (%.3f ms ago)\n", presentTime,
+                          (now - presentTime) / 1000000.0);
         } else {
-            result.appendFormat("  %" PRId64 " (+%" PRId64 " / %.3f)  (%.3f ms ago)\n", presentTime,
-                                presentTime - previous, (presentTime - previous) / (double)mPeriod,
-                                (now - presentTime) / 1000000.0);
+            StringAppendF(&result, "  %" PRId64 " (+%" PRId64 " / %.3f)  (%.3f ms ago)\n",
+                          presentTime, presentTime - previous,
+                          (presentTime - previous) / (double)mPeriod,
+                          (now - presentTime) / 1000000.0);
         }
         previous = presentTime;
     }
 
-    result.appendFormat("current monotonic time: %" PRId64 "\n", now);
+    StringAppendF(&result, "current monotonic time: %" PRId64 "\n", now);
 }
 
+// TODO(b/113612090): Figure out how much of this is still relevant.
+// We need to determine the time when a buffer acquired now will be
+// displayed.  This can be calculated:
+//   time when previous buffer's actual-present fence was signaled
+//    + current display refresh rate * HWC latency
+//    + a little extra padding
+//
+// Buffer producers are expected to set their desired presentation time
+// based on choreographer time stamps, which (coming from vsync events)
+// will be slightly later then the actual-present timing.  If we get a
+// desired-present time that is unintentionally a hair after the next
+// vsync, we'll hold the frame when we really want to display it.  We
+// need to take the offset between actual-present and reported-vsync
+// into account.
+//
+// If the system is configured without a DispSync phase offset for the app,
+// we also want to throw in a bit of padding to avoid edge cases where we
+// just barely miss.  We want to do it here, not in every app.  A major
+// source of trouble is the app's use of the display's ideal refresh time
+// (via Display.getRefreshRate()), which could be off of the actual refresh
+// by a few percent, with the error multiplied by the number of frames
+// between now and when the buffer should be displayed.
+//
+// If the refresh reported to the app has a phase offset, we shouldn't need
+// to tweak anything here.
+nsecs_t DispSync::expectedPresentTime() {
+    // The HWC doesn't currently have a way to report additional latency.
+    // Assume that whatever we submit now will appear right after the flip.
+    // For a smart panel this might be 1.  This is expressed in frames,
+    // rather than time, because we expect to have a constant frame delay
+    // regardless of the refresh rate.
+    const uint32_t hwcLatency = 0;
+
+    // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
+    return computeNextRefresh(hwcLatency);
+}
+
+} // namespace impl
+
 } // namespace android
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
similarity index 76%
rename from services/surfaceflinger/DispSync.h
rename to services/surfaceflinger/Scheduler/DispSync.h
index 077256a..4a90f10 100644
--- a/services/surfaceflinger/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -29,8 +29,38 @@
 
 namespace android {
 
-class String8;
 class FenceTime;
+
+class DispSync {
+public:
+    class Callback {
+    public:
+        virtual ~Callback() = default;
+        virtual void onDispSyncEvent(nsecs_t when) = 0;
+    };
+
+    virtual ~DispSync();
+
+    virtual void reset() = 0;
+    virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
+    virtual void beginResync() = 0;
+    virtual bool addResyncSample(nsecs_t timestamp) = 0;
+    virtual void endResync() = 0;
+    virtual void setPeriod(nsecs_t period) = 0;
+    virtual nsecs_t getPeriod() = 0;
+    virtual void setRefreshSkipCount(int count) = 0;
+    virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) = 0;
+    virtual status_t removeEventListener(Callback* callback) = 0;
+    virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0;
+    virtual nsecs_t computeNextRefresh(int periodOffset) const = 0;
+    virtual void setIgnorePresentFences(bool ignore) = 0;
+    virtual nsecs_t expectedPresentTime() = 0;
+
+    virtual void dump(std::string& result) const = 0;
+};
+
+namespace impl {
+
 class DispSyncThread;
 
 // DispSync maintains a model of the periodic hardware-based vsync events of a
@@ -46,21 +76,15 @@
 // current model accurately represents the hardware event times it will return
 // false to indicate that a resynchronization (via addResyncSample) is not
 // needed.
-class DispSync {
+class DispSync : public android::DispSync {
 public:
-    class Callback {
-    public:
-        virtual ~Callback(){};
-        virtual void onDispSyncEvent(nsecs_t when) = 0;
-    };
-
     explicit DispSync(const char* name);
-    ~DispSync();
+    ~DispSync() override;
 
     void init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset);
 
     // reset clears the resync samples and error value.
-    void reset();
+    void reset() override;
 
     // addPresentFence adds a fence for use in validating the current vsync
     // event model.  The fence need not be signaled at the time
@@ -71,7 +95,7 @@
     //
     // This method should be called with the retire fence from each HWComposer
     // set call that affects the display.
-    bool addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
+    bool addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) override;
 
     // The beginResync, addResyncSample, and endResync methods are used to re-
     // synchronize the DispSync's model to the hardware vsync events.  The re-
@@ -84,52 +108,64 @@
     // is turned on (i.e. once immediately after it's turned on) and whenever
     // addPresentFence returns true indicating that the model has drifted away
     // from the hardware vsync events.
-    void beginResync();
-    bool addResyncSample(nsecs_t timestamp);
-    void endResync();
+    void beginResync() override;
+    bool addResyncSample(nsecs_t timestamp) override;
+    void endResync() override;
 
     // The setPeriod method sets the vsync event model's period to a specific
     // value.  This should be used to prime the model when a display is first
     // turned on.  It should NOT be used after that.
-    void setPeriod(nsecs_t period);
+    void setPeriod(nsecs_t period) override;
 
     // The getPeriod method returns the current vsync period.
-    nsecs_t getPeriod();
+    nsecs_t getPeriod() override;
 
     // setRefreshSkipCount specifies an additional number of refresh
     // cycles to skip.  For example, on a 60Hz display, a skip count of 1
     // will result in events happening at 30Hz.  Default is zero.  The idea
     // is to sacrifice smoothness for battery life.
-    void setRefreshSkipCount(int count);
+    void setRefreshSkipCount(int count) override;
 
     // addEventListener registers a callback to be called repeatedly at the
     // given phase offset from the hardware vsync events.  The callback is
     // called from a separate thread and it should return reasonably quickly
     // (i.e. within a few hundred microseconds).
-    status_t addEventListener(const char* name, nsecs_t phase, Callback* callback);
+    status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) override;
 
     // removeEventListener removes an already-registered event callback.  Once
     // this method returns that callback will no longer be called by the
     // DispSync object.
-    status_t removeEventListener(Callback* callback);
+    status_t removeEventListener(Callback* callback) override;
 
     // changePhaseOffset changes the phase offset of an already-registered event callback. The
     // method will make sure that there is no skipping or double-firing on the listener per frame,
     // even when changing the offsets multiple times.
-    status_t changePhaseOffset(Callback* callback, nsecs_t phase);
+    status_t changePhaseOffset(Callback* callback, nsecs_t phase) override;
 
     // computeNextRefresh computes when the next refresh is expected to begin.
     // The periodOffset value can be used to move forward or backward; an
     // offset of zero is the next refresh, -1 is the previous refresh, 1 is
     // the refresh after next. etc.
-    nsecs_t computeNextRefresh(int periodOffset) const;
+    nsecs_t computeNextRefresh(int periodOffset) const override;
+
+    // In certain situations the present fences aren't a good indicator of vsync
+    // time, e.g. when vr flinger is active, or simply aren't available,
+    // e.g. when the sync framework isn't present. Use this method to toggle
+    // whether or not DispSync ignores present fences. If present fences are
+    // ignored, DispSync will always ask for hardware vsync events by returning
+    // true from addPresentFence() and addResyncSample().
+    void setIgnorePresentFences(bool ignore) override;
+
+    // Determine the expected present time when a buffer acquired now will be displayed.
+    nsecs_t expectedPresentTime();
 
     // dump appends human-readable debug info to the result string.
-    void dump(String8& result) const;
+    void dump(std::string& result) const override;
 
 private:
     void updateModelLocked();
     void updateErrorLocked();
+    void resetLocked();
     void resetErrorLocked();
 
     enum { MAX_RESYNC_SAMPLES = 32 };
@@ -195,8 +231,13 @@
     bool mIgnorePresentFences;
 
     std::unique_ptr<Callback> mZeroPhaseTracer;
+
+    // Flag to turn on logging in systrace.
+    bool mTraceDetailedInfo = false;
 };
 
+} // namespace impl
+
 } // namespace android
 
 #endif // ANDROID_DISPSYNC_H
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
new file mode 100644
index 0000000..697d634
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "DispSyncSource.h"
+
+#include <android-base/stringprintf.h>
+#include <utils/Trace.h>
+#include <mutex>
+
+#include "DispSync.h"
+#include "EventThread.h"
+
+namespace android {
+
+DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
+                               const char* name)
+      : mName(name),
+        mTraceVsync(traceVsync),
+        mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
+        mVsyncEventLabel(base::StringPrintf("VSYNC-%s", name)),
+        mDispSync(dispSync),
+        mPhaseOffset(phaseOffset) {}
+
+void DispSyncSource::setVSyncEnabled(bool enable) {
+    std::lock_guard lock(mVsyncMutex);
+    if (enable) {
+        status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
+                                                   static_cast<DispSync::Callback*>(this));
+        if (err != NO_ERROR) {
+            ALOGE("error registering vsync callback: %s (%d)", strerror(-err), err);
+        }
+        // ATRACE_INT(mVsyncOnLabel.c_str(), 1);
+    } else {
+        status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this));
+        if (err != NO_ERROR) {
+            ALOGE("error unregistering vsync callback: %s (%d)", strerror(-err), err);
+        }
+        // ATRACE_INT(mVsyncOnLabel.c_str(), 0);
+    }
+    mEnabled = enable;
+}
+
+void DispSyncSource::setCallback(VSyncSource::Callback* callback) {
+    std::lock_guard lock(mCallbackMutex);
+    mCallback = callback;
+}
+
+void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
+    std::lock_guard lock(mVsyncMutex);
+
+    // Normalize phaseOffset to [0, period)
+    auto period = mDispSync->getPeriod();
+    phaseOffset %= period;
+    if (phaseOffset < 0) {
+        // If we're here, then phaseOffset is in (-period, 0). After this
+        // operation, it will be in (0, period)
+        phaseOffset += period;
+    }
+    mPhaseOffset = phaseOffset;
+
+    // If we're not enabled, we don't need to mess with the listeners
+    if (!mEnabled) {
+        return;
+    }
+
+    status_t err =
+            mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this), mPhaseOffset);
+    if (err != NO_ERROR) {
+        ALOGE("error changing vsync offset: %s (%d)", strerror(-err), err);
+    }
+}
+
+void DispSyncSource::onDispSyncEvent(nsecs_t when) {
+    VSyncSource::Callback* callback;
+    {
+        std::lock_guard lock(mCallbackMutex);
+        callback = mCallback;
+
+        if (mTraceVsync) {
+            mValue = (mValue + 1) % 2;
+            ATRACE_INT(mVsyncEventLabel.c_str(), mValue);
+        }
+    }
+
+    if (callback != nullptr) {
+        callback->onVSyncEvent(when);
+    }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
new file mode 100644
index 0000000..0fd84a2
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <mutex>
+#include <string>
+
+#include "DispSync.h"
+#include "EventThread.h"
+
+namespace android {
+
+class DispSyncSource final : public VSyncSource, private DispSync::Callback {
+public:
+    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
+
+    ~DispSyncSource() override = default;
+
+    // The following methods are implementation of VSyncSource.
+    void setVSyncEnabled(bool enable) override;
+    void setCallback(VSyncSource::Callback* callback) override;
+    void setPhaseOffset(nsecs_t phaseOffset) override;
+
+private:
+    // The following method is the implementation of the DispSync::Callback.
+    virtual void onDispSyncEvent(nsecs_t when);
+
+    const char* const mName;
+    int mValue = 0;
+
+    const bool mTraceVsync;
+    const std::string mVsyncOnLabel;
+    const std::string mVsyncEventLabel;
+
+    DispSync* mDispSync;
+
+    std::mutex mCallbackMutex;
+    VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
+
+    std::mutex mVsyncMutex;
+    nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex);
+    bool mEnabled GUARDED_BY(mVsyncMutex) = false;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp
similarity index 100%
rename from services/surfaceflinger/EventControlThread.cpp
rename to services/surfaceflinger/Scheduler/EventControlThread.cpp
diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/Scheduler/EventControlThread.h
similarity index 100%
rename from services/surfaceflinger/EventControlThread.h
rename to services/surfaceflinger/Scheduler/EventControlThread.h
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
new file mode 100644
index 0000000..52abe9c
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <pthread.h>
+#include <sched.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <cstdint>
+#include <optional>
+#include <type_traits>
+
+#include <android-base/stringprintf.h>
+
+#include <cutils/compiler.h>
+#include <cutils/sched_policy.h>
+
+#include <gui/DisplayEventReceiver.h>
+
+#include <utils/Errors.h>
+#include <utils/Trace.h>
+
+#include "EventThread.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+using base::StringAppendF;
+using base::StringPrintf;
+
+namespace {
+
+auto vsyncPeriod(VSyncRequest request) {
+    return static_cast<std::underlying_type_t<VSyncRequest>>(request);
+}
+
+std::string toString(VSyncRequest request) {
+    switch (request) {
+        case VSyncRequest::None:
+            return "VSyncRequest::None";
+        case VSyncRequest::Single:
+            return "VSyncRequest::Single";
+        default:
+            return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request));
+    }
+}
+
+std::string toString(const EventThreadConnection& connection) {
+    return StringPrintf("Connection{%p, %s}", &connection,
+                        toString(connection.vsyncRequest).c_str());
+}
+
+std::string toString(const DisplayEventReceiver::Event& event) {
+    switch (event.header.type) {
+        case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+            return StringPrintf("Hotplug{displayId=%u, %s}", event.header.id,
+                                event.hotplug.connected ? "connected" : "disconnected");
+        case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+            return StringPrintf("VSync{displayId=%u, count=%u}", event.header.id,
+                                event.vsync.count);
+        default:
+            return "Event{}";
+    }
+}
+
+DisplayEventReceiver::Event makeHotplug(uint32_t displayId, nsecs_t timestamp, bool connected) {
+    DisplayEventReceiver::Event event;
+    event.header = {DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, displayId, timestamp};
+    event.hotplug.connected = connected;
+    return event;
+}
+
+DisplayEventReceiver::Event makeVSync(uint32_t displayId, nsecs_t timestamp, uint32_t count) {
+    DisplayEventReceiver::Event event;
+    event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
+    event.vsync.count = count;
+    return event;
+}
+
+} // namespace
+
+EventThreadConnection::EventThreadConnection(EventThread* eventThread,
+                                             ResyncCallback resyncCallback)
+      : resyncCallback(std::move(resyncCallback)),
+        mEventThread(eventThread),
+        mChannel(gui::BitTube::DefaultSize) {}
+
+EventThreadConnection::~EventThreadConnection() {
+    // do nothing here -- clean-up will happen automatically
+    // when the main thread wakes up
+}
+
+void EventThreadConnection::onFirstRef() {
+    // NOTE: mEventThread doesn't hold a strong reference on us
+    mEventThread->registerDisplayEventConnection(this);
+}
+
+status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
+    outChannel->setReceiveFd(mChannel.moveReceiveFd());
+    return NO_ERROR;
+}
+
+status_t EventThreadConnection::setVsyncRate(uint32_t rate) {
+    mEventThread->setVsyncRate(rate, this);
+    return NO_ERROR;
+}
+
+void EventThreadConnection::requestNextVsync() {
+    ATRACE_NAME("requestNextVsync");
+    mEventThread->requestNextVsync(this, true);
+}
+
+void EventThreadConnection::requestNextVsyncForHWC() {
+    ATRACE_NAME("requestNextVsyncForHWC");
+    mEventThread->requestNextVsync(this, false);
+}
+
+status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
+    ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
+    return size < 0 ? status_t(size) : status_t(NO_ERROR);
+}
+
+// ---------------------------------------------------------------------------
+
+EventThread::~EventThread() = default;
+
+namespace impl {
+
+EventThread::EventThread(std::unique_ptr<VSyncSource> src,
+                         const InterceptVSyncsCallback& interceptVSyncsCallback,
+                         const ResetIdleTimerCallback& resetIdleTimerCallback,
+                         const char* threadName)
+      : EventThread(nullptr, std::move(src), interceptVSyncsCallback, threadName) {
+    mResetIdleTimer = resetIdleTimerCallback;
+}
+
+EventThread::EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback,
+                         const char* threadName)
+      : EventThread(src, nullptr, interceptVSyncsCallback, threadName) {}
+
+EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
+                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
+      : mVSyncSource(src),
+        mVSyncSourceUnique(std::move(uniqueSrc)),
+        mInterceptVSyncsCallback(interceptVSyncsCallback),
+        mThreadName(threadName) {
+    if (src == nullptr) {
+        mVSyncSource = mVSyncSourceUnique.get();
+    }
+    mVSyncSource->setCallback(this);
+
+    mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
+        std::unique_lock<std::mutex> lock(mMutex);
+        threadMain(lock);
+    });
+
+    pthread_setname_np(mThread.native_handle(), threadName);
+
+    pid_t tid = pthread_gettid_np(mThread.native_handle());
+
+    // Use SCHED_FIFO to minimize jitter
+    constexpr int EVENT_THREAD_PRIORITY = 2;
+    struct sched_param param = {0};
+    param.sched_priority = EVENT_THREAD_PRIORITY;
+    if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, &param) != 0) {
+        ALOGE("Couldn't set SCHED_FIFO for EventThread");
+    }
+
+    set_sched_policy(tid, SP_FOREGROUND);
+}
+
+EventThread::~EventThread() {
+    mVSyncSource->setCallback(nullptr);
+
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mState = State::Quit;
+        mCondition.notify_all();
+    }
+    mThread.join();
+}
+
+void EventThread::setPhaseOffset(nsecs_t phaseOffset) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mVSyncSource->setPhaseOffset(phaseOffset);
+}
+
+sp<EventThreadConnection> EventThread::createEventConnection(ResyncCallback resyncCallback) const {
+    return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback));
+}
+
+status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    // this should never happen
+    auto it = std::find(mDisplayEventConnections.cbegin(),
+            mDisplayEventConnections.cend(), connection);
+    if (it != mDisplayEventConnections.cend()) {
+        ALOGW("DisplayEventConnection %p already exists", connection.get());
+        mCondition.notify_all();
+        return ALREADY_EXISTS;
+    }
+
+    mDisplayEventConnections.push_back(connection);
+    mCondition.notify_all();
+    return NO_ERROR;
+}
+
+void EventThread::removeDisplayEventConnectionLocked(const wp<EventThreadConnection>& connection) {
+    auto it = std::find(mDisplayEventConnections.cbegin(),
+            mDisplayEventConnections.cend(), connection);
+    if (it != mDisplayEventConnections.cend()) {
+        mDisplayEventConnections.erase(it);
+    }
+}
+
+void EventThread::setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) {
+    if (static_cast<std::underlying_type_t<VSyncRequest>>(rate) < 0) {
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    const auto request = rate == 0 ? VSyncRequest::None : static_cast<VSyncRequest>(rate);
+    if (connection->vsyncRequest != request) {
+        connection->vsyncRequest = request;
+        mCondition.notify_all();
+    }
+}
+
+void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection, bool reset) {
+    if (mResetIdleTimer && reset) {
+        ATRACE_NAME("resetIdleTimer");
+        mResetIdleTimer();
+    }
+
+    if (connection->resyncCallback) {
+        connection->resyncCallback();
+    }
+
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    if (connection->vsyncRequest == VSyncRequest::None) {
+        connection->vsyncRequest = VSyncRequest::Single;
+        mCondition.notify_all();
+    }
+}
+
+void EventThread::onScreenReleased() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mVSyncState || mVSyncState->synthetic) {
+        return;
+    }
+
+    mVSyncState->synthetic = true;
+    mCondition.notify_all();
+}
+
+void EventThread::onScreenAcquired() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mVSyncState || !mVSyncState->synthetic) {
+        return;
+    }
+
+    mVSyncState->synthetic = false;
+    mCondition.notify_all();
+}
+
+void EventThread::onVSyncEvent(nsecs_t timestamp) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    LOG_FATAL_IF(!mVSyncState);
+    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count));
+    mCondition.notify_all();
+}
+
+void EventThread::onHotplugReceived(DisplayType displayType, bool connected) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    const uint32_t displayId = displayType == DisplayType::Primary ? 0 : 1;
+    mPendingEvents.push_back(makeHotplug(displayId, systemTime(), connected));
+    mCondition.notify_all();
+}
+
+void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
+    DisplayEventConsumers consumers;
+
+    while (mState != State::Quit) {
+        std::optional<DisplayEventReceiver::Event> event;
+
+        // Determine next event to dispatch.
+        if (!mPendingEvents.empty()) {
+            event = mPendingEvents.front();
+            mPendingEvents.pop_front();
+
+            switch (event->header.type) {
+                case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+                    if (event->hotplug.connected && !mVSyncState) {
+                        mVSyncState.emplace(event->header.id);
+                    } else if (!event->hotplug.connected && mVSyncState &&
+                               mVSyncState->displayId == event->header.id) {
+                        mVSyncState.reset();
+                    }
+                    break;
+
+                case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+                    if (mInterceptVSyncsCallback) {
+                        mInterceptVSyncsCallback(event->header.timestamp);
+                    }
+                    break;
+            }
+        }
+
+        bool vsyncRequested = false;
+
+        // Find connections that should consume this event.
+        auto it = mDisplayEventConnections.begin();
+        while (it != mDisplayEventConnections.end()) {
+            if (const auto connection = it->promote()) {
+                vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
+
+                if (event && shouldConsumeEvent(*event, connection)) {
+                    consumers.push_back(connection);
+                }
+
+                ++it;
+            } else {
+                it = mDisplayEventConnections.erase(it);
+            }
+        }
+
+        if (!consumers.empty()) {
+            dispatchEvent(*event, consumers);
+            consumers.clear();
+        }
+
+        State nextState;
+        if (mVSyncState && vsyncRequested) {
+            nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
+        } else {
+            ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");
+            nextState = State::Idle;
+        }
+
+        if (mState != nextState) {
+            if (mState == State::VSync) {
+                mVSyncSource->setVSyncEnabled(false);
+            } else if (nextState == State::VSync) {
+                mVSyncSource->setVSyncEnabled(true);
+            }
+
+            mState = nextState;
+        }
+
+        if (event) {
+            continue;
+        }
+
+        // Wait for event or client registration/request.
+        if (mState == State::Idle) {
+            mCondition.wait(lock);
+        } else {
+            // Generate a fake VSYNC after a long timeout in case the driver stalls. When the
+            // display is off, keep feeding clients at 60 Hz.
+            const auto timeout = mState == State::SyntheticVSync ? 16ms : 1000ms;
+            if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
+                ALOGW_IF(mState == State::VSync, "Faking VSYNC due to driver stall");
+
+                LOG_FATAL_IF(!mVSyncState);
+                mPendingEvents.push_back(makeVSync(mVSyncState->displayId,
+                                                   systemTime(SYSTEM_TIME_MONOTONIC),
+                                                   ++mVSyncState->count));
+            }
+        }
+    }
+}
+
+bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
+                                     const sp<EventThreadConnection>& connection) const {
+    switch (event.header.type) {
+        case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+            return true;
+
+        case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+            switch (connection->vsyncRequest) {
+                case VSyncRequest::None:
+                    return false;
+                case VSyncRequest::Single:
+                    connection->vsyncRequest = VSyncRequest::None;
+                    return true;
+                case VSyncRequest::Periodic:
+                    return true;
+                default:
+                    return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
+            }
+
+        default:
+            return false;
+    }
+}
+
+void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
+                                const DisplayEventConsumers& consumers) {
+    for (const auto& consumer : consumers) {
+        switch (consumer->postEvent(event)) {
+            case NO_ERROR:
+                break;
+
+            case -EAGAIN:
+                // TODO: Try again if pipe is full.
+                ALOGW("Failed dispatching %s for %s", toString(event).c_str(),
+                      toString(*consumer).c_str());
+                break;
+
+            default:
+                // Treat EPIPE and other errors as fatal.
+                removeDisplayEventConnectionLocked(consumer);
+        }
+    }
+}
+
+void EventThread::dump(std::string& result) const {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    StringAppendF(&result, "%s: state=%s VSyncState=", mThreadName, toCString(mState));
+    if (mVSyncState) {
+        StringAppendF(&result, "{displayId=%u, count=%u%s}\n", mVSyncState->displayId,
+                      mVSyncState->count, mVSyncState->synthetic ? ", synthetic" : "");
+    } else {
+        StringAppendF(&result, "none\n");
+    }
+
+    StringAppendF(&result, "  pending events (count=%zu):\n", mPendingEvents.size());
+    for (const auto& event : mPendingEvents) {
+        StringAppendF(&result, "    %s\n", toString(event).c_str());
+    }
+
+    StringAppendF(&result, "  connections (count=%zu):\n", mDisplayEventConnections.size());
+    for (const auto& ptr : mDisplayEventConnections) {
+        if (const auto connection = ptr.promote()) {
+            StringAppendF(&result, "    %s\n", toString(*connection).c_str());
+        }
+    }
+}
+
+const char* EventThread::toCString(State state) {
+    switch (state) {
+        case State::Idle:
+            return "Idle";
+        case State::Quit:
+            return "Quit";
+        case State::SyntheticVSync:
+            return "SyntheticVSync";
+        case State::VSync:
+            return "VSync";
+    }
+}
+
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
new file mode 100644
index 0000000..89b799e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <condition_variable>
+#include <cstdint>
+#include <deque>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+
+#include <gui/DisplayEventReceiver.h>
+#include <gui/IDisplayEventConnection.h>
+#include <private/gui/BitTube.h>
+
+#include <utils/Errors.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class EventThread;
+class EventThreadTest;
+class SurfaceFlinger;
+
+// ---------------------------------------------------------------------------
+
+using ResyncCallback = std::function<void()>;
+
+enum class VSyncRequest {
+    None = -1,
+    Single = 0,
+    Periodic = 1,
+    // Subsequent values are periods.
+};
+
+class VSyncSource {
+public:
+    class Callback {
+    public:
+        virtual ~Callback() {}
+        virtual void onVSyncEvent(nsecs_t when) = 0;
+    };
+
+    virtual ~VSyncSource() {}
+    virtual void setVSyncEnabled(bool enable) = 0;
+    virtual void setCallback(Callback* callback) = 0;
+    virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+};
+
+class EventThreadConnection : public BnDisplayEventConnection {
+public:
+    EventThreadConnection(EventThread* eventThread, ResyncCallback resyncCallback);
+    virtual ~EventThreadConnection();
+
+    virtual status_t postEvent(const DisplayEventReceiver::Event& event);
+
+    status_t stealReceiveChannel(gui::BitTube* outChannel) override;
+    status_t setVsyncRate(uint32_t rate) override;
+    void requestNextVsync() override; // asynchronous
+    // Requesting Vsync for HWC does not reset the idle timer, since HWC requires a refresh
+    // in order to update the configs.
+    void requestNextVsyncForHWC();
+
+    // Called in response to requestNextVsync.
+    const ResyncCallback resyncCallback;
+
+    VSyncRequest vsyncRequest = VSyncRequest::None;
+
+private:
+    virtual void onFirstRef();
+    EventThread* const mEventThread;
+    gui::BitTube mChannel;
+};
+
+class EventThread {
+public:
+    // TODO: Remove once stable display IDs are plumbed through SF/WM interface.
+    enum class DisplayType { Primary, External };
+
+    virtual ~EventThread();
+
+    virtual sp<EventThreadConnection> createEventConnection(
+            ResyncCallback resyncCallback) const = 0;
+
+    // called before the screen is turned off from main thread
+    virtual void onScreenReleased() = 0;
+
+    // called after the screen is turned on from main thread
+    virtual void onScreenAcquired() = 0;
+
+    // called when receiving a hotplug event
+    virtual void onHotplugReceived(DisplayType displayType, bool connected) = 0;
+
+    virtual void dump(std::string& result) const = 0;
+
+    virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+
+    virtual status_t registerDisplayEventConnection(
+            const sp<EventThreadConnection>& connection) = 0;
+    virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0;
+    // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
+    virtual void requestNextVsync(const sp<EventThreadConnection>& connection,
+                                  bool resetIdleTimer) = 0;
+};
+
+namespace impl {
+
+class EventThread : public android::EventThread, private VSyncSource::Callback {
+public:
+    using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
+    using ResetIdleTimerCallback = std::function<void()>;
+
+    // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete.
+    EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback,
+                const char* threadName);
+    EventThread(std::unique_ptr<VSyncSource> src,
+                const InterceptVSyncsCallback& interceptVSyncsCallback,
+                const ResetIdleTimerCallback& resetIdleTimerCallback, const char* threadName);
+    ~EventThread();
+
+    sp<EventThreadConnection> createEventConnection(ResyncCallback resyncCallback) const override;
+
+    status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
+    void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
+    void requestNextVsync(const sp<EventThreadConnection>& connection,
+                          bool resetIdleTimer) override;
+
+    // called before the screen is turned off from main thread
+    void onScreenReleased() override;
+
+    // called after the screen is turned on from main thread
+    void onScreenAcquired() override;
+
+    // called when receiving a hotplug event
+    void onHotplugReceived(DisplayType displayType, bool connected) override;
+
+    void dump(std::string& result) const override;
+
+    void setPhaseOffset(nsecs_t phaseOffset) override;
+
+private:
+    friend EventThreadTest;
+
+    using DisplayEventConsumers = std::vector<sp<EventThreadConnection>>;
+
+    // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete.
+    EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
+                InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
+
+    void threadMain(std::unique_lock<std::mutex>& lock) REQUIRES(mMutex);
+
+    bool shouldConsumeEvent(const DisplayEventReceiver::Event& event,
+                            const sp<EventThreadConnection>& connection) const REQUIRES(mMutex);
+    void dispatchEvent(const DisplayEventReceiver::Event& event,
+                       const DisplayEventConsumers& consumers) REQUIRES(mMutex);
+
+    void removeDisplayEventConnectionLocked(const wp<EventThreadConnection>& connection)
+            REQUIRES(mMutex);
+
+    // Implements VSyncSource::Callback
+    void onVSyncEvent(nsecs_t timestamp) override;
+
+    // Acquires mutex and requests next vsync.
+    void requestNextVsyncInternal(const sp<EventThreadConnection>& connection) EXCLUDES(mMutex);
+
+    // TODO(b/113612090): Once the Scheduler is complete this pointer will become obsolete.
+    VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
+    std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr;
+
+    const InterceptVSyncsCallback mInterceptVSyncsCallback;
+    const char* const mThreadName;
+
+    std::thread mThread;
+    mutable std::mutex mMutex;
+    mutable std::condition_variable mCondition;
+
+    std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex);
+    std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
+
+    // VSYNC state of connected display.
+    struct VSyncState {
+        explicit VSyncState(uint32_t displayId) : displayId(displayId) {}
+
+        const uint32_t displayId;
+
+        // Number of VSYNC events since display was connected.
+        uint32_t count = 0;
+
+        // True if VSYNC should be faked, e.g. when display is off.
+        bool synthetic = false;
+    };
+
+    // TODO(b/74619554): Create per-display threads waiting on respective VSYNC signals,
+    // and support headless mode by injecting a fake display with synthetic VSYNC.
+    std::optional<VSyncState> mVSyncState GUARDED_BY(mMutex);
+
+    // State machine for event loop.
+    enum class State {
+        Idle,
+        Quit,
+        SyntheticVSync,
+        VSync,
+    };
+
+    State mState GUARDED_BY(mMutex) = State::Idle;
+
+    static const char* toCString(State);
+
+    // Callback that resets the idle timer when the next vsync is received.
+    ResetIdleTimerCallback mResetIdleTimer;
+};
+
+// ---------------------------------------------------------------------------
+
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/IdleTimer.cpp
new file mode 100644
index 0000000..5a76dbc
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/IdleTimer.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "IdleTimer.h"
+
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace scheduler {
+
+IdleTimer::IdleTimer(const Interval& interval, const TimeoutCallback& timeoutCallback)
+      : mInterval(interval), mTimeoutCallback(timeoutCallback) {}
+
+IdleTimer::~IdleTimer() {
+    stop();
+}
+
+void IdleTimer::start() {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mState = TimerState::RESET;
+    }
+    mThread = std::thread(&IdleTimer::loop, this);
+}
+
+void IdleTimer::stop() {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mState = TimerState::STOPPED;
+    }
+    mCondition.notify_all();
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+void IdleTimer::loop() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    while (mState != TimerState::STOPPED) {
+        if (mState == TimerState::IDLE) {
+            mCondition.wait(mMutex);
+        } else if (mState == TimerState::RESET) {
+            mState = TimerState::WAITING;
+            if (mCondition.wait_for(mMutex, mInterval) == std::cv_status::timeout) {
+                if (mTimeoutCallback) {
+                    mTimeoutCallback();
+                }
+            }
+            if (mState == TimerState::WAITING) {
+                mState = TimerState::IDLE;
+            }
+        }
+    }
+}
+
+void IdleTimer::reset() {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mState = TimerState::RESET;
+    }
+    mCondition.notify_all();
+}
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.h b/services/surfaceflinger/Scheduler/IdleTimer.h
new file mode 100644
index 0000000..aee3fa3
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/IdleTimer.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <condition_variable>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+namespace scheduler {
+
+/*
+ * Class that sets off a timer for a given interval, and fires a callback when the
+ * interval expires.
+ */
+class IdleTimer {
+public:
+    using Interval = std::chrono::milliseconds;
+    using TimeoutCallback = std::function<void()>;
+
+    IdleTimer(const Interval& interval, const TimeoutCallback& timeoutCallback);
+    ~IdleTimer();
+
+    void start();
+    void stop();
+    void reset();
+
+private:
+    // Enum to track in what state is the timer.
+    enum class TimerState { STOPPED = 0, RESET = 1, WAITING = 2, IDLE = 3 };
+
+    // Function that loops until the condition for stopping is met.
+    void loop();
+
+    // Thread waiting for timer to expire.
+    std::thread mThread;
+
+    // Condition used to notify mThread.
+    std::condition_variable_any mCondition;
+
+    // Lock used for synchronizing the waiting thread with the application thread.
+    std::mutex mMutex;
+
+    TimerState mState GUARDED_BY(mMutex) = TimerState::RESET;
+
+    // Interval after which timer expires.
+    const Interval mInterval;
+
+    // Callback that happens when timer expires.
+    const TimeoutCallback mTimeoutCallback;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
new file mode 100644
index 0000000..a0e1447
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <mutex>
+
+#include "EventThread.h"
+
+namespace android {
+
+/**
+ * VSync signals used during SurfaceFlinger trace playback (traces we captured
+ * with SurfaceInterceptor).
+ */
+class InjectVSyncSource final : public VSyncSource {
+public:
+    ~InjectVSyncSource() override = default;
+
+    void setCallback(VSyncSource::Callback* callback) override {
+        std::lock_guard<std::mutex> lock(mCallbackMutex);
+        mCallback = callback;
+    }
+
+    void onInjectSyncEvent(nsecs_t when) {
+        std::lock_guard<std::mutex> lock(mCallbackMutex);
+        if (mCallback) {
+            mCallback->onVSyncEvent(when);
+        }
+    }
+
+    void setVSyncEnabled(bool) override {}
+    void setPhaseOffset(nsecs_t) override {}
+
+private:
+    std::mutex mCallbackMutex;
+    VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
new file mode 100644
index 0000000..d5ccbe1
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerHistory.h"
+
+#include <cinttypes>
+#include <cstdint>
+#include <numeric>
+#include <string>
+#include <unordered_map>
+
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include "SchedulerUtils.h"
+
+namespace android {
+
+LayerHistory::LayerHistory() {}
+
+LayerHistory::~LayerHistory() = default;
+
+void LayerHistory::insert(const std::string layerName, nsecs_t presentTime) {
+    mElements[mCounter].insert(std::make_pair(layerName, presentTime));
+}
+
+void LayerHistory::incrementCounter() {
+    mCounter++;
+    mCounter = mCounter % scheduler::ARRAY_SIZE;
+    // Clear all the previous data from the history. This is a ring buffer, so we are
+    // reusing memory.
+    mElements[mCounter].clear();
+}
+
+const std::unordered_map<std::string, nsecs_t>& LayerHistory::get(size_t index) const {
+    // For the purposes of the layer history, the index = 0 always needs to start at the
+    // current counter, and then decrement to access the layers in correct historical order.
+    return mElements.at((scheduler::ARRAY_SIZE + (mCounter - (index % scheduler::ARRAY_SIZE))) %
+                        scheduler::ARRAY_SIZE);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
new file mode 100644
index 0000000..c6fab07
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+#include <cinttypes>
+#include <cstdint>
+#include <numeric>
+#include <string>
+#include <unordered_map>
+
+#include <utils/Timers.h>
+
+#include "SchedulerUtils.h"
+
+namespace android {
+
+/*
+ * This class represents a circular buffer in which we keep layer history for
+ * the past ARRAY_SIZE frames. Each time, a signal for new frame comes, the counter
+ * gets incremented and includes all the layers that are requested to draw in that
+ * frame.
+ *
+ * Once the buffer reaches the end of the array, it starts overriding the elements
+ * at the beginning of the array.
+ */
+class LayerHistory {
+public:
+    LayerHistory();
+    ~LayerHistory();
+
+    // Method for inserting layers and their requested present time into the ring buffer.
+    // The elements are going to be inserted into an unordered_map at the position 'now'.
+    void insert(const std::string layerName, nsecs_t presentTime);
+    // Method for incrementing the current slot in the ring buffer. It also clears the
+    // unordered_map, if it was created previously.
+    void incrementCounter();
+    // Returns unordered_map at the given at index. The index is decremented from 'now'. For
+    // example, 0 is now, 1 is previous frame.
+    const std::unordered_map<std::string, nsecs_t>& get(size_t index) const;
+    // Returns the total size of the ring buffer. The value is always the same regardless
+    // of how many slots we filled in.
+    static constexpr size_t getSize() { return scheduler::ARRAY_SIZE; }
+
+private:
+    size_t mCounter = 0;
+    std::array<std::unordered_map<std::string, nsecs_t>, scheduler::ARRAY_SIZE> mElements;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
similarity index 88%
rename from services/surfaceflinger/MessageQueue.cpp
rename to services/surfaceflinger/Scheduler/MessageQueue.cpp
index 056d381..75a410b 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -85,7 +85,8 @@
     mHandler = new Handler(*this);
 }
 
-void MessageQueue::setEventThread(android::EventThread* eventThread) {
+void MessageQueue::setEventThread(android::EventThread* eventThread,
+                                  ResyncCallback resyncCallback) {
     if (mEventThread == eventThread) {
         return;
     }
@@ -95,7 +96,18 @@
     }
 
     mEventThread = eventThread;
-    mEvents = eventThread->createEventConnection();
+    mEvents = eventThread->createEventConnection(std::move(resyncCallback));
+    mEvents->stealReceiveChannel(&mEventTube);
+    mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
+                   this);
+}
+
+void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
+    if (mEventTube.getFd() >= 0) {
+        mLooper->removeFd(mEventTube.getFd());
+    }
+
+    mEvents = connection;
     mEvents->stealReceiveChannel(&mEventTube);
     mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
                    this);
@@ -137,6 +149,10 @@
     mEvents->requestNextVsync();
 }
 
+void MessageQueue::invalidateForHWC() {
+    mEvents->requestNextVsyncForHWC();
+}
+
 void MessageQueue::refresh() {
     mHandler->dispatchRefresh();
 }
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
similarity index 84%
rename from services/surfaceflinger/MessageQueue.h
rename to services/surfaceflinger/Scheduler/MessageQueue.h
index 90d1c72..56a00c0 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -29,12 +29,12 @@
 #include <private/gui/BitTube.h>
 
 #include "Barrier.h"
+#include "EventThread.h"
 
 #include <functional>
 
 namespace android {
 
-class EventThread;
 class SurfaceFlinger;
 
 // ---------------------------------------------------------------------------
@@ -85,10 +85,13 @@
     virtual ~MessageQueue();
 
     virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
-    virtual void setEventThread(EventThread* events) = 0;
+    // TODO(akrulec): Remove this function once everything is migrated to Scheduler.
+    virtual void setEventThread(EventThread* events, ResyncCallback resyncCallback) = 0;
+    virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
     virtual void waitMessage() = 0;
     virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0;
     virtual void invalidate() = 0;
+    virtual void invalidateForHWC() = 0;
     virtual void refresh() = 0;
 };
 
@@ -114,7 +117,7 @@
     sp<SurfaceFlinger> mFlinger;
     sp<Looper> mLooper;
     android::EventThread* mEventThread;
-    sp<IDisplayEventConnection> mEvents;
+    sp<EventThreadConnection> mEvents;
     gui::BitTube mEventTube;
     sp<Handler> mHandler;
 
@@ -124,13 +127,17 @@
 public:
     ~MessageQueue() override = default;
     void init(const sp<SurfaceFlinger>& flinger) override;
-    void setEventThread(android::EventThread* events) override;
+    void setEventThread(android::EventThread* events, ResyncCallback resyncCallback) override;
+    void setEventConnection(const sp<EventThreadConnection>& connection) override;
 
     void waitMessage() override;
     status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) override;
 
     // sends INVALIDATE message at next VSYNC
     void invalidate() override;
+
+    // sends INVALIDATE message at next VSYNC, without resetting the idle timer in the Scheduler
+    void invalidateForHWC();
     // sends REFRESH message at next VSYNC
     void refresh() override;
 };
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
new file mode 100644
index 0000000..a0a4455
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2019 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 "PhaseOffsets.h"
+
+#include <cutils/properties.h>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace android {
+using namespace android::sysprop;
+
+namespace scheduler {
+
+PhaseOffsets::~PhaseOffsets() = default;
+
+namespace impl {
+PhaseOffsets::PhaseOffsets() {
+    int64_t vsyncPhaseOffsetNs = vsync_event_phase_offset_ns(1000000);
+
+    int64_t sfVsyncPhaseOffsetNs = vsync_sf_event_phase_offset_ns(1000000);
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.sf.early_phase_offset_ns", value, "-1");
+    const int earlySfOffsetNs = atoi(value);
+
+    property_get("debug.sf.early_gl_phase_offset_ns", value, "-1");
+    const int earlyGlSfOffsetNs = atoi(value);
+
+    property_get("debug.sf.early_app_phase_offset_ns", value, "-1");
+    const int earlyAppOffsetNs = atoi(value);
+
+    property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
+    const int earlyGlAppOffsetNs = atoi(value);
+
+    property_get("debug.sf.high_fps_early_phase_offset_ns", value, "-1");
+    const int highFpsEarlySfOffsetNs = atoi(value);
+
+    property_get("debug.sf.high_fps_early_gl_phase_offset_ns", value, "-1");
+    const int highFpsEarlyGlSfOffsetNs = atoi(value);
+
+    property_get("debug.sf.high_fps_early_app_phase_offset_ns", value, "-1");
+    const int highFpsEarlyAppOffsetNs = atoi(value);
+
+    property_get("debug.sf.high_fps_early_gl_app_phase_offset_ns", value, "-1");
+    const int highFpsEarlyGlAppOffsetNs = atoi(value);
+
+    // TODO(b/122905996): Define these in device.mk.
+    property_get("debug.sf.high_fps_late_app_phase_offset_ns", value, "1000000");
+    const int highFpsLateAppOffsetNs = atoi(value);
+
+    property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "8000000");
+    const int highFpsLateSfOffsetNs = atoi(value);
+
+    mDefaultRefreshRateOffsets.early = {earlySfOffsetNs != -1 ? earlySfOffsetNs
+                                                              : sfVsyncPhaseOffsetNs,
+                                        earlyAppOffsetNs != -1 ? earlyAppOffsetNs
+                                                               : vsyncPhaseOffsetNs};
+    mDefaultRefreshRateOffsets.earlyGl = {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs
+                                                                  : sfVsyncPhaseOffsetNs,
+                                          earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs
+                                                                   : vsyncPhaseOffsetNs};
+    mDefaultRefreshRateOffsets.late = {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
+
+    mHighRefreshRateOffsets.early = {highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
+                                                                  : highFpsLateAppOffsetNs,
+                                     highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
+                                                                   : highFpsLateSfOffsetNs};
+    mHighRefreshRateOffsets.earlyGl = {highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
+                                                                      : highFpsLateAppOffsetNs,
+                                       highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
+                                                                       : highFpsLateSfOffsetNs};
+    mHighRefreshRateOffsets.late = {highFpsLateAppOffsetNs, highFpsLateSfOffsetNs};
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getCurrentOffsets() const {
+    switch (mRefreshRateType) {
+        case RefreshRateConfigs::RefreshRateType::PERFORMANCE:
+            return mHighRefreshRateOffsets;
+        default:
+            return mDefaultRefreshRateOffsets;
+    }
+}
+
+void PhaseOffsets::dump(std::string& result) const {
+    const auto [early, earlyGl, late] = getCurrentOffsets();
+    base::StringAppendF(&result,
+                        "         app phase: %9" PRId64 " ns\t         SF phase: %9" PRId64 " ns\n"
+                        "   early app phase: %9" PRId64 " ns\t   early SF phase: %9" PRId64 " ns\n"
+                        "GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n",
+                        late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf);
+}
+
+nsecs_t PhaseOffsets::getCurrentAppOffset() {
+    return getCurrentOffsets().late.app;
+}
+
+nsecs_t PhaseOffsets::getCurrentSfOffset() {
+    return getCurrentOffsets().late.sf;
+}
+
+} // namespace impl
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
new file mode 100644
index 0000000..cbcaade
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 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 <cinttypes>
+
+#include "RefreshRateConfigs.h"
+#include "VSyncModulator.h"
+
+namespace android {
+namespace scheduler {
+
+/*
+ * This class encapsulates offsets for different refresh rates. Depending
+ * on what refresh rate we are using, and wheter we are composing in GL,
+ * different offsets will help us with latency. This class keeps track of
+ * which mode the device is on, and returns approprate offsets when needed.
+ */
+class PhaseOffsets {
+public:
+    struct Offsets {
+        VSyncModulator::Offsets early;
+        VSyncModulator::Offsets earlyGl;
+        VSyncModulator::Offsets late;
+    };
+
+    virtual ~PhaseOffsets();
+
+    virtual nsecs_t getCurrentAppOffset() = 0;
+    virtual nsecs_t getCurrentSfOffset() = 0;
+    virtual Offsets getCurrentOffsets() const = 0;
+    virtual void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) = 0;
+    virtual void dump(std::string& result) const = 0;
+};
+
+namespace impl {
+class PhaseOffsets : public scheduler::PhaseOffsets {
+public:
+    PhaseOffsets();
+
+    nsecs_t getCurrentAppOffset() override;
+    nsecs_t getCurrentSfOffset() override;
+
+    // Returns early, early GL, and late offsets for Apps and SF.
+    Offsets getCurrentOffsets() const override;
+
+    // This function should be called when the device is switching between different
+    // refresh rates, to properly update the offsets.
+    void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) override {
+        mRefreshRateType = refreshRateType;
+    }
+
+    // Returns current offsets in human friendly format.
+    void dump(std::string& result) const override;
+
+private:
+    Offsets getmDefaultRefreshRateOffsets() { return mDefaultRefreshRateOffsets; }
+    Offsets getmHighRefreshRateOffsets() { return mHighRefreshRateOffsets; }
+
+    std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType =
+            RefreshRateConfigs::RefreshRateType::DEFAULT;
+
+    Offsets mDefaultRefreshRateOffsets;
+    Offsets mHighRefreshRateOffsets;
+};
+} // namespace impl
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
new file mode 100644
index 0000000..bfe5da5
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2019 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 <algorithm>
+#include <numeric>
+
+#include "android-base/stringprintf.h"
+
+#include "DisplayHardware/HWComposer.h"
+#include "Scheduler/SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+/**
+ * This class is used to encapsulate configuration for refresh rates. It holds infomation
+ * about available refresh rates on the device, and the mapping between the numbers and human
+ * readable names.
+ */
+class RefreshRateConfigs {
+public:
+    // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest
+    // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance
+    // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
+    enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE };
+
+    struct RefreshRate {
+        // Type of the refresh rate.
+        RefreshRateType type;
+        // This config ID corresponds to the position of the config in the vector that is stored
+        // on the device.
+        int configId;
+        // Human readable name of the refresh rate.
+        std::string name;
+    };
+
+    // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
+    // baking them in.
+    explicit RefreshRateConfigs(
+            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+        init(configs);
+    }
+    ~RefreshRateConfigs() = default;
+
+    const std::vector<RefreshRate>& getRefreshRates() { return mRefreshRates; }
+
+private:
+    void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+        // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
+        mRefreshRates.push_back(
+                RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff"});
+
+        if (configs.size() < 1) {
+            ALOGE("Device does not have valid configs. Config size is 0.");
+            return;
+        }
+
+        // Create a map between config index and vsync period. This is all the info we need
+        // from the configs.
+        std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod;
+        for (int i = 0; i < configs.size(); ++i) {
+            configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod());
+        }
+
+        std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(),
+                  [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) {
+                      return a.second > b.second;
+                  });
+
+        // When the configs are ordered by the resync rate. We assume that the first one is DEFAULT.
+        nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
+        if (vsyncPeriod != 0) {
+            const float fps = 1e9 / vsyncPeriod;
+            mRefreshRates.push_back(RefreshRate{RefreshRateType::DEFAULT,
+                                                configIdToVsyncPeriod[0].first,
+                                                base::StringPrintf("%2.ffps", fps)});
+        }
+        if (configs.size() < 2) {
+            return;
+        }
+
+        // When the configs are ordered by the resync rate. We assume that the second one is
+        // PERFORMANCE, eg. the higher rate.
+        vsyncPeriod = configIdToVsyncPeriod[1].second;
+        if (vsyncPeriod != 0) {
+            const float fps = 1e9 / vsyncPeriod;
+            mRefreshRates.push_back(RefreshRate{RefreshRateType::PERFORMANCE,
+                                                configIdToVsyncPeriod[1].first,
+                                                base::StringPrintf("%2.ffps", fps)});
+        }
+    }
+
+    std::vector<RefreshRate> mRefreshRates;
+};
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
new file mode 100644
index 0000000..7e22232
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2019 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 <numeric>
+
+#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/SchedulerUtils.h"
+
+#include "android-base/stringprintf.h"
+#include "utils/Timers.h"
+
+namespace android {
+namespace scheduler {
+
+/**
+ * Class to encapsulate statistics about refresh rates that the display is using. When the power
+ * mode is set to HWC_POWER_MODE_NORMAL, SF is switching between refresh rates that are stored in
+ * the device's configs. Otherwise, we assume the HWC is running in power saving mode under the
+ * hood (eg. the device is in DOZE, or screen off mode).
+ */
+class RefreshRateStats {
+    static constexpr int64_t MS_PER_S = 1000;
+    static constexpr int64_t MS_PER_MIN = 60 * MS_PER_S;
+    static constexpr int64_t MS_PER_HOUR = 60 * MS_PER_MIN;
+    static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
+
+public:
+    explicit RefreshRateStats(
+            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs)
+          : mRefreshRateConfigs(std::make_unique<RefreshRateConfigs>(configs)),
+            mPreviousRecordedTime(systemTime()) {}
+    ~RefreshRateStats() = default;
+
+    // Sets power mode. We only collect the information when the power mode is not
+    // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based
+    // on config mode.
+    void setPowerMode(int mode) {
+        if (mCurrentPowerMode == mode) {
+            return;
+        }
+        // If power mode is normal, the time is going to be recorded under config modes.
+        if (mode == HWC_POWER_MODE_NORMAL) {
+            mCurrentPowerMode = mode;
+            return;
+        }
+        flushTime();
+        mCurrentPowerMode = mode;
+    }
+
+    // Sets config mode. If the mode has changed, it records how much time was spent in the previous
+    // mode.
+    void setConfigMode(int mode) {
+        if (mCurrentConfigMode == mode) {
+            return;
+        }
+        flushTime();
+        mCurrentConfigMode = mode;
+    }
+
+    // Returns a map between human readable refresh rate and number of seconds the device spent in
+    // that mode.
+    std::unordered_map<std::string, int64_t> getTotalTimes() {
+        // If the power mode is on, then we are probably switching between the config modes. If
+        // it's not then the screen is probably off. Make sure to flush times before printing
+        // them.
+        flushTime();
+
+        std::unordered_map<std::string, int64_t> totalTime;
+        for (auto config : mRefreshRateConfigs->getRefreshRates()) {
+            int64_t totalTimeForConfig = 0;
+            if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) {
+                totalTimeForConfig = mConfigModesTotalTime.at(config.configId);
+            }
+            totalTime[config.name] = totalTimeForConfig;
+        }
+        return totalTime;
+    }
+
+    // Traverses through the map of config modes and returns how long they've been running in easy
+    // to read format.
+    std::string doDump() {
+        std::ostringstream stream;
+        stream << "+  Refresh rate: running time in seconds\n";
+        for (auto stats : getTotalTimes()) {
+            stream << stats.first.c_str() << ": " << getDateFormatFromMs(stats.second) << "\n";
+        }
+        return stream.str();
+    }
+
+private:
+    void flushTime() {
+        // Normal power mode is counted under different config modes.
+        if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
+            flushTimeForMode(mCurrentConfigMode);
+        } else {
+            flushTimeForMode(SCREEN_OFF_CONFIG_ID);
+        }
+    }
+
+    // Calculates the time that passed in ms between the last time we recorded time and the time
+    // this method was called.
+    void flushTimeForMode(int mode) {
+        nsecs_t currentTime = systemTime();
+        int64_t timeElapsedMs = ns2ms(currentTime - mPreviousRecordedTime);
+        mPreviousRecordedTime = currentTime;
+
+        mConfigModesTotalTime[mode] += timeElapsedMs;
+    }
+
+    // Formats the time in milliseconds into easy to read format.
+    static std::string getDateFormatFromMs(int64_t timeMs) {
+        auto [days, dayRemainderMs] = std::div(timeMs, MS_PER_DAY);
+        auto [hours, hourRemainderMs] = std::div(dayRemainderMs, MS_PER_HOUR);
+        auto [mins, minsRemainderMs] = std::div(hourRemainderMs, MS_PER_MIN);
+        auto [sec, secRemainderMs] = std::div(minsRemainderMs, MS_PER_S);
+        return base::StringPrintf("%" PRId64 "d%02" PRId64 ":%02" PRId64 ":%02" PRId64
+                                  ".%03" PRId64,
+                                  days, hours, mins, sec, secRemainderMs);
+    }
+
+    // Keeps information about refresh rate configs that device has.
+    std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
+
+    int64_t mCurrentConfigMode = 0;
+    int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
+
+    std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
+
+    nsecs_t mPreviousRecordedTime;
+};
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
new file mode 100644
index 0000000..00820f1
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "Scheduler.h"
+
+#include <algorithm>
+#include <cinttypes>
+#include <cstdint>
+#include <memory>
+#include <numeric>
+
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+#include <cutils/properties.h>
+#include <gui/ISurfaceComposer.h>
+#include <ui/DisplayStatInfo.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include "DispSync.h"
+#include "DispSyncSource.h"
+#include "EventControlThread.h"
+#include "EventThread.h"
+#include "IdleTimer.h"
+#include "InjectVSyncSource.h"
+#include "SchedulerUtils.h"
+#include "SurfaceFlingerProperties.h"
+
+namespace android {
+
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+using namespace android::sysprop;
+
+#define RETURN_VALUE_IF_INVALID(value) \
+    if (handle == nullptr || mConnections.count(handle->id) == 0) return value
+#define RETURN_IF_INVALID() \
+    if (handle == nullptr || mConnections.count(handle->id) == 0) return
+
+std::atomic<int64_t> Scheduler::sNextId = 0;
+
+Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function)
+      : mHasSyncFramework(running_without_sync_framework(true)),
+        mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)),
+        mPrimaryHWVsyncEnabled(false),
+        mHWVsyncAvailable(false) {
+    // Note: We create a local temporary with the real DispSync implementation
+    // type temporarily so we can initialize it with the configured values,
+    // before storing it for more generic use using the interface type.
+    auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync");
+    primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset);
+    mPrimaryDispSync = std::move(primaryDispSync);
+    mEventControlThread = std::make_unique<impl::EventControlThread>(function);
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.sf.set_idle_timer_ms", value, "30");
+    mSetIdleTimerMs = atoi(value);
+
+    if (mSetIdleTimerMs > 0) {
+        mIdleTimer =
+                std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetIdleTimerMs),
+                                                       [this] { expiredTimerCallback(); });
+        mIdleTimer->start();
+    }
+}
+
+Scheduler::~Scheduler() = default;
+
+sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
+        const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
+        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
+    const int64_t id = sNextId++;
+    ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
+
+    std::unique_ptr<EventThread> eventThread =
+            makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
+                            std::move(interceptCallback));
+    auto connection = std::make_unique<Connection>(new ConnectionHandle(id),
+                                                   eventThread->createEventConnection(
+                                                           std::move(resyncCallback)),
+                                                   std::move(eventThread));
+
+    mConnections.insert(std::make_pair(id, std::move(connection)));
+    return mConnections[id]->handle;
+}
+
+std::unique_ptr<EventThread> Scheduler::makeEventThread(
+        const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
+    std::unique_ptr<VSyncSource> eventThreadSource =
+            std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, connectionName);
+    return std::make_unique<impl::EventThread>(std::move(eventThreadSource),
+                                               std::move(interceptCallback),
+                                               [this] { resetIdleTimer(); }, connectionName);
+}
+
+sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
+        const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback) {
+    RETURN_VALUE_IF_INVALID(nullptr);
+    return mConnections[handle->id]->thread->createEventConnection(std::move(resyncCallback));
+}
+
+EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
+    RETURN_VALUE_IF_INVALID(nullptr);
+    return mConnections[handle->id]->thread.get();
+}
+
+sp<EventThreadConnection> Scheduler::getEventConnection(const sp<ConnectionHandle>& handle) {
+    RETURN_VALUE_IF_INVALID(nullptr);
+    return mConnections[handle->id]->eventConnection;
+}
+
+void Scheduler::hotplugReceived(const sp<Scheduler::ConnectionHandle>& handle,
+                                EventThread::DisplayType displayType, bool connected) {
+    RETURN_IF_INVALID();
+    mConnections[handle->id]->thread->onHotplugReceived(displayType, connected);
+}
+
+void Scheduler::onScreenAcquired(const sp<Scheduler::ConnectionHandle>& handle) {
+    RETURN_IF_INVALID();
+    mConnections[handle->id]->thread->onScreenAcquired();
+}
+
+void Scheduler::onScreenReleased(const sp<Scheduler::ConnectionHandle>& handle) {
+    RETURN_IF_INVALID();
+    mConnections[handle->id]->thread->onScreenReleased();
+}
+
+void Scheduler::dump(const sp<Scheduler::ConnectionHandle>& handle, std::string& result) const {
+    RETURN_IF_INVALID();
+    mConnections.at(handle->id)->thread->dump(result);
+}
+
+void Scheduler::setPhaseOffset(const sp<Scheduler::ConnectionHandle>& handle, nsecs_t phaseOffset) {
+    RETURN_IF_INVALID();
+    mConnections[handle->id]->thread->setPhaseOffset(phaseOffset);
+}
+
+void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
+    stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0);
+    stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
+}
+
+void Scheduler::enableHardwareVsync() {
+    std::lock_guard<std::mutex> lock(mHWVsyncLock);
+    if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
+        mPrimaryDispSync->beginResync();
+        mEventControlThread->setVsyncEnabled(true);
+        mPrimaryHWVsyncEnabled = true;
+    }
+}
+
+void Scheduler::disableHardwareVsync(bool makeUnavailable) {
+    std::lock_guard<std::mutex> lock(mHWVsyncLock);
+    if (mPrimaryHWVsyncEnabled) {
+        mEventControlThread->setVsyncEnabled(false);
+        mPrimaryDispSync->endResync();
+        mPrimaryHWVsyncEnabled = false;
+    }
+    if (makeUnavailable) {
+        mHWVsyncAvailable = false;
+    }
+}
+
+void Scheduler::setVsyncPeriod(const nsecs_t period) {
+    mPrimaryDispSync->reset();
+    mPrimaryDispSync->setPeriod(period);
+    enableHardwareVsync();
+}
+
+void Scheduler::addResyncSample(const nsecs_t timestamp) {
+    bool needsHwVsync = false;
+    { // Scope for the lock
+        std::lock_guard<std::mutex> lock(mHWVsyncLock);
+        if (mPrimaryHWVsyncEnabled) {
+            needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp);
+        }
+    }
+
+    if (needsHwVsync) {
+        enableHardwareVsync();
+    } else {
+        disableHardwareVsync(false);
+    }
+}
+
+void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
+    if (mPrimaryDispSync->addPresentFence(fenceTime)) {
+        enableHardwareVsync();
+    } else {
+        disableHardwareVsync(false);
+    }
+}
+
+void Scheduler::setIgnorePresentFences(bool ignore) {
+    mPrimaryDispSync->setIgnorePresentFences(ignore);
+}
+
+void Scheduler::makeHWSyncAvailable(bool makeAvailable) {
+    std::lock_guard<std::mutex> lock(mHWVsyncLock);
+    mHWVsyncAvailable = makeAvailable;
+}
+
+void Scheduler::addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
+                                            const std::string layerName) {
+    // This is V1 logic. It calculates the average FPS based on the timestamp frequency
+    // regardless of which layer the timestamp came from.
+    // For now, the averages and FPS are recorded in the systrace.
+    determineTimestampAverage(isAutoTimestamp, framePresentTime);
+
+    // This is V2 logic. It calculates the average and median timestamp difference based on the
+    // individual layer history. The results are recorded in the systrace.
+    determineLayerTimestampStats(layerName, framePresentTime);
+}
+
+void Scheduler::incrementFrameCounter() {
+    mLayerHistory.incrementCounter();
+}
+
+void Scheduler::setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback) {
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    mExpiredTimerCallback = expiredTimerCallback;
+}
+
+void Scheduler::setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback) {
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    mResetTimerCallback = resetTimerCallback;
+}
+
+void Scheduler::updateFrameSkipping(const int64_t skipCount) {
+    ATRACE_INT("FrameSkipCount", skipCount);
+    if (mSkipCount != skipCount) {
+        // Only update DispSync if it hasn't been updated yet.
+        mPrimaryDispSync->setRefreshSkipCount(skipCount);
+        mSkipCount = skipCount;
+    }
+}
+
+void Scheduler::determineLayerTimestampStats(const std::string layerName,
+                                             const nsecs_t framePresentTime) {
+    mLayerHistory.insert(layerName, framePresentTime);
+    std::vector<int64_t> differencesMs;
+
+    // Traverse through the layer history, and determine the differences in present times.
+    nsecs_t newestPresentTime = framePresentTime;
+    std::string differencesText = "";
+    for (int i = 1; i < mLayerHistory.getSize(); i++) {
+        std::unordered_map<std::string, nsecs_t> layers = mLayerHistory.get(i);
+        for (auto layer : layers) {
+            if (layer.first != layerName) {
+                continue;
+            }
+            int64_t differenceMs = (newestPresentTime - layer.second) / 1000000;
+            // Dismiss noise.
+            if (differenceMs > 10 && differenceMs < 60) {
+                differencesMs.push_back(differenceMs);
+            }
+            IF_ALOGV() { differencesText += (std::to_string(differenceMs) + " "); }
+            newestPresentTime = layer.second;
+        }
+    }
+    ALOGV("Layer %s timestamp intervals: %s", layerName.c_str(), differencesText.c_str());
+
+    if (!differencesMs.empty()) {
+        // Mean/Average is a good indicator for when 24fps videos are playing, because the frames
+        // come in 33, and 49 ms intervals with occasional 41ms.
+        const int64_t meanMs = scheduler::calculate_mean(differencesMs);
+        const auto tagMean = "TimestampMean_" + layerName;
+        ATRACE_INT(tagMean.c_str(), meanMs);
+
+        // Mode and median are good indicators for 30 and 60 fps videos, because the majority of
+        // frames come in 16, or 33 ms intervals.
+        const auto tagMedian = "TimestampMedian_" + layerName;
+        ATRACE_INT(tagMedian.c_str(), scheduler::calculate_median(&differencesMs));
+
+        const auto tagMode = "TimestampMode_" + layerName;
+        ATRACE_INT(tagMode.c_str(), scheduler::calculate_mode(differencesMs));
+    }
+}
+
+void Scheduler::determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime) {
+    ATRACE_INT("AutoTimestamp", isAutoTimestamp);
+
+    // Video does not have timestamp automatically set, so we discard timestamps that are
+    // coming in from other sources for now.
+    if (isAutoTimestamp) {
+        return;
+    }
+    int64_t differenceMs = (framePresentTime - mPreviousFrameTimestamp) / 1000000;
+    mPreviousFrameTimestamp = framePresentTime;
+
+    if (differenceMs < 10 || differenceMs > 100) {
+        // Dismiss noise.
+        return;
+    }
+    ATRACE_INT("TimestampDiff", differenceMs);
+
+    mTimeDifferences[mCounter % scheduler::ARRAY_SIZE] = differenceMs;
+    mCounter++;
+    int64_t mean = scheduler::calculate_mean(mTimeDifferences);
+    ATRACE_INT("AutoTimestampMean", mean);
+
+    // TODO(b/113612090): This are current numbers from trial and error while running videos
+    // from YouTube at 24, 30, and 60 fps.
+    if (mean > 14 && mean < 18) {
+        ATRACE_INT("MediaFPS", 60);
+    } else if (mean > 31 && mean < 34) {
+        ATRACE_INT("MediaFPS", 30);
+        return;
+    } else if (mean > 39 && mean < 42) {
+        ATRACE_INT("MediaFPS", 24);
+    }
+}
+
+void Scheduler::resetIdleTimer() {
+    if (mIdleTimer) {
+        mIdleTimer->reset();
+        ATRACE_INT("ExpiredIdleTimer", 0);
+    }
+
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    if (mResetTimerCallback) {
+        mResetTimerCallback();
+    }
+}
+
+void Scheduler::expiredTimerCallback() {
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    if (mExpiredTimerCallback) {
+        mExpiredTimerCallback();
+        ATRACE_INT("ExpiredIdleTimer", 1);
+    }
+}
+
+std::string Scheduler::doDump() {
+    std::ostringstream stream;
+    stream << "+  Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl;
+    return stream.str();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
new file mode 100644
index 0000000..b717605
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <memory>
+
+#include <gui/ISurfaceComposer.h>
+#include <ui/DisplayStatInfo.h>
+
+#include "DispSync.h"
+#include "EventControlThread.h"
+#include "EventThread.h"
+#include "IdleTimer.h"
+#include "InjectVSyncSource.h"
+#include "LayerHistory.h"
+#include "SchedulerUtils.h"
+
+namespace android {
+
+class EventControlThread;
+
+class Scheduler {
+public:
+    using ExpiredIdleTimerCallback = std::function<void()>;
+    using ResetIdleTimerCallback = std::function<void()>;
+
+    // Enum to indicate whether to start the transaction early, or at vsync time.
+    enum class TransactionStart { EARLY, NORMAL };
+
+    /* The scheduler handle is a BBinder object passed to the client from which we can extract
+     * an ID for subsequent operations.
+     */
+    class ConnectionHandle : public BBinder {
+    public:
+        ConnectionHandle(int64_t id) : id(id) {}
+
+        ~ConnectionHandle() = default;
+
+        const int64_t id;
+    };
+
+    class Connection {
+    public:
+        Connection(sp<ConnectionHandle> handle, sp<EventThreadConnection> eventConnection,
+                   std::unique_ptr<EventThread> eventThread)
+              : handle(handle), eventConnection(eventConnection), thread(std::move(eventThread)) {}
+
+        ~Connection() = default;
+
+        sp<ConnectionHandle> handle;
+        sp<EventThreadConnection> eventConnection;
+        const std::unique_ptr<EventThread> thread;
+    };
+
+    explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function);
+
+    virtual ~Scheduler();
+
+    /** Creates an EventThread connection. */
+    sp<ConnectionHandle> createConnection(
+            const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
+            impl::EventThread::InterceptVSyncsCallback interceptCallback);
+
+    sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle,
+                                                             ResyncCallback resyncCallback);
+
+    // Getter methods.
+    EventThread* getEventThread(const sp<ConnectionHandle>& handle);
+
+    sp<EventThreadConnection> getEventConnection(const sp<ConnectionHandle>& handle);
+
+    // Should be called when receiving a hotplug event.
+    void hotplugReceived(const sp<ConnectionHandle>& handle, EventThread::DisplayType displayType,
+                         bool connected);
+
+    // Should be called after the screen is turned on.
+    void onScreenAcquired(const sp<ConnectionHandle>& handle);
+
+    // Should be called before the screen is turned off.
+    void onScreenReleased(const sp<ConnectionHandle>& handle);
+
+    // Should be called when dumpsys command is received.
+    void dump(const sp<ConnectionHandle>& handle, std::string& result) const;
+
+    // Offers ability to modify phase offset in the event thread.
+    void setPhaseOffset(const sp<ConnectionHandle>& handle, nsecs_t phaseOffset);
+
+    void getDisplayStatInfo(DisplayStatInfo* stats);
+
+    void enableHardwareVsync();
+    void disableHardwareVsync(bool makeUnavailable);
+    void setVsyncPeriod(const nsecs_t period);
+    void addResyncSample(const nsecs_t timestamp);
+    void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
+    void setIgnorePresentFences(bool ignore);
+    void makeHWSyncAvailable(bool makeAvailable);
+    // Adds the present time for given layer to the history of present times.
+    void addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
+                                     const std::string layerName);
+    // Increments counter in the layer history to indicate that SF has started a new frame.
+    void incrementFrameCounter();
+    // Callback that gets invoked once the idle timer expires.
+    void setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback);
+    // Callback that gets invoked once the idle timer is reset.
+    void setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback);
+    // Returns relevant information about Scheduler for dumpsys purposes.
+    std::string doDump();
+
+protected:
+    virtual std::unique_ptr<EventThread> makeEventThread(
+            const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+            impl::EventThread::InterceptVSyncsCallback interceptCallback);
+
+private:
+    nsecs_t calculateAverage() const;
+    void updateFrameSkipping(const int64_t skipCount);
+    // Collects the statistical mean (average) and median between timestamp
+    // intervals for each frame for each layer.
+    void determineLayerTimestampStats(const std::string layerName, const nsecs_t framePresentTime);
+    // Collects the average difference between timestamps for each frame regardless
+    // of which layer the timestamp came from.
+    void determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime);
+    // Function that resets the idle timer.
+    void resetIdleTimer();
+    // Function that is called when the timer expires.
+    void expiredTimerCallback();
+
+    // TODO(b/113612090): Instead of letting BufferQueueLayer to access mDispSync directly, it
+    // should make request to Scheduler to compute next refresh.
+    friend class BufferQueueLayer;
+
+    // If fences from sync Framework are supported.
+    const bool mHasSyncFramework;
+
+    // The offset in nanoseconds to use, when DispSync timestamps present fence
+    // signaling time.
+    nsecs_t mDispSyncPresentTimeOffset;
+
+    // Each connection has it's own ID. This variable keeps track of the count.
+    static std::atomic<int64_t> sNextId;
+
+    // Connections are stored in a map <connection ID, connection> for easy retrieval.
+    std::unordered_map<int64_t, std::unique_ptr<Connection>> mConnections;
+
+    std::mutex mHWVsyncLock;
+    bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock);
+    bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock);
+
+    std::unique_ptr<DispSync> mPrimaryDispSync;
+    std::unique_ptr<EventControlThread> mEventControlThread;
+
+    // TODO(b/113612090): The following set of variables needs to be revised. For now, this is
+    // a proof of concept. We turn on frame skipping if the difference between the timestamps
+    // is between 32 and 34ms. We expect this currently for 30fps videos, so we render them at 30Hz.
+    nsecs_t mPreviousFrameTimestamp = 0;
+    // Keeping track of whether we are skipping the refresh count. If we want to
+    // simulate 30Hz rendering, we skip every other frame, and this variable is set
+    // to 1.
+    int64_t mSkipCount = 0;
+    std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{};
+    size_t mCounter = 0;
+
+    LayerHistory mLayerHistory;
+
+    // Timer that records time between requests for next vsync. If the time is higher than a given
+    // interval, a callback is fired. Set this variable to >0 to use this feature.
+    int64_t mSetIdleTimerMs = 0;
+    std::unique_ptr<scheduler::IdleTimer> mIdleTimer;
+
+    std::mutex mCallbackLock;
+    ExpiredIdleTimerCallback mExpiredTimerCallback GUARDED_BY(mCallbackLock);
+    ResetIdleTimerCallback mResetTimerCallback GUARDED_BY(mCallbackLock);
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
new file mode 100644
index 0000000..191022d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SchedulerUtils.h"
+
+#include <cinttypes>
+#include <numeric>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace scheduler {
+
+int64_t calculate_median(std::vector<int64_t>* v) {
+    if (!v || v->empty()) {
+        return 0;
+    }
+
+    size_t n = v->size() / 2;
+    nth_element(v->begin(), v->begin() + n, v->end());
+    return v->at(n);
+}
+
+int64_t calculate_mode(const std::vector<int64_t>& v) {
+    if (v.empty()) {
+        return 0;
+    }
+
+    // Create a map with all the counts for the indivicual values in the vector.
+    std::unordered_map<int64_t, int64_t> counts;
+    for (int64_t value : v) {
+        counts[value]++;
+    }
+
+    // Sort the map, and return the number with the highest count. If two numbers have
+    // the same count, first one is returned.
+    using ValueType = const decltype(counts)::value_type&;
+    const auto compareCounts = [](ValueType l, ValueType r) { return l.second <= r.second; };
+    return std::max_element(counts.begin(), counts.end(), compareCounts)->first;
+}
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
new file mode 100644
index 0000000..edd23de
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cinttypes>
+#include <numeric>
+#include <vector>
+
+namespace android {
+namespace scheduler {
+// This number is used to set the size of the arrays in scheduler that hold information
+// about layers.
+static constexpr size_t ARRAY_SIZE = 30;
+
+// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently
+// the config is not visible to SF, and is completely maintained by HWC. However, we would
+// still like to keep track of time when the device is in this config.
+static constexpr int SCREEN_OFF_CONFIG_ID = -1;
+
+// Calculates the statistical mean (average) in the data structure (array, vector). The
+// function does not modify the contents of the array.
+template <typename T>
+auto calculate_mean(const T& v) {
+    using V = typename T::value_type;
+    V sum = std::accumulate(v.begin(), v.end(), 0);
+    return sum / static_cast<V>(v.size());
+}
+
+// Calculates the statistical median in the vector. Return 0 if the vector is empty. The
+// function modifies the vector contents.
+int64_t calculate_median(std::vector<int64_t>* v);
+
+// Calculates the statistical mode in the vector. Return 0 if the vector is empty.
+int64_t calculate_mode(const std::vector<int64_t>& v);
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
similarity index 68%
rename from services/surfaceflinger/VSyncModulator.h
rename to services/surfaceflinger/Scheduler/VSyncModulator.h
index e071a59..d7ec733 100644
--- a/services/surfaceflinger/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -18,9 +18,10 @@
 
 #include <utils/Errors.h>
 
+#include <cinttypes>
 #include <mutex>
 
-using namespace android::surfaceflinger;
+#include "Scheduler.h"
 
 namespace android {
 
@@ -29,23 +30,16 @@
  */
 class VSyncModulator {
 private:
-
     // Number of frames we'll keep the early phase offsets once they are activated. This acts as a
     // low-pass filter in case the client isn't quick enough in sending new transactions.
     const int MIN_EARLY_FRAME_COUNT = 2;
 
 public:
-
     struct Offsets {
         nsecs_t sf;
         nsecs_t app;
     };
 
-    enum TransactionStart {
-        EARLY,
-        NORMAL
-    };
-
     // Sets the phase offsets
     //
     // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction
@@ -63,27 +57,31 @@
         mOffsets = late;
     }
 
-    Offsets getEarlyOffsets() const {
-        return mEarlyOffsets;
-    }
+    Offsets getEarlyOffsets() const { return mEarlyOffsets; }
 
-    Offsets getEarlyGlOffsets() const {
-        return mEarlyGlOffsets;
-    }
+    Offsets getEarlyGlOffsets() const { return mEarlyGlOffsets; }
 
     void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) {
         mSfEventThread = sfEventThread;
         mAppEventThread = appEventThread;
     }
 
-    void setTransactionStart(TransactionStart transactionStart) {
+    void setSchedulerAndHandles(Scheduler* scheduler,
+                                Scheduler::ConnectionHandle* appConnectionHandle,
+                                Scheduler::ConnectionHandle* sfConnectionHandle) {
+        mScheduler = scheduler;
+        mAppConnectionHandle = appConnectionHandle;
+        mSfConnectionHandle = sfConnectionHandle;
+    }
 
-        if (transactionStart == TransactionStart::EARLY) {
+    void setTransactionStart(Scheduler::TransactionStart transactionStart) {
+        if (transactionStart == Scheduler::TransactionStart::EARLY) {
             mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT;
         }
 
         // An early transaction stays an early transaction.
-        if (transactionStart == mTransactionStart || mTransactionStart == TransactionStart::EARLY) {
+        if (transactionStart == mTransactionStart ||
+            mTransactionStart == Scheduler::TransactionStart::EARLY) {
             return;
         }
         mTransactionStart = transactionStart;
@@ -91,8 +89,8 @@
     }
 
     void onTransactionHandled() {
-        if (mTransactionStart == TransactionStart::NORMAL) return;
-        mTransactionStart = TransactionStart::NORMAL;
+        if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
+        mTransactionStart = Scheduler::TransactionStart::NORMAL;
         updateOffsets();
     }
 
@@ -112,18 +110,25 @@
     }
 
 private:
-
     void updateOffsets() {
         const Offsets desired = getOffsets();
         const Offsets current = mOffsets;
 
         bool changed = false;
         if (desired.sf != current.sf) {
-            mSfEventThread->setPhaseOffset(desired.sf);
+            if (mSfConnectionHandle != nullptr) {
+                mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf);
+            } else {
+                mSfEventThread->setPhaseOffset(desired.sf);
+            }
             changed = true;
         }
         if (desired.app != current.app) {
-            mAppEventThread->setPhaseOffset(desired.app);
+            if (mSfConnectionHandle != nullptr) {
+                mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
+            } else {
+                mAppEventThread->setPhaseOffset(desired.app);
+            }
             changed = true;
         }
 
@@ -133,7 +138,8 @@
     }
 
     Offsets getOffsets() {
-        if (mTransactionStart == TransactionStart::EARLY || mRemainingEarlyFrameCount > 0) {
+        if (mTransactionStart == Scheduler::TransactionStart::EARLY ||
+            mRemainingEarlyFrameCount > 0) {
             return mEarlyOffsets;
         } else if (mLastFrameUsedRenderEngine) {
             return mEarlyGlOffsets;
@@ -149,9 +155,14 @@
     EventThread* mSfEventThread = nullptr;
     EventThread* mAppEventThread = nullptr;
 
+    Scheduler* mScheduler = nullptr;
+    Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr;
+    Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr;
+
     std::atomic<Offsets> mOffsets;
 
-    std::atomic<TransactionStart> mTransactionStart = TransactionStart::NORMAL;
+    std::atomic<Scheduler::TransactionStart> mTransactionStart =
+            Scheduler::TransactionStart::NORMAL;
     std::atomic<bool> mLastFrameUsedRenderEngine = false;
     std::atomic<int> mRemainingEarlyFrameCount = 0;
 };
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 82bf19f..2cf2cd8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -14,19 +14,21 @@
  * limitations under the License.
  */
 
-// #define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <stdint.h>
 #include <sys/types.h>
-#include <algorithm>
 #include <errno.h>
-#include <math.h>
-#include <mutex>
 #include <dlfcn.h>
-#include <inttypes.h>
-#include <stdatomic.h>
+
+#include <algorithm>
+#include <cinttypes>
+#include <cmath>
+#include <cstdint>
+#include <functional>
+#include <mutex>
 #include <optional>
+#include <unordered_map>
 
 #include <cutils/properties.h>
 #include <log/log.h>
@@ -35,84 +37,139 @@
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
 
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <dvr/vr_flinger.h>
-
+#include <gui/BufferQueue.h>
+#include <gui/GuiConfig.h>
+#include <gui/IDisplayEventConnection.h>
+#include <gui/IProducerListener.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/Surface.h>
+#include <input/IInputFlinger.h>
+#include <renderengine/RenderEngine.h>
 #include <ui/ColorSpace.h>
 #include <ui/DebugUtils.h>
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
-
-#include <gui/BufferQueue.h>
-#include <gui/GuiConfig.h>
-#include <gui/IDisplayEventConnection.h>
-#include <gui/LayerDebugInfo.h>
-#include <gui/Surface.h>
-
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/PixelFormat.h>
 #include <ui/UiConfig.h>
-
-#include <utils/misc.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
+#include <utils/CallStack.h>
 #include <utils/StopWatch.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
+#include <utils/misc.h>
 
 #include <private/android_filesystem_config.h>
 #include <private/gui/SyncFeatures.h>
 
 #include "BufferLayer.h"
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
 #include "Client.h"
 #include "ColorLayer.h"
 #include "Colorizer.h"
 #include "ContainerLayer.h"
 #include "DdmConnection.h"
-#include "DispSync.h"
 #include "DisplayDevice.h"
-#include "EventControlThread.h"
-#include "EventThread.h"
 #include "Layer.h"
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
+#include "NativeWindowSurface.h"
+#include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
-#include "clz.h"
+#include "SurfaceInterceptor.h"
 
 #include "DisplayHardware/ComposerHal.h"
+#include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/FramebufferSurface.h"
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/VirtualDisplaySurface.h"
-
 #include "Effects/Daltonizer.h"
+#include "Scheduler/DispSync.h"
+#include "Scheduler/DispSyncSource.h"
+#include "Scheduler/EventControlThread.h"
+#include "Scheduler/EventThread.h"
+#include "Scheduler/InjectVSyncSource.h"
+#include "Scheduler/MessageQueue.h"
+#include "Scheduler/Scheduler.h"
+#include "TimeStats/TimeStats.h"
 
-#include "RenderEngine/RenderEngine.h"
 #include <cutils/compiler.h>
 
+#include "android-base/stringprintf.h"
+
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
 #include <configstore/Utils.h>
 
 #include <layerproto/LayerProtoParser.h>
-
-#define DISPLAY_COUNT       1
-
-/*
- * DEBUG_SCREENSHOTS: set to true to check that screenshots are not all
- * black pixels.
- */
-#define DEBUG_SCREENSHOTS   false
+#include "SurfaceFlingerProperties.h"
 
 namespace android {
 
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
+using namespace android::sysprop;
+using base::StringAppendF;
 using ui::ColorMode;
 using ui::Dataspace;
+using ui::DisplayPrimaries;
 using ui::Hdr;
 using ui::RenderIntent;
 
 namespace {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wswitch-enum"
+
+bool isWideColorMode(const ColorMode colorMode) {
+    switch (colorMode) {
+        case ColorMode::DISPLAY_P3:
+        case ColorMode::ADOBE_RGB:
+        case ColorMode::DCI_P3:
+        case ColorMode::BT2020:
+        case ColorMode::DISPLAY_BT2020:
+        case ColorMode::BT2100_PQ:
+        case ColorMode::BT2100_HLG:
+            return true;
+        case ColorMode::NATIVE:
+        case ColorMode::STANDARD_BT601_625:
+        case ColorMode::STANDARD_BT601_625_UNADJUSTED:
+        case ColorMode::STANDARD_BT601_525:
+        case ColorMode::STANDARD_BT601_525_UNADJUSTED:
+        case ColorMode::STANDARD_BT709:
+        case ColorMode::SRGB:
+            return false;
+    }
+    return false;
+}
+
+ui::Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) {
+    switch (rotation) {
+        case ISurfaceComposer::eRotateNone:
+            return ui::Transform::ROT_0;
+        case ISurfaceComposer::eRotate90:
+            return ui::Transform::ROT_90;
+        case ISurfaceComposer::eRotate180:
+            return ui::Transform::ROT_180;
+        case ISurfaceComposer::eRotate270:
+            return ui::Transform::ROT_270;
+    }
+    ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation);
+    return ui::Transform::ROT_0;
+}
+
+#pragma clang diagnostic pop
+
 class ConditionalLock {
 public:
     ConditionalLock(Mutex& mutex, bool lock) : mMutex(mutex), mLocked(lock) {
@@ -125,6 +182,12 @@
     Mutex& mMutex;
     bool mLocked;
 };
+
+// Currently we only support V0_SRGB and DISPLAY_P3 as composition preference.
+bool validateCompositionDataspace(Dataspace dataspace) {
+    return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
+}
+
 }  // namespace anonymous
 
 // ---------------------------------------------------------------------------
@@ -134,18 +197,34 @@
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sDump("android.permission.DUMP");
 
+constexpr float kSrgbRedX = 0.4123f;
+constexpr float kSrgbRedY = 0.2126f;
+constexpr float kSrgbRedZ = 0.0193f;
+constexpr float kSrgbGreenX = 0.3576f;
+constexpr float kSrgbGreenY = 0.7152f;
+constexpr float kSrgbGreenZ = 0.1192f;
+constexpr float kSrgbBlueX = 0.1805f;
+constexpr float kSrgbBlueY = 0.0722f;
+constexpr float kSrgbBlueZ = 0.9506f;
+constexpr float kSrgbWhiteX = 0.9505f;
+constexpr float kSrgbWhiteY = 1.0000f;
+constexpr float kSrgbWhiteZ = 1.0891f;
+
 // ---------------------------------------------------------------------------
-int64_t SurfaceFlinger::vsyncPhaseOffsetNs;
-int64_t SurfaceFlinger::sfVsyncPhaseOffsetNs;
 int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
 bool SurfaceFlinger::useHwcForRgbToYuv;
 uint64_t SurfaceFlinger::maxVirtualDisplaySize;
 bool SurfaceFlinger::hasSyncFramework;
 bool SurfaceFlinger::useVrFlinger;
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
-// TODO(courtneygo): Rename hasWideColorDisplay to clarify its actual meaning.
 bool SurfaceFlinger::hasWideColorDisplay;
-
+int SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault;
+bool SurfaceFlinger::useColorManagement;
+bool SurfaceFlinger::useContextPriority;
+Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB;
+ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
+Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
+ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
 
 std::string getHwcServiceName() {
     char value[PROPERTY_VALUE_MAX] = {};
@@ -175,51 +254,24 @@
     }
 }
 
-NativeWindowSurface::~NativeWindowSurface() = default;
-
-namespace impl {
-
-class NativeWindowSurface final : public android::NativeWindowSurface {
-public:
-    static std::unique_ptr<android::NativeWindowSurface> create(
-            const sp<IGraphicBufferProducer>& producer) {
-        return std::make_unique<NativeWindowSurface>(producer);
-    }
-
-    explicit NativeWindowSurface(const sp<IGraphicBufferProducer>& producer)
-          : surface(new Surface(producer, false)) {}
-
-    ~NativeWindowSurface() override = default;
-
-private:
-    sp<ANativeWindow> getNativeWindow() const override { return surface; }
-
-    void preallocateBuffers() override { surface->allocateBuffers(); }
-
-    sp<Surface> surface;
-};
-
-} // namespace impl
-
 SurfaceFlingerBE::SurfaceFlingerBE()
       : mHwcServiceName(getHwcServiceName()),
-        mRenderEngine(nullptr),
         mFrameBuckets(),
         mTotalTime(0),
         mLastSwapTime(0),
         mComposerSequenceId(0) {
 }
 
-SurfaceFlinger::SurfaceFlinger(SurfaceFlinger::SkipInitializationTag)
+SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory,
+                               SurfaceFlinger::SkipInitializationTag)
       : BnSurfaceComposer(),
-        mTransactionFlags(0),
+        mFactory(factory),
         mTransactionPending(false),
         mAnimTransactionPending(false),
         mLayersRemoved(false),
         mLayersAdded(false),
-        mRepaintEverything(0),
         mBootTime(systemTime()),
-        mBuiltinDisplays(),
+        mPhaseOffsets{getFactory().createPhaseOffsets()},
         mVisibleRegionsDirty(false),
         mGeometryInvalid(false),
         mAnimCompositionPending(false),
@@ -228,73 +280,84 @@
         mDebugDDMS(0),
         mDebugDisableHWC(0),
         mDebugDisableTransformHint(0),
-        mDebugInSwapBuffers(0),
-        mLastSwapBufferTime(0),
         mDebugInTransaction(0),
         mLastTransactionTime(0),
         mForceFullDamage(false),
-        mPrimaryDispSync("PrimaryDispSync"),
+        mTimeStats(factory.createTimeStats()),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
+        mRefreshStartTime(0),
         mHasPoweredOff(false),
         mNumLayers(0),
         mVrFlingerRequestsDisplay(false),
         mMainThreadId(std::this_thread::get_id()),
-        mCreateBufferQueue(&BufferQueue::createBufferQueue),
-        mCreateNativeWindowSurface(&impl::NativeWindowSurface::create) {}
+        mCompositionEngine{getFactory().createCompositionEngine()} {}
 
-SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) {
+SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory)
+      : SurfaceFlinger(factory, SkipInitialization) {
     ALOGI("SurfaceFlinger is starting");
 
-    vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
+    hasSyncFramework = running_without_sync_framework(true);
 
-    sfVsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(1000000);
+    dispSyncPresentTimeOffset = present_time_offset_from_vsync_ns(0);
 
-    hasSyncFramework = getBool< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::hasSyncFramework>(true);
+    useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false);
 
-    dispSyncPresentTimeOffset = getInt64< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(0);
-
-    useHwcForRgbToYuv = getBool< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::useHwcForRGBtoYUV>(false);
-
-    maxVirtualDisplaySize = getUInt64<ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::maxVirtualDisplaySize>(0);
+    maxVirtualDisplaySize = max_virtual_display_dimension(0);
 
     // Vr flinger is only enabled on Daydream ready devices.
-    useVrFlinger = getBool< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::useVrFlinger>(false);
+    useVrFlinger = use_vr_flinger(false);
 
-    maxFrameBufferAcquiredBuffers = getInt64< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2);
+    maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
 
-    hasWideColorDisplay =
-            getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+    hasWideColorDisplay = has_wide_color_display(false);
 
-    V1_1::DisplayOrientation primaryDisplayOrientation =
-        getDisplayOrientation< V1_1::ISurfaceFlingerConfigs, &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>(
-            V1_1::DisplayOrientation::ORIENTATION_0);
+    useColorManagement = use_color_management(false);
 
-    switch (primaryDisplayOrientation) {
-        case V1_1::DisplayOrientation::ORIENTATION_90:
-            mPrimaryDisplayOrientation = DisplayState::eOrientation90;
+    mDefaultCompositionDataspace =
+            static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
+    mWideColorGamutCompositionDataspace =
+            static_cast<ui::Dataspace>(wcg_composition_dataspace(Dataspace::V0_SRGB));
+    defaultCompositionDataspace = mDefaultCompositionDataspace;
+    wideColorGamutCompositionDataspace = mWideColorGamutCompositionDataspace;
+    defaultCompositionPixelFormat = static_cast<ui::PixelFormat>(
+            default_composition_pixel_format(ui::PixelFormat::RGBA_8888));
+    wideColorGamutCompositionPixelFormat =
+            static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
+
+    useContextPriority = use_context_priority(true);
+
+    auto tmpPrimaryDisplayOrientation = primary_display_orientation(
+            SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0);
+    switch (tmpPrimaryDisplayOrientation) {
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90:
+            SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation90;
             break;
-        case V1_1::DisplayOrientation::ORIENTATION_180:
-            mPrimaryDisplayOrientation = DisplayState::eOrientation180;
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180:
+            SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation180;
             break;
-        case V1_1::DisplayOrientation::ORIENTATION_270:
-            mPrimaryDisplayOrientation = DisplayState::eOrientation270;
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270:
+            SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation270;
             break;
         default:
-            mPrimaryDisplayOrientation = DisplayState::eOrientationDefault;
+            SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault;
             break;
     }
-    ALOGV("Primary Display Orientation is set to %2d.", mPrimaryDisplayOrientation);
+    ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation);
 
-    mPrimaryDispSync.init(SurfaceFlinger::hasSyncFramework, SurfaceFlinger::dispSyncPresentTimeOffset);
+    mPrimaryDispSync =
+            getFactory().createDispSync("PrimaryDispSync", SurfaceFlinger::hasSyncFramework,
+                                        SurfaceFlinger::dispSyncPresentTimeOffset);
+
+    auto surfaceFlingerConfigsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+    if (surfaceFlingerConfigsServiceV1_2) {
+        surfaceFlingerConfigsServiceV1_2->getDisplayNativePrimaries(
+                [&](auto tmpPrimaries) {
+                    memcpy(&mInternalDisplayPrimaries, &tmpPrimaries, sizeof(ui::DisplayPrimaries));
+                });
+    } else {
+        initDefaultDisplayNativePrimaries();
+    }
 
     // debugging stuff...
     char value[PROPERTY_VALUE_MAX];
@@ -322,9 +385,9 @@
 
     property_get("debug.sf.enable_hwc_vds", value, "0");
     mUseHwcVirtualDisplays = atoi(value);
-    ALOGI_IF(!mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
+    ALOGI_IF(mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
 
-    property_get("ro.sf.disable_triple_buffer", value, "1");
+    property_get("ro.sf.disable_triple_buffer", value, "0");
     mLayerTripleBufferingDisabled = atoi(value);
     ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
 
@@ -332,26 +395,11 @@
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
 
-    property_get("debug.sf.early_phase_offset_ns", value, "-1");
-    const int earlySfOffsetNs = atoi(value);
+    property_get("debug.sf.use_scheduler", value, "0");
+    mUseScheduler = atoi(value);
 
-    property_get("debug.sf.early_gl_phase_offset_ns", value, "-1");
-    const int earlyGlSfOffsetNs = atoi(value);
-
-    property_get("debug.sf.early_app_phase_offset_ns", value, "-1");
-    const int earlyAppOffsetNs = atoi(value);
-
-    property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
-    const int earlyGlAppOffsetNs = atoi(value);
-
-    const VSyncModulator::Offsets earlyOffsets =
-            {earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs,
-            earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs};
-    const VSyncModulator::Offsets earlyGlOffsets =
-            {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs,
-            earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs};
-    mVsyncModulator.setPhaseOffsets(earlyOffsets, earlyGlOffsets,
-            {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs});
+    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+    mVsyncModulator.setPhaseOffsets(early, gl, late);
 
     // We should be reading 'persist.sys.sf.color_saturation' here
     // but since /data may be encrypted, we need to wait until after vold
@@ -400,19 +448,6 @@
     return initClient(new Client(this));
 }
 
-sp<ISurfaceComposerClient> SurfaceFlinger::createScopedConnection(
-        const sp<IGraphicBufferProducer>& gbp) {
-    if (authenticateSurfaceTexture(gbp) == false) {
-        return nullptr;
-    }
-    const auto& layer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
-    if (layer == nullptr) {
-        return nullptr;
-    }
-
-   return initClient(new Client(this, layer));
-}
-
 sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName,
         bool secure)
 {
@@ -433,38 +468,69 @@
     sp<BBinder> token = new DisplayToken(this);
 
     Mutex::Autolock _l(mStateLock);
-    DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
-    info.displayName = displayName;
-    mCurrentState.displays.add(token, info);
-    mInterceptor->saveDisplayCreation(info);
+    // Display ID is assigned when virtual display is allocated by HWC.
+    DisplayDeviceState state;
+    state.isSecure = secure;
+    state.displayName = displayName;
+    mCurrentState.displays.add(token, state);
+    mInterceptor->saveDisplayCreation(state);
     return token;
 }
 
-void SurfaceFlinger::destroyDisplay(const sp<IBinder>& display) {
+void SurfaceFlinger::destroyDisplay(const sp<IBinder>& displayToken) {
     Mutex::Autolock _l(mStateLock);
 
-    ssize_t idx = mCurrentState.displays.indexOfKey(display);
-    if (idx < 0) {
-        ALOGW("destroyDisplay: invalid display token");
+    ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
+    if (index < 0) {
+        ALOGE("destroyDisplay: Invalid display token %p", displayToken.get());
         return;
     }
 
-    const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx));
-    if (!info.isVirtualDisplay()) {
+    const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
+    if (!state.isVirtual()) {
         ALOGE("destroyDisplay called for non-virtual display");
         return;
     }
-    mInterceptor->saveDisplayDeletion(info.displayId);
-    mCurrentState.displays.removeItemsAt(idx);
+    mInterceptor->saveDisplayDeletion(state.sequenceId);
+    mCurrentState.displays.removeItemsAt(index);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
 sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
-    if (uint32_t(id) >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
-        ALOGE("getDefaultDisplay: id=%d is not a valid default display id", id);
+    std::optional<DisplayId> displayId;
+
+    if (id == HWC_DISPLAY_PRIMARY) {
+        displayId = getInternalDisplayId();
+    } else if (id == HWC_DISPLAY_EXTERNAL) {
+        displayId = getExternalDisplayId();
+    }
+
+    if (!displayId) {
+        ALOGE("%s: Invalid display %d", __FUNCTION__, id);
         return nullptr;
     }
-    return mBuiltinDisplays[id];
+
+    return getPhysicalDisplayToken(*displayId);
+}
+
+status_t SurfaceFlinger::getColorManagement(bool* outGetColorManagement) const {
+    if (!outGetColorManagement) {
+        return BAD_VALUE;
+    }
+    *outGetColorManagement = useColorManagement;
+    return NO_ERROR;
+}
+
+HWComposer& SurfaceFlinger::getHwComposer() const {
+    return mCompositionEngine->getHwComposer();
+}
+
+renderengine::RenderEngine& SurfaceFlinger::getRenderEngine() const {
+    return mCompositionEngine->getRenderEngine();
+}
+
+compositionengine::CompositionEngine& SurfaceFlinger::getCompositionEngine() const {
+    return *mCompositionEngine.get();
 }
 
 void SurfaceFlinger::bootFinished()
@@ -482,6 +548,13 @@
     if (window != 0) {
         window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
     }
+    sp<IBinder> input(defaultServiceManager()->getService(
+            String16("inputflinger")));
+    if (input == nullptr) {
+        ALOGE("Failed to link to input service");
+    } else {
+        mInputFlinger = interface_cast<IInputFlinger>(input);
+    }
 
     if (mVrFlinger) {
       mVrFlinger->OnBootFinished();
@@ -496,11 +569,10 @@
     LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
                    ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
 
-    sp<LambdaMessage> readProperties = new LambdaMessage([&]() {
+    postMessageAsync(new LambdaMessage([this] {
         readPersistentProperties();
         mBootStage = BootStage::FINISHED;
-    });
-    postMessageAsync(readProperties);
+    }));
 }
 
 uint32_t SurfaceFlinger::getNewTexture() {
@@ -525,230 +597,115 @@
 }
 
 void SurfaceFlinger::deleteTextureAsync(uint32_t texture) {
-    class MessageDestroyGLTexture : public MessageBase {
-        RE::RenderEngine& engine;
-        uint32_t texture;
-    public:
-        MessageDestroyGLTexture(RE::RenderEngine& engine, uint32_t texture)
-              : engine(engine), texture(texture) {}
-        virtual bool handler() {
-            engine.deleteTextures(1, &texture);
-            return true;
-        }
-    };
-    postMessageAsync(new MessageDestroyGLTexture(getRenderEngine(), texture));
+    postMessageAsync(new LambdaMessage([=] { getRenderEngine().deleteTextures(1, &texture); }));
 }
 
-class DispSyncSource final : public VSyncSource, private DispSync::Callback {
-public:
-    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
-        const char* name) :
-            mName(name),
-            mValue(0),
-            mTraceVsync(traceVsync),
-            mVsyncOnLabel(String8::format("VsyncOn-%s", name)),
-            mVsyncEventLabel(String8::format("VSYNC-%s", name)),
-            mDispSync(dispSync),
-            mCallbackMutex(),
-            mVsyncMutex(),
-            mPhaseOffset(phaseOffset),
-            mEnabled(false) {}
-
-    ~DispSyncSource() override = default;
-
-    void setVSyncEnabled(bool enable) override {
-        Mutex::Autolock lock(mVsyncMutex);
-        if (enable) {
-            status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
-                    static_cast<DispSync::Callback*>(this));
-            if (err != NO_ERROR) {
-                ALOGE("error registering vsync callback: %s (%d)",
-                        strerror(-err), err);
-            }
-            //ATRACE_INT(mVsyncOnLabel.string(), 1);
-        } else {
-            status_t err = mDispSync->removeEventListener(
-                    static_cast<DispSync::Callback*>(this));
-            if (err != NO_ERROR) {
-                ALOGE("error unregistering vsync callback: %s (%d)",
-                        strerror(-err), err);
-            }
-            //ATRACE_INT(mVsyncOnLabel.string(), 0);
-        }
-        mEnabled = enable;
-    }
-
-    void setCallback(VSyncSource::Callback* callback) override{
-        Mutex::Autolock lock(mCallbackMutex);
-        mCallback = callback;
-    }
-
-    void setPhaseOffset(nsecs_t phaseOffset) override {
-        Mutex::Autolock lock(mVsyncMutex);
-
-        // Normalize phaseOffset to [0, period)
-        auto period = mDispSync->getPeriod();
-        phaseOffset %= period;
-        if (phaseOffset < 0) {
-            // If we're here, then phaseOffset is in (-period, 0). After this
-            // operation, it will be in (0, period)
-            phaseOffset += period;
-        }
-        mPhaseOffset = phaseOffset;
-
-        // If we're not enabled, we don't need to mess with the listeners
-        if (!mEnabled) {
-            return;
-        }
-
-        status_t err = mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this),
-                mPhaseOffset);
-        if (err != NO_ERROR) {
-            ALOGE("error changing vsync offset: %s (%d)",
-                    strerror(-err), err);
-        }
-    }
-
-private:
-    virtual void onDispSyncEvent(nsecs_t when) {
-        VSyncSource::Callback* callback;
-        {
-            Mutex::Autolock lock(mCallbackMutex);
-            callback = mCallback;
-
-            if (mTraceVsync) {
-                mValue = (mValue + 1) % 2;
-                ATRACE_INT(mVsyncEventLabel.string(), mValue);
-            }
-        }
-
-        if (callback != nullptr) {
-            callback->onVSyncEvent(when);
-        }
-    }
-
-    const char* const mName;
-
-    int mValue;
-
-    const bool mTraceVsync;
-    const String8 mVsyncOnLabel;
-    const String8 mVsyncEventLabel;
-
-    DispSync* mDispSync;
-
-    Mutex mCallbackMutex; // Protects the following
-    VSyncSource::Callback* mCallback = nullptr;
-
-    Mutex mVsyncMutex; // Protects the following
-    nsecs_t mPhaseOffset;
-    bool mEnabled;
-};
-
-class InjectVSyncSource final : public VSyncSource {
-public:
-    InjectVSyncSource() = default;
-    ~InjectVSyncSource() override = default;
-
-    void setCallback(VSyncSource::Callback* callback) override {
-        std::lock_guard<std::mutex> lock(mCallbackMutex);
-        mCallback = callback;
-    }
-
-    void onInjectSyncEvent(nsecs_t when) {
-        std::lock_guard<std::mutex> lock(mCallbackMutex);
-        if (mCallback) {
-            mCallback->onVSyncEvent(when);
-        }
-    }
-
-    void setVSyncEnabled(bool) override {}
-    void setPhaseOffset(nsecs_t) override {}
-
-private:
-    std::mutex mCallbackMutex; // Protects the following
-    VSyncSource::Callback* mCallback = nullptr;
-};
-
 // Do not call property_set on main thread which will be blocked by init
 // Use StartPropertySetThread instead.
 void SurfaceFlinger::init() {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
 
-    ALOGI("Phase offest NS: %" PRId64 "", vsyncPhaseOffsetNs);
+    ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset());
 
     Mutex::Autolock _l(mStateLock);
 
-    // start the EventThread
-    mEventThreadSource =
-            std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs,
-                                             true, "app");
-    mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(),
-                                                       [this]() { resyncWithRateLimit(); },
-                                                       impl::EventThread::InterceptVSyncsCallback(),
-                                                       "appEventThread");
-    mSfEventThreadSource =
-            std::make_unique<DispSyncSource>(&mPrimaryDispSync,
-                                             SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
+    auto resyncCallback = makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
 
-    mSFEventThread =
-            std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
-                                                [this]() { resyncWithRateLimit(); },
-                                                [this](nsecs_t timestamp) {
-                                                    mInterceptor->saveVSyncEvent(timestamp);
-                                                },
-                                                "sfEventThread");
-    mEventQueue->setEventThread(mSFEventThread.get());
-    mVsyncModulator.setEventThreads(mSFEventThread.get(), mEventThread.get());
+    // start the EventThread
+    if (mUseScheduler) {
+        mScheduler = getFactory().createScheduler([this](bool enabled) {
+            setVsyncEnabled(EventThread::DisplayType::Primary, enabled);
+        });
+        // TODO(b/113612090): Currently we assume that if scheduler is turned on, then the refresh
+        // rate is 90. Once b/122905403 is completed, this should be updated accordingly.
+        mPhaseOffsets->setRefreshRateType(
+                scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
+
+        mAppConnectionHandle =
+                mScheduler->createConnection("appConnection", mPhaseOffsets->getCurrentAppOffset(),
+                                             resyncCallback,
+                                             impl::EventThread::InterceptVSyncsCallback());
+        mSfConnectionHandle =
+                mScheduler->createConnection("sfConnection", mPhaseOffsets->getCurrentSfOffset(),
+                                             resyncCallback, [this](nsecs_t timestamp) {
+                                                 mInterceptor->saveVSyncEvent(timestamp);
+                                             });
+
+        mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
+        mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
+                                               mSfConnectionHandle.get());
+    } else {
+        mEventThreadSource =
+                std::make_unique<DispSyncSource>(mPrimaryDispSync.get(),
+                                                 mPhaseOffsets->getCurrentAppOffset(), true, "app");
+        mEventThread =
+                std::make_unique<impl::EventThread>(mEventThreadSource.get(),
+                                                    impl::EventThread::InterceptVSyncsCallback(),
+                                                    "appEventThread");
+        mSfEventThreadSource =
+                std::make_unique<DispSyncSource>(mPrimaryDispSync.get(),
+                                                 mPhaseOffsets->getCurrentSfOffset(), true, "sf");
+
+        mSFEventThread =
+                std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
+                                                    [this](nsecs_t timestamp) {
+                                                        mInterceptor->saveVSyncEvent(timestamp);
+                                                    },
+                                                    "sfEventThread");
+        mEventQueue->setEventThread(mSFEventThread.get(), std::move(resyncCallback));
+        mVsyncModulator.setEventThreads(mSFEventThread.get(), mEventThread.get());
+    }
 
     // Get a RenderEngine for the given display / config (can't fail)
-    getBE().mRenderEngine =
-            RE::impl::RenderEngine::create(HAL_PIXEL_FORMAT_RGBA_8888,
-                                           hasWideColorDisplay
-                                                   ? RE::RenderEngine::WIDE_COLOR_SUPPORT
-                                                   : 0);
-    LOG_ALWAYS_FATAL_IF(getBE().mRenderEngine == nullptr, "couldn't create RenderEngine");
+    int32_t renderEngineFeature = 0;
+    renderEngineFeature |= (useColorManagement ?
+                            renderengine::RenderEngine::USE_COLOR_MANAGEMENT : 0);
+    renderEngineFeature |= (useContextPriority ?
+                            renderengine::RenderEngine::USE_HIGH_PRIORITY_CONTEXT : 0);
+
+    // TODO(b/77156734): We need to stop casting and use HAL types when possible.
+    mCompositionEngine->setRenderEngine(
+            renderengine::RenderEngine::create(static_cast<int32_t>(defaultCompositionPixelFormat),
+                                               renderEngineFeature));
 
     LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
             "Starting with vr flinger active is not currently supported.");
-    getBE().mHwc.reset(
-            new HWComposer(std::make_unique<Hwc2::impl::Composer>(getBE().mHwcServiceName)));
-    getBE().mHwc->registerCallback(this, getBE().mComposerSequenceId);
+    mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
+    mCompositionEngine->getHwComposer().registerCallback(this, getBE().mComposerSequenceId);
     // Process any initial hotplug and resulting display changes.
     processDisplayHotplugEventsLocked();
-    LOG_ALWAYS_FATAL_IF(!getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY),
-            "Registered composer callback but didn't create the default primary display");
-
-    // make the default display GLContext current so that we can create textures
-    // when creating Layers (which may happens before we render something)
-    getDefaultDisplayDeviceLocked()->makeCurrent();
+    const auto display = getDefaultDisplayDeviceLocked();
+    LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback.");
+    LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(*display->getId()),
+                        "Internal display is disconnected.");
 
     if (useVrFlinger) {
-        auto vrFlingerRequestDisplayCallback = [this] (bool requestDisplay) {
+        auto vrFlingerRequestDisplayCallback = [this](bool requestDisplay) {
             // This callback is called from the vr flinger dispatch thread. We
             // need to call signalTransaction(), which requires holding
             // mStateLock when we're not on the main thread. Acquiring
             // mStateLock from the vr flinger dispatch thread might trigger a
             // deadlock in surface flinger (see b/66916578), so post a message
             // to be handled on the main thread instead.
-            sp<LambdaMessage> message = new LambdaMessage([=]() {
+            postMessageAsync(new LambdaMessage([=] {
                 ALOGI("VR request display mode: requestDisplay=%d", requestDisplay);
                 mVrFlingerRequestsDisplay = requestDisplay;
                 signalTransaction();
-            });
-            postMessageAsync(message);
+            }));
         };
-        mVrFlinger = dvr::VrFlinger::Create(getBE().mHwc->getComposer(),
-                getBE().mHwc->getHwcDisplayId(HWC_DISPLAY_PRIMARY).value_or(0),
-                vrFlingerRequestDisplayCallback);
+        mVrFlinger = dvr::VrFlinger::Create(getHwComposer().getComposer(),
+                                            getHwComposer()
+                                                    .fromPhysicalDisplayId(*display->getId())
+                                                    .value_or(0),
+                                            vrFlingerRequestDisplayCallback);
         if (!mVrFlinger) {
             ALOGE("Failed to start vrflinger");
         }
     }
 
-    mEventControlThread = std::make_unique<impl::EventControlThread>(
-            [this](bool enabled) { setVsyncEnabled(HWC_DISPLAY_PRIMARY, enabled); });
+    mEventControlThread = getFactory().createEventControlThread(
+            [this](bool enabled) { setVsyncEnabled(EventThread::DisplayType::Primary, enabled); });
 
     // initialize our drawing state
     mDrawingState = mCurrentState;
@@ -756,36 +713,35 @@
     // set initial conditions (e.g. unblank default device)
     initializeDisplays();
 
-    getBE().mRenderEngine->primeCache();
+    getRenderEngine().primeCache();
 
     // Inform native graphics APIs whether the present timestamp is supported:
-    if (getHwComposer().hasCapability(
-            HWC2::Capability::PresentFenceIsNotReliable)) {
-        mStartPropertySetThread = new StartPropertySetThread(false);
-    } else {
-        mStartPropertySetThread = new StartPropertySetThread(true);
-    }
+
+    const bool presentFenceReliable =
+            !getHwComposer().hasCapability(HWC2::Capability::PresentFenceIsNotReliable);
+    mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);
 
     if (mStartPropertySetThread->Start() != NO_ERROR) {
         ALOGE("Run StartPropertySetThread failed!");
     }
 
-    // This is a hack. Per definition of getDataspaceSaturationMatrix, the returned matrix
-    // is used to saturate legacy sRGB content. However, to make sure the same color under
-    // Display P3 will be saturated to the same color, we intentionally break the API spec
-    // and apply this saturation matrix on Display P3 content. Unless the risk of applying
-    // such saturation matrix on Display P3 is understood fully, the API should always return
-    // identify matrix.
-    mEnhancedSaturationMatrix = getBE().mHwc->getDataspaceSaturationMatrix(HWC_DISPLAY_PRIMARY,
-            Dataspace::SRGB_LINEAR);
-
-    // we will apply this on Display P3.
-    if (mEnhancedSaturationMatrix != mat4()) {
-        ColorSpace srgb(ColorSpace::sRGB());
-        ColorSpace displayP3(ColorSpace::DisplayP3());
-        mat4 srgbToP3 = mat4(ColorSpaceConnector(srgb, displayP3).getTransform());
-        mat4 p3ToSrgb = mat4(ColorSpaceConnector(displayP3, srgb).getTransform());
-        mEnhancedSaturationMatrix = srgbToP3 * mEnhancedSaturationMatrix * p3ToSrgb;
+    if (mUseScheduler) {
+        mScheduler->setExpiredIdleTimerCallback([this]() {
+            mPhaseOffsets->setRefreshRateType(
+                    scheduler::RefreshRateConfigs::RefreshRateType::DEFAULT);
+            const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+            mVsyncModulator.setPhaseOffsets(early, gl, late);
+            setRefreshRateTo(60.f /* fps */);
+        });
+        mScheduler->setResetIdleTimerCallback([this]() {
+            mPhaseOffsets->setRefreshRateType(
+                    scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
+            const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+            mVsyncModulator.setPhaseOffsets(early, gl, late);
+            setRefreshRateTo(90.f /* fps */);
+        });
+        mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(
+                getHwComposer().getConfigs(*display->getId()));
     }
 
     ALOGV("Done initializing");
@@ -816,11 +772,11 @@
 }
 
 size_t SurfaceFlinger::getMaxTextureSize() const {
-    return getBE().mRenderEngine->getMaxTextureSize();
+    return getRenderEngine().getMaxTextureSize();
 }
 
 size_t SurfaceFlinger::getMaxViewportDims() const {
-    return getBE().mRenderEngine->getMaxViewportDims();
+    return getRenderEngine().getMaxViewportDims();
 }
 
 // ----------------------------------------------------------------------------
@@ -858,41 +814,31 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
-        Vector<DisplayInfo>* configs) {
-    if (configs == nullptr || display.get() == nullptr) {
+status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
+                                           Vector<DisplayInfo>* configs) {
+    if (!displayToken || !configs) {
         return BAD_VALUE;
     }
 
-    if (!display.get())
+    const auto displayId = getPhysicalDisplayId(displayToken);
+    if (!displayId) {
         return NAME_NOT_FOUND;
-
-    int32_t type = NAME_NOT_FOUND;
-    for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
-        if (display == mBuiltinDisplays[i]) {
-            type = i;
-            break;
-        }
-    }
-
-    if (type < 0) {
-        return type;
     }
 
     // TODO: Not sure if display density should handled by SF any longer
     class Density {
-        static int getDensityFromProperty(char const* propName) {
+        static float getDensityFromProperty(char const* propName) {
             char property[PROPERTY_VALUE_MAX];
-            int density = 0;
+            float density = 0.0f;
             if (property_get(propName, property, nullptr) > 0) {
-                density = atoi(property);
+                density = strtof(property, nullptr);
             }
             return density;
         }
     public:
-        static int getEmuDensity() {
+        static float getEmuDensity() {
             return getDensityFromProperty("qemu.sf.lcd_density"); }
-        static int getBuildDensity()  {
+        static float getBuildDensity()  {
             return getDensityFromProperty("ro.sf.lcd_density"); }
     };
 
@@ -900,13 +846,19 @@
 
     ConditionalLock _l(mStateLock,
             std::this_thread::get_id() != mMainThreadId);
-    for (const auto& hwConfig : getHwComposer().getConfigs(type)) {
+    for (const auto& hwConfig : getHwComposer().getConfigs(*displayId)) {
         DisplayInfo info = DisplayInfo();
 
         float xdpi = hwConfig->getDpiX();
         float ydpi = hwConfig->getDpiY();
 
-        if (type == DisplayDevice::DISPLAY_PRIMARY) {
+        info.w = hwConfig->getWidth();
+        info.h = hwConfig->getHeight();
+        // Default display viewport to display width and height
+        info.viewportW = info.w;
+        info.viewportH = info.h;
+
+        if (displayId == getInternalDisplayId()) {
             // The density of the device is provided by a build property
             float density = Density::getBuildDensity() / 160.0f;
             if (density == 0) {
@@ -923,8 +875,15 @@
             info.density = density;
 
             // TODO: this needs to go away (currently needed only by webkit)
-            sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
-            info.orientation = hw ? hw->getOrientation() : 0;
+            const auto display = getDefaultDisplayDeviceLocked();
+            info.orientation = display ? display->getOrientation() : 0;
+
+            // This is for screenrecord
+            const Rect viewport = display->getViewport();
+            if (viewport.isValid()) {
+                info.viewportW = uint32_t(viewport.getWidth());
+                info.viewportH = uint32_t(viewport.getHeight());
+            }
         } else {
             // TODO: where should this value come from?
             static const int TV_DENSITY = 213;
@@ -932,12 +891,10 @@
             info.orientation = 0;
         }
 
-        info.w = hwConfig->getWidth();
-        info.h = hwConfig->getHeight();
         info.xdpi = xdpi;
         info.ydpi = ydpi;
         info.fps = 1e9 / hwConfig->getVsyncPeriod();
-        info.appVsyncOffset = vsyncPhaseOffsetNs;
+        info.appVsyncOffset = mPhaseOffsets->getCurrentAppOffset();
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -951,14 +908,14 @@
         //
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
-        info.presentationDeadline = hwConfig->getVsyncPeriod() -
-                sfVsyncPhaseOffsetNs + 1000000;
+        info.presentationDeadline =
+                hwConfig->getVsyncPeriod() - mPhaseOffsets->getCurrentSfOffset() + 1000000;
 
         // All non-virtual displays are currently considered secure.
         info.secure = true;
 
-        if (type == DisplayDevice::DISPLAY_PRIMARY &&
-            mPrimaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
+        if (displayId == getInternalDisplayId() &&
+            primaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
             std::swap(info.w, info.h);
         }
 
@@ -968,129 +925,103 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& /* display */,
-        DisplayStatInfo* stats) {
-    if (stats == nullptr) {
+status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* stats) {
+    if (!stats) {
         return BAD_VALUE;
     }
 
-    // FIXME for now we always return stats for the primary display
-    memset(stats, 0, sizeof(*stats));
-    stats->vsyncTime   = mPrimaryDispSync.computeNextRefresh(0);
-    stats->vsyncPeriod = mPrimaryDispSync.getPeriod();
+    if (mUseScheduler) {
+        mScheduler->getDisplayStatInfo(stats);
+    } else {
+        stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0);
+        stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
+    }
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) {
-    if (outViewport == nullptr || display.get() == nullptr) {
+int SurfaceFlinger::getActiveConfig(const sp<IBinder>& displayToken) {
+    const auto display = getDisplayDevice(displayToken);
+    if (!display) {
+        ALOGE("getActiveConfig: Invalid display token %p", displayToken.get());
         return BAD_VALUE;
     }
 
-    sp<const DisplayDevice> device(getDisplayDevice(display));
-    if (device == nullptr) {
-        return BAD_VALUE;
-    }
+    return display->getActiveConfig();
+}
 
-    *outViewport = device->getViewport();
-
+status_t SurfaceFlinger::setActiveConfigAsync(const sp<IBinder>& displayToken, int mode) {
+    ATRACE_NAME("setActiveConfigAsync");
+    postMessageAsync(new LambdaMessage(
+            [=]() NO_THREAD_SAFETY_ANALYSIS { setActiveConfigInternal(displayToken, mode); }));
     return NO_ERROR;
 }
 
-int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) {
-    if (display == nullptr) {
-        ALOGE("%s : display is nullptr", __func__);
-        return BAD_VALUE;
-    }
-
-    sp<const DisplayDevice> device(getDisplayDevice(display));
-    if (device != nullptr) {
-        return device->getActiveConfig();
-    }
-
-    return BAD_VALUE;
+status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
+    ATRACE_NAME("setActiveConfigSync");
+    postMessageSync(new LambdaMessage(
+            [&]() NO_THREAD_SAFETY_ANALYSIS { setActiveConfigInternal(displayToken, mode); }));
+    return NO_ERROR;
 }
 
-void SurfaceFlinger::setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode) {
-    ALOGD("Set active config mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(),
-          this);
-    int32_t type = hw->getDisplayType();
-    int currentMode = hw->getActiveConfig();
+void SurfaceFlinger::setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) {
+    Vector<DisplayInfo> configs;
+    getDisplayConfigs(displayToken, &configs);
+    if (mode < 0 || mode >= static_cast<int>(configs.size())) {
+        ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size());
+        return;
+    }
 
+    const auto display = getDisplayDevice(displayToken);
+    if (!display) {
+        ALOGE("Attempt to set active config %d for invalid display token %p", mode,
+              displayToken.get());
+        return;
+    }
+    if (display->isVirtual()) {
+        ALOGW("Attempt to set active config %d for virtual display", mode);
+        return;
+    }
+    int currentDisplayPowerMode = display->getPowerMode();
+    if (currentDisplayPowerMode != HWC_POWER_MODE_NORMAL) {
+        // Don't change active config when in AoD.
+        return;
+    }
+
+    int currentMode = display->getActiveConfig();
     if (mode == currentMode) {
-        ALOGD("Screen type=%d is already mode=%d", hw->getDisplayType(), mode);
+        // Don't update config if we are already running in the desired mode.
         return;
     }
-
-    if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
-        ALOGW("Trying to set config for virtual display");
-        return;
+    if (mUseScheduler) {
+        mRefreshRateStats->setConfigMode(mode);
     }
 
-    hw->setActiveConfig(mode);
-    getHwComposer().setActiveConfig(type, mode);
+    const auto displayId = display->getId();
+    LOG_ALWAYS_FATAL_IF(!displayId);
+
+    display->setActiveConfig(mode);
+    getHwComposer().setActiveConfig(*displayId, mode);
+
+    ATRACE_INT("ActiveConfigMode", mode);
+    resyncToHardwareVsync(true, getVsyncPeriod());
 }
 
-status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& display, int mode) {
-    class MessageSetActiveConfig: public MessageBase {
-        SurfaceFlinger& mFlinger;
-        sp<IBinder> mDisplay;
-        int mMode;
-    public:
-        MessageSetActiveConfig(SurfaceFlinger& flinger, const sp<IBinder>& disp,
-                               int mode) :
-            mFlinger(flinger), mDisplay(disp) { mMode = mode; }
-        virtual bool handler() {
-            Vector<DisplayInfo> configs;
-            mFlinger.getDisplayConfigs(mDisplay, &configs);
-            if (mMode < 0 || mMode >= static_cast<int>(configs.size())) {
-                ALOGE("Attempt to set active config = %d for display with %zu configs",
-                        mMode, configs.size());
-                return true;
-            }
-            sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
-            if (hw == nullptr) {
-                ALOGE("Attempt to set active config = %d for null display %p",
-                        mMode, mDisplay.get());
-            } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
-                ALOGW("Attempt to set active config = %d for virtual display",
-                        mMode);
-            } else {
-                mFlinger.setActiveConfigInternal(hw, mMode);
-            }
-            return true;
-        }
-    };
-    sp<MessageBase> msg = new MessageSetActiveConfig(*this, display, mode);
-    postMessageSync(msg);
-    return NO_ERROR;
-}
-status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display,
-        Vector<ColorMode>* outColorModes) {
-    if ((outColorModes == nullptr) || (display.get() == nullptr)) {
+status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
+                                              Vector<ColorMode>* outColorModes) {
+    if (!displayToken || !outColorModes) {
         return BAD_VALUE;
     }
 
-    if (!display.get()) {
+    const auto displayId = getPhysicalDisplayId(displayToken);
+    if (!displayId) {
         return NAME_NOT_FOUND;
     }
 
-    int32_t type = NAME_NOT_FOUND;
-    for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
-        if (display == mBuiltinDisplays[i]) {
-            type = i;
-            break;
-        }
-    }
-
-    if (type < 0) {
-        return type;
-    }
-
     std::vector<ColorMode> modes;
     {
         ConditionalLock _l(mStateLock,
                 std::this_thread::get_id() != mMainThreadId);
-        modes = getHwComposer().getColorModes(type);
+        modes = getHwComposer().getColorModes(*displayId);
     }
     outColorModes->clear();
     std::copy(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes));
@@ -1098,79 +1029,51 @@
     return NO_ERROR;
 }
 
-ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& display) {
-    sp<const DisplayDevice> device(getDisplayDevice(display));
-    if (device != nullptr) {
-        return device->getActiveColorMode();
+status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+                                                   ui::DisplayPrimaries &primaries) {
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    // Currently we only support this API for a single internal display.
+    if (getInternalDisplayToken() != displayToken) {
+        return BAD_VALUE;
+    }
+
+    memcpy(&primaries, &mInternalDisplayPrimaries, sizeof(ui::DisplayPrimaries));
+    return NO_ERROR;
+}
+
+ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& displayToken) {
+    if (const auto display = getDisplayDevice(displayToken)) {
+        return display->getCompositionDisplay()->getState().colorMode;
     }
     return static_cast<ColorMode>(BAD_VALUE);
 }
 
-void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& hw,
-                                                ColorMode mode, Dataspace dataSpace,
-                                                RenderIntent renderIntent) {
-    int32_t type = hw->getDisplayType();
-    ColorMode currentMode = hw->getActiveColorMode();
-    Dataspace currentDataSpace = hw->getCompositionDataSpace();
-    RenderIntent currentRenderIntent = hw->getActiveRenderIntent();
-
-    if (mode == currentMode && dataSpace == currentDataSpace &&
-        renderIntent == currentRenderIntent) {
-        return;
-    }
-
-    if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
-        ALOGW("Trying to set config for virtual display");
-        return;
-    }
-
-    hw->setActiveColorMode(mode);
-    hw->setCompositionDataSpace(dataSpace);
-    hw->setActiveRenderIntent(renderIntent);
-    getHwComposer().setActiveColorMode(type, mode, renderIntent);
-
-    ALOGV("Set active color mode: %s (%d), active render intent: %s (%d), type=%d",
-          decodeColorMode(mode).c_str(), mode,
-          decodeRenderIntent(renderIntent).c_str(), renderIntent,
-          hw->getDisplayType());
-}
-
-
-status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& display,
-        ColorMode colorMode) {
-    class MessageSetActiveColorMode: public MessageBase {
-        SurfaceFlinger& mFlinger;
-        sp<IBinder> mDisplay;
-        ColorMode mMode;
-    public:
-        MessageSetActiveColorMode(SurfaceFlinger& flinger, const sp<IBinder>& disp,
-                               ColorMode mode) :
-            mFlinger(flinger), mDisplay(disp) { mMode = mode; }
-        virtual bool handler() {
-            Vector<ColorMode> modes;
-            mFlinger.getDisplayColorModes(mDisplay, &modes);
-            bool exists = std::find(std::begin(modes), std::end(modes), mMode) != std::end(modes);
-            if (mMode < ColorMode::NATIVE || !exists) {
-                ALOGE("Attempt to set invalid active color mode %s (%d) for display %p",
-                      decodeColorMode(mMode).c_str(), mMode, mDisplay.get());
-                return true;
-            }
-            sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
-            if (hw == nullptr) {
-                ALOGE("Attempt to set active color mode %s (%d) for null display %p",
-                      decodeColorMode(mMode).c_str(), mMode, mDisplay.get());
-            } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
-                ALOGW("Attempt to set active color mode %s %d for virtual display",
-                      decodeColorMode(mMode).c_str(), mMode);
-            } else {
-                mFlinger.setActiveColorModeInternal(hw, mMode, Dataspace::UNKNOWN,
-                                                    RenderIntent::COLORIMETRIC);
-            }
-            return true;
+status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
+    postMessageSync(new LambdaMessage([&] {
+        Vector<ColorMode> modes;
+        getDisplayColorModes(displayToken, &modes);
+        bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes);
+        if (mode < ColorMode::NATIVE || !exists) {
+            ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
+                  decodeColorMode(mode).c_str(), mode, displayToken.get());
+            return;
         }
-    };
-    sp<MessageBase> msg = new MessageSetActiveColorMode(*this, display, colorMode);
-    postMessageSync(msg);
+        const auto display = getDisplayDevice(displayToken);
+        if (!display) {
+            ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p",
+                  decodeColorMode(mode).c_str(), mode, displayToken.get());
+        } else if (display->isVirtual()) {
+            ALOGW("Attempt to set active color mode %s (%d) for virtual display",
+                  decodeColorMode(mode).c_str(), mode);
+        } else {
+            display->getCompositionDisplay()->setColorMode(mode, Dataspace::UNKNOWN,
+                                                           RenderIntent::COLORIMETRIC);
+        }
+    }));
+
     return NO_ERROR;
 }
 
@@ -1186,20 +1089,20 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& display,
-        HdrCapabilities* outCapabilities) const {
+status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& displayToken,
+                                            HdrCapabilities* outCapabilities) const {
     Mutex::Autolock _l(mStateLock);
 
-    sp<const DisplayDevice> displayDevice(getDisplayDeviceLocked(display));
-    if (displayDevice == nullptr) {
-        ALOGE("getHdrCapabilities: Invalid display %p", displayDevice.get());
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        ALOGE("getHdrCapabilities: Invalid display token %p", displayToken.get());
         return BAD_VALUE;
     }
 
     // At this point the DisplayDeivce should already be set up,
     // meaning the luminance information is already queried from
     // hardware composer and stored properly.
-    const HdrCapabilities& capabilities = displayDevice->getHdrCapabilities();
+    const HdrCapabilities& capabilities = display->getHdrCapabilities();
     *outCapabilities = HdrCapabilities(capabilities.getSupportedHdrTypes(),
                                        capabilities.getDesiredMaxLuminance(),
                                        capabilities.getDesiredMaxAverageLuminance(),
@@ -1208,33 +1111,114 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
+                                                               ui::PixelFormat* outFormat,
+                                                               ui::Dataspace* outDataspace,
+                                                               uint8_t* outComponentMask) const {
+    if (!outFormat || !outDataspace || !outComponentMask) {
+        return BAD_VALUE;
+    }
+    const auto display = getDisplayDevice(displayToken);
+    if (!display || !display->getId()) {
+        ALOGE("getDisplayedContentSamplingAttributes: Bad display token: %p", display.get());
+        return BAD_VALUE;
+    }
+    return getHwComposer().getDisplayedContentSamplingAttributes(*display->getId(), outFormat,
+                                                                 outDataspace, outComponentMask);
+}
+
+status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken,
+                                                          bool enable, uint8_t componentMask,
+                                                          uint64_t maxFrames) const {
+    const auto display = getDisplayDevice(displayToken);
+    if (!display || !display->getId()) {
+        ALOGE("setDisplayContentSamplingEnabled: Bad display token: %p", display.get());
+        return BAD_VALUE;
+    }
+
+    return getHwComposer().setDisplayContentSamplingEnabled(*display->getId(), enable,
+                                                            componentMask, maxFrames);
+}
+
+status_t SurfaceFlinger::getDisplayedContentSample(const sp<IBinder>& displayToken,
+                                                   uint64_t maxFrames, uint64_t timestamp,
+                                                   DisplayedFrameStats* outStats) const {
+    const auto display = getDisplayDevice(displayToken);
+    if (!display || !display->getId()) {
+        ALOGE("getDisplayContentSample: Bad display token: %p", displayToken.get());
+        return BAD_VALUE;
+    }
+
+    return getHwComposer().getDisplayedContentSample(*display->getId(), maxFrames, timestamp,
+                                                     outStats);
+}
+
+status_t SurfaceFlinger::getProtectedContentSupport(bool* outSupported) const {
+    if (!outSupported) {
+        return BAD_VALUE;
+    }
+    *outSupported = getRenderEngine().supportsProtectedContent();
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+                                     int32_t* outBufferId) {
+    if (!outBufferId) {
+        return BAD_VALUE;
+    }
+    *outBufferId = mBufferStateLayerCache.add(token, buffer);
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) {
+    mBufferStateLayerCache.release(token, bufferId);
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken,
+                                            bool* outIsWideColorDisplay) const {
+    if (!displayToken || !outIsWideColorDisplay) {
+        return BAD_VALUE;
+    }
+    Mutex::Autolock _l(mStateLock);
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        return BAD_VALUE;
+    }
+    *outIsWideColorDisplay = display->hasWideColorGamut();
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
-    sp<LambdaMessage> enableVSyncInjections = new LambdaMessage([&]() {
+    postMessageSync(new LambdaMessage([&] {
         Mutex::Autolock _l(mStateLock);
 
         if (mInjectVSyncs == enable) {
             return;
         }
 
+        auto resyncCallback = makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
+
+        // TODO(akrulec): Part of the Injector should be refactored, so that it
+        // can be passed to Scheduler.
         if (enable) {
             ALOGV("VSync Injections enabled");
             if (mVSyncInjector.get() == nullptr) {
                 mVSyncInjector = std::make_unique<InjectVSyncSource>();
                 mInjectorEventThread = std::make_unique<
                         impl::EventThread>(mVSyncInjector.get(),
-                                           [this]() { resyncWithRateLimit(); },
                                            impl::EventThread::InterceptVSyncsCallback(),
                                            "injEventThread");
             }
-            mEventQueue->setEventThread(mInjectorEventThread.get());
+            mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback));
         } else {
             ALOGV("VSync Injections disabled");
-            mEventQueue->setEventThread(mSFEventThread.get());
+            mEventQueue->setEventThread(mSFEventThread.get(), std::move(resyncCallback));
         }
 
         mInjectVSyncs = enable;
-    });
-    postMessageSync(enableVSyncInjections);
+    }));
+
     return NO_ERROR;
 }
 
@@ -1254,15 +1238,6 @@
 
 status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
         NO_THREAD_SAFETY_ANALYSIS {
-    IPCThreadState* ipc = IPCThreadState::self();
-    const int pid = ipc->getCallingPid();
-    const int uid = ipc->getCallingUid();
-    if ((uid != AID_SHELL) &&
-            !PermissionCache::checkPermission(sDump, pid, uid)) {
-        ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
-        return PERMISSION_DENIED;
-    }
-
     // Try to acquire a lock for 1s, fail gracefully
     const status_t err = mStateLock.timedLock(s2ns(1));
     const bool locked = (err == NO_ERROR);
@@ -1280,14 +1255,38 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getCompositionPreference(
+        Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
+        Dataspace* outWideColorGamutDataspace,
+        ui::PixelFormat* outWideColorGamutPixelFormat) const {
+    *outDataspace = mDefaultCompositionDataspace;
+    *outPixelFormat = defaultCompositionPixelFormat;
+    *outWideColorGamutDataspace = mWideColorGamutCompositionDataspace;
+    *outWideColorGamutPixelFormat = wideColorGamutCompositionPixelFormat;
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
         ISurfaceComposer::VsyncSource vsyncSource) {
-    if (vsyncSource == eVsyncSourceSurfaceFlinger) {
-        return mSFEventThread->createEventConnection();
+    auto resyncCallback = makeResyncCallback([this] {
+        Mutex::Autolock lock(mStateLock);
+        return getVsyncPeriod();
+    });
+
+    if (mUseScheduler) {
+        if (vsyncSource == eVsyncSourceSurfaceFlinger) {
+            return mScheduler->createDisplayEventConnection(mSfConnectionHandle, resyncCallback);
+        } else {
+            return mScheduler->createDisplayEventConnection(mAppConnectionHandle, resyncCallback);
+        }
     } else {
-        return mEventThread->createEventConnection();
+        if (vsyncSource == eVsyncSourceSurfaceFlinger) {
+            return mSFEventThread->createEventConnection(resyncCallback);
+        } else {
+            return mEventThread->createEventConnection(resyncCallback);
+        }
     }
 }
 
@@ -1330,47 +1329,63 @@
     } while (true);
 }
 
+nsecs_t SurfaceFlinger::getVsyncPeriod() const {
+    const auto displayId = getInternalDisplayId();
+    if (!displayId || !getHwComposer().isConnected(*displayId)) {
+        return 0;
+    }
+
+    const auto config = getHwComposer().getActiveConfig(*displayId);
+    return config ? config->getVsyncPeriod() : 0;
+}
+
 void SurfaceFlinger::enableHardwareVsync() {
     Mutex::Autolock _l(mHWVsyncLock);
     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
-        mPrimaryDispSync.beginResync();
-        //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
+        mPrimaryDispSync->beginResync();
         mEventControlThread->setVsyncEnabled(true);
         mPrimaryHWVsyncEnabled = true;
     }
 }
 
-void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) {
+void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
     Mutex::Autolock _l(mHWVsyncLock);
 
     if (makeAvailable) {
         mHWVsyncAvailable = true;
+        // TODO(b/113612090): This is silly, but necessary evil until we turn on the flag for good.
+        if (mUseScheduler) {
+            mScheduler->makeHWSyncAvailable(true);
+        }
     } else if (!mHWVsyncAvailable) {
         // Hardware vsync is not currently available, so abort the resync
         // attempt for now
         return;
     }
 
-    const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
-    const nsecs_t period = activeConfig->getVsyncPeriod();
+    if (period <= 0) {
+        return;
+    }
 
-    mPrimaryDispSync.reset();
-    mPrimaryDispSync.setPeriod(period);
+    if (mUseScheduler) {
+        mScheduler->setVsyncPeriod(period);
+    } else {
+        mPrimaryDispSync->reset();
+        mPrimaryDispSync->setPeriod(period);
 
-    if (!mPrimaryHWVsyncEnabled) {
-        mPrimaryDispSync.beginResync();
-        //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
-        mEventControlThread->setVsyncEnabled(true);
-        mPrimaryHWVsyncEnabled = true;
+        if (!mPrimaryHWVsyncEnabled) {
+            mPrimaryDispSync->beginResync();
+            mEventControlThread->setVsyncEnabled(true);
+            mPrimaryHWVsyncEnabled = true;
+        }
     }
 }
 
 void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) {
     Mutex::Autolock _l(mHWVsyncLock);
     if (mPrimaryHWVsyncEnabled) {
-        //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, false);
         mEventControlThread->setVsyncEnabled(false);
-        mPrimaryDispSync.endResync();
+        mPrimaryDispSync->endResync();
         mPrimaryHWVsyncEnabled = false;
     }
     if (makeUnavailable) {
@@ -1378,44 +1393,52 @@
     }
 }
 
-void SurfaceFlinger::resyncWithRateLimit() {
+void SurfaceFlinger::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
     static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
 
-    // No explicit locking is needed here since EventThread holds a lock while calling this method
-    static nsecs_t sLastResyncAttempted = 0;
     const nsecs_t now = systemTime();
-    if (now - sLastResyncAttempted > kIgnoreDelay) {
-        resyncToHardwareVsync(false);
+    const nsecs_t last = lastResyncTime.exchange(now);
+
+    if (now - last > kIgnoreDelay) {
+        flinger.resyncToHardwareVsync(false, getVsyncPeriod());
     }
-    sLastResyncAttempted = now;
 }
 
-void SurfaceFlinger::onVsyncReceived(int32_t sequenceId,
-        hwc2_display_t displayId, int64_t timestamp) {
+void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
+                                     int64_t timestamp) {
+    ATRACE_NAME("SF onVsync");
+
     Mutex::Autolock lock(mStateLock);
     // Ignore any vsyncs from a previous hardware composer.
     if (sequenceId != getBE().mComposerSequenceId) {
         return;
     }
 
-    int32_t type;
-    if (!getBE().mHwc->onVsync(displayId, timestamp, &type)) {
+    if (!getHwComposer().onVsync(hwcDisplayId, timestamp)) {
         return;
     }
 
-    bool needsHwVsync = false;
-
-    { // Scope for the lock
-        Mutex::Autolock _l(mHWVsyncLock);
-        if (type == DisplayDevice::DISPLAY_PRIMARY && mPrimaryHWVsyncEnabled) {
-            needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
-        }
+    if (hwcDisplayId != getHwComposer().getInternalHwcDisplayId()) {
+        // For now, we don't do anything with external display vsyncs.
+        return;
     }
 
-    if (needsHwVsync) {
-        enableHardwareVsync();
+    if (mUseScheduler) {
+        mScheduler->addResyncSample(timestamp);
     } else {
-        disableHardwareVsync(false);
+        bool needsHwVsync = false;
+        { // Scope for the lock
+            Mutex::Autolock _l(mHWVsyncLock);
+            if (mPrimaryHWVsyncEnabled) {
+                needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp);
+            }
+        }
+
+        if (needsHwVsync) {
+            enableHardwareVsync();
+        } else {
+            disableHardwareVsync(false);
+        }
     }
 }
 
@@ -1424,9 +1447,52 @@
     *compositorTiming = getBE().mCompositorTiming;
 }
 
-void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
+void SurfaceFlinger::setRefreshRateTo(float newFps) {
+    const auto displayId = getInternalDisplayId();
+    if (!displayId || mBootStage != BootStage::FINISHED) {
+        return;
+    }
+    // TODO(b/113612090): There should be a message queue flush here. Because this esentially
+    // runs on a mainthread, we cannot call postMessageSync. This can be resolved in a better
+    // manner, once the setActiveConfig is synchronous, and is executed at a known time in a
+    // refresh cycle.
+
+    // Don't do any updating if the current fps is the same as the new one.
+    const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
+    const nsecs_t currentVsyncPeriod = activeConfig->getVsyncPeriod();
+    if (currentVsyncPeriod == 0) {
+        return;
+    }
+    // TODO(b/113612090): Consider having an enum value for correct refresh rates, rather than
+    // floating numbers.
+    const float currentFps = 1e9 / currentVsyncPeriod;
+    if (std::abs(currentFps - newFps) <= 1) {
+        return;
+    }
+
+    auto configs = getHwComposer().getConfigs(*displayId);
+    for (int i = 0; i < configs.size(); i++) {
+        const nsecs_t vsyncPeriod = configs.at(i)->getVsyncPeriod();
+        if (vsyncPeriod == 0) {
+            continue;
+        }
+        const float fps = 1e9 / vsyncPeriod;
+        // TODO(b/113612090): There should be a better way at determining which config
+        // has the right refresh rate.
+        if (std::abs(fps - newFps) <= 1) {
+            const auto display = getBuiltInDisplay(HWC_DISPLAY_PRIMARY);
+            if (!display) return;
+            // This is posted in async function to avoid deadlock when getDisplayDevice
+            // requires mStateLock.
+            setActiveConfigAsync(display, i);
+            ATRACE_INT("FPS", newFps);
+        }
+    }
+}
+
+void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
                                        HWC2::Connection connection) {
-    ALOGV("onHotplugReceived(%d, %" PRIu64 ", %s)", sequenceId, display,
+    ALOGV("%s(%d, %" PRIu64 ", %s)", __FUNCTION__, sequenceId, hwcDisplayId,
           connection == HWC2::Connection::Connected ? "connected" : "disconnected");
 
     // Ignore events that do not have the right sequenceId.
@@ -1440,7 +1506,7 @@
     // acquire it here.
     ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
 
-    mPendingHotplugEvents.emplace_back(HotplugEvent{display, connection});
+    mPendingHotplugEvents.emplace_back(HotplugEvent{hwcDisplayId, connection});
 
     if (std::this_thread::get_id() == mMainThreadId) {
         // Process all pending hot plug events immediately if we are on the main thread.
@@ -1450,31 +1516,35 @@
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
-void SurfaceFlinger::onRefreshReceived(int sequenceId,
-                                       hwc2_display_t /*display*/) {
+void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDisplayId*/) {
     Mutex::Autolock lock(mStateLock);
     if (sequenceId != getBE().mComposerSequenceId) {
         return;
     }
-    repaintEverything();
+    repaintEverythingForHWC();
 }
 
-void SurfaceFlinger::setVsyncEnabled(int disp, int enabled) {
+void SurfaceFlinger::setVsyncEnabled(EventThread::DisplayType /*displayType*/, bool enabled) {
     ATRACE_CALL();
     Mutex::Autolock lock(mStateLock);
-    getHwComposer().setVsyncEnabled(disp,
-            enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
+    if (const auto displayId = getInternalDisplayId()) {
+        getHwComposer().setVsyncEnabled(*displayId,
+                                        enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
+    }
 }
 
 // Note: it is assumed the caller holds |mStateLock| when this is called
 void SurfaceFlinger::resetDisplayState() {
-    disableHardwareVsync(true);
+    if (mUseScheduler) {
+        mScheduler->disableHardwareVsync(true);
+    } else {
+        disableHardwareVsync(true);
+    }
     // Clear the drawing state so that the logic inside of
     // handleTransactionLocked will fire. It will determine the delta between
     // mCurrentState and mDrawingState and re-apply all changes when we make the
     // transition.
     mDrawingState.displays.clear();
-    getRenderEngine().resetCurrentSurface();
     mDisplays.clear();
 }
 
@@ -1482,11 +1552,11 @@
     if (!mVrFlinger)
         return;
     bool vrFlingerRequestsDisplay = mVrFlingerRequestsDisplay;
-    if (vrFlingerRequestsDisplay == getBE().mHwc->isUsingVrComposer()) {
+    if (vrFlingerRequestsDisplay == getHwComposer().isUsingVrComposer()) {
         return;
     }
 
-    if (vrFlingerRequestsDisplay && !getBE().mHwc->getComposer()->isRemote()) {
+    if (vrFlingerRequestsDisplay && !getHwComposer().getComposer()->isRemote()) {
         ALOGE("Vr flinger is only supported for remote hardware composer"
               " service connections. Ignoring request to transition to vr"
               " flinger.");
@@ -1496,46 +1566,62 @@
 
     Mutex::Autolock _l(mStateLock);
 
-    int currentDisplayPowerMode = getDisplayDeviceLocked(
-            mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY])->getPowerMode();
+    sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
+    LOG_ALWAYS_FATAL_IF(!display);
+    const int currentDisplayPowerMode = display->getPowerMode();
+    // This DisplayDevice will no longer be relevant once resetDisplayState() is
+    // called below. Clear the reference now so we don't accidentally use it
+    // later.
+    display.clear();
 
     if (!vrFlingerRequestsDisplay) {
         mVrFlinger->SeizeDisplayOwnership();
     }
 
     resetDisplayState();
-    getBE().mHwc.reset(); // Delete the current instance before creating the new one
-    getBE().mHwc.reset(new HWComposer(std::make_unique<Hwc2::impl::Composer>(
-            vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName)));
-    getBE().mHwc->registerCallback(this, ++getBE().mComposerSequenceId);
+    // Delete the current instance before creating the new one
+    mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
+    mCompositionEngine->setHwComposer(getFactory().createHWComposer(
+            vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName));
+    getHwComposer().registerCallback(this, ++getBE().mComposerSequenceId);
 
-    LOG_ALWAYS_FATAL_IF(!getBE().mHwc->getComposer()->isRemote(),
+    LOG_ALWAYS_FATAL_IF(!getHwComposer().getComposer()->isRemote(),
                         "Switched to non-remote hardware composer");
 
     if (vrFlingerRequestsDisplay) {
         mVrFlinger->GrantDisplayOwnership();
-    } else {
-        enableHardwareVsync();
     }
 
     mVisibleRegionsDirty = true;
     invalidateHwcGeometry();
 
     // Re-enable default display.
-    sp<DisplayDevice> hw(getDisplayDeviceLocked(
-            mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]));
-    setPowerModeInternal(hw, currentDisplayPowerMode, /*stateLockHeld*/ true);
+    display = getDefaultDisplayDeviceLocked();
+    LOG_ALWAYS_FATAL_IF(!display);
+    setPowerModeInternal(display, currentDisplayPowerMode);
 
     // Reset the timing values to account for the period of the swapped in HWC
-    const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
-    const nsecs_t period = activeConfig->getVsyncPeriod();
-    mAnimFrameTracker.setDisplayRefreshPeriod(period);
+    const nsecs_t vsyncPeriod = getVsyncPeriod();
+    mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
+
+    // The present fences returned from vr_hwc are not an accurate
+    // representation of vsync times.
+    if (mUseScheduler) {
+        mScheduler->setIgnorePresentFences(getHwComposer().isUsingVrComposer() ||
+                                           !hasSyncFramework);
+    } else {
+        mPrimaryDispSync->setIgnorePresentFences(getHwComposer().isUsingVrComposer() ||
+                                                 !hasSyncFramework);
+    }
 
     // Use phase of 0 since phase is not known.
     // Use latency of 0, which will snap to the ideal latency.
-    setCompositorTimingSnapped(0, period, 0);
+    DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
+    setCompositorTimingSnapped(stats, 0);
 
-    android_atomic_or(1, &mRepaintEverything);
+    resyncToHardwareVsync(false, vsyncPeriod);
+
+    mRepaintEverything = true;
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
@@ -1543,13 +1629,18 @@
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
+            if (mUseScheduler) {
+                // This call is made each time SF wakes up and creates a new frame.
+                mScheduler->incrementFrameCounter();
+            }
             bool frameMissed = !mHadClientComposition &&
                     mPreviousPresentFence != Fence::NO_FENCE &&
                     (mPreviousPresentFence->getSignalTime() ==
                             Fence::SIGNAL_TIME_PENDING);
+            mFrameMissedCount += frameMissed;
             ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
             if (frameMissed) {
-                mTimeStats.incrementMissedFrames();
+                mTimeStats->incrementMissedFrames();
                 if (mPropagateBackpressure) {
                     signalLayerUpdate();
                     break;
@@ -1581,16 +1672,23 @@
 
 bool SurfaceFlinger::handleMessageTransaction() {
     uint32_t transactionFlags = peekTransactionFlags();
+
+    // Apply any ready transactions in the queues if there are still transactions that have not been
+    // applied, wake up during the next vsync period and check again
+    bool transactionNeeded = false;
+    if (!flushTransactionQueues()) {
+        transactionNeeded = true;
+    }
+
     if (transactionFlags) {
         handleTransaction(transactionFlags);
-        return true;
     }
-    return false;
-}
 
-bool SurfaceFlinger::handleMessageInvalidate() {
-    ATRACE_CALL();
-    return handlePageFlip();
+    if (transactionNeeded) {
+        setTransactionFlags(eTransactionNeeded);
+    }
+
+    return transactionFlags;
 }
 
 void SurfaceFlinger::handleMessageRefresh() {
@@ -1598,74 +1696,196 @@
 
     mRefreshPending = false;
 
-    nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
-    preComposition(refreshStartTime);
+    const bool repaintEverything = mRepaintEverything.exchange(false);
+    preComposition();
     rebuildLayerStacks();
-    setUpHWComposer();
-    doDebugFlashRegions();
+    calculateWorkingSet();
+    for (const auto& [token, display] : mDisplays) {
+        beginFrame(display);
+        prepareFrame(display);
+        doDebugFlashRegions(display, repaintEverything);
+        doComposition(display, repaintEverything);
+    }
+
     doTracing("handleRefresh");
     logLayerStats();
-    doComposition();
-    postComposition(refreshStartTime);
 
-    mPreviousPresentFence = getBE().mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
+    postFrame();
+    postComposition();
 
     mHadClientComposition = false;
-    for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
-        const sp<DisplayDevice>& displayDevice = mDisplays[displayId];
-        mHadClientComposition = mHadClientComposition ||
-                getBE().mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
+    for (const auto& [token, displayDevice] : mDisplays) {
+        auto display = displayDevice->getCompositionDisplay();
+        const auto displayId = display->getId();
+        mHadClientComposition =
+                mHadClientComposition || getHwComposer().hasClientComposition(displayId);
     }
+
+    // Setup RenderEngine sync fences if native sync is supported.
+    if (getRenderEngine().useNativeFenceSync()) {
+        if (mHadClientComposition) {
+            base::unique_fd flushFence(getRenderEngine().flush());
+            ALOGE_IF(flushFence < 0, "Failed to flush RenderEngine!");
+            getBE().flushFence = new Fence(std::move(flushFence));
+        } else {
+            // Cleanup for hygiene.
+            getBE().flushFence = Fence::NO_FENCE;
+        }
+    }
+
     mVsyncModulator.onRefreshed(mHadClientComposition);
 
+    getBE().mEndOfFrameCompositionInfo = std::move(getBE().mCompositionInfo);
+    for (const auto& [token, display] : mDisplays) {
+        for (auto& compositionInfo : getBE().mEndOfFrameCompositionInfo[token]) {
+            compositionInfo.hwc.hwcLayer = nullptr;
+        }
+    }
+
     mLayersWithQueuedFrames.clear();
 }
 
-void SurfaceFlinger::doDebugFlashRegions()
-{
-    // is debugging enabled
-    if (CC_LIKELY(!mDebugRegion))
-        return;
 
-    const bool repaintEverything = mRepaintEverything;
-    for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
-        const sp<DisplayDevice>& hw(mDisplays[dpy]);
-        if (hw->isDisplayOn()) {
-            // transform the dirty region into this screen's coordinate space
-            const Region dirtyRegion(hw->getDirtyRegion(repaintEverything));
-            if (!dirtyRegion.isEmpty()) {
-                // redraw the whole screen
-                doComposeSurfaces(hw);
+bool SurfaceFlinger::handleMessageInvalidate() {
+    ATRACE_CALL();
+    return handlePageFlip();
+}
 
-                // and draw the dirty region
-                const int32_t height = hw->getHeight();
-                auto& engine(getRenderEngine());
-                engine.fillRegionWithColor(dirtyRegion, height, 1, 0, 1, 1);
+void SurfaceFlinger::calculateWorkingSet() {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
 
-                hw->swapBuffers(getHwComposer());
+    // build the h/w work list
+    if (CC_UNLIKELY(mGeometryInvalid)) {
+        mGeometryInvalid = false;
+        for (const auto& [token, displayDevice] : mDisplays) {
+            auto display = displayDevice->getCompositionDisplay();
+            const auto displayId = display->getId();
+            if (!displayId) {
+                continue;
+            }
+
+            const Vector<sp<Layer>>& currentLayers = displayDevice->getVisibleLayersSortedByZ();
+            for (size_t i = 0; i < currentLayers.size(); i++) {
+                const auto& layer = currentLayers[i];
+
+                if (!layer->hasHwcLayer(*displayId)) {
+                    if (!layer->createHwcLayer(&getHwComposer(), *displayId)) {
+                        layer->forceClientComposition(*displayId);
+                        continue;
+                    }
+                }
+
+                layer->setGeometry(displayDevice, i);
+                if (mDebugDisableHWC || mDebugRegion) {
+                    layer->forceClientComposition(*displayId);
+                }
             }
         }
     }
 
-    postFramebuffer();
+    // Set the per-frame data
+    for (const auto& [token, displayDevice] : mDisplays) {
+        auto display = displayDevice->getCompositionDisplay();
+        const auto displayId = display->getId();
+        if (!displayId) {
+            continue;
+        }
+        auto* profile = display->getDisplayColorProfile();
+
+        if (mDrawingState.colorMatrixChanged) {
+            display->setColorTransform(mDrawingState.colorMatrix);
+        }
+        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
+            if (layer->isHdrY410()) {
+                layer->forceClientComposition(*displayId);
+            } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
+                        layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
+                       !profile->hasHDR10Support()) {
+                layer->forceClientComposition(*displayId);
+            } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
+                        layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
+                       !profile->hasHLGSupport()) {
+                layer->forceClientComposition(*displayId);
+            }
+
+            // TODO(b/111562338) remove when composer 2.3 is shipped.
+            if (layer->hasColorTransform()) {
+                layer->forceClientComposition(*displayId);
+            }
+
+            if (layer->getRoundedCornerState().radius > 0.0f) {
+                layer->forceClientComposition(*displayId);
+            }
+
+            if (layer->getForceClientComposition(*displayId)) {
+                ALOGV("[%s] Requesting Client composition", layer->getName().string());
+                layer->setCompositionType(*displayId, HWC2::Composition::Client);
+                continue;
+            }
+
+            const auto& displayState = display->getState();
+            layer->setPerFrameData(*displayId, displayState.transform, displayState.viewport,
+                                   displayDevice->getSupportedPerFrameMetadata());
+        }
+
+        if (useColorManagement) {
+            ColorMode  colorMode;
+            Dataspace dataSpace;
+            RenderIntent renderIntent;
+            pickColorMode(displayDevice, &colorMode, &dataSpace, &renderIntent);
+            display->setColorMode(colorMode, dataSpace, renderIntent);
+        }
+    }
+
+    mDrawingState.colorMatrixChanged = false;
+
+    for (const auto& [token, display] : mDisplays) {
+        for (auto& layer : display->getVisibleLayersSortedByZ()) {
+            const auto displayId = display->getId();
+            layer->getBE().compositionInfo.compositionType = layer->getCompositionType(displayId);
+
+            if (displayId) {
+                if (!layer->setHwcLayer(*displayId)) {
+                    ALOGV("Need to create HWCLayer for %s", layer->getName().string());
+                }
+                layer->getBE().compositionInfo.hwc.displayId = *displayId;
+            }
+
+            getBE().mCompositionInfo[token].push_back(layer->getBE().compositionInfo);
+            layer->getBE().compositionInfo.hwc.hwcLayer = nullptr;
+        }
+    }
+}
+
+void SurfaceFlinger::doDebugFlashRegions(const sp<DisplayDevice>& displayDevice,
+                                         bool repaintEverything) {
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+
+    // is debugging enabled
+    if (CC_LIKELY(!mDebugRegion))
+        return;
+
+    if (displayState.isEnabled) {
+        // transform the dirty region into this screen's coordinate space
+        const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
+        if (!dirtyRegion.isEmpty()) {
+            base::unique_fd readyFence;
+            // redraw the whole screen
+            doComposeSurfaces(displayDevice, dirtyRegion, &readyFence);
+
+            display->getRenderSurface()->queueBuffer(std::move(readyFence));
+        }
+    }
+
+    postFramebuffer(displayDevice);
 
     if (mDebugRegion > 1) {
         usleep(mDebugRegion * 1000);
     }
 
-    for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
-        auto& displayDevice = mDisplays[displayId];
-        if (!displayDevice->isDisplayOn()) {
-            continue;
-        }
-
-        status_t result = displayDevice->prepareFrame(*getBE().mHwc);
-        ALOGE_IF(result != NO_ERROR,
-                 "prepareFrame for display %zd failed:"
-                 " %d (%s)",
-                 displayId, result, strerror(-result));
-    }
+    prepareFrame(displayDevice);
 }
 
 void SurfaceFlinger::doTracing(const char* where) {
@@ -1679,30 +1899,27 @@
 void SurfaceFlinger::logLayerStats() {
     ATRACE_CALL();
     if (CC_UNLIKELY(mLayerStats.isEnabled())) {
-        int32_t hwcId = -1;
-        for (size_t dpy = 0; dpy < mDisplays.size(); ++dpy) {
-            const sp<const DisplayDevice>& displayDevice(mDisplays[dpy]);
-            if (displayDevice->isPrimary()) {
-                hwcId = displayDevice->getHwcDisplayId();
-                break;
+        for (const auto& [token, display] : mDisplays) {
+            if (display->isPrimary()) {
+                mLayerStats.logLayerStats(dumpVisibleLayersProtoInfo(*display));
+                return;
             }
         }
-        if (hwcId < 0) {
-            ALOGE("LayerStats: Hmmm, no primary display?");
-            return;
-        }
-        mLayerStats.logLayerStats(dumpVisibleLayersProtoInfo(hwcId));
+
+        ALOGE("logLayerStats: no primary display");
     }
 }
 
-void SurfaceFlinger::preComposition(nsecs_t refreshStartTime)
+void SurfaceFlinger::preComposition()
 {
     ATRACE_CALL();
     ALOGV("preComposition");
 
+    mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
     bool needExtraInvalidate = false;
     mDrawingState.traverseInZOrder([&](Layer* layer) {
-        if (layer->onPreComposition(refreshStartTime)) {
+        if (layer->onPreComposition(mRefreshStartTime)) {
             needExtraInvalidate = true;
         }
     });
@@ -1712,9 +1929,8 @@
     }
 }
 
-void SurfaceFlinger::updateCompositorTiming(
-        nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime,
-        std::shared_ptr<FenceTime>& presentFenceTime) {
+void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
+                                            std::shared_ptr<FenceTime>& presentFenceTime) {
     // Update queue of past composite+present times and determine the
     // most recently known composite to present latency.
     getBE().mCompositePresentTimes.push({compositeTime, presentFenceTime});
@@ -1736,185 +1952,316 @@
         getBE().mCompositePresentTimes.pop();
     }
 
-    setCompositorTimingSnapped(
-            vsyncPhase, vsyncInterval, compositeToPresentLatency);
+    setCompositorTimingSnapped(stats, compositeToPresentLatency);
 }
 
-void SurfaceFlinger::setCompositorTimingSnapped(nsecs_t vsyncPhase,
-        nsecs_t vsyncInterval, nsecs_t compositeToPresentLatency) {
+void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats,
+                                                nsecs_t compositeToPresentLatency) {
     // Integer division and modulo round toward 0 not -inf, so we need to
     // treat negative and positive offsets differently.
-    nsecs_t idealLatency = (sfVsyncPhaseOffsetNs > 0) ?
-            (vsyncInterval - (sfVsyncPhaseOffsetNs % vsyncInterval)) :
-            ((-sfVsyncPhaseOffsetNs) % vsyncInterval);
+    nsecs_t idealLatency = (mPhaseOffsets->getCurrentSfOffset() > 0)
+            ? (stats.vsyncPeriod - (mPhaseOffsets->getCurrentSfOffset() % stats.vsyncPeriod))
+            : ((-mPhaseOffsets->getCurrentSfOffset()) % stats.vsyncPeriod);
 
-    // Just in case sfVsyncPhaseOffsetNs == -vsyncInterval.
+    // Just in case mPhaseOffsets->getCurrentSfOffset() == -vsyncInterval.
     if (idealLatency <= 0) {
-        idealLatency = vsyncInterval;
+        idealLatency = stats.vsyncPeriod;
     }
 
     // Snap the latency to a value that removes scheduling jitter from the
     // composition and present times, which often have >1ms of jitter.
     // Reducing jitter is important if an app attempts to extrapolate
     // something (such as user input) to an accurate diasplay time.
-    // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs
+    // Snapping also allows an app to precisely calculate mPhaseOffsets->getCurrentSfOffset()
     // with (presentLatency % interval).
-    nsecs_t bias = vsyncInterval / 2;
-    int64_t extraVsyncs =
-            (compositeToPresentLatency - idealLatency + bias) / vsyncInterval;
-    nsecs_t snappedCompositeToPresentLatency = (extraVsyncs > 0) ?
-            idealLatency + (extraVsyncs * vsyncInterval) : idealLatency;
+    nsecs_t bias = stats.vsyncPeriod / 2;
+    int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
+    nsecs_t snappedCompositeToPresentLatency =
+            (extraVsyncs > 0) ? idealLatency + (extraVsyncs * stats.vsyncPeriod) : idealLatency;
 
     std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
-    getBE().mCompositorTiming.deadline = vsyncPhase - idealLatency;
-    getBE().mCompositorTiming.interval = vsyncInterval;
+    getBE().mCompositorTiming.deadline = stats.vsyncTime - idealLatency;
+    getBE().mCompositorTiming.interval = stats.vsyncPeriod;
     getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
 }
 
-void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+// debug patch for b/119477596 - add stack guards to catch stack
+// corruptions and disable clang optimizations.
+// The code below is temporary and planned to be removed once stack
+// corruptions are found.
+#pragma clang optimize off
+class StackGuard {
+public:
+    StackGuard(const char* name, const char* func, int line) {
+        guarders.reserve(MIN_CAPACITY);
+        guarders.push_back({this, name, func, line});
+        validate();
+    }
+    ~StackGuard() {
+        for (auto i = guarders.end() - 1; i >= guarders.begin(); --i) {
+            if (i->guard == this) {
+                guarders.erase(i);
+                break;
+            }
+        }
+    }
+
+    static void validate() {
+        for (const auto& guard : guarders) {
+            if (guard.guard->cookie != COOKIE_VALUE) {
+                ALOGE("%s:%d: Stack corruption detected at %s", guard.func, guard.line, guard.name);
+                CallStack stack(LOG_TAG);
+                abort();
+            }
+        }
+    }
+
+private:
+    uint64_t cookie = COOKIE_VALUE;
+    static constexpr uint64_t COOKIE_VALUE = 0xc0febebedeadbeef;
+    static constexpr size_t MIN_CAPACITY = 16;
+
+    struct GuarderElement {
+        StackGuard* guard;
+        const char* name;
+        const char* func;
+        int line;
+    };
+
+    static std::vector<GuarderElement> guarders;
+};
+std::vector<StackGuard::GuarderElement> StackGuard::guarders;
+
+#define DEFINE_STACK_GUARD(__n) StackGuard __n##StackGuard(#__n, __FUNCTION__, __LINE__);
+
+#define ASSERT_ON_STACK_GUARD() StackGuard::validate();
+void SurfaceFlinger::postComposition()
 {
+    DEFINE_STACK_GUARD(begin);
     ATRACE_CALL();
     ALOGV("postComposition");
 
     // Release any buffers which were replaced this frame
     nsecs_t dequeueReadyTime = systemTime();
+    DEFINE_STACK_GUARD(dequeueReadyTime);
     for (auto& layer : mLayersWithQueuedFrames) {
         layer->releasePendingBuffer(dequeueReadyTime);
     }
+    ASSERT_ON_STACK_GUARD();
 
     // |mStateLock| not needed as we are on the main thread
-    const sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
+    const auto displayDevice = getDefaultDisplayDeviceLocked();
+    DEFINE_STACK_GUARD(displayDevice);
 
     getBE().mGlCompositionDoneTimeline.updateSignalTimes();
     std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
-    if (hw && getBE().mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) {
+    DEFINE_STACK_GUARD(glCompositionDoneFenceTime);
+
+    if (displayDevice && getHwComposer().hasClientComposition(displayDevice->getId())) {
         glCompositionDoneFenceTime =
-                std::make_shared<FenceTime>(hw->getClientTargetAcquireFence());
+                std::make_shared<FenceTime>(displayDevice->getCompositionDisplay()
+                                                    ->getRenderSurface()
+                                                    ->getClientTargetAcquireFence());
         getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
     } else {
         glCompositionDoneFenceTime = FenceTime::NO_FENCE;
     }
 
+    ASSERT_ON_STACK_GUARD();
+
     getBE().mDisplayTimeline.updateSignalTimes();
-    sp<Fence> presentFence = getBE().mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
-    auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
+    mPreviousPresentFence = displayDevice ? getHwComposer().getPresentFence(*displayDevice->getId())
+                                          : Fence::NO_FENCE;
+    auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFence);
+    DEFINE_STACK_GUARD(presentFenceTime);
     getBE().mDisplayTimeline.push(presentFenceTime);
 
-    nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0);
-    nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod();
+    DisplayStatInfo stats;
+    DEFINE_STACK_GUARD(stats);
+    if (mUseScheduler) {
+        mScheduler->getDisplayStatInfo(&stats);
+    } else {
+        stats.vsyncTime = mPrimaryDispSync->computeNextRefresh(0);
+        stats.vsyncPeriod = mPrimaryDispSync->getPeriod();
+    }
 
-    // We use the refreshStartTime which might be sampled a little later than
+    ASSERT_ON_STACK_GUARD();
+
+    // We use the mRefreshStartTime which might be sampled a little later than
     // when we started doing work for this frame, but that should be okay
     // since updateCompositorTiming has snapping logic.
-    updateCompositorTiming(
-        vsyncPhase, vsyncInterval, refreshStartTime, presentFenceTime);
+    updateCompositorTiming(stats, mRefreshStartTime, presentFenceTime);
     CompositorTiming compositorTiming;
+    DEFINE_STACK_GUARD(compositorTiming);
+
     {
         std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
+        DEFINE_STACK_GUARD(lock);
         compositorTiming = getBE().mCompositorTiming;
+
+        ASSERT_ON_STACK_GUARD();
     }
 
     mDrawingState.traverseInZOrder([&](Layer* layer) {
-        bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime,
-                presentFenceTime, compositorTiming);
+        bool frameLatched =
+                layer->onPostComposition(displayDevice->getId(), glCompositionDoneFenceTime,
+                                         presentFenceTime, compositorTiming);
+        DEFINE_STACK_GUARD(frameLatched);
         if (frameLatched) {
             recordBufferingStats(layer->getName().string(),
                     layer->getOccupancyHistory(false));
         }
+        ASSERT_ON_STACK_GUARD();
     });
 
     if (presentFenceTime->isValid()) {
-        if (mPrimaryDispSync.addPresentFence(presentFenceTime)) {
-            enableHardwareVsync();
+        ASSERT_ON_STACK_GUARD();
+        if (mUseScheduler) {
+            mScheduler->addPresentFence(presentFenceTime);
+            ASSERT_ON_STACK_GUARD();
         } else {
-            disableHardwareVsync(false);
+            if (mPrimaryDispSync->addPresentFence(presentFenceTime)) {
+                enableHardwareVsync();
+            } else {
+                disableHardwareVsync(false);
+            }
+            ASSERT_ON_STACK_GUARD();
         }
     }
 
     if (!hasSyncFramework) {
-        if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) && hw->isDisplayOn()) {
-            enableHardwareVsync();
+        if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
+            displayDevice->isPoweredOn()) {
+            if (mUseScheduler) {
+                mScheduler->enableHardwareVsync();
+            } else {
+                enableHardwareVsync();
+            }
         }
     }
 
+    ASSERT_ON_STACK_GUARD();
+
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
 
         if (presentFenceTime->isValid()) {
             mAnimFrameTracker.setActualPresentFence(
                     std::move(presentFenceTime));
-        } else if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) {
+
+            ASSERT_ON_STACK_GUARD();
+        } else if (displayDevice && getHwComposer().isConnected(*displayDevice->getId())) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
-            nsecs_t presentTime =
-                    getBE().mHwc->getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
+            const nsecs_t presentTime =
+                    getHwComposer().getRefreshTimestamp(*displayDevice->getId());
+            DEFINE_STACK_GUARD(presentTime);
+
             mAnimFrameTracker.setActualPresentTime(presentTime);
+            ASSERT_ON_STACK_GUARD();
         }
         mAnimFrameTracker.advanceFrame();
     }
 
-    mTimeStats.incrementTotalFrames();
+    ASSERT_ON_STACK_GUARD();
+
+    mTimeStats->incrementTotalFrames();
     if (mHadClientComposition) {
-        mTimeStats.incrementClientCompositionFrames();
+        mTimeStats->incrementClientCompositionFrames();
     }
 
-    if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) &&
-            hw->getPowerMode() == HWC_POWER_MODE_OFF) {
+    ASSERT_ON_STACK_GUARD();
+
+    mTimeStats->setPresentFenceGlobal(presentFenceTime);
+
+    ASSERT_ON_STACK_GUARD();
+
+    if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
+        !displayDevice->isPoweredOn()) {
         return;
     }
 
     nsecs_t currentTime = systemTime();
+    DEFINE_STACK_GUARD(currentTime);
     if (mHasPoweredOff) {
         mHasPoweredOff = false;
     } else {
         nsecs_t elapsedTime = currentTime - getBE().mLastSwapTime;
-        size_t numPeriods = static_cast<size_t>(elapsedTime / vsyncInterval);
+        DEFINE_STACK_GUARD(elapsedTime);
+        size_t numPeriods = static_cast<size_t>(elapsedTime / stats.vsyncPeriod);
+        DEFINE_STACK_GUARD(numPeriods);
         if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) {
             getBE().mFrameBuckets[numPeriods] += elapsedTime;
         } else {
             getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] += elapsedTime;
         }
         getBE().mTotalTime += elapsedTime;
+
+        ASSERT_ON_STACK_GUARD();
     }
     getBE().mLastSwapTime = currentTime;
+    ASSERT_ON_STACK_GUARD();
 
     {
         std::lock_guard lock(mTexturePoolMutex);
+        DEFINE_STACK_GUARD(lock);
         const size_t refillCount = mTexturePoolSize - mTexturePool.size();
+        DEFINE_STACK_GUARD(refillCount);
         if (refillCount > 0) {
             const size_t offset = mTexturePool.size();
             mTexturePool.resize(mTexturePoolSize);
             getRenderEngine().genTextures(refillCount, mTexturePool.data() + offset);
             ATRACE_INT("TexturePoolSize", mTexturePool.size());
         }
+        ASSERT_ON_STACK_GUARD();
     }
+
+    mTransactionCompletedThread.addPresentFence(mPreviousPresentFence);
+    mTransactionCompletedThread.sendCallbacks();
+
+    ASSERT_ON_STACK_GUARD();
 }
+#pragma clang optimize on // b/119477596
 
 void SurfaceFlinger::rebuildLayerStacks() {
     ATRACE_CALL();
     ALOGV("rebuildLayerStacks");
 
+    // We need to clear these out now as these may be holding on to a
+    // HWC2::Layer reference at the same time as the LayerBE::HWCInfo structure
+    // also holds a reference. When the set of visible layers is recomputed,
+    // some layers may be destroyed if the only thing keeping them alive was
+    // that list of visible layers associated with each display. The layer
+    // destruction code asserts that the HWC2::Layer is properly destroyed, but
+    // that doesn't happen if SurfaceFlingerBE::mCompositionInfo keeps it alive.
+    for (const auto& [token, display] : mDisplays) {
+        getBE().mCompositionInfo[token].clear();
+    }
+
     // rebuild the visible layer list per screen
     if (CC_UNLIKELY(mVisibleRegionsDirty)) {
         ATRACE_NAME("rebuildLayerStacks VR Dirty");
         mVisibleRegionsDirty = false;
         invalidateHwcGeometry();
 
-        for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
+        for (const auto& pair : mDisplays) {
+            const auto& displayDevice = pair.second;
+            auto display = displayDevice->getCompositionDisplay();
+            const auto& displayState = display->getState();
             Region opaqueRegion;
             Region dirtyRegion;
             Vector<sp<Layer>> layersSortedByZ;
             Vector<sp<Layer>> layersNeedingFences;
-            const sp<DisplayDevice>& displayDevice(mDisplays[dpy]);
-            const Transform& tr(displayDevice->getTransform());
-            const Rect bounds(displayDevice->getBounds());
-            if (displayDevice->isDisplayOn()) {
+            const ui::Transform& tr = displayState.transform;
+            const Rect bounds = displayState.bounds;
+            if (displayState.isEnabled) {
                 computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion);
 
                 mDrawingState.traverseInZOrder([&](Layer* layer) {
                     bool hwcLayerDestroyed = false;
-                    if (layer->belongsToDisplay(displayDevice->getLayerStack(),
-                                displayDevice->isPrimary())) {
+                    const auto displayId = displayDevice->getId();
+                    if (display->belongsInOutput(layer->getLayerStack(),
+                                                 layer->getPrimaryDisplayOnly())) {
                         Region drawRegion(tr.transform(
                                 layer->visibleNonTransparentRegion));
                         drawRegion.andSelf(bounds);
@@ -1923,15 +2270,13 @@
                         } else {
                             // Clear out the HWC layer if this layer was
                             // previously visible, but no longer is
-                            hwcLayerDestroyed = layer->destroyHwcLayer(
-                                    displayDevice->getHwcDisplayId());
+                            hwcLayerDestroyed = displayId && layer->destroyHwcLayer(*displayId);
                         }
                     } else {
-                        // WM changes displayDevice->layerStack upon sleep/awake.
+                        // WM changes display->layerStack upon sleep/awake.
                         // Here we make sure we delete the HWC layers even if
                         // WM changed their layer stack.
-                        hwcLayerDestroyed = layer->destroyHwcLayer(
-                                displayDevice->getHwcDisplayId());
+                        hwcLayerDestroyed = displayId && layer->destroyHwcLayer(*displayId);
                     }
 
                     // If a layer is not going to get a release fence because
@@ -1949,10 +2294,12 @@
             }
             displayDevice->setVisibleLayersSortedByZ(layersSortedByZ);
             displayDevice->setLayersNeedingFences(layersNeedingFences);
-            displayDevice->undefinedRegion.set(bounds);
-            displayDevice->undefinedRegion.subtractSelf(
-                    tr.transform(opaqueRegion));
-            displayDevice->dirtyRegion.orSelf(dirtyRegion);
+
+            Region undefinedRegion{bounds};
+            undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
+
+            display->editState().undefinedRegion = undefinedRegion;
+            display->editState().dirtyRegion.orSelf(dirtyRegion);
         }
     }
 }
@@ -1961,19 +2308,26 @@
 // can only be one of
 //  - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced)
 //  - Dataspace::DISPLAY_P3
+//  - Dataspace::DISPLAY_BT2020
 // The returned HDR data space is one of
 //  - Dataspace::UNKNOWN
 //  - Dataspace::BT2020_HLG
 //  - Dataspace::BT2020_PQ
-Dataspace SurfaceFlinger::getBestDataspace(
-    const sp<const DisplayDevice>& displayDevice, Dataspace* outHdrDataSpace) const {
-    Dataspace bestDataSpace = Dataspace::SRGB;
+Dataspace SurfaceFlinger::getBestDataspace(const sp<const DisplayDevice>& display,
+                                           Dataspace* outHdrDataSpace) const {
+    Dataspace bestDataSpace = Dataspace::V0_SRGB;
     *outHdrDataSpace = Dataspace::UNKNOWN;
 
-    for (const auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
+    for (const auto& layer : display->getVisibleLayersSortedByZ()) {
         switch (layer->getDataSpace()) {
             case Dataspace::V0_SCRGB:
             case Dataspace::V0_SCRGB_LINEAR:
+            case Dataspace::BT2020:
+            case Dataspace::BT2020_ITU:
+            case Dataspace::BT2020_LINEAR:
+            case Dataspace::DISPLAY_BT2020:
+                bestDataSpace = Dataspace::DISPLAY_BT2020;
+                break;
             case Dataspace::DISPLAY_P3:
                 bestDataSpace = Dataspace::DISPLAY_P3;
                 break;
@@ -1998,9 +2352,8 @@
 }
 
 // Pick the ColorMode / Dataspace for the display device.
-void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& displayDevice,
-                                   ColorMode* outMode, Dataspace* outDataSpace,
-                                   RenderIntent* outRenderIntent) const {
+void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& display, ColorMode* outMode,
+                                   Dataspace* outDataSpace, RenderIntent* outRenderIntent) const {
     if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) {
         *outMode = ColorMode::NATIVE;
         *outDataSpace = Dataspace::UNKNOWN;
@@ -2009,11 +2362,13 @@
     }
 
     Dataspace hdrDataSpace;
-    Dataspace bestDataSpace = getBestDataspace(displayDevice, &hdrDataSpace);
+    Dataspace bestDataSpace = getBestDataspace(display, &hdrDataSpace);
+
+    auto* profile = display->getCompositionDisplay()->getDisplayColorProfile();
 
     // respect hdrDataSpace only when there is no legacy HDR support
-    const bool isHdr = hdrDataSpace != Dataspace::UNKNOWN &&
-        !displayDevice->hasLegacyHdrSupport(hdrDataSpace);
+    const bool isHdr =
+            hdrDataSpace != Dataspace::UNKNOWN && !profile->hasLegacyHdrSupport(hdrDataSpace);
     if (isHdr) {
         bestDataSpace = hdrDataSpace;
     }
@@ -2032,177 +2387,111 @@
             break;
     }
 
-    displayDevice->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
+    profile->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
 }
 
-void SurfaceFlinger::setUpHWComposer() {
-    ATRACE_CALL();
-    ALOGV("setUpHWComposer");
+void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& displayDevice) {
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
 
-    for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
-        bool dirty = !mDisplays[dpy]->getDirtyRegion(mRepaintEverything).isEmpty();
-        bool empty = mDisplays[dpy]->getVisibleLayersSortedByZ().size() == 0;
-        bool wasEmpty = !mDisplays[dpy]->lastCompositionHadVisibleLayers;
+    bool dirty = !display->getDirtyRegion(false).isEmpty();
+    bool empty = displayDevice->getVisibleLayersSortedByZ().size() == 0;
+    bool wasEmpty = !displayState.lastCompositionHadVisibleLayers;
 
-        // If nothing has changed (!dirty), don't recompose.
-        // If something changed, but we don't currently have any visible layers,
-        //   and didn't when we last did a composition, then skip it this time.
-        // The second rule does two things:
-        // - When all layers are removed from a display, we'll emit one black
-        //   frame, then nothing more until we get new layers.
-        // - When a display is created with a private layer stack, we won't
-        //   emit any black frames until a layer is added to the layer stack.
-        bool mustRecompose = dirty && !(empty && wasEmpty);
+    // If nothing has changed (!dirty), don't recompose.
+    // If something changed, but we don't currently have any visible layers,
+    //   and didn't when we last did a composition, then skip it this time.
+    // The second rule does two things:
+    // - When all layers are removed from a display, we'll emit one black
+    //   frame, then nothing more until we get new layers.
+    // - When a display is created with a private layer stack, we won't
+    //   emit any black frames until a layer is added to the layer stack.
+    bool mustRecompose = dirty && !(empty && wasEmpty);
 
-        ALOGV_IF(mDisplays[dpy]->getDisplayType() == DisplayDevice::DISPLAY_VIRTUAL,
-                "dpy[%zu]: %s composition (%sdirty %sempty %swasEmpty)", dpy,
-                mustRecompose ? "doing" : "skipping",
-                dirty ? "+" : "-",
-                empty ? "+" : "-",
-                wasEmpty ? "+" : "-");
+    const char flagPrefix[] = {'-', '+'};
+    static_cast<void>(flagPrefix);
+    ALOGV_IF(displayDevice->isVirtual(), "%s: %s composition for %s (%cdirty %cempty %cwasEmpty)",
+             __FUNCTION__, mustRecompose ? "doing" : "skipping",
+             displayDevice->getDebugName().c_str(), flagPrefix[dirty], flagPrefix[empty],
+             flagPrefix[wasEmpty]);
 
-        mDisplays[dpy]->beginFrame(mustRecompose);
+    display->getRenderSurface()->beginFrame(mustRecompose);
 
-        if (mustRecompose) {
-            mDisplays[dpy]->lastCompositionHadVisibleLayers = !empty;
-        }
-    }
-
-    // build the h/w work list
-    if (CC_UNLIKELY(mGeometryInvalid)) {
-        mGeometryInvalid = false;
-        for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
-            sp<const DisplayDevice> displayDevice(mDisplays[dpy]);
-            const auto hwcId = displayDevice->getHwcDisplayId();
-            if (hwcId >= 0) {
-                const Vector<sp<Layer>>& currentLayers(
-                        displayDevice->getVisibleLayersSortedByZ());
-                for (size_t i = 0; i < currentLayers.size(); i++) {
-                    const auto& layer = currentLayers[i];
-                    if (!layer->hasHwcLayer(hwcId)) {
-                        if (!layer->createHwcLayer(getBE().mHwc.get(), hwcId)) {
-                            layer->forceClientComposition(hwcId);
-                            continue;
-                        }
-                    }
-
-                    layer->setGeometry(displayDevice, i);
-                    if (mDebugDisableHWC || mDebugRegion) {
-                        layer->forceClientComposition(hwcId);
-                    }
-                }
-            }
-        }
-    }
-
-    // Set the per-frame data
-    for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
-        auto& displayDevice = mDisplays[displayId];
-        const auto hwcId = displayDevice->getHwcDisplayId();
-
-        if (hwcId < 0) {
-            continue;
-        }
-        if (mDrawingState.colorMatrixChanged) {
-            displayDevice->setColorTransform(mDrawingState.colorMatrix);
-            status_t result = getBE().mHwc->setColorTransform(hwcId, mDrawingState.colorMatrix);
-            ALOGE_IF(result != NO_ERROR, "Failed to set color transform on "
-                    "display %zd: %d", displayId, result);
-        }
-        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-            if (layer->isHdrY410()) {
-                layer->forceClientComposition(hwcId);
-            } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
-                        layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
-                    !displayDevice->hasHDR10Support()) {
-                layer->forceClientComposition(hwcId);
-            } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
-                        layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
-                    !displayDevice->hasHLGSupport()) {
-                layer->forceClientComposition(hwcId);
-            }
-
-            if (layer->getForceClientComposition(hwcId)) {
-                ALOGV("[%s] Requesting Client composition", layer->getName().string());
-                layer->setCompositionType(hwcId, HWC2::Composition::Client);
-                continue;
-            }
-
-            layer->setPerFrameData(displayDevice);
-        }
-
-        if (hasWideColorDisplay) {
-            ColorMode colorMode;
-            Dataspace dataSpace;
-            RenderIntent renderIntent;
-            pickColorMode(displayDevice, &colorMode, &dataSpace, &renderIntent);
-            setActiveColorModeInternal(displayDevice, colorMode, dataSpace, renderIntent);
-        }
-    }
-
-    mDrawingState.colorMatrixChanged = false;
-
-    for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
-        auto& displayDevice = mDisplays[displayId];
-        if (!displayDevice->isDisplayOn()) {
-            continue;
-        }
-
-        status_t result = displayDevice->prepareFrame(*getBE().mHwc);
-        ALOGE_IF(result != NO_ERROR, "prepareFrame for display %zd failed:"
-                " %d (%s)", displayId, result, strerror(-result));
+    if (mustRecompose) {
+        display->editState().lastCompositionHadVisibleLayers = !empty;
     }
 }
 
-void SurfaceFlinger::doComposition() {
+void SurfaceFlinger::prepareFrame(const sp<DisplayDevice>& displayDevice) {
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+
+    if (!displayState.isEnabled) {
+        return;
+    }
+
+    status_t result = display->getRenderSurface()->prepareFrame(
+            getBE().mCompositionInfo[displayDevice->getDisplayToken()]);
+    ALOGE_IF(result != NO_ERROR, "prepareFrame failed for %s: %d (%s)",
+             displayDevice->getDebugName().c_str(), result, strerror(-result));
+}
+
+void SurfaceFlinger::doComposition(const sp<DisplayDevice>& displayDevice, bool repaintEverything) {
     ATRACE_CALL();
     ALOGV("doComposition");
 
-    const bool repaintEverything = android_atomic_and(0, &mRepaintEverything);
-    for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
-        const sp<DisplayDevice>& hw(mDisplays[dpy]);
-        if (hw->isDisplayOn()) {
-            // transform the dirty region into this screen's coordinate space
-            const Region dirtyRegion(hw->getDirtyRegion(repaintEverything));
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
 
-            // repaint the framebuffer (if needed)
-            doDisplayComposition(hw, dirtyRegion);
+    if (displayState.isEnabled) {
+        // transform the dirty region into this screen's coordinate space
+        const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
 
-            hw->dirtyRegion.clear();
-            hw->flip();
-        }
+        // repaint the framebuffer (if needed)
+        doDisplayComposition(displayDevice, dirtyRegion);
+
+        display->editState().dirtyRegion.clear();
+        display->getRenderSurface()->flip();
     }
-    postFramebuffer();
+    postFramebuffer(displayDevice);
 }
 
-void SurfaceFlinger::postFramebuffer()
+void SurfaceFlinger::postFrame()
 {
+    // |mStateLock| not needed as we are on the main thread
+    const auto display = getDefaultDisplayDeviceLocked();
+    if (display && getHwComposer().isConnected(*display->getId())) {
+        uint32_t flipCount = display->getPageFlipCount();
+        if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
+            logFrameStats();
+        }
+    }
+}
+
+void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& displayDevice) {
     ATRACE_CALL();
     ALOGV("postFramebuffer");
 
-    const nsecs_t now = systemTime();
-    mDebugInSwapBuffers = now;
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+    const auto displayId = display->getId();
 
-    for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
-        auto& displayDevice = mDisplays[displayId];
-        if (!displayDevice->isDisplayOn()) {
-            continue;
+    mPostFramebufferTime = systemTime();
+
+    if (displayState.isEnabled) {
+        if (displayId) {
+            getHwComposer().presentAndGetReleaseFences(*displayId);
         }
-        const auto hwcId = displayDevice->getHwcDisplayId();
-        if (hwcId >= 0) {
-            getBE().mHwc->presentAndGetReleaseFences(hwcId);
-        }
-        displayDevice->onSwapBuffersCompleted();
-        displayDevice->makeCurrent();
+        display->getRenderSurface()->onPresentDisplayCompleted();
         for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
             sp<Fence> releaseFence = Fence::NO_FENCE;
 
             // The layer buffer from the previous frame (if any) is released
             // by HWC only when the release fence from this frame (if any) is
             // signaled.  Always get the release fence from HWC first.
-            auto hwcLayer = layer->getHwcLayer(hwcId);
-            if (hwcId >= 0) {
-                releaseFence = getBE().mHwc->getLayerReleaseFence(hwcId, hwcLayer);
+            if (displayId && layer->hasHwcLayer(*displayId)) {
+                releaseFence = getHwComposer().getLayerReleaseFence(*displayId,
+                                                                    layer->getHwcLayer(*displayId));
             }
 
             // If the layer was client composited in the previous frame, we
@@ -2210,37 +2499,28 @@
             // Since we do not track that, always merge with the current
             // client target acquire fence when it is available, even though
             // this is suboptimal.
-            if (layer->getCompositionType(hwcId) == HWC2::Composition::Client) {
-                releaseFence = Fence::merge("LayerRelease", releaseFence,
-                        displayDevice->getClientTargetAcquireFence());
+            if (layer->getCompositionType(displayId) == HWC2::Composition::Client) {
+                releaseFence =
+                        Fence::merge("LayerRelease", releaseFence,
+                                     display->getRenderSurface()->getClientTargetAcquireFence());
             }
 
-            layer->onLayerDisplayed(releaseFence);
+            layer->getBE().onLayerDisplayed(releaseFence);
         }
 
         // We've got a list of layers needing fences, that are disjoint with
-        // displayDevice->getVisibleLayersSortedByZ.  The best we can do is to
+        // display->getVisibleLayersSortedByZ.  The best we can do is to
         // supply them with the present fence.
         if (!displayDevice->getLayersNeedingFences().isEmpty()) {
-            sp<Fence> presentFence = getBE().mHwc->getPresentFence(hwcId);
+            sp<Fence> presentFence =
+                    displayId ? getHwComposer().getPresentFence(*displayId) : Fence::NO_FENCE;
             for (auto& layer : displayDevice->getLayersNeedingFences()) {
-                layer->onLayerDisplayed(presentFence);
+                layer->getBE().onLayerDisplayed(presentFence);
             }
         }
 
-        if (hwcId >= 0) {
-            getBE().mHwc->clearReleaseFences(hwcId);
-        }
-    }
-
-    mLastSwapBufferTime = systemTime() - now;
-    mDebugInSwapBuffers = 0;
-
-    // |mStateLock| not needed as we are on the main thread
-    if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) {
-        uint32_t flipCount = getDefaultDisplayDeviceLocked()->getPageFlipCount();
-        if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
-            logFrameStats();
+        if (displayId) {
+            getHwComposer().clearReleaseFences(*displayId);
         }
     }
 }
@@ -2275,68 +2555,36 @@
     // here the transaction has been committed
 }
 
-DisplayDevice::DisplayType SurfaceFlinger::determineDisplayType(hwc2_display_t display,
-                                                                HWC2::Connection connection) const {
-    // Figure out whether the event is for the primary display or an
-    // external display by matching the Hwc display id against one for a
-    // connected display. If we did not find a match, we then check what
-    // displays are not already connected to determine the type. If we don't
-    // have a connected primary display, we assume the new display is meant to
-    // be the primary display, and then if we don't have an external display,
-    // we assume it is that.
-    const auto primaryDisplayId =
-            getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_PRIMARY);
-    const auto externalDisplayId =
-            getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_EXTERNAL);
-    if (primaryDisplayId && primaryDisplayId == display) {
-        return DisplayDevice::DISPLAY_PRIMARY;
-    } else if (externalDisplayId && externalDisplayId == display) {
-        return  DisplayDevice::DISPLAY_EXTERNAL;
-    } else if (connection == HWC2::Connection::Connected && !primaryDisplayId) {
-        return DisplayDevice::DISPLAY_PRIMARY;
-    } else if (connection == HWC2::Connection::Connected && !externalDisplayId) {
-        return DisplayDevice::DISPLAY_EXTERNAL;
-    }
-
-    return DisplayDevice::DISPLAY_ID_INVALID;
-}
-
 void SurfaceFlinger::processDisplayHotplugEventsLocked() {
     for (const auto& event : mPendingHotplugEvents) {
-        auto displayType = determineDisplayType(event.display, event.connection);
-        if (displayType == DisplayDevice::DISPLAY_ID_INVALID) {
-            ALOGW("Unable to determine the display type for display %" PRIu64, event.display);
+        const std::optional<DisplayIdentificationInfo> info =
+                getHwComposer().onHotplug(event.hwcDisplayId, event.connection);
+
+        if (!info) {
             continue;
         }
 
-        if (getBE().mHwc->isUsingVrComposer() && displayType == DisplayDevice::DISPLAY_EXTERNAL) {
-            ALOGE("External displays are not supported by the vr hardware composer.");
-            continue;
-        }
-
-        getBE().mHwc->onHotplug(event.display, displayType, event.connection);
-
         if (event.connection == HWC2::Connection::Connected) {
-            if (!mBuiltinDisplays[displayType].get()) {
-                ALOGV("Creating built in display %d", displayType);
-                mBuiltinDisplays[displayType] = new BBinder();
-                // All non-virtual displays are currently considered secure.
-                DisplayDeviceState info(displayType, true);
-                info.displayName = displayType == DisplayDevice::DISPLAY_PRIMARY ?
-                        "Built-in Screen" : "External Screen";
-                mCurrentState.displays.add(mBuiltinDisplays[displayType], info);
-                mInterceptor->saveDisplayCreation(info);
+            if (!mPhysicalDisplayTokens.count(info->id)) {
+                ALOGV("Creating display %s", to_string(info->id).c_str());
+                mPhysicalDisplayTokens[info->id] = new BBinder();
+                DisplayDeviceState state;
+                state.displayId = info->id;
+                state.isSecure = true; // All physical displays are currently considered secure.
+                state.displayName = info->name;
+                mCurrentState.displays.add(mPhysicalDisplayTokens[info->id], state);
+                mInterceptor->saveDisplayCreation(state);
             }
         } else {
-            ALOGV("Removing built in display %d", displayType);
+            ALOGV("Removing display %s", to_string(info->id).c_str());
 
-            ssize_t idx = mCurrentState.displays.indexOfKey(mBuiltinDisplays[displayType]);
-            if (idx >= 0) {
-                const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx));
-                mInterceptor->saveDisplayDeletion(info.displayId);
-                mCurrentState.displays.removeItemsAt(idx);
+            ssize_t index = mCurrentState.displays.indexOfKey(mPhysicalDisplayTokens[info->id]);
+            if (index >= 0) {
+                const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
+                mInterceptor->saveDisplayDeletion(state.sequenceId);
+                mCurrentState.displays.removeItemsAt(index);
             }
-            mBuiltinDisplays[displayType].clear();
+            mPhysicalDisplayTokens.erase(info->id);
         }
 
         processDisplayChangesLocked();
@@ -2345,71 +2593,69 @@
     mPendingHotplugEvents.clear();
 }
 
-sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
-        const wp<IBinder>& display, int hwcId, const DisplayDeviceState& state,
-        const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer) {
-    bool hasWideColorGamut = false;
-    std::unordered_map<ColorMode, std::vector<RenderIntent>> hwcColorModes;
-    HdrCapabilities hdrCapabilities;
-    int32_t supportedPerFrameMetadata = 0;
+void SurfaceFlinger::dispatchDisplayHotplugEvent(EventThread::DisplayType displayType,
+                                                 bool connected) {
+    if (mUseScheduler) {
+        mScheduler->hotplugReceived(mAppConnectionHandle, displayType, connected);
+        mScheduler->hotplugReceived(mSfConnectionHandle, displayType, connected);
+    } else {
+        mEventThread->onHotplugReceived(displayType, connected);
+        mSFEventThread->onHotplugReceived(displayType, connected);
+    }
+}
 
-    if (hasWideColorDisplay && hwcId >= 0) {
-        std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId);
+sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
+        const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
+        const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& dispSurface,
+        const sp<IGraphicBufferProducer>& producer) {
+    DisplayDeviceCreationArgs creationArgs(this, displayToken, displayId);
+    creationArgs.sequenceId = state.sequenceId;
+    creationArgs.isVirtual = state.isVirtual();
+    creationArgs.isSecure = state.isSecure;
+    creationArgs.displaySurface = dispSurface;
+    creationArgs.hasWideColorGamut = false;
+    creationArgs.supportedPerFrameMetadata = 0;
+
+    const bool isInternalDisplay = displayId && displayId == getInternalDisplayId();
+    creationArgs.isPrimary = isInternalDisplay;
+
+    if (useColorManagement && displayId) {
+        std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
         for (ColorMode colorMode : modes) {
-            switch (colorMode) {
-                case ColorMode::DISPLAY_P3:
-                case ColorMode::ADOBE_RGB:
-                case ColorMode::DCI_P3:
-                    hasWideColorGamut = true;
-                    break;
-                default:
-                    break;
+            if (isWideColorMode(colorMode)) {
+                creationArgs.hasWideColorGamut = true;
             }
 
-            std::vector<RenderIntent> renderIntents = getHwComposer().getRenderIntents(hwcId,
-                                                                                       colorMode);
-            hwcColorModes.emplace(colorMode, renderIntents);
+            std::vector<RenderIntent> renderIntents =
+                    getHwComposer().getRenderIntents(*displayId, colorMode);
+            creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
         }
     }
 
-    if (hwcId >= 0) {
-        getHwComposer().getHdrCapabilities(hwcId, &hdrCapabilities);
-        supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(hwcId);
+    if (displayId) {
+        getHwComposer().getHdrCapabilities(*displayId, &creationArgs.hdrCapabilities);
+        creationArgs.supportedPerFrameMetadata =
+                getHwComposer().getSupportedPerFrameMetadata(*displayId);
     }
 
-    auto nativeWindowSurface = mCreateNativeWindowSurface(producer);
+    auto nativeWindowSurface = getFactory().createNativeWindowSurface(producer);
     auto nativeWindow = nativeWindowSurface->getNativeWindow();
-
-    /*
-     * Create our display's surface
-     */
-    std::unique_ptr<RE::Surface> renderSurface = getRenderEngine().createSurface();
-    renderSurface->setCritical(state.type == DisplayDevice::DISPLAY_PRIMARY);
-    renderSurface->setAsync(state.type >= DisplayDevice::DISPLAY_VIRTUAL);
-    renderSurface->setNativeWindow(nativeWindow.get());
-    const int displayWidth = renderSurface->queryWidth();
-    const int displayHeight = renderSurface->queryHeight();
+    creationArgs.nativeWindow = nativeWindow;
 
     // Make sure that composition can never be stalled by a virtual display
     // consumer that isn't processing buffers fast enough. We have to do this
-    // in two places:
-    // * Here, in case the display is composed entirely by HWC.
-    // * In makeCurrent(), using eglSwapInterval. Some EGL drivers set the
-    //   window's swap interval in eglMakeCurrent, so they'll override the
-    //   interval we set here.
-    if (state.type >= DisplayDevice::DISPLAY_VIRTUAL) {
+    // here, in case the display is composed entirely by HWC.
+    if (state.isVirtual()) {
         nativeWindow->setSwapInterval(nativeWindow.get(), 0);
     }
 
-    // virtual displays are always considered enabled
-    auto initialPowerMode = (state.type >= DisplayDevice::DISPLAY_VIRTUAL) ? HWC_POWER_MODE_NORMAL
-                                                                           : HWC_POWER_MODE_OFF;
+    creationArgs.displayInstallOrientation =
+            isInternalDisplay ? primaryDisplayOrientation : DisplayState::eOrientationDefault;
 
-    sp<DisplayDevice> hw =
-            new DisplayDevice(this, state.type, hwcId, state.isSecure, display, nativeWindow,
-                              dispSurface, std::move(renderSurface), displayWidth, displayHeight,
-                              hasWideColorGamut, hdrCapabilities,
-                              supportedPerFrameMetadata, hwcColorModes, initialPowerMode);
+    // virtual displays are always considered enabled
+    creationArgs.initialPowerMode = state.isVirtual() ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
+
+    sp<DisplayDevice> display = getFactory().createDisplayDevice(std::move(creationArgs));
 
     if (maxFrameBufferAcquiredBuffers >= 3) {
         nativeWindowSurface->preallocateBuffers();
@@ -2417,20 +2663,22 @@
 
     ColorMode defaultColorMode = ColorMode::NATIVE;
     Dataspace defaultDataSpace = Dataspace::UNKNOWN;
-    if (hasWideColorGamut) {
+    if (display->hasWideColorGamut()) {
         defaultColorMode = ColorMode::SRGB;
-        defaultDataSpace = Dataspace::SRGB;
+        defaultDataSpace = Dataspace::V0_SRGB;
     }
-    setActiveColorModeInternal(hw, defaultColorMode, defaultDataSpace,
-                               RenderIntent::COLORIMETRIC);
-    if (state.type < DisplayDevice::DISPLAY_VIRTUAL) {
-        hw->setActiveConfig(getHwComposer().getActiveConfigIndex(state.type));
+    display->getCompositionDisplay()->setColorMode(defaultColorMode, defaultDataSpace,
+                                                   RenderIntent::COLORIMETRIC);
+    if (!state.isVirtual()) {
+        LOG_ALWAYS_FATAL_IF(!displayId);
+        display->setActiveConfig(getHwComposer().getActiveConfigIndex(*displayId));
     }
-    hw->setLayerStack(state.layerStack);
-    hw->setProjection(state.orientation, state.viewport, state.frame);
-    hw->setDisplayName(state.displayName);
 
-    return hw;
+    display->setLayerStack(state.layerStack);
+    display->setProjection(state.orientation, state.viewport, state.frame);
+    display->setDisplayName(state.displayName);
+
+    return display;
 }
 
 void SurfaceFlinger::processDisplayChangesLocked() {
@@ -2451,21 +2699,24 @@
         for (size_t i = 0; i < dc;) {
             const ssize_t j = curr.indexOfKey(draw.keyAt(i));
             if (j < 0) {
+                // Save display IDs before disconnecting.
+                const auto internalDisplayId = getInternalDisplayId();
+                const auto externalDisplayId = getExternalDisplayId();
+
                 // in drawing state but not in current state
-                // Call makeCurrent() on the primary display so we can
-                // be sure that nothing associated with this display
-                // is current.
-                const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDeviceLocked());
-                if (defaultDisplay != nullptr) defaultDisplay->makeCurrent();
-                sp<DisplayDevice> hw(getDisplayDeviceLocked(draw.keyAt(i)));
-                if (hw != nullptr) hw->disconnect(getHwComposer());
-                if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
-                    mEventThread->onHotplugReceived(draw[i].type, false);
-                mDisplays.removeItem(draw.keyAt(i));
+                if (const auto display = getDisplayDeviceLocked(draw.keyAt(i))) {
+                    display->disconnect();
+                }
+                if (internalDisplayId && internalDisplayId == draw[i].displayId) {
+                    dispatchDisplayHotplugEvent(EventThread::DisplayType::Primary, false);
+                } else if (externalDisplayId && externalDisplayId == draw[i].displayId) {
+                    dispatchDisplayHotplugEvent(EventThread::DisplayType::External, false);
+                }
+                mDisplays.erase(draw.keyAt(i));
             } else {
                 // this display is in both lists. see if something changed.
                 const DisplayDeviceState& state(curr[j]);
-                const wp<IBinder>& display(curr.keyAt(j));
+                const wp<IBinder>& displayToken = curr.keyAt(j);
                 const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
                 const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
                 if (state_binder != draw_binder) {
@@ -2473,26 +2724,26 @@
                     // recreating the DisplayDevice, so we just remove it
                     // from the drawing state, so that it get re-added
                     // below.
-                    sp<DisplayDevice> hw(getDisplayDeviceLocked(display));
-                    if (hw != nullptr) hw->disconnect(getHwComposer());
-                    mDisplays.removeItem(display);
+                    if (const auto display = getDisplayDeviceLocked(displayToken)) {
+                        display->disconnect();
+                    }
+                    mDisplays.erase(displayToken);
                     mDrawingState.displays.removeItemsAt(i);
                     dc--;
                     // at this point we must loop to the next item
                     continue;
                 }
 
-                const sp<DisplayDevice> disp(getDisplayDeviceLocked(display));
-                if (disp != nullptr) {
+                if (const auto display = getDisplayDeviceLocked(displayToken)) {
                     if (state.layerStack != draw[i].layerStack) {
-                        disp->setLayerStack(state.layerStack);
+                        display->setLayerStack(state.layerStack);
                     }
                     if ((state.orientation != draw[i].orientation) ||
                         (state.viewport != draw[i].viewport) || (state.frame != draw[i].frame)) {
-                        disp->setProjection(state.orientation, state.viewport, state.frame);
+                        display->setProjection(state.orientation, state.viewport, state.frame);
                     }
                     if (state.width != draw[i].width || state.height != draw[i].height) {
-                        disp->setDisplaySize(state.width, state.height);
+                        display->setDisplaySize(state.width, state.height);
                     }
                 }
             }
@@ -2505,20 +2756,20 @@
             if (draw.indexOfKey(curr.keyAt(i)) < 0) {
                 const DisplayDeviceState& state(curr[i]);
 
-                sp<DisplaySurface> dispSurface;
+                sp<compositionengine::DisplaySurface> dispSurface;
                 sp<IGraphicBufferProducer> producer;
                 sp<IGraphicBufferProducer> bqProducer;
                 sp<IGraphicBufferConsumer> bqConsumer;
-                mCreateBufferQueue(&bqProducer, &bqConsumer, false);
+                getFactory().createBufferQueue(&bqProducer, &bqConsumer, false);
 
-                int32_t hwcId = -1;
-                if (state.isVirtualDisplay()) {
+                std::optional<DisplayId> displayId;
+                if (state.isVirtual()) {
                     // Virtual displays without a surface are dormant:
                     // they have external state (layer stack, projection,
                     // etc.) but no internal state (i.e. a DisplayDevice).
                     if (state.surface != nullptr) {
                         // Allow VR composer to use virtual displays.
-                        if (mUseHwcVirtualDisplays || getBE().mHwc->isUsingVrComposer()) {
+                        if (mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer()) {
                             int width = 0;
                             int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
                             ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
@@ -2530,13 +2781,14 @@
                             ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
                             auto format = static_cast<ui::PixelFormat>(intFormat);
 
-                            getBE().mHwc->allocateVirtualDisplay(width, height, &format, &hwcId);
+                            displayId =
+                                    getHwComposer().allocateVirtualDisplay(width, height, &format);
                         }
 
                         // TODO: Plumb requested format back up to consumer
 
                         sp<VirtualDisplaySurface> vds =
-                                new VirtualDisplaySurface(*getBE().mHwc, hwcId, state.surface,
+                                new VirtualDisplaySurface(getHwComposer(), displayId, state.surface,
                                                           bqProducer, bqConsumer,
                                                           state.displayName);
 
@@ -2549,18 +2801,25 @@
                              "surface is provided (%p), ignoring it",
                              state.surface.get());
 
-                    hwcId = state.type;
-                    dispSurface = new FramebufferSurface(*getBE().mHwc, hwcId, bqConsumer);
+                    displayId = state.displayId;
+                    LOG_ALWAYS_FATAL_IF(!displayId);
+                    dispSurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer);
                     producer = bqProducer;
                 }
 
-                const wp<IBinder>& display(curr.keyAt(i));
+                const wp<IBinder>& displayToken = curr.keyAt(i);
                 if (dispSurface != nullptr) {
-                    mDisplays.add(display,
-                                  setupNewDisplayDeviceInternal(display, hwcId, state, dispSurface,
-                                                                producer));
-                    if (!state.isVirtualDisplay()) {
-                        mEventThread->onHotplugReceived(state.type, true);
+                    mDisplays.emplace(displayToken,
+                                      setupNewDisplayDeviceInternal(displayToken, displayId, state,
+                                                                    dispSurface, producer));
+                    if (!state.isVirtual()) {
+                        LOG_ALWAYS_FATAL_IF(!displayId);
+
+                        if (displayId == getInternalDisplayId()) {
+                            dispatchDisplayHotplugEvent(EventThread::DisplayType::Primary, true);
+                        } else if (displayId == getExternalDisplayId()) {
+                            dispatchDisplayHotplugEvent(EventThread::DisplayType::External, true);
+                        }
                     }
                 }
             }
@@ -2582,6 +2841,7 @@
      * (perform the transaction for each of them if needed)
      */
 
+    bool inputChanged = false;
     if (transactionFlags & eTraversalNeeded) {
         mCurrentState.traverseInZOrder([&](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
@@ -2590,6 +2850,10 @@
             const uint32_t flags = layer->doTransaction(0);
             if (flags & Layer::eVisibleRegion)
                 mVisibleRegionsDirty = true;
+
+            if (flags & Layer::eInputInfoChanged) {
+                inputChanged = true;
+            }
         });
     }
 
@@ -2622,7 +2886,7 @@
         // happened yet, so we must use the current state layer list
         // (soon to become the drawing state list).
         //
-        sp<const DisplayDevice> disp;
+        sp<const DisplayDevice> hintDisplay;
         uint32_t currentlayerStack = 0;
         bool first = true;
         mCurrentState.traverseInZOrder([&](Layer* layer) {
@@ -2635,34 +2899,35 @@
                 // figure out if this layerstack is mirrored
                 // (more than one display) if so, pick the default display,
                 // if not, pick the only display it's on.
-                disp.clear();
-                for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
-                    sp<const DisplayDevice> hw(mDisplays[dpy]);
-                    if (layer->belongsToDisplay(hw->getLayerStack(), hw->isPrimary())) {
-                        if (disp == nullptr) {
-                            disp = std::move(hw);
-                        } else {
-                            disp = nullptr;
+                hintDisplay = nullptr;
+                for (const auto& [token, display] : mDisplays) {
+                    if (display->getCompositionDisplay()
+                                ->belongsInOutput(layer->getLayerStack(),
+                                                  layer->getPrimaryDisplayOnly())) {
+                        if (hintDisplay) {
+                            hintDisplay = nullptr;
                             break;
+                        } else {
+                            hintDisplay = display;
                         }
                     }
                 }
             }
 
-            if (disp == nullptr) {
+            if (!hintDisplay) {
                 // NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to
                 // redraw after transform hint changes. See bug 8508397.
 
                 // could be null when this layer is using a layerStack
                 // that is not visible on any display. Also can occur at
                 // screen off/on times.
-                disp = getDefaultDisplayDeviceLocked();
+                hintDisplay = getDefaultDisplayDeviceLocked();
             }
 
-            // disp can be null if there is no display available at all to get
+            // could be null if there is no display available at all to get
             // the transform hint from.
-            if (disp != nullptr) {
-                layer->updateTransformHint(disp);
+            if (hintDisplay) {
+                layer->updateTransformHint(hintDisplay);
             }
 
             first = false;
@@ -2700,23 +2965,74 @@
 
     commitTransaction();
 
+    if (inputChanged || mVisibleRegionsDirty) {
+        updateInputWindows();
+    }
+    executeInputWindowCommands();
+
     updateCursorAsync();
 }
 
+void SurfaceFlinger::updateInputWindows() {
+    ATRACE_CALL();
+
+    if (mInputFlinger == nullptr) {
+        return;
+    }
+
+    Vector<InputWindowInfo> inputHandles;
+
+    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
+        if (layer->hasInput()) {
+            // When calculating the screen bounds we ignore the transparent region since it may
+            // result in an unwanted offset.
+            inputHandles.add(layer->fillInputInfo());
+        }
+    });
+    mInputFlinger->setInputWindows(inputHandles);
+}
+
+void SurfaceFlinger::executeInputWindowCommands() {
+    if (!mInputFlinger) {
+        return;
+    }
+
+    for (const auto& transferTouchFocusCommand : mInputWindowCommands.transferTouchFocusCommands) {
+        if (transferTouchFocusCommand.fromToken != nullptr &&
+            transferTouchFocusCommand.toToken != nullptr &&
+            transferTouchFocusCommand.fromToken != transferTouchFocusCommand.toToken) {
+            mInputFlinger->transferTouchFocus(transferTouchFocusCommand.fromToken,
+                                              transferTouchFocusCommand.toToken);
+        }
+    }
+
+    mInputWindowCommands.clear();
+}
+
 void SurfaceFlinger::updateCursorAsync()
 {
-    for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
-        auto& displayDevice = mDisplays[displayId];
-        if (displayDevice->getHwcDisplayId() < 0) {
+    for (const auto& [token, display] : mDisplays) {
+        if (!display->getId()) {
             continue;
         }
 
-        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-            layer->updateCursorPosition(displayDevice);
+        for (auto& layer : display->getVisibleLayersSortedByZ()) {
+            layer->updateCursorPosition(display);
         }
     }
 }
 
+void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) {
+    if (layer->hasReadyFrame()) {
+        const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+        if (layer->shouldPresentNow(expectedPresentTime)) {
+            bool ignored = false;
+            layer->latchBuffer(ignored, systemTime(), Fence::NO_FENCE);
+        }
+    }
+    layer->releasePendingBuffer(systemTime());
+}
+
 void SurfaceFlinger::commitTransaction()
 {
     if (!mLayersPendingRemoval.isEmpty()) {
@@ -2724,7 +3040,15 @@
         for (const auto& l : mLayersPendingRemoval) {
             recordBufferingStats(l->getName().string(),
                     l->getOccupancyHistory(true));
-            l->onRemoved();
+
+            // We need to release the HWC layers when the Layer is removed
+            // from the current state otherwise the HWC layer just continues
+            // showing at its last configured state until we eventually
+            // abandon the buffer queue.
+            if (l->isRemovedFromCurrentState()) {
+                l->destroyHwcLayersForAllDisplays();
+                latchAndReleaseBuffer(l);
+            }
         }
         mLayersPendingRemoval.clear();
     }
@@ -2746,11 +3070,12 @@
 }
 
 void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displayDevice,
-        Region& outDirtyRegion, Region& outOpaqueRegion)
-{
+                                           Region& outDirtyRegion, Region& outOpaqueRegion) {
     ATRACE_CALL();
     ALOGV("computeVisibleRegions");
 
+    auto display = displayDevice->getCompositionDisplay();
+
     Region aboveOpaqueLayers;
     Region aboveCoveredLayers;
     Region dirty;
@@ -2762,8 +3087,9 @@
         const Layer::State& s(layer->getDrawingState());
 
         // only consider the layers on the given layer stack
-        if (!layer->belongsToDisplay(displayDevice->getLayerStack(), displayDevice->isPrimary()))
+        if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
             return;
+        }
 
         /*
          * opaqueRegion: area of a surface that is fully opaque.
@@ -2799,14 +3125,15 @@
         if (CC_LIKELY(layer->isVisible())) {
             const bool translucent = !layer->isOpaque(s);
             Rect bounds(layer->computeScreenBounds());
+
             visibleRegion.set(bounds);
-            Transform tr = layer->getTransform();
+            ui::Transform tr = layer->getTransform();
             if (!visibleRegion.isEmpty()) {
                 // Remove the transparent area from the visible region
                 if (translucent) {
                     if (tr.preserveRects()) {
                         // transform the transparent region
-                        transparentRegion = tr.transform(s.activeTransparentRegion);
+                        transparentRegion = tr.transform(layer->getActiveTransparentRegion(s));
                     } else {
                         // transformation too complex, can't do the
                         // transparent region optimization.
@@ -2817,7 +3144,8 @@
                 // compute the opaque region
                 const int32_t layerOrientation = tr.getOrientation();
                 if (layer->getAlpha() == 1.0f && !translucent &&
-                        ((layerOrientation & Transform::ROT_INVALID) == false)) {
+                        layer->getRoundedCornerState().radius == 0.0f &&
+                        ((layerOrientation & ui::Transform::ROT_INVALID) == false)) {
                     // the opaque region is the layer's footprint
                     opaqueRegion = visibleRegion;
                 }
@@ -2883,14 +3211,29 @@
 }
 
 void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
-    for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
-        const sp<DisplayDevice>& hw(mDisplays[dpy]);
-        if (layer->belongsToDisplay(hw->getLayerStack(), hw->isPrimary())) {
-            hw->dirtyRegion.orSelf(dirty);
+    for (const auto& [token, displayDevice] : mDisplays) {
+        auto display = displayDevice->getCompositionDisplay();
+        if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+            display->editState().dirtyRegion.orSelf(dirty);
         }
     }
 }
 
+void SurfaceFlinger::initDefaultDisplayNativePrimaries() {
+    mInternalDisplayPrimaries.red.X = kSrgbRedX;
+    mInternalDisplayPrimaries.red.Y = kSrgbRedY;
+    mInternalDisplayPrimaries.red.Z = kSrgbRedZ;
+    mInternalDisplayPrimaries.green.X = kSrgbGreenX;
+    mInternalDisplayPrimaries.green.Y = kSrgbGreenY;
+    mInternalDisplayPrimaries.green.Z = kSrgbGreenZ;
+    mInternalDisplayPrimaries.blue.X = kSrgbBlueX;
+    mInternalDisplayPrimaries.blue.Y = kSrgbBlueY;
+    mInternalDisplayPrimaries.blue.Z = kSrgbBlueZ;
+    mInternalDisplayPrimaries.white.X = kSrgbWhiteX;
+    mInternalDisplayPrimaries.white.Y = kSrgbWhiteY;
+    mInternalDisplayPrimaries.white.Z = kSrgbWhiteZ;
+}
+
 bool SurfaceFlinger::handlePageFlip()
 {
     ALOGV("handlePageFlip");
@@ -2911,9 +3254,10 @@
     // Display is now waiting on Layer 1's frame, which is behind layer 0's
     // second frame. But layer 0's second frame could be waiting on display.
     mDrawingState.traverseInZOrder([&](Layer* layer) {
-        if (layer->hasQueuedFrame()) {
+        if (layer->hasReadyFrame()) {
             frameQueued = true;
-            if (layer->shouldPresentNow(mPrimaryDispSync)) {
+            const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+            if (layer->shouldPresentNow(expectedPresentTime)) {
                 mLayersWithQueuedFrames.push_back(layer);
             } else {
                 layer->useEmptyDamage();
@@ -2923,17 +3267,33 @@
         }
     });
 
-    for (auto& layer : mLayersWithQueuedFrames) {
-        const Region dirty(layer->latchBuffer(visibleRegions, latchTime));
-        layer->useSurfaceDamage();
-        invalidateLayerStack(layer, dirty);
-        if (layer->isBufferLatched()) {
-            newDataLatched = true;
+    if (!mLayersWithQueuedFrames.empty()) {
+        // mStateLock is needed for latchBuffer as LayerRejecter::reject()
+        // writes to Layer current state. See also b/119481871
+        Mutex::Autolock lock(mStateLock);
+
+        for (auto& layer : mLayersWithQueuedFrames) {
+            const Region dirty(layer->latchBuffer(visibleRegions, latchTime, getBE().flushFence));
+            layer->useSurfaceDamage();
+            invalidateLayerStack(layer, dirty);
+            if (layer->isBufferLatched()) {
+                newDataLatched = true;
+            }
         }
     }
 
+    // Clear the renderengine fence here...
+    // downstream code assumes that a cleared fence == NO_FENCE, so reassign to
+    // clear instead of sp::clear.
+    getBE().flushFence = Fence::NO_FENCE;
+
     mVisibleRegionsDirty |= visibleRegions;
 
+    if (visibleRegions) {
+        // Update input window info if the layer receives its first buffer.
+        updateInputWindows();
+    }
+
     // If we will need to wake up at some time in the future to deal with a
     // queued frame that shouldn't be displayed during this vsync period, wake
     // up during the next vsync period to check again.
@@ -2956,121 +3316,94 @@
     mGeometryInvalid = true;
 }
 
+void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& displayDevice,
+                                          const Region& inDirtyRegion) {
+    auto display = displayDevice->getCompositionDisplay();
 
-void SurfaceFlinger::doDisplayComposition(
-        const sp<const DisplayDevice>& displayDevice,
-        const Region& inDirtyRegion)
-{
     // We only need to actually compose the display if:
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
     // 2) There is work to be done (the dirty region isn't empty)
-    bool isHwcDisplay = displayDevice->getHwcDisplayId() >= 0;
-    if (!isHwcDisplay && inDirtyRegion.isEmpty()) {
+    if (!displayDevice->getId() && inDirtyRegion.isEmpty()) {
         ALOGV("Skipping display composition");
         return;
     }
 
     ALOGV("doDisplayComposition");
-    if (!doComposeSurfaces(displayDevice)) return;
+    base::unique_fd readyFence;
+    if (!doComposeSurfaces(displayDevice, Region::INVALID_REGION, &readyFence)) return;
 
     // swap buffers (presentation)
-    displayDevice->swapBuffers(getHwComposer());
+    display->getRenderSurface()->queueBuffer(std::move(readyFence));
 }
 
-bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDevice)
-{
+bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice,
+                                       const Region& debugRegion, base::unique_fd* readyFence) {
     ALOGV("doComposeSurfaces");
 
-    const Region bounds(displayDevice->bounds());
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+    const auto displayId = display->getId();
+
+    const Region bounds(displayState.bounds);
     const DisplayRenderArea renderArea(displayDevice);
-    const auto hwcId = displayDevice->getHwcDisplayId();
-    const bool hasClientComposition = getBE().mHwc->hasClientComposition(hwcId);
+    const bool hasClientComposition = getHwComposer().hasClientComposition(displayId);
     ATRACE_INT("hasClientComposition", hasClientComposition);
 
+    mat4 colorMatrix;
     bool applyColorMatrix = false;
-    bool needsEnhancedColorMatrix = false;
+
+    renderengine::DisplaySettings clientCompositionDisplay;
+    std::vector<renderengine::LayerSettings> clientCompositionLayers;
+    sp<GraphicBuffer> buf;
 
     if (hasClientComposition) {
         ALOGV("hasClientComposition");
 
-        Dataspace outputDataspace = Dataspace::UNKNOWN;
-        if (displayDevice->hasWideColorGamut()) {
-            outputDataspace = displayDevice->getCompositionDataSpace();
-        }
-        getBE().mRenderEngine->setOutputDataSpace(outputDataspace);
-        getBE().mRenderEngine->setDisplayMaxLuminance(
-                displayDevice->getHdrCapabilities().getDesiredMaxLuminance());
+        buf = display->getRenderSurface()->dequeueBuffer();
 
-        const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(hwcId);
-        const bool skipClientColorTransform = getBE().mHwc->hasCapability(
-            HWC2::Capability::SkipClientColorTransform);
-
-        mat4 colorMatrix;
-        applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
-        if (applyColorMatrix) {
-            colorMatrix = mDrawingState.colorMatrix;
-        }
-
-        // The current enhanced saturation matrix is designed to enhance Display P3,
-        // thus we only apply this matrix when the render intent is not colorimetric
-        // and the output color space is Display P3.
-        needsEnhancedColorMatrix =
-            (displayDevice->getActiveRenderIntent() >= RenderIntent::ENHANCE &&
-             outputDataspace == Dataspace::DISPLAY_P3);
-        if (needsEnhancedColorMatrix) {
-            colorMatrix *= mEnhancedSaturationMatrix;
-        }
-
-        getRenderEngine().setupColorTransform(colorMatrix);
-
-        if (!displayDevice->makeCurrent()) {
-            ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
-                  displayDevice->getDisplayName().string());
-            getRenderEngine().resetCurrentSurface();
-
-            // |mStateLock| not needed as we are on the main thread
-            if(!getDefaultDisplayDeviceLocked()->makeCurrent()) {
-              ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting.");
-            }
+        if (buf == nullptr) {
+            ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
+                  "client composition for this frame",
+                  displayDevice->getDisplayName().c_str());
             return false;
         }
 
-        // Never touch the framebuffer if we don't have any framebuffer layers
-        if (hasDeviceComposition) {
-            // when using overlays, we assume a fully transparent framebuffer
-            // NOTE: we could reduce how much we need to clear, for instance
-            // remove where there are opaque FB layers. however, on some
-            // GPUs doing a "clean slate" clear might be more efficient.
-            // We'll revisit later if needed.
-            getBE().mRenderEngine->clearWithColor(0, 0, 0, 0);
-        } else {
-            // we start with the whole screen area and remove the scissor part
-            // we're left with the letterbox region
-            // (common case is that letterbox ends-up being empty)
-            const Region letterbox(bounds.subtract(displayDevice->getScissor()));
+        clientCompositionDisplay.physicalDisplay = displayState.scissor;
+        clientCompositionDisplay.clip = displayState.scissor;
+        const ui::Transform& displayTransform = displayState.transform;
+        mat4 m;
+        m[0][0] = displayTransform[0][0];
+        m[0][1] = displayTransform[0][1];
+        m[0][3] = displayTransform[0][2];
+        m[1][0] = displayTransform[1][0];
+        m[1][1] = displayTransform[1][1];
+        m[1][3] = displayTransform[1][2];
+        m[3][0] = displayTransform[2][0];
+        m[3][1] = displayTransform[2][1];
+        m[3][3] = displayTransform[2][2];
 
-            // compute the area to clear
-            Region region(displayDevice->undefinedRegion.merge(letterbox));
+        clientCompositionDisplay.globalTransform = m;
 
-            // screen is already cleared here
-            if (!region.isEmpty()) {
-                // can happen with SurfaceView
-                drawWormhole(displayDevice, region);
-            }
+        const auto* profile = display->getDisplayColorProfile();
+        Dataspace outputDataspace = Dataspace::UNKNOWN;
+        if (profile->hasWideColorGamut()) {
+            outputDataspace = displayState.dataspace;
         }
+        clientCompositionDisplay.outputDataspace = outputDataspace;
+        clientCompositionDisplay.maxLuminance =
+                profile->getHdrCapabilities().getDesiredMaxLuminance();
 
-        const Rect& bounds(displayDevice->getBounds());
-        const Rect& scissor(displayDevice->getScissor());
-        if (scissor != bounds) {
-            // scissor doesn't match the screen's dimensions, so we
-            // need to clear everything outside of it and enable
-            // the GL scissor so we don't draw anything where we shouldn't
+        const bool hasDeviceComposition = getHwComposer().hasDeviceComposition(displayId);
+        const bool skipClientColorTransform =
+                getHwComposer()
+                        .hasDisplayCapability(displayId,
+                                              HWC2::DisplayCapability::SkipClientColorTransform);
 
-            // enable scissor for this frame
-            const uint32_t height = displayDevice->getHeight();
-            getBE().mRenderEngine->setScissor(scissor.left, height - scissor.bottom,
-                                              scissor.getWidth(), scissor.getHeight());
+        // Compute the global color transform matrix.
+        applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
+        if (applyColorMatrix) {
+            clientCompositionDisplay.colorTransform = colorMatrix;
         }
     }
 
@@ -3079,32 +3412,48 @@
      */
 
     ALOGV("Rendering client layers");
-    const Transform& displayTransform = displayDevice->getTransform();
     bool firstLayer = true;
+    Region clearRegion = Region::INVALID_REGION;
     for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-        const Region clip(bounds.intersect(
-                displayTransform.transform(layer->visibleRegion)));
+        const Region viewportRegion(displayState.viewport);
+        const Region clip(viewportRegion.intersect(layer->visibleRegion));
         ALOGV("Layer: %s", layer->getName().string());
-        ALOGV("  Composition type: %s",
-                to_string(layer->getCompositionType(hwcId)).c_str());
+        ALOGV("  Composition type: %s", to_string(layer->getCompositionType(displayId)).c_str());
         if (!clip.isEmpty()) {
-            switch (layer->getCompositionType(hwcId)) {
+            switch (layer->getCompositionType(displayId)) {
                 case HWC2::Composition::Cursor:
                 case HWC2::Composition::Device:
                 case HWC2::Composition::Sideband:
                 case HWC2::Composition::SolidColor: {
+                    LOG_ALWAYS_FATAL_IF(!displayId);
                     const Layer::State& state(layer->getDrawingState());
-                    if (layer->getClearClientTarget(hwcId) && !firstLayer &&
-                            layer->isOpaque(state) && (state.color.a == 1.0f)
-                            && hasClientComposition) {
+                    if (layer->getClearClientTarget(*displayId) && !firstLayer &&
+                        layer->isOpaque(state) && (layer->getAlpha() == 1.0f) &&
+                        layer->getRoundedCornerState().radius == 0.0f && hasClientComposition) {
                         // never clear the very first layer since we're
                         // guaranteed the FB is already cleared
-                        layer->clearWithOpenGL(renderArea);
+                        renderengine::LayerSettings layerSettings;
+                        Region dummyRegion;
+                        bool prepared = layer->prepareClientLayer(renderArea, clip, dummyRegion,
+                                                                  layerSettings);
+
+                        if (prepared) {
+                            layerSettings.source.buffer.buffer = nullptr;
+                            layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
+                            layerSettings.alpha = half(0.0);
+                            layerSettings.disableBlending = true;
+                            clientCompositionLayers.push_back(layerSettings);
+                        }
                     }
                     break;
                 }
                 case HWC2::Composition::Client: {
-                    layer->draw(renderArea, clip);
+                    renderengine::LayerSettings layerSettings;
+                    bool prepared =
+                            layer->prepareClientLayer(renderArea, clip, clearRegion, layerSettings);
+                    if (prepared) {
+                        clientCompositionLayers.push_back(layerSettings);
+                    }
                     break;
                 }
                 default:
@@ -3116,26 +3465,38 @@
         firstLayer = false;
     }
 
-    if (applyColorMatrix || needsEnhancedColorMatrix) {
-        getRenderEngine().setupColorTransform(mat4());
+    if (hasClientComposition) {
+        clientCompositionDisplay.clearRegion = clearRegion;
+        if (!debugRegion.isEmpty()) {
+            Region::const_iterator it = debugRegion.begin();
+            Region::const_iterator end = debugRegion.end();
+            while (it != end) {
+                const Rect& rect = *it++;
+                renderengine::LayerSettings layerSettings;
+                layerSettings.source.buffer.buffer = nullptr;
+                layerSettings.source.solidColor = half3(1.0, 0.0, 1.0);
+                layerSettings.geometry.boundaries = rect.toFloatRect();
+                layerSettings.alpha = half(1.0);
+                clientCompositionLayers.push_back(layerSettings);
+            }
+        }
+        getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers,
+                                     buf->getNativeBuffer(), readyFence);
     }
-
-    // disable scissor at the end of the frame
-    getBE().mRenderEngine->disableScissor();
     return true;
 }
 
-void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& displayDevice, const Region& region) const {
-    const int32_t height = displayDevice->getHeight();
+void SurfaceFlinger::drawWormhole(const Region& region) const {
     auto& engine(getRenderEngine());
-    engine.fillRegionWithColor(region, height, 0, 0, 0, 0);
+    engine.fillRegionWithColor(region, 0, 0, 0, 0);
 }
 
 status_t SurfaceFlinger::addClientLayer(const sp<Client>& client,
         const sp<IBinder>& handle,
         const sp<IGraphicBufferProducer>& gbc,
         const sp<Layer>& lbc,
-        const sp<Layer>& parent)
+        const sp<Layer>& parent,
+        bool addToCurrentState)
 {
     // add this layer to the current state list
     {
@@ -3145,13 +3506,14 @@
                   MAX_LAYERS);
             return NO_MEMORY;
         }
-        if (parent == nullptr) {
+        if (parent == nullptr && addToCurrentState) {
             mCurrentState.layersSortedByZ.add(lbc);
+        } else if (parent == nullptr) {
+            lbc->onRemovedFromCurrentState();
+        } else if (parent->isRemovedFromCurrentState()) {
+            parent->addChild(lbc);
+            lbc->onRemovedFromCurrentState();
         } else {
-            if (parent->isPendingRemoval()) {
-                ALOGE("addClientLayer called with a removed parent");
-                return NAME_NOT_FOUND;
-            }
             parent->addChild(lbc);
         }
 
@@ -3164,7 +3526,6 @@
                                 mMaxGraphicBufferProducerListSize, mNumLayers);
         }
         mLayersAdded = true;
-        mNumLayers++;
     }
 
     // attach this layer to the client
@@ -3173,75 +3534,21 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer, bool topLevelOnly) {
-    Mutex::Autolock _l(mStateLock);
-    return removeLayerLocked(mStateLock, layer, topLevelOnly);
-}
-
-status_t SurfaceFlinger::removeLayerLocked(const Mutex&, const sp<Layer>& layer,
-                                           bool topLevelOnly) {
-    if (layer->isPendingRemoval()) {
-        return NO_ERROR;
-    }
-
-    const auto& p = layer->getParent();
-    ssize_t index;
-    if (p != nullptr) {
-        if (topLevelOnly) {
-            return NO_ERROR;
-        }
-
-        sp<Layer> ancestor = p;
-        while (ancestor->getParent() != nullptr) {
-            ancestor = ancestor->getParent();
-        }
-        if (mCurrentState.layersSortedByZ.indexOf(ancestor) < 0) {
-            ALOGE("removeLayer called with a layer whose parent has been removed");
-            return NAME_NOT_FOUND;
-        }
-
-        index = p->removeChild(layer);
-    } else {
-        index = mCurrentState.layersSortedByZ.remove(layer);
-    }
-
-    // As a matter of normal operation, the LayerCleaner will produce a second
-    // attempt to remove the surface. The Layer will be kept alive in mDrawingState
-    // so we will succeed in promoting it, but it's already been removed
-    // from mCurrentState. As long as we can find it in mDrawingState we have no problem
-    // otherwise something has gone wrong and we are leaking the layer.
-    if (index < 0 && mDrawingState.layersSortedByZ.indexOf(layer) < 0) {
-        ALOGE("Failed to find layer (%s) in layer parent (%s).",
-                layer->getName().string(),
-                (p != nullptr) ? p->getName().string() : "no-parent");
-        return BAD_VALUE;
-    } else if (index < 0) {
-        return NO_ERROR;
-    }
-
-    layer->onRemovedFromCurrentState();
-    mLayersPendingRemoval.add(layer);
-    mLayersRemoved = true;
-    mNumLayers -= 1 + layer->getChildrenCount();
-    setTransactionFlags(eTransactionNeeded);
-    return NO_ERROR;
-}
-
 uint32_t SurfaceFlinger::peekTransactionFlags() {
-    return android_atomic_release_load(&mTransactionFlags);
+    return mTransactionFlags;
 }
 
 uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) {
-    return android_atomic_and(~flags, &mTransactionFlags) & flags;
+    return mTransactionFlags.fetch_and(~flags) & flags;
 }
 
 uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
-    return setTransactionFlags(flags, VSyncModulator::TransactionStart::NORMAL);
+    return setTransactionFlags(flags, Scheduler::TransactionStart::NORMAL);
 }
 
 uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
-        VSyncModulator::TransactionStart transactionStart) {
-    uint32_t old = android_atomic_or(flags, &mTransactionFlags);
+                                             Scheduler::TransactionStart transactionStart) {
+    uint32_t old = mTransactionFlags.fetch_or(flags);
     mVsyncModulator.setTransactionStart(transactionStart);
     if ((old & flags)==0) { // wake the server up
         signalTransaction();
@@ -3249,6 +3556,26 @@
     return old;
 }
 
+bool SurfaceFlinger::flushTransactionQueues() {
+    Mutex::Autolock _l(mStateLock);
+    auto it = mTransactionQueues.begin();
+    while (it != mTransactionQueues.end()) {
+        auto& [applyToken, transactionQueue] = *it;
+
+        while (!transactionQueue.empty()) {
+            const auto& [states, displays, flags, desiredPresentTime] = transactionQueue.front();
+            if (!transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+                break;
+            }
+            applyTransactionState(states, displays, flags, mInputWindowCommands);
+            transactionQueue.pop();
+        }
+
+        it = (transactionQueue.empty()) ? mTransactionQueues.erase(it) : std::next(it, 1);
+    }
+    return mTransactionQueues.empty();
+}
+
 bool SurfaceFlinger::containsAnyInvalidClientState(const Vector<ComposerState>& states) {
     for (const ComposerState& state : states) {
         // Here we need to check that the interface we're given is indeed
@@ -3271,19 +3598,56 @@
     return false;
 }
 
-void SurfaceFlinger::setTransactionState(
-        const Vector<ComposerState>& states,
-        const Vector<DisplayState>& displays,
-        uint32_t flags)
-{
+bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+                                                   const Vector<ComposerState>& states) {
+    const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+    // Do not present if the desiredPresentTime has not passed unless it is more than one second
+    // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
+    if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
+        desiredPresentTime < expectedPresentTime + s2ns(1)) {
+        return false;
+    }
+
+    for (const ComposerState& state : states) {
+        const layer_state_t& s = state.state;
+        if (!(s.what & layer_state_t::eAcquireFenceChanged)) {
+            continue;
+        }
+        if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states,
+                                         const Vector<DisplayState>& displays, uint32_t flags,
+                                         const sp<IBinder>& applyToken,
+                                         const InputWindowCommands& inputWindowCommands,
+                                         int64_t desiredPresentTime) {
     ATRACE_CALL();
     Mutex::Autolock _l(mStateLock);
-    uint32_t transactionFlags = 0;
 
     if (containsAnyInvalidClientState(states)) {
         return;
     }
 
+    // If its TransactionQueue already has a pending TransactionState or if it is pending
+    if (mTransactionQueues.find(applyToken) != mTransactionQueues.end() ||
+        !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+        mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime);
+        setTransactionFlags(eTransactionNeeded);
+        return;
+    }
+
+    applyTransactionState(states, displays, flags, inputWindowCommands);
+}
+
+void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states,
+                                           const Vector<DisplayState>& displays, uint32_t flags,
+                                           const InputWindowCommands& inputWindowCommands) {
+    uint32_t transactionFlags = 0;
+
     if (flags & eAnimation) {
         // For window updates that are part of an animation we must wait for
         // previous animation "frames" to be handled.
@@ -3304,17 +3668,17 @@
         transactionFlags |= setDisplayStateLocked(display);
     }
 
+    uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        transactionFlags |= setClientStateLocked(state);
+        clientStateFlags |= setClientStateLocked(state);
     }
+    // If the state doesn't require a traversal and there are callbacks, send them now
+    if (!(clientStateFlags & eTraversalNeeded)) {
+        mTransactionCompletedThread.sendCallbacks();
+    }
+    transactionFlags |= clientStateFlags;
 
-    // Iterate through all layers again to determine if any need to be destroyed. Marking layers
-    // as destroyed should only occur after setting all other states. This is to allow for a
-    // child re-parent to happen before marking its original parent as destroyed (which would
-    // then mark the child as destroyed).
-    for (const ComposerState& state : states) {
-        setDestroyStateLocked(state);
-    }
+    transactionFlags |= addInputWindowCommands(inputWindowCommands);
 
     // If a synchronous transaction is explicitly requested without any changes, force a transaction
     // anyway. This can be used as a flush mechanism for previous async transactions.
@@ -3331,9 +3695,8 @@
         }
 
         // this triggers the transaction
-        const auto start = (flags & eEarlyWakeup)
-                ? VSyncModulator::TransactionStart::EARLY
-                : VSyncModulator::TransactionStart::NORMAL;
+        const auto start = (flags & eEarlyWakeup) ? Scheduler::TransactionStart::EARLY
+                                                  : Scheduler::TransactionStart::NORMAL;
         setTransactionFlags(transactionFlags, start);
 
         // if this is a synchronous transaction, wait for it to take effect
@@ -3357,53 +3720,51 @@
     }
 }
 
-uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s)
-{
-    ssize_t dpyIdx = mCurrentState.displays.indexOfKey(s.token);
-    if (dpyIdx < 0)
-        return 0;
+uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) {
+    const ssize_t index = mCurrentState.displays.indexOfKey(s.token);
+    if (index < 0) return 0;
 
     uint32_t flags = 0;
-    DisplayDeviceState& disp(mCurrentState.displays.editValueAt(dpyIdx));
-    if (disp.isValid()) {
-        const uint32_t what = s.what;
-        if (what & DisplayState::eSurfaceChanged) {
-            if (IInterface::asBinder(disp.surface) != IInterface::asBinder(s.surface)) {
-                disp.surface = s.surface;
-                flags |= eDisplayTransactionNeeded;
-            }
-        }
-        if (what & DisplayState::eLayerStackChanged) {
-            if (disp.layerStack != s.layerStack) {
-                disp.layerStack = s.layerStack;
-                flags |= eDisplayTransactionNeeded;
-            }
-        }
-        if (what & DisplayState::eDisplayProjectionChanged) {
-            if (disp.orientation != s.orientation) {
-                disp.orientation = s.orientation;
-                flags |= eDisplayTransactionNeeded;
-            }
-            if (disp.frame != s.frame) {
-                disp.frame = s.frame;
-                flags |= eDisplayTransactionNeeded;
-            }
-            if (disp.viewport != s.viewport) {
-                disp.viewport = s.viewport;
-                flags |= eDisplayTransactionNeeded;
-            }
-        }
-        if (what & DisplayState::eDisplaySizeChanged) {
-            if (disp.width != s.width) {
-                disp.width = s.width;
-                flags |= eDisplayTransactionNeeded;
-            }
-            if (disp.height != s.height) {
-                disp.height = s.height;
-                flags |= eDisplayTransactionNeeded;
-            }
+    DisplayDeviceState& state = mCurrentState.displays.editValueAt(index);
+
+    const uint32_t what = s.what;
+    if (what & DisplayState::eSurfaceChanged) {
+        if (IInterface::asBinder(state.surface) != IInterface::asBinder(s.surface)) {
+            state.surface = s.surface;
+            flags |= eDisplayTransactionNeeded;
         }
     }
+    if (what & DisplayState::eLayerStackChanged) {
+        if (state.layerStack != s.layerStack) {
+            state.layerStack = s.layerStack;
+            flags |= eDisplayTransactionNeeded;
+        }
+    }
+    if (what & DisplayState::eDisplayProjectionChanged) {
+        if (state.orientation != s.orientation) {
+            state.orientation = s.orientation;
+            flags |= eDisplayTransactionNeeded;
+        }
+        if (state.frame != s.frame) {
+            state.frame = s.frame;
+            flags |= eDisplayTransactionNeeded;
+        }
+        if (state.viewport != s.viewport) {
+            state.viewport = s.viewport;
+            flags |= eDisplayTransactionNeeded;
+        }
+    }
+    if (what & DisplayState::eDisplaySizeChanged) {
+        if (state.width != s.width) {
+            state.width = s.width;
+            flags |= eDisplayTransactionNeeded;
+        }
+        if (state.height != s.height) {
+            state.height = s.height;
+            flags |= eDisplayTransactionNeeded;
+        }
+    }
+
     return flags;
 }
 
@@ -3411,9 +3772,8 @@
     IPCThreadState* ipc = IPCThreadState::self();
     const int pid = ipc->getCallingPid();
     const int uid = ipc->getCallingUid();
-
     if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) &&
-            !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {
+        !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {
         return false;
     }
     return true;
@@ -3428,20 +3788,15 @@
         return 0;
     }
 
-    if (layer->isPendingRemoval()) {
-        ALOGW("Attempting to set client state on removed layer: %s", layer->getName().string());
-        return 0;
-    }
-
     uint32_t flags = 0;
 
-    const uint32_t what = s.what;
+    const uint64_t what = s.what;
     bool geometryAppliesWithResize =
             what & layer_state_t::eGeometryAppliesWithResize;
 
     // If we are deferring transaction, make sure to push the pending state, as otherwise the
     // pending state will also be deferred.
-    if (what & layer_state_t::eDeferTransaction) {
+    if (what & layer_state_t::eDeferTransaction_legacy) {
         layer->pushPendingState();
     }
 
@@ -3499,6 +3854,17 @@
         if (layer->setColor(s.color))
             flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eColorAlphaChanged) {
+        if (layer->setColorAlpha(s.colorAlpha)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eColorDataspaceChanged) {
+        if (layer->setColorDataspace(s.colorDataspace)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eColorTransformChanged) {
+        if (layer->setColorTransform(s.colorTransform)) {
+            flags |= eTraversalNeeded;
+        }
+    }
     if (what & layer_state_t::eMatrixChanged) {
         // TODO: b/109894387
         //
@@ -3526,12 +3892,12 @@
         if (layer->setFlags(s.flags, s.mask))
             flags |= eTraversalNeeded;
     }
-    if (what & layer_state_t::eCropChanged) {
-        if (layer->setCrop(s.crop, !geometryAppliesWithResize))
+    if (what & layer_state_t::eCropChanged_legacy) {
+        if (layer->setCrop_legacy(s.crop_legacy, !geometryAppliesWithResize))
             flags |= eTraversalNeeded;
     }
-    if (what & layer_state_t::eFinalCropChanged) {
-        if (layer->setFinalCrop(s.finalCrop, !geometryAppliesWithResize))
+    if (what & layer_state_t::eCornerRadiusChanged) {
+        if (layer->setCornerRadius(s.cornerRadius))
             flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eLayerStackChanged) {
@@ -3553,15 +3919,15 @@
             flags |= eTransactionNeeded|eTraversalNeeded|eDisplayLayerStackChanged;
         }
     }
-    if (what & layer_state_t::eDeferTransaction) {
-        if (s.barrierHandle != nullptr) {
-            layer->deferTransactionUntil(s.barrierHandle, s.frameNumber);
-        } else if (s.barrierGbp != nullptr) {
-            const sp<IGraphicBufferProducer>& gbp = s.barrierGbp;
+    if (what & layer_state_t::eDeferTransaction_legacy) {
+        if (s.barrierHandle_legacy != nullptr) {
+            layer->deferTransactionUntil_legacy(s.barrierHandle_legacy, s.frameNumber_legacy);
+        } else if (s.barrierGbp_legacy != nullptr) {
+            const sp<IGraphicBufferProducer>& gbp = s.barrierGbp_legacy;
             if (authenticateSurfaceTextureLocked(gbp)) {
                 const auto& otherLayer =
                     (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
-                layer->deferTransactionUntil(otherLayer, s.frameNumber);
+                layer->deferTransactionUntil_legacy(otherLayer, s.frameNumber_legacy);
             } else {
                 ALOGE("Attempt to defer transaction to to an"
                         " unrecognized GraphicBufferProducer");
@@ -3592,35 +3958,83 @@
         // We don't trigger a traversal here because if no other state is
         // changed, we don't want this to cause any more work
     }
+    if (what & layer_state_t::eTransformChanged) {
+        if (layer->setTransform(s.transform)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eTransformToDisplayInverseChanged) {
+        if (layer->setTransformToDisplayInverse(s.transformToDisplayInverse))
+            flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eCropChanged) {
+        if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eFrameChanged) {
+        if (layer->setFrame(s.frame)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eBufferChanged) {
+        if (layer->setBuffer(s.buffer)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eAcquireFenceChanged) {
+        if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eDataspaceChanged) {
+        if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eHdrMetadataChanged) {
+        if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eSurfaceDamageRegionChanged) {
+        if (layer->setSurfaceDamageRegion(s.surfaceDamageRegion)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eApiChanged) {
+        if (layer->setApi(s.api)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eSidebandStreamChanged) {
+        if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eInputInfoChanged) {
+        if (callingThreadHasUnscopedSurfaceFlingerAccess()) {
+            layer->setInputInfo(s.inputInfo);
+            flags |= eTraversalNeeded;
+        } else {
+            ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER");
+        }
+    }
+    if (what & layer_state_t::eMetadataChanged) {
+        if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
+    }
+    std::vector<sp<CallbackHandle>> callbackHandles;
+    if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) {
+        mTransactionCompletedThread.run();
+        for (const auto& [listener, callbackIds] : s.listenerCallbacks) {
+            callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
+        }
+    }
+    if (what & layer_state_t::eCachedBufferChanged) {
+        sp<GraphicBuffer> buffer =
+                mBufferStateLayerCache.get(s.cachedBuffer.token, s.cachedBuffer.bufferId);
+        if (layer->setBuffer(buffer)) flags |= eTraversalNeeded;
+    }
+    if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
+    // Do not put anything that updates layer state or modifies flags after
+    // setTransactionCompletedListener
     return flags;
 }
 
-void SurfaceFlinger::setDestroyStateLocked(const ComposerState& composerState) {
-    const layer_state_t& state = composerState.state;
-    sp<Client> client(static_cast<Client*>(composerState.client.get()));
-
-    sp<Layer> layer(client->getLayerUser(state.surface));
-    if (layer == nullptr) {
-        return;
+uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
+    uint32_t flags = 0;
+    if (!inputWindowCommands.transferTouchFocusCommands.empty()) {
+        flags |= eTraversalNeeded;
     }
 
-    if (layer->isPendingRemoval()) {
-        ALOGW("Attempting to destroy on removed layer: %s", layer->getName().string());
-        return;
-    }
-
-    if (state.what & layer_state_t::eDestroySurface) {
-        removeLayerLocked(mStateLock, layer);
-    }
+    mInputWindowCommands.merge(inputWindowCommands);
+    return flags;
 }
 
-status_t SurfaceFlinger::createLayer(
-        const String8& name,
-        const sp<Client>& client,
-        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-        int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
-        sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent)
-{
+status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
+                                     uint32_t h, PixelFormat format, uint32_t flags,
+                                     LayerMetadata metadata, sp<IBinder>* handle,
+                                     sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent) {
     if (int32_t(w|h) < 0) {
         ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
                 int(w), int(h));
@@ -3634,18 +4048,33 @@
     String8 uniqueName = getUniqueLayerName(name);
 
     switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
-        case ISurfaceComposerClient::eFXSurfaceNormal:
-            result = createBufferLayer(client,
-                    uniqueName, w, h, flags, format,
-                    handle, gbp, &layer);
+        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+            result = createBufferQueueLayer(client, uniqueName, w, h, flags, format, handle, gbp,
+                                            &layer);
 
             break;
+        case ISurfaceComposerClient::eFXSurfaceBufferState:
+            result = createBufferStateLayer(client, uniqueName, w, h, flags, handle, &layer);
+            break;
         case ISurfaceComposerClient::eFXSurfaceColor:
+            // check if buffer size is set for color layer.
+            if (w > 0 || h > 0) {
+                ALOGE("createLayer() failed, w or h cannot be set for color layer (w=%d, h=%d)",
+                      int(w), int(h));
+                return BAD_VALUE;
+            }
+
             result = createColorLayer(client,
                     uniqueName, w, h, flags,
                     handle, &layer);
             break;
         case ISurfaceComposerClient::eFXSurfaceContainer:
+            // check if buffer size is set for container layer.
+            if (w > 0 || h > 0) {
+                ALOGE("createLayer() failed, w or h cannot be set for container layer (w=%d, h=%d)",
+                      int(w), int(h));
+                return BAD_VALUE;
+            }
             result = createContainerLayer(client,
                     uniqueName, w, h, flags,
                     handle, &layer);
@@ -3661,14 +4090,19 @@
 
     // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java
     // TODO b/64227542
-    if (windowType == 441731) {
-        windowType = 2024; // TYPE_NAVIGATION_BAR_PANEL
-        layer->setPrimaryDisplayOnly();
+    if (metadata.has(METADATA_WINDOW_TYPE)) {
+        int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+        if (windowType == 441731) {
+            metadata.setInt32(METADATA_WINDOW_TYPE, 2024); // TYPE_NAVIGATION_BAR_PANEL
+            layer->setPrimaryDisplayOnly();
+        }
     }
 
-    layer->setInfo(windowType, ownerUid);
+    layer->setMetadata(std::move(metadata));
 
-    result = addClientLayer(client, *handle, *gbp, layer, *parent);
+    bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess();
+    result = addClientLayer(client, *handle, *gbp, layer, *parent,
+            addToCurrentState);
     if (result != NO_ERROR) {
         return result;
     }
@@ -3700,15 +4134,17 @@
         });
     }
 
-    ALOGD_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name.c_str(), uniqueName.c_str());
+    ALOGV_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name.c_str(),
+             uniqueName.c_str());
 
     return uniqueName;
 }
 
-status_t SurfaceFlinger::createBufferLayer(const sp<Client>& client,
-        const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
-        sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
-{
+status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const String8& name,
+                                                uint32_t w, uint32_t h, uint32_t flags,
+                                                PixelFormat& format, sp<IBinder>* handle,
+                                                sp<IGraphicBufferProducer>* gbp,
+                                                sp<Layer>* outLayer) {
     // initialize the surfaces
     switch (format) {
     case PIXEL_FORMAT_TRANSPARENT:
@@ -3720,23 +4156,35 @@
         break;
     }
 
-    sp<BufferLayer> layer = new BufferLayer(this, client, name, w, h, flags);
-    status_t err = layer->setBuffers(w, h, format, flags);
+    sp<BufferQueueLayer> layer =
+            getFactory().createBufferQueueLayer(LayerCreationArgs(this, client, name, w, h, flags));
+    status_t err = layer->setDefaultBufferProperties(w, h, format);
     if (err == NO_ERROR) {
         *handle = layer->getHandle();
         *gbp = layer->getProducer();
         *outLayer = layer;
     }
 
-    ALOGE_IF(err, "createBufferLayer() failed (%s)", strerror(-err));
+    ALOGE_IF(err, "createBufferQueueLayer() failed (%s)", strerror(-err));
     return err;
 }
 
+status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, const String8& name,
+                                                uint32_t w, uint32_t h, uint32_t flags,
+                                                sp<IBinder>* handle, sp<Layer>* outLayer) {
+    sp<BufferStateLayer> layer =
+            getFactory().createBufferStateLayer(LayerCreationArgs(this, client, name, w, h, flags));
+    *handle = layer->getHandle();
+    *outLayer = layer;
+
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::createColorLayer(const sp<Client>& client,
         const String8& name, uint32_t w, uint32_t h, uint32_t flags,
         sp<IBinder>* handle, sp<Layer>* outLayer)
 {
-    *outLayer = new ColorLayer(this, client, name, w, h, flags);
+    *outLayer = getFactory().createColorLayer(LayerCreationArgs(this, client, name, w, h, flags));
     *handle = (*outLayer)->getHandle();
     return NO_ERROR;
 }
@@ -3745,49 +4193,49 @@
         const String8& name, uint32_t w, uint32_t h, uint32_t flags,
         sp<IBinder>* handle, sp<Layer>* outLayer)
 {
-    *outLayer = new ContainerLayer(this, client, name, w, h, flags);
+    *outLayer =
+            getFactory().createContainerLayer(LayerCreationArgs(this, client, name, w, h, flags));
     *handle = (*outLayer)->getHandle();
     return NO_ERROR;
 }
 
 
-status_t SurfaceFlinger::onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle)
-{
-    // called by a client when it wants to remove a Layer
-    status_t err = NO_ERROR;
-    sp<Layer> l(client->getLayerUser(handle));
-    if (l != nullptr) {
-        mInterceptor->saveSurfaceDeletion(l);
-        err = removeLayer(l);
-        ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
-                "error removing layer=%p (%s)", l.get(), strerror(-err));
-    }
-    return err;
+void SurfaceFlinger::markLayerPendingRemovalLocked(const sp<Layer>& layer) {
+    mLayersPendingRemoval.add(layer);
+    mLayersRemoved = true;
+    setTransactionFlags(eTransactionNeeded);
 }
 
-status_t SurfaceFlinger::onLayerDestroyed(const wp<Layer>& layer)
+void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer)
 {
-    // called by ~LayerCleaner() when all references to the IBinder (handle)
-    // are gone
-    sp<Layer> l = layer.promote();
-    if (l == nullptr) {
-        // The layer has already been removed, carry on
-        return NO_ERROR;
+    Mutex::Autolock lock(mStateLock);
+    // If a layer has a parent, we allow it to out-live it's handle
+    // with the idea that the parent holds a reference and will eventually
+    // be cleaned up. However no one cleans up the top-level so we do so
+    // here.
+    if (layer->getParent() == nullptr) {
+        mCurrentState.layersSortedByZ.remove(layer);
     }
-    // If we have a parent, then we can continue to live as long as it does.
-    return removeLayer(l, true);
+    markLayerPendingRemovalLocked(layer);
+    layer.clear();
 }
 
 // ---------------------------------------------------------------------------
 
 void SurfaceFlinger::onInitializeDisplays() {
+    const auto display = getDefaultDisplayDeviceLocked();
+    if (!display) return;
+
+    const sp<IBinder> token = display->getDisplayToken().promote();
+    LOG_ALWAYS_FATAL_IF(token == nullptr);
+
     // reset screen orientation and use primary layer stack
     Vector<ComposerState> state;
     Vector<DisplayState> displays;
     DisplayState d;
     d.what = DisplayState::eDisplayProjectionChanged |
              DisplayState::eLayerStackChanged;
-    d.token = mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY];
+    d.token = token;
     d.layerStack = 0;
     d.orientation = DisplayState::eOrientationDefault;
     d.frame.makeInvalid();
@@ -3795,68 +4243,57 @@
     d.width = 0;
     d.height = 0;
     displays.add(d);
-    setTransactionState(state, displays, 0);
-    setPowerModeInternal(getDisplayDevice(d.token), HWC_POWER_MODE_NORMAL,
-                         /*stateLockHeld*/ false);
+    setTransactionState(state, displays, 0, nullptr, mInputWindowCommands, -1);
 
-    const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
-    const nsecs_t period = activeConfig->getVsyncPeriod();
-    mAnimFrameTracker.setDisplayRefreshPeriod(period);
+    setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
+
+    const nsecs_t vsyncPeriod = getVsyncPeriod();
+    mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
 
     // Use phase of 0 since phase is not known.
     // Use latency of 0, which will snap to the ideal latency.
-    setCompositorTimingSnapped(0, period, 0);
+    DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
+    setCompositorTimingSnapped(stats, 0);
 }
 
 void SurfaceFlinger::initializeDisplays() {
-    class MessageScreenInitialized : public MessageBase {
-        SurfaceFlinger* flinger;
-    public:
-        explicit MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
-        virtual bool handler() {
-            flinger->onInitializeDisplays();
-            return true;
-        }
-    };
-    sp<MessageBase> msg = new MessageScreenInitialized(this);
-    postMessageAsync(msg);  // we may be called from main thread, use async message
+    // Async since we may be called from the main thread.
+    postMessageAsync(
+            new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); }));
 }
 
-void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw,
-             int mode, bool stateLockHeld) {
-    ALOGD("Set power mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(),
-            this);
-    int32_t type = hw->getDisplayType();
-    int currentMode = hw->getPowerMode();
+void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mode) {
+    if (display->isVirtual()) {
+        ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
+        return;
+    }
 
+    const auto displayId = display->getId();
+    LOG_ALWAYS_FATAL_IF(!displayId);
+
+    ALOGD("Setting power mode %d on display %s", mode, to_string(*displayId).c_str());
+
+    int currentMode = display->getPowerMode();
     if (mode == currentMode) {
         return;
     }
 
-    hw->setPowerMode(mode);
-    if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
-        ALOGW("Trying to set power mode for virtual display");
-        return;
-    }
+    display->setPowerMode(mode);
 
     if (mInterceptor->isEnabled()) {
-        ConditionalLock lock(mStateLock, !stateLockHeld);
-        ssize_t idx = mCurrentState.displays.indexOfKey(hw->getDisplayToken());
-        if (idx < 0) {
-            ALOGW("Surface Interceptor SavePowerMode: invalid display token");
-            return;
-        }
-        mInterceptor->savePowerModeUpdate(mCurrentState.displays.valueAt(idx).displayId, mode);
+        mInterceptor->savePowerModeUpdate(display->getSequenceId(), mode);
     }
 
     if (currentMode == HWC_POWER_MODE_OFF) {
         // Turn on the display
-        getHwComposer().setPowerMode(type, mode);
-        if (type == DisplayDevice::DISPLAY_PRIMARY &&
-            mode != HWC_POWER_MODE_DOZE_SUSPEND) {
-            // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenAcquired();
-            resyncToHardwareVsync(true);
+        getHwComposer().setPowerMode(*displayId, mode);
+        if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
+            if (mUseScheduler) {
+                mScheduler->onScreenAcquired(mAppConnectionHandle);
+            } else {
+                mEventThread->onScreenAcquired();
+            }
+            resyncToHardwareVsync(true, getVsyncPeriod());
         }
 
         mVisibleRegionsDirty = true;
@@ -3875,75 +4312,84 @@
             ALOGW("Couldn't set SCHED_OTHER on display off");
         }
 
-        if (type == DisplayDevice::DISPLAY_PRIMARY &&
-            currentMode != HWC_POWER_MODE_DOZE_SUSPEND) {
-            disableHardwareVsync(true); // also cancels any in-progress resync
-
-            // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenReleased();
+        if (display->isPrimary() && currentMode != HWC_POWER_MODE_DOZE_SUSPEND) {
+            if (mUseScheduler) {
+                mScheduler->disableHardwareVsync(true);
+            } else {
+                disableHardwareVsync(true); // also cancels any in-progress resync
+            }
+            if (mUseScheduler) {
+                mScheduler->onScreenReleased(mAppConnectionHandle);
+            } else {
+                mEventThread->onScreenReleased();
+            }
         }
 
-        getHwComposer().setPowerMode(type, mode);
+        getHwComposer().setPowerMode(*displayId, mode);
         mVisibleRegionsDirty = true;
         // from this point on, SF will stop drawing on this display
     } else if (mode == HWC_POWER_MODE_DOZE ||
                mode == HWC_POWER_MODE_NORMAL) {
         // Update display while dozing
-        getHwComposer().setPowerMode(type, mode);
-        if (type == DisplayDevice::DISPLAY_PRIMARY &&
-            currentMode == HWC_POWER_MODE_DOZE_SUSPEND) {
-            // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenAcquired();
-            resyncToHardwareVsync(true);
+        getHwComposer().setPowerMode(*displayId, mode);
+        if (display->isPrimary() && currentMode == HWC_POWER_MODE_DOZE_SUSPEND) {
+            if (mUseScheduler) {
+                mScheduler->onScreenAcquired(mAppConnectionHandle);
+            } else {
+                mEventThread->onScreenAcquired();
+            }
+            resyncToHardwareVsync(true, getVsyncPeriod());
         }
     } else if (mode == HWC_POWER_MODE_DOZE_SUSPEND) {
         // Leave display going to doze
-        if (type == DisplayDevice::DISPLAY_PRIMARY) {
-            disableHardwareVsync(true); // also cancels any in-progress resync
-            // FIXME: eventthread only knows about the main display right now
-            mEventThread->onScreenReleased();
+        if (display->isPrimary()) {
+            if (mUseScheduler) {
+                mScheduler->disableHardwareVsync(true);
+            } else {
+                disableHardwareVsync(true); // also cancels any in-progress resync
+            }
+            if (mUseScheduler) {
+                mScheduler->onScreenReleased(mAppConnectionHandle);
+            } else {
+                mEventThread->onScreenReleased();
+            }
         }
-        getHwComposer().setPowerMode(type, mode);
+        getHwComposer().setPowerMode(*displayId, mode);
     } else {
         ALOGE("Attempting to set unknown power mode: %d\n", mode);
-        getHwComposer().setPowerMode(type, mode);
+        getHwComposer().setPowerMode(*displayId, mode);
     }
-    ALOGD("Finished set power mode=%d, type=%d", mode, hw->getDisplayType());
+
+    if (display->isPrimary()) {
+        mTimeStats->setPowerMode(mode);
+        if (mUseScheduler && mRefreshRateStats) {
+            // Update refresh rate stats.
+            mRefreshRateStats->setPowerMode(mode);
+        }
+    }
+
+    ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
 }
 
-void SurfaceFlinger::setPowerMode(const sp<IBinder>& display, int mode) {
-    class MessageSetPowerMode: public MessageBase {
-        SurfaceFlinger& mFlinger;
-        sp<IBinder> mDisplay;
-        int mMode;
-    public:
-        MessageSetPowerMode(SurfaceFlinger& flinger,
-                const sp<IBinder>& disp, int mode) : mFlinger(flinger),
-                    mDisplay(disp) { mMode = mode; }
-        virtual bool handler() {
-            sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
-            if (hw == nullptr) {
-                ALOGE("Attempt to set power mode = %d for null display %p",
-                        mMode, mDisplay.get());
-            } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
-                ALOGW("Attempt to set power mode = %d for virtual display",
-                        mMode);
-            } else {
-                mFlinger.setPowerModeInternal(
-                        hw, mMode, /*stateLockHeld*/ false);
-            }
-            return true;
+void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
+    postMessageSync(new LambdaMessage([&]() NO_THREAD_SAFETY_ANALYSIS {
+        const auto display = getDisplayDevice(displayToken);
+        if (!display) {
+            ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
+                  displayToken.get());
+        } else if (display->isVirtual()) {
+            ALOGW("Attempt to set power mode %d for virtual display", mode);
+        } else {
+            setPowerModeInternal(display, mode);
         }
-    };
-    sp<MessageBase> msg = new MessageSetPowerMode(*this, display, mode);
-    postMessageSync(msg);
+    }));
 }
 
 // ---------------------------------------------------------------------------
 
-status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asProto)
-        NO_THREAD_SAFETY_ANALYSIS {
-    String8 result;
+status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args,
+                                bool asProto) NO_THREAD_SAFETY_ANALYSIS {
+    std::string result;
 
     IPCThreadState* ipc = IPCThreadState::self();
     const int pid = ipc->getCallingPid();
@@ -3951,8 +4397,8 @@
 
     if ((uid != AID_SHELL) &&
             !PermissionCache::checkPermission(sDump, pid, uid)) {
-        result.appendFormat("Permission Denial: "
-                "can't dump SurfaceFlinger from pid=%d, uid=%d\n", pid, uid);
+        StringAppendF(&result, "Permission Denial: can't dump SurfaceFlinger from pid=%d, uid=%d\n",
+                      pid, uid);
     } else {
         // Try to get the main lock, but give up after one second
         // (this would indicate SF is stuck, but we want to be able to
@@ -3960,105 +4406,42 @@
         status_t err = mStateLock.timedLock(s2ns(1));
         bool locked = (err == NO_ERROR);
         if (!locked) {
-            result.appendFormat(
-                    "SurfaceFlinger appears to be unresponsive (%s [%d]), "
-                    "dumping anyways (no locks held)\n", strerror(-err), err);
+            StringAppendF(&result,
+                          "SurfaceFlinger appears to be unresponsive (%s [%d]), dumping anyways "
+                          "(no locks held)\n",
+                          strerror(-err), err);
         }
 
-        bool dumpAll = true;
-        size_t index = 0;
-        size_t numArgs = args.size();
+        using namespace std::string_literals;
 
-        if (numArgs) {
-            if ((index < numArgs) &&
-                    (args[index] == String16("--list"))) {
-                index++;
-                listLayersLocked(args, index, result);
-                dumpAll = false;
-            }
+        static const std::unordered_map<std::string, Dumper> dumpers = {
+                {"--clear-layer-stats"s, dumper([this](std::string&) { mLayerStats.clear(); })},
+                {"--disable-layer-stats"s, dumper([this](std::string&) { mLayerStats.disable(); })},
+                {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
+                {"--dispsync"s, dumper([this](std::string& s) { mPrimaryDispSync->dump(s); })},
+                {"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })},
+                {"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })},
+                {"--frame-composition"s, dumper(&SurfaceFlinger::dumpFrameCompositionInfo)},
+                {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
+                {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
+                {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
+                {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+                {"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)},
+                {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
+                {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
+                {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
+        };
 
-            if ((index < numArgs) &&
-                    (args[index] == String16("--latency"))) {
-                index++;
-                dumpStatsLocked(args, index, result);
-                dumpAll = false;
-            }
+        const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
 
-            if ((index < numArgs) &&
-                    (args[index] == String16("--latency-clear"))) {
-                index++;
-                clearStatsLocked(args, index, result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                    (args[index] == String16("--dispsync"))) {
-                index++;
-                mPrimaryDispSync.dump(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                    (args[index] == String16("--static-screen"))) {
-                index++;
-                dumpStaticScreenStats(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                    (args[index] == String16("--frame-events"))) {
-                index++;
-                dumpFrameEventsLocked(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) && (args[index] == String16("--wide-color"))) {
-                index++;
-                dumpWideColorInfo(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                (args[index] == String16("--enable-layer-stats"))) {
-                index++;
-                mLayerStats.enable();
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                (args[index] == String16("--disable-layer-stats"))) {
-                index++;
-                mLayerStats.disable();
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                (args[index] == String16("--clear-layer-stats"))) {
-                index++;
-                mLayerStats.clear();
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                (args[index] == String16("--dump-layer-stats"))) {
-                index++;
-                mLayerStats.dump(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) && (args[index] == String16("--timestats"))) {
-                index++;
-                mTimeStats.parseArgs(asProto, args, index, result);
-                dumpAll = false;
-            }
-        }
-
-        if (dumpAll) {
+        if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+            (it->second)(args, asProto, result);
+        } else {
             if (asProto) {
                 LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
                 result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
             } else {
-                dumpAllLocked(args, index, result);
+                dumpAllLocked(args, result);
             }
         }
 
@@ -4066,53 +4449,33 @@
             mStateLock.unlock();
         }
     }
-    write(fd, result.string(), result.size());
+    write(fd, result.c_str(), result.size());
     return NO_ERROR;
 }
 
-void SurfaceFlinger::listLayersLocked(const Vector<String16>& /* args */,
-        size_t& /* index */, String8& result) const
-{
-    mCurrentState.traverseInZOrder([&](Layer* layer) {
-        result.appendFormat("%s\n", layer->getName().string());
-    });
+void SurfaceFlinger::listLayersLocked(std::string& result) const {
+    mCurrentState.traverseInZOrder(
+            [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); });
 }
 
-void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index,
-        String8& result) const
-{
-    String8 name;
-    if (index < args.size()) {
-        name = String8(args[index]);
-        index++;
-    }
+void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
+    StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriod());
 
-    const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
-    const nsecs_t period = activeConfig->getVsyncPeriod();
-    result.appendFormat("%" PRId64 "\n", period);
-
-    if (name.isEmpty()) {
-        mAnimFrameTracker.dumpStats(result);
-    } else {
+    if (args.size() > 1) {
+        const auto name = String8(args[1]);
         mCurrentState.traverseInZOrder([&](Layer* layer) {
             if (name == layer->getName()) {
                 layer->dumpFrameStats(result);
             }
         });
+    } else {
+        mAnimFrameTracker.dumpStats(result);
     }
 }
 
-void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index,
-        String8& /* result */)
-{
-    String8 name;
-    if (index < args.size()) {
-        name = String8(args[index]);
-        index++;
-    }
-
+void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) {
     mCurrentState.traverseInZOrder([&](Layer* layer) {
-        if (name.isEmpty() || (name == layer->getName())) {
+        if (args.size() < 2 || String8(args[1]) == layer->getName()) {
             layer->clearFrameStats();
         }
     });
@@ -4120,6 +4483,10 @@
     mAnimFrameTracker.clearStats();
 }
 
+void SurfaceFlinger::dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const {
+    mTimeStats->parseArgs(asProto, args, result);
+}
+
 // This should only be called from the main thread.  Otherwise it would need
 // the lock and should use mCurrentState rather than mDrawingState.
 void SurfaceFlinger::logFrameStats() {
@@ -4130,37 +4497,49 @@
     mAnimFrameTracker.logAndResetStats(String8("<win-anim>"));
 }
 
-void SurfaceFlinger::appendSfConfigString(String8& result) const
-{
+void SurfaceFlinger::appendSfConfigString(std::string& result) const {
     result.append(" [sf");
 
     if (isLayerTripleBufferingDisabled())
         result.append(" DISABLE_TRIPLE_BUFFERING");
 
-    result.appendFormat(" PRESENT_TIME_OFFSET=%" PRId64 , dispSyncPresentTimeOffset);
-    result.appendFormat(" FORCE_HWC_FOR_RBG_TO_YUV=%d", useHwcForRgbToYuv);
-    result.appendFormat(" MAX_VIRT_DISPLAY_DIM=%" PRIu64, maxVirtualDisplaySize);
-    result.appendFormat(" RUNNING_WITHOUT_SYNC_FRAMEWORK=%d", !hasSyncFramework);
-    result.appendFormat(" NUM_FRAMEBUFFER_SURFACE_BUFFERS=%" PRId64,
-                        maxFrameBufferAcquiredBuffers);
+    StringAppendF(&result, " PRESENT_TIME_OFFSET=%" PRId64, dispSyncPresentTimeOffset);
+    StringAppendF(&result, " FORCE_HWC_FOR_RBG_TO_YUV=%d", useHwcForRgbToYuv);
+    StringAppendF(&result, " MAX_VIRT_DISPLAY_DIM=%" PRIu64, maxVirtualDisplaySize);
+    StringAppendF(&result, " RUNNING_WITHOUT_SYNC_FRAMEWORK=%d", !hasSyncFramework);
+    StringAppendF(&result, " NUM_FRAMEBUFFER_SURFACE_BUFFERS=%" PRId64,
+                  maxFrameBufferAcquiredBuffers);
     result.append("]");
 }
 
-void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
-{
-    result.appendFormat("Static screen stats:\n");
+void SurfaceFlinger::dumpVSync(std::string& result) const {
+    mPhaseOffsets->dump(result);
+    StringAppendF(&result,
+                  "    present offset: %9" PRId64 " ns\t     VSYNC period: %9" PRId64 " ns\n\n",
+                  dispSyncPresentTimeOffset, getVsyncPeriod());
+
+    StringAppendF(&result, "Scheduler: %s\n\n", mUseScheduler ? "enabled" : "disabled");
+
+    if (mUseScheduler) {
+        mScheduler->dump(mAppConnectionHandle, result);
+    } else {
+        mEventThread->dump(result);
+    }
+}
+
+void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
+    result.append("Static screen stats:\n");
     for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) {
         float bucketTimeSec = getBE().mFrameBuckets[b] / 1e9;
         float percent = 100.0f *
                 static_cast<float>(getBE().mFrameBuckets[b]) / getBE().mTotalTime;
-        result.appendFormat("  < %zd frames: %.3f s (%.1f%%)\n",
-                b + 1, bucketTimeSec, percent);
+        StringAppendF(&result, "  < %zd frames: %.3f s (%.1f%%)\n", b + 1, bucketTimeSec, percent);
     }
     float bucketTimeSec = getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] / 1e9;
     float percent = 100.0f *
             static_cast<float>(getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1]) / getBE().mTotalTime;
-    result.appendFormat("  %zd+ frames: %.3f s (%.1f%%)\n",
-            SurfaceFlingerBE::NUM_BUCKETS - 1, bucketTimeSec, percent);
+    StringAppendF(&result, "  %zd+ frames: %.3f s (%.1f%%)\n", SurfaceFlingerBE::NUM_BUCKETS - 1,
+                  bucketTimeSec, percent);
 }
 
 void SurfaceFlinger::recordBufferingStats(const char* layerName,
@@ -4181,8 +4560,8 @@
     }
 }
 
-void SurfaceFlinger::dumpFrameEventsLocked(String8& result) {
-    result.appendFormat("Layer frame timestamps:\n");
+void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) {
+    result.append("Layer frame timestamps:\n");
 
     const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
     const size_t count = currentLayers.size();
@@ -4191,7 +4570,7 @@
     }
 }
 
-void SurfaceFlinger::dumpBufferingStats(String8& result) const {
+void SurfaceFlinger::dumpBufferingStats(std::string& result) const {
     result.append("Buffering stats:\n");
     result.append("  [Layer name] <Active time> <Two buffer> "
             "<Double buffered> <Triple buffered>\n");
@@ -4217,41 +4596,102 @@
     for (const auto& sortedPair : sorted) {
         float activeTime = sortedPair.first;
         const BufferTuple& values = sortedPair.second;
-        result.appendFormat("  [%s] %.2f %.3f %.3f %.3f\n",
-                std::get<0>(values).c_str(), activeTime,
-                std::get<1>(values), std::get<2>(values),
-                std::get<3>(values));
+        StringAppendF(&result, "  [%s] %.2f %.3f %.3f %.3f\n", std::get<0>(values).c_str(),
+                      activeTime, std::get<1>(values), std::get<2>(values), std::get<3>(values));
     }
     result.append("\n");
 }
 
-void SurfaceFlinger::dumpWideColorInfo(String8& result) const {
-    result.appendFormat("hasWideColorDisplay: %d\n", hasWideColorDisplay);
-    result.appendFormat("DisplayColorSetting: %s\n",
-            decodeDisplayColorSetting(mDisplayColorSetting).c_str());
-
-    // TODO: print out if wide-color mode is active or not
-
-    for (size_t d = 0; d < mDisplays.size(); d++) {
-        const sp<const DisplayDevice>& displayDevice(mDisplays[d]);
-        int32_t hwcId = displayDevice->getHwcDisplayId();
-        if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) {
+void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
+    for (const auto& [token, display] : mDisplays) {
+        const auto displayId = display->getId();
+        if (!displayId) {
+            continue;
+        }
+        const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
+        if (!hwcDisplayId) {
             continue;
         }
 
-        result.appendFormat("Display %d color modes:\n", hwcId);
-        std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId);
-        for (auto&& mode : modes) {
-            result.appendFormat("    %s (%d)\n", decodeColorMode(mode).c_str(), mode);
+        StringAppendF(&result,
+                      "Display %s (HWC display %" PRIu64 "): ", to_string(*displayId).c_str(),
+                      *hwcDisplayId);
+        uint8_t port;
+        DisplayIdentificationData data;
+        if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
+            result.append("no identification data\n");
+            continue;
         }
 
-        ColorMode currentMode = displayDevice->getActiveColorMode();
-        result.appendFormat("    Current color mode: %s (%d)\n",
-                            decodeColorMode(currentMode).c_str(), currentMode);
+        if (!isEdid(data)) {
+            result.append("unknown identification data: ");
+            for (uint8_t byte : data) {
+                StringAppendF(&result, "%x ", byte);
+            }
+            result.append("\n");
+            continue;
+        }
+
+        const auto edid = parseEdid(data);
+        if (!edid) {
+            result.append("invalid EDID: ");
+            for (uint8_t byte : data) {
+                StringAppendF(&result, "%x ", byte);
+            }
+            result.append("\n");
+            continue;
+        }
+
+        StringAppendF(&result, "port=%u pnpId=%s displayName=\"", port, edid->pnpId.data());
+        result.append(edid->displayName.data(), edid->displayName.length());
+        result.append("\"\n");
+    }
+}
+
+void SurfaceFlinger::dumpWideColorInfo(std::string& result) const {
+    StringAppendF(&result, "Device has wide color display: %d\n", hasWideColorDisplay);
+    StringAppendF(&result, "Device uses color management: %d\n", useColorManagement);
+    StringAppendF(&result, "DisplayColorSetting: %s\n",
+                  decodeDisplayColorSetting(mDisplayColorSetting).c_str());
+
+    // TODO: print out if wide-color mode is active or not
+
+    for (const auto& [token, display] : mDisplays) {
+        const auto displayId = display->getId();
+        if (!displayId) {
+            continue;
+        }
+
+        StringAppendF(&result, "Display %s color modes:\n", to_string(*displayId).c_str());
+        std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
+        for (auto&& mode : modes) {
+            StringAppendF(&result, "    %s (%d)\n", decodeColorMode(mode).c_str(), mode);
+        }
+
+        ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode;
+        StringAppendF(&result, "    Current color mode: %s (%d)\n",
+                      decodeColorMode(currentMode).c_str(), currentMode);
     }
     result.append("\n");
 }
 
+void SurfaceFlinger::dumpFrameCompositionInfo(std::string& result) const {
+    for (const auto& [token, display] : mDisplays) {
+        const auto it = getBE().mEndOfFrameCompositionInfo.find(token);
+        if (it == getBE().mEndOfFrameCompositionInfo.end()) {
+            continue;
+        }
+
+        const auto& compositionInfoList = it->second;
+        StringAppendF(&result, "%s\n", display->getDebugName().c_str());
+        StringAppendF(&result, "numComponents: %zu\n", compositionInfoList.size());
+        for (const auto& compositionInfo : compositionInfoList) {
+            compositionInfo.dump(result, nullptr);
+            result.append("\n");
+        }
+    }
+}
+
 LayersProto SurfaceFlinger::dumpProtoInfo(LayerVector::StateSet stateSet) const {
     LayersProto layersProto;
     const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
@@ -4264,46 +4704,39 @@
     return layersProto;
 }
 
-LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(int32_t hwcId) const {
+LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(const DisplayDevice& displayDevice) const {
     LayersProto layersProto;
-    const sp<DisplayDevice>& displayDevice(mDisplays[hwcId]);
 
     SizeProto* resolution = layersProto.mutable_resolution();
-    resolution->set_w(displayDevice->getWidth());
-    resolution->set_h(displayDevice->getHeight());
+    resolution->set_w(displayDevice.getWidth());
+    resolution->set_h(displayDevice.getHeight());
 
-    layersProto.set_color_mode(decodeColorMode(displayDevice->getActiveColorMode()));
-    layersProto.set_color_transform(decodeColorTransform(displayDevice->getColorTransform()));
-    layersProto.set_global_transform(
-            static_cast<int32_t>(displayDevice->getOrientationTransform()));
+    auto display = displayDevice.getCompositionDisplay();
+    const auto& displayState = display->getState();
 
+    layersProto.set_color_mode(decodeColorMode(displayState.colorMode));
+    layersProto.set_color_transform(decodeColorTransform(displayState.colorTransform));
+    layersProto.set_global_transform(displayState.orientation);
+
+    const auto displayId = displayDevice.getId();
+    LOG_ALWAYS_FATAL_IF(!displayId);
     mDrawingState.traverseInZOrder([&](Layer* layer) {
-        if (!layer->visibleRegion.isEmpty() && layer->getBE().mHwcLayers.count(hwcId)) {
+        if (!layer->visibleRegion.isEmpty() && layer->getBE().mHwcLayers.count(*displayId)) {
             LayerProto* layerProto = layersProto.add_layers();
-            layer->writeToProto(layerProto, hwcId);
+            layer->writeToProto(layerProto, *displayId);
         }
     });
 
     return layersProto;
 }
 
-void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
-        String8& result) const
-{
-    bool colorize = false;
-    if (index < args.size()
-            && (args[index] == String16("--color"))) {
-        colorize = true;
-        index++;
-    }
-
+void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
+    const bool colorize = !args.empty() && args[0] == String16("--color");
     Colorizer colorizer(colorize);
 
     // figure out if we're stuck somewhere
     const nsecs_t now = systemTime();
-    const nsecs_t inSwapBuffers(mDebugInSwapBuffers);
     const nsecs_t inTransaction(mDebugInTransaction);
-    nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0;
     nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0;
 
     /*
@@ -4318,6 +4751,9 @@
     appendGuiConfigString(result);
     result.append("\n");
 
+    result.append("\nDisplay identification data:\n");
+    dumpDisplayIdentificationData(result);
+
     result.append("\nWide-Color information:\n");
     dumpWideColorInfo(result);
 
@@ -4325,51 +4761,39 @@
     result.append("Sync configuration: ");
     colorizer.reset(result);
     result.append(SyncFeatures::getInstance().toString());
-    result.append("\n");
-
-    const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
+    result.append("\n\n");
 
     colorizer.bold(result);
-    result.append("DispSync configuration: ");
+    result.append("VSYNC configuration:\n");
     colorizer.reset(result);
-    const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets();
-    const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets();
-    result.appendFormat(
-        "app phase %" PRId64 " ns, "
-        "sf phase %" PRId64 " ns, "
-        "early app phase %" PRId64 " ns, "
-        "early sf phase %" PRId64 " ns, "
-        "early app gl phase %" PRId64 " ns, "
-        "early sf gl phase %" PRId64 " ns, "
-        "present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
-        vsyncPhaseOffsetNs,
-        sfVsyncPhaseOffsetNs,
-        appEarlyOffset,
-        sfEarlyOffset,
-        appEarlyGlOffset,
-        sfEarlyOffset,
-        dispSyncPresentTimeOffset, activeConfig->getVsyncPeriod());
+    dumpVSync(result);
     result.append("\n");
 
-    // Dump static screen stats
-    result.append("\n");
     dumpStaticScreenStats(result);
     result.append("\n");
 
+    StringAppendF(&result, "Missed frame count: %u\n\n", mFrameMissedCount.load());
+
     dumpBufferingStats(result);
 
     /*
      * Dump the visible layer list
      */
     colorizer.bold(result);
-    result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
-    result.appendFormat("GraphicBufferProducers: %zu, max %zu\n",
-                        mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize);
+    StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers);
+    StringAppendF(&result, "GraphicBufferProducers: %zu, max %zu\n",
+                  mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize);
     colorizer.reset(result);
 
-    LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
-    auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
-    result.append(LayerProtoParser::layersToString(std::move(layerTree)).c_str());
+    {
+        LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
+        auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
+        result.append(LayerProtoParser::layerTreeToString(layerTree));
+        result.append("\n");
+    }
+
+    result.append("\nFrame-Composition information:\n");
+    dumpFrameCompositionInfo(result);
     result.append("\n");
 
     /*
@@ -4377,11 +4801,10 @@
      */
 
     colorizer.bold(result);
-    result.appendFormat("Displays (%zu entries)\n", mDisplays.size());
+    StringAppendF(&result, "Displays (%zu entries)\n", mDisplays.size());
     colorizer.reset(result);
-    for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
-        const sp<const DisplayDevice>& hw(mDisplays[dpy]);
-        hw->dump(result);
+    for (const auto& [token, display] : mDisplays) {
+        display->dump(result);
     }
     result.append("\n");
 
@@ -4393,44 +4816,31 @@
     result.append("SurfaceFlinger global state:\n");
     colorizer.reset(result);
 
-    HWComposer& hwc(getHwComposer());
-    sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
+    getRenderEngine().dump(result);
 
-    getBE().mRenderEngine->dump(result);
-
-    if (hw) {
-        hw->undefinedRegion.dump(result, "undefinedRegion");
-        result.appendFormat("  orientation=%d, isDisplayOn=%d\n",
-                hw->getOrientation(), hw->isDisplayOn());
+    if (const auto display = getDefaultDisplayDeviceLocked()) {
+        display->getCompositionDisplay()->getState().undefinedRegion.dump(result,
+                                                                          "undefinedRegion");
+        StringAppendF(&result, "  orientation=%d, isPoweredOn=%d\n", display->getOrientation(),
+                      display->isPoweredOn());
     }
-    result.appendFormat(
-            "  last eglSwapBuffers() time: %f us\n"
-            "  last transaction time     : %f us\n"
-            "  transaction-flags         : %08x\n"
-            "  refresh-rate              : %f fps\n"
-            "  x-dpi                     : %f\n"
-            "  y-dpi                     : %f\n"
-            "  gpu_to_cpu_unsupported    : %d\n"
-            ,
-            mLastSwapBufferTime/1000.0,
-            mLastTransactionTime/1000.0,
-            mTransactionFlags,
-            1e9 / activeConfig->getVsyncPeriod(),
-            activeConfig->getDpiX(),
-            activeConfig->getDpiY(),
-            !mGpuToCpuSupported);
+    StringAppendF(&result,
+                  "  transaction-flags         : %08x\n"
+                  "  gpu_to_cpu_unsupported    : %d\n",
+                  mTransactionFlags.load(), !mGpuToCpuSupported);
 
-    result.appendFormat("  eglSwapBuffers time: %f us\n",
-            inSwapBuffersDuration/1000.0);
+    if (const auto displayId = getInternalDisplayId();
+        displayId && getHwComposer().isConnected(*displayId)) {
+        const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
+        StringAppendF(&result,
+                      "  refresh-rate              : %f fps\n"
+                      "  x-dpi                     : %f\n"
+                      "  y-dpi                     : %f\n",
+                      1e9 / activeConfig->getVsyncPeriod(), activeConfig->getDpiX(),
+                      activeConfig->getDpiY());
+    }
 
-    result.appendFormat("  transaction time: %f us\n",
-            inTransactionDuration/1000.0);
-
-    /*
-     * VSYNC state
-     */
-    mEventThread->dump(result);
-    result.append("\n");
+    StringAppendF(&result, "  transaction time: %f us\n", inTransactionDuration / 1000.0);
 
     /*
      * Tracing state
@@ -4441,18 +4851,15 @@
     /*
      * HWC layer minidump
      */
-    for (size_t d = 0; d < mDisplays.size(); d++) {
-        const sp<const DisplayDevice>& displayDevice(mDisplays[d]);
-        int32_t hwcId = displayDevice->getHwcDisplayId();
-        if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) {
+    for (const auto& [token, display] : mDisplays) {
+        const auto displayId = display->getId();
+        if (!displayId) {
             continue;
         }
 
-        result.appendFormat("Display %d HWC layers:\n", hwcId);
+        StringAppendF(&result, "Display %s HWC layers:\n", to_string(*displayId).c_str());
         Layer::miniDumpHeader(result);
-        mCurrentState.traverseInZOrder([&](Layer* layer) {
-            layer->miniDump(result, hwcId);
-        });
+        mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, *displayId); });
         result.append("\n");
     }
 
@@ -4463,9 +4870,8 @@
     result.append("h/w composer state:\n");
     colorizer.reset(result);
     bool hwcDisabled = mDebugDisableHWC || mDebugRegion;
-    result.appendFormat("  h/w composer %s\n",
-            hwcDisabled ? "disabled" : "enabled");
-    hwc.dump(result);
+    StringAppendF(&result, "  h/w composer %s\n", hwcDisabled ? "disabled" : "enabled");
+    getHwComposer().dump(result);
 
     /*
      * Dump gralloc state
@@ -4478,27 +4884,31 @@
      */
     if (mVrFlingerRequestsDisplay && mVrFlinger) {
         result.append("VrFlinger state:\n");
-        result.append(mVrFlinger->Dump().c_str());
+        result.append(mVrFlinger->Dump());
         result.append("\n");
     }
+
+    /**
+     * Scheduler dump state.
+     */
+    if (mUseScheduler) {
+        result.append("\nScheduler state:\n");
+        result.append(mScheduler->doDump() + "\n");
+        result.append(mRefreshRateStats->doDump() + "\n");
+    }
 }
 
-const Vector< sp<Layer> >&
-SurfaceFlinger::getLayerSortedByZForHwcDisplay(int id) {
+const Vector<sp<Layer>>& SurfaceFlinger::getLayerSortedByZForHwcDisplay(DisplayId displayId) {
     // Note: mStateLock is held here
-    wp<IBinder> dpy;
-    for (size_t i=0 ; i<mDisplays.size() ; i++) {
-        if (mDisplays.valueAt(i)->getHwcDisplayId() == id) {
-            dpy = mDisplays.keyAt(i);
-            break;
+    for (const auto& [token, display] : mDisplays) {
+        if (display->getId() == displayId) {
+            return getDisplayDeviceLocked(token)->getVisibleLayersSortedByZ();
         }
     }
-    if (dpy == nullptr) {
-        ALOGE("getLayerSortedByZForHwcDisplay: invalid hwc display id %d", id);
-        // Just use the primary display so we have something to return
-        dpy = getBuiltInDisplay(DisplayDevice::DISPLAY_PRIMARY);
-    }
-    return getDisplayDeviceLocked(dpy)->getVisibleLayersSortedByZ();
+
+    ALOGE("%s: Invalid display %s", __FUNCTION__, to_string(displayId).c_str());
+    static const Vector<sp<Layer>> empty;
+    return empty;
 }
 
 bool SurfaceFlinger::startDdmConnection()
@@ -4544,51 +4954,72 @@
 }
 
 status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
-    switch (code) {
-        case CREATE_CONNECTION:
-        case CREATE_DISPLAY:
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wswitch-enum"
+    switch (static_cast<ISurfaceComposerTag>(code)) {
+        // These methods should at minimum make sure that the client requested
+        // access to SF.
         case BOOT_FINISHED:
         case CLEAR_ANIMATION_FRAME_STATS:
-        case GET_ANIMATION_FRAME_STATS:
-        case SET_POWER_MODE:
-        case GET_HDR_CAPABILITIES:
+        case CREATE_DISPLAY:
+        case DESTROY_DISPLAY:
         case ENABLE_VSYNC_INJECTIONS:
+        case GET_ACTIVE_COLOR_MODE:
+        case GET_ANIMATION_FRAME_STATS:
+        case GET_HDR_CAPABILITIES:
+        case SET_ACTIVE_CONFIG:
+        case SET_ACTIVE_COLOR_MODE:
         case INJECT_VSYNC:
-        {
-            // codes that require permission check
+        case SET_POWER_MODE:
+        case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
+        case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
+        case GET_DISPLAYED_CONTENT_SAMPLE: {
             if (!callingThreadHasUnscopedSurfaceFlingerAccess()) {
                 IPCThreadState* ipc = IPCThreadState::self();
                 ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
                         ipc->getCallingPid(), ipc->getCallingUid());
                 return PERMISSION_DENIED;
             }
-            break;
-        }
-        /*
-         * Calling setTransactionState is safe, because you need to have been
-         * granted a reference to Client* and Handle* to do anything with it.
-         *
-         * Creating a scoped connection is safe, as per discussion in ISurfaceComposer.h
-         */
-        case SET_TRANSACTION_STATE:
-        case CREATE_SCOPED_CONNECTION:
-        {
             return OK;
         }
-        case CAPTURE_SCREEN:
-        {
-            // codes that require permission check
+        case GET_LAYER_DEBUG_INFO: {
             IPCThreadState* ipc = IPCThreadState::self();
             const int pid = ipc->getCallingPid();
             const int uid = ipc->getCallingUid();
-            if ((uid != AID_GRAPHICS) &&
-                    !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
-                ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid);
+            if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) {
+                ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
                 return PERMISSION_DENIED;
             }
-            break;
+            return OK;
         }
-        case CAPTURE_LAYERS: {
+        // Used by apps to hook Choreographer to SurfaceFlinger.
+        case CREATE_DISPLAY_EVENT_CONNECTION:
+        // The following calls are currently used by clients that do not
+        // request necessary permissions. However, they do not expose any secret
+        // information, so it is OK to pass them.
+        case AUTHENTICATE_SURFACE:
+        case GET_ACTIVE_CONFIG:
+        case GET_BUILT_IN_DISPLAY:
+        case GET_DISPLAY_COLOR_MODES:
+        case GET_DISPLAY_NATIVE_PRIMARIES:
+        case GET_DISPLAY_CONFIGS:
+        case GET_DISPLAY_STATS:
+        case GET_SUPPORTED_FRAME_TIMESTAMPS:
+        // Calling setTransactionState is safe, because you need to have been
+        // granted a reference to Client* and Handle* to do anything with it.
+        case SET_TRANSACTION_STATE:
+        case CREATE_CONNECTION:
+        case GET_COLOR_MANAGEMENT:
+        case GET_COMPOSITION_PREFERENCE:
+        case GET_PROTECTED_CONTENT_SUPPORT:
+        case CACHE_BUFFER:
+        case UNCACHE_BUFFER:
+        case IS_WIDE_COLOR_DISPLAY: {
+            return OK;
+        }
+        case CAPTURE_LAYERS:
+        case CAPTURE_SCREEN: {
+            // codes that require permission check
             IPCThreadState* ipc = IPCThreadState::self();
             const int pid = ipc->getCallingPid();
             const int uid = ipc->getCallingUid();
@@ -4597,15 +5028,37 @@
                 ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid);
                 return PERMISSION_DENIED;
             }
-            break;
+            return OK;
+        }
+        // The following codes are deprecated and should never be allowed to access SF.
+        case CONNECT_DISPLAY_UNUSED:
+        case CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED: {
+            ALOGE("Attempting to access SurfaceFlinger with unused code: %u", code);
+            return PERMISSION_DENIED;
         }
     }
-    return OK;
+
+    // These codes are used for the IBinder protocol to either interrogate the recipient
+    // side of the transaction for its canonical interface descriptor or to dump its state.
+    // We let them pass by default.
+    if (code == IBinder::INTERFACE_TRANSACTION || code == IBinder::DUMP_TRANSACTION ||
+        code == IBinder::PING_TRANSACTION || code == IBinder::SHELL_COMMAND_TRANSACTION ||
+        code == IBinder::SYSPROPS_TRANSACTION) {
+        return OK;
+    }
+    // Numbers from 1000 to 1031 are currently use for backdoors. The code
+    // in onTransact verifies that the user is root, and has access to use SF.
+    if (code >= 1000 && code <= 1031) {
+        ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
+        return OK;
+    }
+    ALOGE("Permission Denial: SurfaceFlinger did not recognize request code: %u", code);
+    return PERMISSION_DENIED;
+#pragma clang diagnostic pop
 }
 
-status_t SurfaceFlinger::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
+status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                                    uint32_t flags) {
     status_t credentialCheck = CheckTransactCodeCredentials(code);
     if (credentialCheck != OK) {
         return credentialCheck;
@@ -4670,8 +5123,12 @@
                 reply->writeInt32(mDebugDisableHWC);
                 return NO_ERROR;
             case 1013: {
-                sp<const DisplayDevice> hw(getDefaultDisplayDevice());
-                reply->writeInt32(hw->getPageFlipCount());
+                const auto display = getDefaultDisplayDevice();
+                if (!display) {
+                    return NAME_NOT_FOUND;
+                }
+
+                reply->writeInt32(display->getPageFlipCount());
                 return NO_ERROR;
             }
             case 1014: {
@@ -4730,7 +5187,8 @@
             // Needs to be shifted to proper binder interface when we productize
             case 1016: {
                 n = data.readInt32();
-                mPrimaryDispSync.setRefreshSkipCount(n);
+                // TODO(b/113612090): Evaluate if this can be removed.
+                mPrimaryDispSync->setRefreshSkipCount(n);
                 return NO_ERROR;
             }
             case 1017: {
@@ -4740,12 +5198,20 @@
             }
             case 1018: { // Modify Choreographer's phase offset
                 n = data.readInt32();
-                mEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+                if (mUseScheduler) {
+                    mScheduler->setPhaseOffset(mAppConnectionHandle, static_cast<nsecs_t>(n));
+                } else {
+                    mEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+                }
                 return NO_ERROR;
             }
             case 1019: { // Modify SurfaceFlinger's phase offset
                 n = data.readInt32();
-                mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+                if (mUseScheduler) {
+                    mScheduler->setPhaseOffset(mSfConnectionHandle, static_cast<nsecs_t>(n));
+                } else {
+                    mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+                }
                 return NO_ERROR;
             }
             case 1020: { // Layer updates interceptor
@@ -4778,9 +5244,9 @@
                 repaintEverything();
                 return NO_ERROR;
             }
-            case 1024: { // Is wide color gamut rendering/color management supported?
-                reply->writeBool(hasWideColorDisplay);
-                return NO_ERROR;
+            // Deprecate, use 1030 to check whether the device is color managed.
+            case 1024: {
+                return NAME_NOT_FOUND;
             }
             case 1025: { // Set layer tracing
                 n = data.readInt32();
@@ -4802,38 +5268,87 @@
             }
             // Is a DisplayColorSetting supported?
             case 1027: {
-                sp<const DisplayDevice> hw(getDefaultDisplayDevice());
-                if (!hw) {
+                const auto display = getDefaultDisplayDevice();
+                if (!display) {
                     return NAME_NOT_FOUND;
                 }
 
                 DisplayColorSetting setting = static_cast<DisplayColorSetting>(data.readInt32());
                 switch (setting) {
                     case DisplayColorSetting::MANAGED:
-                        reply->writeBool(hasWideColorDisplay);
+                        reply->writeBool(useColorManagement);
                         break;
                     case DisplayColorSetting::UNMANAGED:
                         reply->writeBool(true);
                         break;
                     case DisplayColorSetting::ENHANCED:
-                        reply->writeBool(hw->hasRenderIntent(RenderIntent::ENHANCE));
+                        reply->writeBool(display->hasRenderIntent(RenderIntent::ENHANCE));
                         break;
                     default: // vendor display color setting
-                        reply->writeBool(hw->hasRenderIntent(static_cast<RenderIntent>(setting)));
+                        reply->writeBool(
+                                display->hasRenderIntent(static_cast<RenderIntent>(setting)));
                         break;
                 }
                 return NO_ERROR;
             }
+            // Is VrFlinger active?
+            case 1028: {
+                Mutex::Autolock _l(mStateLock);
+                reply->writeBool(getHwComposer().isUsingVrComposer());
+                return NO_ERROR;
+            }
+            // Is device color managed?
+            case 1030: {
+                reply->writeBool(useColorManagement);
+                return NO_ERROR;
+            }
+            // Override default composition data space
+            // adb shell service call SurfaceFlinger 1031 i32 1 DATASPACE_NUMBER DATASPACE_NUMBER \
+            // && adb shell stop zygote && adb shell start zygote
+            // to restore: adb shell service call SurfaceFlinger 1031 i32 0 && \
+            // adb shell stop zygote && adb shell start zygote
+            case 1031: {
+                Mutex::Autolock _l(mStateLock);
+                n = data.readInt32();
+                if (n) {
+                    n = data.readInt32();
+                    if (n) {
+                        Dataspace dataspace = static_cast<Dataspace>(n);
+                        if (!validateCompositionDataspace(dataspace)) {
+                            return BAD_VALUE;
+                        }
+                        mDefaultCompositionDataspace = dataspace;
+                    }
+                    n = data.readInt32();
+                    if (n) {
+                        Dataspace dataspace = static_cast<Dataspace>(n);
+                        if (!validateCompositionDataspace(dataspace)) {
+                            return BAD_VALUE;
+                        }
+                        mWideColorGamutCompositionDataspace = dataspace;
+                    }
+                } else {
+                    // restore composition data space.
+                    mDefaultCompositionDataspace = defaultCompositionDataspace;
+                    mWideColorGamutCompositionDataspace = wideColorGamutCompositionDataspace;
+                }
+                return NO_ERROR;
+            }
         }
     }
     return err;
 }
 
 void SurfaceFlinger::repaintEverything() {
-    android_atomic_or(1, &mRepaintEverything);
+    mRepaintEverything = true;
     signalTransaction();
 }
 
+void SurfaceFlinger::repaintEverythingForHWC() {
+    mRepaintEverything = true;
+    mEventQueue->invalidateForHWC();
+}
+
 // A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
 class WindowDisconnector {
 public:
@@ -4847,58 +5362,72 @@
     const int mApi;
 };
 
-status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
-                                       Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-                                       int32_t minLayerZ, int32_t maxLayerZ,
+status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken,
+                                       sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace,
+                                       const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                       uint32_t reqWidth, uint32_t reqHeight,
                                        bool useIdentityTransform,
                                        ISurfaceComposer::Rotation rotation) {
     ATRACE_CALL();
 
-    if (CC_UNLIKELY(display == 0)) return BAD_VALUE;
+    if (!displayToken) return BAD_VALUE;
 
-    const sp<const DisplayDevice> device(getDisplayDeviceLocked(display));
-    if (CC_UNLIKELY(device == 0)) return BAD_VALUE;
+    auto renderAreaRotation = fromSurfaceComposerRotation(rotation);
 
-    const Rect& dispScissor = device->getScissor();
-    if (!dispScissor.isEmpty()) {
-        sourceCrop.set(dispScissor);
-        // adb shell screencap will default reqWidth and reqHeight to zeros.
+    sp<DisplayDevice> display;
+    {
+        Mutex::Autolock _l(mStateLock);
+
+        display = getDisplayDeviceLocked(displayToken);
+        if (!display) return BAD_VALUE;
+
+        // set the requested width/height to the logical display viewport size
+        // by default
         if (reqWidth == 0 || reqHeight == 0) {
-            reqWidth = uint32_t(device->getViewport().width());
-            reqHeight = uint32_t(device->getViewport().height());
+            reqWidth = uint32_t(display->getViewport().width());
+            reqHeight = uint32_t(display->getViewport().height());
         }
     }
 
-    DisplayRenderArea renderArea(device, sourceCrop, reqHeight, reqWidth, rotation);
+    DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
+                                 renderAreaRotation);
 
     auto traverseLayers = std::bind(std::mem_fn(&SurfaceFlinger::traverseLayersInDisplay), this,
-                                    device, minLayerZ, maxLayerZ, std::placeholders::_1);
-    return captureScreenCommon(renderArea, traverseLayers, outBuffer, useIdentityTransform);
+                                    display, std::placeholders::_1);
+    return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
+                               useIdentityTransform);
 }
 
 status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder,
-                                       sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
+                                       sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace,
+                                       const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                        float frameScale, bool childrenOnly) {
     ATRACE_CALL();
 
     class LayerRenderArea : public RenderArea {
     public:
         LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
-                        int32_t reqWidth, int32_t reqHeight, bool childrenOnly)
-              : RenderArea(reqHeight, reqWidth, CaptureFill::CLEAR),
+                        int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
+                        bool childrenOnly)
+              : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace),
                 mLayer(layer),
                 mCrop(crop),
+                mNeedsFiltering(false),
                 mFlinger(flinger),
                 mChildrenOnly(childrenOnly) {}
-        const Transform& getTransform() const override { return mTransform; }
+        const ui::Transform& getTransform() const override { return mTransform; }
         Rect getBounds() const override {
             const Layer::State& layerState(mLayer->getDrawingState());
-            return Rect(layerState.active.w, layerState.active.h);
+            return mLayer->getBufferSize(layerState);
         }
-        int getHeight() const override { return mLayer->getDrawingState().active.h; }
-        int getWidth() const override { return mLayer->getDrawingState().active.w; }
+        int getHeight() const override {
+            return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
+        }
+        int getWidth() const override {
+            return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
+        }
         bool isSecure() const override { return false; }
-        bool needsFiltering() const override { return false; }
+        bool needsFiltering() const override { return mNeedsFiltering; }
         Rect getSourceCrop() const override {
             if (mCrop.isEmpty()) {
                 return getBounds();
@@ -4919,14 +5448,19 @@
         };
 
         void render(std::function<void()> drawLayers) override {
+            const Rect sourceCrop = getSourceCrop();
+            // no need to check rotation because there is none
+            mNeedsFiltering = sourceCrop.width() != getReqWidth() ||
+                sourceCrop.height() != getReqHeight();
+
             if (!mChildrenOnly) {
                 mTransform = mLayer->getTransform().inverse();
                 drawLayers();
             } else {
                 Rect bounds = getBounds();
-                screenshotParentLayer =
-                        new ContainerLayer(mFlinger, nullptr, String8("Screenshot Parent"),
-                                           bounds.getWidth(), bounds.getHeight(), 0);
+                screenshotParentLayer = mFlinger->getFactory().createContainerLayer(
+                        LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"),
+                                          bounds.getWidth(), bounds.getHeight(), 0));
 
                 ReparentForDrawing reparent(mLayer, screenshotParentLayer);
                 drawLayers();
@@ -4940,7 +5474,8 @@
         // In the "childrenOnly" case we reparent the children to a screenshot
         // layer which has no properties set and which does not draw.
         sp<ContainerLayer> screenshotParentLayer;
-        Transform mTransform;
+        ui::Transform mTransform;
+        bool mNeedsFiltering;
 
         SurfaceFlinger* mFlinger;
         const bool mChildrenOnly;
@@ -4949,7 +5484,7 @@
     auto layerHandle = reinterpret_cast<Layer::Handle*>(layerHandleBinder.get());
     auto parent = layerHandle->owner.promote();
 
-    if (parent == nullptr || parent->isPendingRemoval()) {
+    if (parent == nullptr || parent->isRemovedFromCurrentState()) {
         ALOGE("captureLayers called with a removed parent");
         return NAME_NOT_FOUND;
     }
@@ -4964,18 +5499,26 @@
     Rect crop(sourceCrop);
     if (sourceCrop.width() <= 0) {
         crop.left = 0;
-        crop.right = parent->getCurrentState().active.w;
+        crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
     }
 
     if (sourceCrop.height() <= 0) {
         crop.top = 0;
-        crop.bottom = parent->getCurrentState().active.h;
+        crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
     }
 
     int32_t reqWidth = crop.width() * frameScale;
     int32_t reqHeight = crop.height() * frameScale;
 
-    LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, childrenOnly);
+    // really small crop or frameScale
+    if (reqWidth <= 0) {
+        reqWidth = 1;
+    }
+    if (reqHeight <= 0) {
+        reqHeight = 1;
+    }
+
+    LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly);
 
     auto traverseLayers = [parent, childrenOnly](const LayerVector::Visitor& visitor) {
         parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -4987,21 +5530,23 @@
             visitor(layer);
         });
     };
-    return captureScreenCommon(renderArea, traverseLayers, outBuffer, false);
+    return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false);
 }
 
 status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
                                              TraverseLayersFunction traverseLayers,
                                              sp<GraphicBuffer>* outBuffer,
+                                             const ui::PixelFormat reqPixelFormat,
                                              bool useIdentityTransform) {
     ATRACE_CALL();
 
-    renderArea.updateDimensions(mPrimaryDisplayOrientation);
-
+    // TODO(b/116112787) Make buffer usage a parameter.
     const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
             GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
-    *outBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
-                                   HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
+    *outBuffer =
+            getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
+                                             static_cast<android_pixel_format>(reqPixelFormat), 1,
+                                             usage, "screenshot");
 
     // This mutex protects syncFd and captureResult for communication of the return values from the
     // main thread back to this Binder thread
@@ -5014,7 +5559,7 @@
     const int uid = IPCThreadState::self()->getCallingUid();
     const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
 
-    sp<LambdaMessage> message = new LambdaMessage([&]() {
+    sp<LambdaMessage> message = new LambdaMessage([&] {
         // If there is a refresh pending, bug out early and tell the binder thread to try again
         // after the refresh.
         if (mRefreshPending) {
@@ -5029,7 +5574,7 @@
         int fd = -1;
         {
             Mutex::Autolock _l(mStateLock);
-            renderArea.render([&]() {
+            renderArea.render([&] {
                 result = captureScreenImplLocked(renderArea, traverseLayers, (*outBuffer).get(),
                                                  useIdentityTransform, forSystem, &fd);
             });
@@ -5045,14 +5590,14 @@
 
     status_t result = postMessageAsync(message);
     if (result == NO_ERROR) {
-        captureCondition.wait(captureLock, [&]() { return captureResult; });
+        captureCondition.wait(captureLock, [&] { return captureResult; });
         while (*captureResult == EAGAIN) {
             captureResult.reset();
             result = postMessageAsync(message);
             if (result != NO_ERROR) {
                 return result;
             }
-            captureCondition.wait(captureLock, [&]() { return captureResult; });
+            captureCondition.wait(captureLock, [&] { return captureResult; });
         }
         result = *captureResult;
     }
@@ -5066,117 +5611,110 @@
 }
 
 void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
-                                            TraverseLayersFunction traverseLayers, bool yswap,
-                                            bool useIdentityTransform) {
+                                            TraverseLayersFunction traverseLayers,
+                                            ANativeWindowBuffer* buffer, bool useIdentityTransform,
+                                            int* outSyncFd) {
     ATRACE_CALL();
 
-    auto& engine(getRenderEngine());
-
-    // get screen geometry
-    const auto raWidth = renderArea.getWidth();
-    const auto raHeight = renderArea.getHeight();
-
     const auto reqWidth = renderArea.getReqWidth();
     const auto reqHeight = renderArea.getReqHeight();
-    Rect sourceCrop = renderArea.getSourceCrop();
+    const auto sourceCrop = renderArea.getSourceCrop();
+    const auto rotation = renderArea.getRotationFlags();
 
-    bool filtering = false;
-    if (mPrimaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
-        filtering = static_cast<int32_t>(reqWidth) != raHeight ||
-                static_cast<int32_t>(reqHeight) != raWidth;
-    } else {
-        filtering = static_cast<int32_t>(reqWidth) != raWidth ||
-                static_cast<int32_t>(reqHeight) != raHeight;
+    renderengine::DisplaySettings clientCompositionDisplay;
+    std::vector<renderengine::LayerSettings> clientCompositionLayers;
+
+    // assume that bounds are never offset, and that they are the same as the
+    // buffer bounds.
+    clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
+    ui::Transform transform = renderArea.getTransform();
+    mat4 m;
+    m[0][0] = transform[0][0];
+    m[0][1] = transform[0][1];
+    m[0][3] = transform[0][2];
+    m[1][0] = transform[1][0];
+    m[1][1] = transform[1][1];
+    m[1][3] = transform[1][2];
+    m[3][0] = transform[2][0];
+    m[3][1] = transform[2][1];
+    m[3][3] = transform[2][2];
+
+    clientCompositionDisplay.globalTransform = m;
+    mat4 rotMatrix;
+    // Displacement for repositioning the clipping rectangle after rotating it
+    // with the rotation hint.
+    int displacementX = 0;
+    int displacementY = 0;
+    float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
+    switch (rotation) {
+        case ui::Transform::ROT_90:
+            rotMatrix = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
+            displacementX = reqWidth;
+            break;
+        case ui::Transform::ROT_180:
+            rotMatrix = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
+            displacementX = reqWidth;
+            displacementY = reqHeight;
+            break;
+        case ui::Transform::ROT_270:
+            rotMatrix = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
+            displacementY = reqHeight;
+            break;
+        default:
+            break;
     }
+    // We need to transform the clipping window into the right spot.
+    // First, rotate the clipping rectangle by the rotation hint to get the
+    // right orientation
+    const vec4 clipTL = vec4(sourceCrop.left, sourceCrop.top, 0, 1);
+    const vec4 clipBR = vec4(sourceCrop.right, sourceCrop.bottom, 0, 1);
+    const vec4 rotClipTL = rotMatrix * clipTL;
+    const vec4 rotClipBR = rotMatrix * clipBR;
+    const int newClipLeft = std::min(rotClipTL[0], rotClipBR[0]);
+    const int newClipTop = std::min(rotClipTL[1], rotClipBR[1]);
+    const int newClipRight = std::max(rotClipTL[0], rotClipBR[0]);
+    const int newClipBottom = std::max(rotClipTL[1], rotClipBR[1]);
 
-    // if a default or invalid sourceCrop is passed in, set reasonable values
-    if (sourceCrop.width() == 0 || sourceCrop.height() == 0 || !sourceCrop.isValid()) {
-        sourceCrop.setLeftTop(Point(0, 0));
-        sourceCrop.setRightBottom(Point(raWidth, raHeight));
-    } else if (mPrimaryDisplayOrientation != DisplayState::eOrientationDefault) {
-        Transform tr;
-        uint32_t flags = 0x00;
-        switch (mPrimaryDisplayOrientation) {
-            case DisplayState::eOrientation90:
-                flags = Transform::ROT_90;
-                break;
-            case DisplayState::eOrientation180:
-                flags = Transform::ROT_180;
-                break;
-            case DisplayState::eOrientation270:
-                flags = Transform::ROT_270;
-                break;
-        }
-        tr.set(flags, raWidth, raHeight);
-        sourceCrop = tr.transform(sourceCrop);
-    }
+    // Now reposition the clipping rectangle with the displacement vector
+    // computed above.
+    const mat4 displacementMat = mat4::translate(vec4(displacementX, displacementY, 0, 1));
 
-    // ensure that sourceCrop is inside screen
-    if (sourceCrop.left < 0) {
-        ALOGE("Invalid crop rect: l = %d (< 0)", sourceCrop.left);
-    }
-    if (sourceCrop.right > raWidth) {
-        ALOGE("Invalid crop rect: r = %d (> %d)", sourceCrop.right, raWidth);
-    }
-    if (sourceCrop.top < 0) {
-        ALOGE("Invalid crop rect: t = %d (< 0)", sourceCrop.top);
-    }
-    if (sourceCrop.bottom > raHeight) {
-        ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, raHeight);
-    }
+    clientCompositionDisplay.clip =
+            Rect(newClipLeft + displacementX, newClipTop + displacementY,
+                 newClipRight + displacementX, newClipBottom + displacementY);
 
-    // assume ColorMode::SRGB / RenderIntent::COLORIMETRIC
-    engine.setOutputDataSpace(Dataspace::SRGB);
-    engine.setDisplayMaxLuminance(DisplayDevice::sDefaultMaxLumiance);
-
-    // make sure to clear all GL error flags
-    engine.checkErrors();
-
-    Transform::orientation_flags rotation = renderArea.getRotationFlags();
-    if (mPrimaryDisplayOrientation != DisplayState::eOrientationDefault) {
-        // convert hw orientation into flag presentation
-        // here inverse transform needed
-        uint8_t hw_rot_90  = 0x00;
-        uint8_t hw_flip_hv = 0x00;
-        switch (mPrimaryDisplayOrientation) {
-            case DisplayState::eOrientation90:
-                hw_rot_90 = Transform::ROT_90;
-                hw_flip_hv = Transform::ROT_180;
-                break;
-            case DisplayState::eOrientation180:
-                hw_flip_hv = Transform::ROT_180;
-                break;
-            case DisplayState::eOrientation270:
-                hw_rot_90  = Transform::ROT_90;
-                break;
-        }
-
-        // transform flags operation
-        // 1) flip H V if both have ROT_90 flag
-        // 2) XOR these flags
-        uint8_t rotation_rot_90  = rotation & Transform::ROT_90;
-        uint8_t rotation_flip_hv = rotation & Transform::ROT_180;
-        if (rotation_rot_90 & hw_rot_90) {
-            rotation_flip_hv = (~rotation_flip_hv) & Transform::ROT_180;
-        }
-        rotation = static_cast<Transform::orientation_flags>
-                   ((rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv));
-    }
-
-    // set-up our viewport
-    engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, raHeight, yswap,
-                                    rotation);
-    engine.disableTexturing();
+    // We need to perform the same transformation in layer space, so propagate
+    // it to the global transform.
+    mat4 clipTransform = displacementMat * rotMatrix;
+    clientCompositionDisplay.globalTransform *= clipTransform;
+    clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace();
+    clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
 
     const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
-    // redraw the screen entirely...
-    engine.clearWithColor(0, 0, 0, alpha);
 
+    renderengine::LayerSettings fillLayer;
+    fillLayer.source.buffer.buffer = nullptr;
+    fillLayer.source.solidColor = half3(0.0, 0.0, 0.0);
+    fillLayer.geometry.boundaries = FloatRect(0.0, 0.0, 1.0, 1.0);
+    fillLayer.alpha = half(alpha);
+    clientCompositionLayers.push_back(fillLayer);
+
+    Region clearRegion = Region::INVALID_REGION;
     traverseLayers([&](Layer* layer) {
-        if (filtering) layer->setFiltering(true);
-        layer->draw(renderArea, useIdentityTransform);
-        if (filtering) layer->setFiltering(false);
+        renderengine::LayerSettings layerSettings;
+        bool prepared = layer->prepareClientLayer(renderArea, useIdentityTransform, clearRegion,
+                                                  layerSettings);
+        if (prepared) {
+            clientCompositionLayers.push_back(layerSettings);
+        }
     });
+
+    clientCompositionDisplay.clearRegion = clearRegion;
+    base::unique_fd drawFence;
+    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers, buffer,
+                                 &drawFence);
+
+    *outSyncFd = drawFence.release();
 }
 
 status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
@@ -5200,66 +5738,10 @@
         ALOGW("FB is protected: PERMISSION_DENIED");
         return PERMISSION_DENIED;
     }
-
-    // this binds the given EGLImage as a framebuffer for the
-    // duration of this scope.
-    RE::BindNativeBufferAsFramebuffer bufferBond(getRenderEngine(), buffer);
-    if (bufferBond.getStatus() != NO_ERROR) {
-        ALOGE("got ANWB binding error while taking screenshot");
-        return INVALID_OPERATION;
-    }
-
-    // this will in fact render into our dequeued buffer
-    // via an FBO, which means we didn't have to create
-    // an EGLSurface and therefore we're not
-    // dependent on the context's EGLConfig.
-    renderScreenImplLocked(renderArea, traverseLayers, true, useIdentityTransform);
-
-    if (DEBUG_SCREENSHOTS) {
-        getRenderEngine().finish();
-        *outSyncFd = -1;
-
-        const auto reqWidth = renderArea.getReqWidth();
-        const auto reqHeight = renderArea.getReqHeight();
-
-        uint32_t* pixels = new uint32_t[reqWidth*reqHeight];
-        getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
-        checkScreenshot(reqWidth, reqHeight, reqWidth, pixels, traverseLayers);
-        delete [] pixels;
-    } else {
-        base::unique_fd syncFd = getRenderEngine().flush();
-        if (syncFd < 0) {
-            getRenderEngine().finish();
-        }
-        *outSyncFd = syncFd.release();
-    }
-
+    renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, outSyncFd);
     return NO_ERROR;
 }
 
-void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
-                                     TraverseLayersFunction traverseLayers) {
-    if (DEBUG_SCREENSHOTS) {
-        for (size_t y = 0; y < h; y++) {
-            uint32_t const* p = (uint32_t const*)vaddr + y * s;
-            for (size_t x = 0; x < w; x++) {
-                if (p[x] != 0xFF000000) return;
-            }
-        }
-        ALOGE("*** we just took a black screenshot ***");
-
-        size_t i = 0;
-        traverseLayers([&](Layer* layer) {
-            const Layer::State& state(layer->getDrawingState());
-            ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f",
-                  layer->isVisible() ? '+' : '-', i, layer->getName().string(),
-                  layer->getLayerStack(), state.z, layer->isVisible(), state.flags,
-                  static_cast<float>(state.color.a));
-            i++;
-        });
-    }
-}
-
 // ---------------------------------------------------------------------------
 
 void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const {
@@ -5270,22 +5752,17 @@
     layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
 }
 
-void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& hw, int32_t minLayerZ,
-                                             int32_t maxLayerZ,
+void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& display,
                                              const LayerVector::Visitor& visitor) {
     // We loop through the first level of layers without traversing,
-    // as we need to interpret min/max layer Z in the top level Z space.
+    // as we need to determine which layers belong to the requested display.
     for (const auto& layer : mDrawingState.layersSortedByZ) {
-        if (!layer->belongsToDisplay(hw->getLayerStack(), false)) {
+        if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
             continue;
         }
-        const Layer::State& state(layer->getDrawingState());
         // relative layers are traversed in Layer::traverseInZOrder
-        if (state.zOrderRelativeOf != nullptr || state.z < minLayerZ || state.z > maxLayerZ) {
-            continue;
-        }
         layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
-            if (!layer->belongsToDisplay(hw->getLayerStack(), false)) {
+            if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
                 return;
             }
             if (!layer->isVisible()) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 463a551..a48e811 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -17,69 +17,70 @@
 #ifndef ANDROID_SURFACE_FLINGER_H
 #define ANDROID_SURFACE_FLINGER_H
 
-#include <memory>
-#include <stdint.h>
 #include <sys/types.h>
 
 /*
  * NOTE: Make sure this file doesn't include  anything from <gl/ > or <gl2/ >
  */
 
-#include <cutils/compiler.h>
+#include <android-base/thread_annotations.h>
 #include <cutils/atomic.h>
-
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/SortedVector.h>
-#include <utils/threads.h>
-#include <utils/Trace.h>
-
-#include <ui/FenceTime.h>
-#include <ui/PixelFormat.h>
-#include <math/mat4.h>
-
+#include <cutils/compiler.h>
+#include <gui/BufferQueue.h>
 #include <gui/FrameTimestamps.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerState.h>
-
 #include <gui/OccupancyTracker.h>
-
 #include <hardware/hwcomposer_defs.h>
-
+#include <layerproto/LayerProtoHeader.h>
+#include <math/mat4.h>
 #include <serviceutils/PriorityDumper.h>
-
 #include <system/graphics.h>
+#include <ui/FenceTime.h>
+#include <ui/PixelFormat.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/Trace.h>
+#include <utils/threads.h>
 
 #include "Barrier.h"
+#include "BufferStateLayerCache.h"
 #include "DisplayDevice.h"
-#include "DispSync.h"
-#include "EventThread.h"
-#include "FrameTracker.h"
-#include "LayerStats.h"
-#include "LayerVector.h"
-#include "MessageQueue.h"
-#include "SurfaceInterceptor.h"
-#include "SurfaceTracing.h"
-#include "StartPropertySetThread.h"
-#include "TimeStats/TimeStats.h"
-#include "VSyncModulator.h"
-
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/HWComposer.h"
-
 #include "Effects/Daltonizer.h"
+#include "FrameTracker.h"
+#include "LayerBE.h"
+#include "LayerStats.h"
+#include "LayerVector.h"
+#include "Scheduler/DispSync.h"
+#include "Scheduler/EventThread.h"
+#include "Scheduler/MessageQueue.h"
+#include "Scheduler/PhaseOffsets.h"
+#include "Scheduler/RefreshRateStats.h"
+#include "Scheduler/Scheduler.h"
+#include "Scheduler/VSyncModulator.h"
+#include "SurfaceFlingerFactory.h"
+#include "SurfaceInterceptor.h"
+#include "SurfaceTracing.h"
+#include "TransactionCompletedThread.h"
 
+#include <atomic>
+#include <cstdint>
+#include <functional>
 #include <map>
+#include <memory>
 #include <mutex>
 #include <queue>
+#include <set>
 #include <string>
 #include <thread>
+#include <type_traits>
+#include <unordered_map>
 #include <utility>
-#include "RenderArea.h"
-
-#include <layerproto/LayerProtoHeader.h>
 
 using namespace android::surfaceflinger;
 
@@ -94,17 +95,24 @@
 class EventThread;
 class IGraphicBufferConsumer;
 class IGraphicBufferProducer;
+class IInputFlinger;
 class InjectVSyncSource;
 class Layer;
 class Surface;
 class SurfaceFlingerBE;
+class TimeStats;
 class VSyncSource;
+struct CompositionInfo;
+
+namespace compositionengine {
+class DisplaySurface;
+} // namespace compositionengine
 
 namespace impl {
 class EventThread;
 } // namespace impl
 
-namespace RE {
+namespace renderengine {
 class RenderEngine;
 }
 
@@ -114,6 +122,10 @@
 class VrFlinger;
 } // namespace dvr
 
+namespace surfaceflinger {
+class NativeWindowSurface;
+} // namespace surfaceflinger
+
 // ---------------------------------------------------------------------------
 
 enum {
@@ -130,53 +142,13 @@
     ENHANCED = 2,
 };
 
-// A thin interface to abstract creating instances of Surface (gui/Surface.h) to
-// use as a NativeWindow.
-class NativeWindowSurface {
-public:
-    virtual ~NativeWindowSurface();
-
-    // Gets the NativeWindow to use for the surface.
-    virtual sp<ANativeWindow> getNativeWindow() const = 0;
-
-    // Indicates that the surface should allocate its buffers now.
-    virtual void preallocateBuffers() = 0;
-};
-
 class SurfaceFlingerBE
 {
 public:
     SurfaceFlingerBE();
 
-    // The current hardware composer interface.
-    //
-    // The following thread safety rules apply when accessing mHwc, either
-    // directly or via getHwComposer():
-    //
-    // 1. When recreating mHwc, acquire mStateLock. We currently recreate mHwc
-    //    only when switching into and out of vr. Recreating mHwc must only be
-    //    done on the main thread.
-    //
-    // 2. When accessing mHwc on the main thread, it's not necessary to acquire
-    //    mStateLock.
-    //
-    // 3. When accessing mHwc on a thread other than the main thread, we always
-    //    need to acquire mStateLock. This is because the main thread could be
-    //    in the process of destroying the current mHwc instance.
-    //
-    // The above thread safety rules only apply to SurfaceFlinger.cpp. In
-    // SurfaceFlinger_hwc1.cpp we create mHwc at surface flinger init and never
-    // destroy it, so it's always safe to access mHwc from any thread without
-    // acquiring mStateLock.
-    std::unique_ptr<HWComposer> mHwc;
-
     const std::string mHwcServiceName; // "default" for real use, something else for testing.
 
-    // constant members (no synchronization needed for access)
-    std::unique_ptr<RE::RenderEngine> mRenderEngine;
-    EGLContext mEGLContext;
-    EGLDisplay mEGLDisplay;
-
     FenceTimeline mGlCompositionDoneTimeline;
     FenceTimeline mDisplayTimeline;
 
@@ -196,6 +168,9 @@
     nsecs_t mTotalTime;
     std::atomic<nsecs_t> mLastSwapTime;
 
+    // Synchronization fence from a GL composition.
+    sp<Fence> flushFence = Fence::NO_FENCE;
+
     // Double- vs. triple-buffering stats
     struct BufferingStats {
         BufferingStats()
@@ -223,6 +198,9 @@
     // use to differentiate callbacks from different hardware composer
     // instances. Each hardware composer instance gets a different sequence id.
     int32_t mComposerSequenceId;
+
+    std::map<wp<IBinder>, std::vector<CompositionInfo>> mCompositionInfo;
+    std::map<wp<IBinder>, std::vector<CompositionInfo>> mEndOfFrameCompositionInfo;
 };
 
 
@@ -279,21 +257,37 @@
     // FramebufferSurface
     static int64_t maxFrameBufferAcquiredBuffers;
 
-    // Indicate if platform supports color management on its
-    // wide-color display. This is typically found on devices
-    // with wide gamut (e.g. Display-P3) display.
-    // This also allows devices with wide-color displays that don't
-    // want to support color management to disable color management.
+    // Indicate if a device has wide color gamut display. This is typically
+    // found on devices with wide color gamut (e.g. Display-P3) display.
     static bool hasWideColorDisplay;
 
+    static int primaryDisplayOrientation;
+
+    // Indicate if device wants color management on its display.
+    static bool useColorManagement;
+
+    static bool useContextPriority;
+
+    // The data space and pixel format that SurfaceFlinger expects hardware composer
+    // to composite efficiently. Meaning under most scenarios, hardware composer
+    // will accept layers with the data space and pixel format.
+    static ui::Dataspace defaultCompositionDataspace;
+    static ui::PixelFormat defaultCompositionPixelFormat;
+
+    // The data space and pixel format that SurfaceFlinger expects hardware composer
+    // to composite efficiently for wide color gamut surfaces. Meaning under most scenarios,
+    // hardware composer will accept layers with the data space and pixel format.
+    static ui::Dataspace wideColorGamutCompositionDataspace;
+    static ui::PixelFormat wideColorGamutCompositionPixelFormat;
+
     static char const* getServiceName() ANDROID_API {
         return "SurfaceFlinger";
     }
 
     struct SkipInitializationTag {};
     static constexpr SkipInitializationTag SkipInitialization;
-    explicit SurfaceFlinger(SkipInitializationTag) ANDROID_API;
-    SurfaceFlinger() ANDROID_API;
+    SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API;
+    explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API;
 
     // must be called before clients can connect
     void init() ANDROID_API;
@@ -301,10 +295,6 @@
     // starts SurfaceFlinger main loop in the current thread
     void run() ANDROID_API;
 
-    enum {
-        EVENT_VSYNC = HWC_EVENT_VSYNC
-    };
-
     // post an asynchronous message to the main thread
     status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0);
 
@@ -314,6 +304,14 @@
     // force full composition on all displays
     void repaintEverything();
 
+    // force full composition on all displays without resetting the scheduler idle timer.
+    void repaintEverythingForHWC();
+
+    surfaceflinger::Factory& getFactory() { return mFactory; }
+
+    // The CompositionEngine encapsulates all composition related interfaces and actions.
+    compositionengine::CompositionEngine& getCompositionEngine() const;
+
     // returns the default Display
     sp<const DisplayDevice> getDefaultDisplayDevice() const {
         Mutex::Autolock _l(mStateLock);
@@ -329,7 +327,7 @@
 
     // enable/disable h/w composer event
     // TODO: this should be made accessible only to EventThread
-    void setVsyncEnabled(int disp, int enabled);
+    void setVsyncEnabled(EventThread::DisplayType displayType, bool enabled);
 
     // called on the main thread by MessageQueue when an internal message
     // is received
@@ -338,14 +336,19 @@
 
     // for debugging only
     // TODO: this should be made accessible only to HWComposer
-    const Vector< sp<Layer> >& getLayerSortedByZForHwcDisplay(int id);
+    const Vector<sp<Layer>>& getLayerSortedByZForHwcDisplay(DisplayId displayId);
 
-    RE::RenderEngine& getRenderEngine() const { return *getBE().mRenderEngine; }
+    renderengine::RenderEngine& getRenderEngine() const;
 
     bool authenticateSurfaceTextureLocked(
         const sp<IGraphicBufferProducer>& bufferProducer) const;
 
-    int getPrimaryDisplayOrientation() const { return mPrimaryDisplayOrientation; }
+    inline void onLayerCreated() { mNumLayers++; }
+    inline void onLayerDestroyed() { mNumLayers--; }
+
+    TransactionCompletedThread& getTransactionCompletedThread() {
+        return mTransactionCompletedThread;
+    }
 
 private:
     friend class Client;
@@ -353,6 +356,8 @@
     friend class impl::EventThread;
     friend class Layer;
     friend class BufferLayer;
+    friend class BufferQueueLayer;
+    friend class BufferStateLayer;
     friend class MonitoredProducer;
 
     // For unit tests
@@ -400,72 +405,92 @@
     /* ------------------------------------------------------------------------
      * IBinder interface
      */
-    virtual status_t onTransact(uint32_t code, const Parcel& data,
-        Parcel* reply, uint32_t flags);
-    virtual status_t dump(int fd, const Vector<String16>& args) { return priorityDump(fd, args); }
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
+    status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
 
     /* ------------------------------------------------------------------------
      * ISurfaceComposer interface
      */
-    virtual sp<ISurfaceComposerClient> createConnection();
-    virtual sp<ISurfaceComposerClient> createScopedConnection(const sp<IGraphicBufferProducer>& gbp);
-    virtual sp<IBinder> createDisplay(const String8& displayName, bool secure);
-    virtual void destroyDisplay(const sp<IBinder>& display);
-    virtual sp<IBinder> getBuiltInDisplay(int32_t id);
-    virtual void setTransactionState(const Vector<ComposerState>& state,
-            const Vector<DisplayState>& displays, uint32_t flags);
-    virtual void bootFinished();
-    virtual bool authenticateSurfaceTexture(
-        const sp<IGraphicBufferProducer>& bufferProducer) const;
-    virtual status_t getSupportedFrameTimestamps(
-            std::vector<FrameEvent>* outSupported) const;
-    virtual sp<IDisplayEventConnection> createDisplayEventConnection(
-            ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp);
-    virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
-                                   Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-                                   int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform,
-                                   ISurfaceComposer::Rotation rotation);
-    virtual status_t captureLayers(const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
-                                   const Rect& sourceCrop, float frameScale, bool childrenOnly);
-    virtual status_t getDisplayStats(const sp<IBinder>& display,
-            DisplayStatInfo* stats);
-    virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport);
-    virtual status_t getDisplayConfigs(const sp<IBinder>& display,
-            Vector<DisplayInfo>* configs);
-    virtual int getActiveConfig(const sp<IBinder>& display);
-    virtual status_t getDisplayColorModes(const sp<IBinder>& display,
-            Vector<ui::ColorMode>* configs);
-    virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& display);
-    virtual status_t setActiveColorMode(const sp<IBinder>& display, ui::ColorMode colorMode);
-    virtual void setPowerMode(const sp<IBinder>& display, int mode);
-    virtual status_t setActiveConfig(const sp<IBinder>& display, int id);
-    virtual status_t clearAnimationFrameStats();
-    virtual status_t getAnimationFrameStats(FrameStats* outStats) const;
-    virtual status_t getHdrCapabilities(const sp<IBinder>& display,
-            HdrCapabilities* outCapabilities) const;
-    virtual status_t enableVSyncInjections(bool enable);
-    virtual status_t injectVSync(nsecs_t when);
-    virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const;
-
+    sp<ISurfaceComposerClient> createConnection() override;
+    sp<IBinder> createDisplay(const String8& displayName, bool secure) override;
+    void destroyDisplay(const sp<IBinder>& displayToken) override;
+    sp<IBinder> getBuiltInDisplay(int32_t id) override;
+    void setTransactionState(const Vector<ComposerState>& state,
+                             const Vector<DisplayState>& displays, uint32_t flags,
+                             const sp<IBinder>& applyToken,
+                             const InputWindowCommands& inputWindowCommands,
+                             int64_t desiredPresentTime) override;
+    void bootFinished() override;
+    bool authenticateSurfaceTexture(
+            const sp<IGraphicBufferProducer>& bufferProducer) const override;
+    status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override;
+    sp<IDisplayEventConnection> createDisplayEventConnection(
+            ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp) override;
+    status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
+                           const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
+                           Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+                           bool useIdentityTransform, ISurfaceComposer::Rotation rotation) override;
+    status_t captureLayers(const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
+                           const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
+                           const Rect& sourceCrop, float frameScale, bool childrenOnly) override;
+    status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
+    status_t getDisplayConfigs(const sp<IBinder>& displayToken,
+                               Vector<DisplayInfo>* configs) override;
+    int getActiveConfig(const sp<IBinder>& displayToken) override;
+    status_t getDisplayColorModes(const sp<IBinder>& displayToken,
+                                  Vector<ui::ColorMode>* configs) override;
+    status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+                                       ui::DisplayPrimaries &primaries);
+    ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
+    status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
+    void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
+    status_t setActiveConfig(const sp<IBinder>& displayToken, int id) override;
+    status_t clearAnimationFrameStats() override;
+    status_t getAnimationFrameStats(FrameStats* outStats) const override;
+    status_t getHdrCapabilities(const sp<IBinder>& displayToken,
+                                HdrCapabilities* outCapabilities) const override;
+    status_t enableVSyncInjections(bool enable) override;
+    status_t injectVSync(nsecs_t when) override;
+    status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const override;
+    status_t getColorManagement(bool* outGetColorManagement) const override;
+    status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
+                                      ui::Dataspace* outWideColorGamutDataspace,
+                                      ui::PixelFormat* outWideColorGamutPixelFormat) const override;
+    status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+                                                   ui::PixelFormat* outFormat,
+                                                   ui::Dataspace* outDataspace,
+                                                   uint8_t* outComponentMask) const override;
+    status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
+                                              uint8_t componentMask,
+                                              uint64_t maxFrames) const override;
+    status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+                                       uint64_t timestamp,
+                                       DisplayedFrameStats* outStats) const override;
+    status_t getProtectedContentSupport(bool* outSupported) const override;
+    status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+                         int32_t* outBufferId) override;
+    status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) override;
+    status_t isWideColorDisplay(const sp<IBinder>& displayToken,
+                                bool* outIsWideColorDisplay) const override;
 
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
      */
-    virtual void binderDied(const wp<IBinder>& who);
+    void binderDied(const wp<IBinder>& who) override;
 
     /* ------------------------------------------------------------------------
      * RefBase interface
      */
-    virtual void onFirstRef();
+    void onFirstRef() override;
 
     /* ------------------------------------------------------------------------
      * HWC2::ComposerCallback / HWComposer::EventHandler interface
      */
-    void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
+    void onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
                          int64_t timestamp) override;
-    void onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
+    void onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
                            HWC2::Connection connection) override;
-    void onRefreshReceived(int32_t sequenceId, hwc2_display_t display) override;
+    void onRefreshReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId) override;
 
     /* ------------------------------------------------------------------------
      * Message handling
@@ -478,18 +503,13 @@
     void signalRefresh();
 
     // called on the main thread in response to initializeDisplays()
-    void onInitializeDisplays();
+    void onInitializeDisplays() REQUIRES(mStateLock);
+    // setActiveConfigInternal() posted on a main thread for async execution
+    status_t setActiveConfigAsync(const sp<IBinder>& displayToken, int mode);
     // called on the main thread in response to setActiveConfig()
-    void setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode);
+    void setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
     // called on the main thread in response to setPowerMode()
-    void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode,
-                              bool stateLockHeld);
-
-    // Called on the main thread in response to setActiveColorMode()
-    void setActiveColorModeInternal(const sp<DisplayDevice>& hw,
-                                    ui::ColorMode colorMode,
-                                    ui::Dataspace dataSpace,
-                                    ui::RenderIntent renderIntent);
+    void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
 
     // Returns whether the transaction actually modified any state
     bool handleMessageTransaction();
@@ -502,6 +522,8 @@
     void handleTransaction(uint32_t transactionFlags);
     void handleTransactionLocked(uint32_t transactionFlags);
 
+    void updateInputWindows();
+    void executeInputWindowCommands();
     void updateCursorAsync();
 
     /* handlePageFlip - latch a new buffer if available and compute the dirty
@@ -513,29 +535,39 @@
     /* ------------------------------------------------------------------------
      * Transactions
      */
+    void applyTransactionState(const Vector<ComposerState>& state,
+                               const Vector<DisplayState>& displays, uint32_t flags,
+                               const InputWindowCommands& inputWindowCommands) REQUIRES(mStateLock);
+    bool flushTransactionQueues();
     uint32_t getTransactionFlags(uint32_t flags);
     uint32_t peekTransactionFlags();
     // Can only be called from the main thread or with mStateLock held
     uint32_t setTransactionFlags(uint32_t flags);
-    uint32_t setTransactionFlags(uint32_t flags, VSyncModulator::TransactionStart transactionStart);
+    uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
+    void latchAndReleaseBuffer(const sp<Layer>& layer);
     void commitTransaction();
     bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
+    bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+                                       const Vector<ComposerState>& states);
     uint32_t setClientStateLocked(const ComposerState& composerState);
     uint32_t setDisplayStateLocked(const DisplayState& s);
-    void setDestroyStateLocked(const ComposerState& composerState);
+    uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands);
 
     /* ------------------------------------------------------------------------
      * Layer management
      */
-    status_t createLayer(const String8& name, const sp<Client>& client,
-            uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-            int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
-            sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent);
+    status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
+                         PixelFormat format, uint32_t flags, LayerMetadata metadata,
+                         sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent);
 
-    status_t createBufferLayer(const sp<Client>& client, const String8& name,
-            uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
-            sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp,
-            sp<Layer>* outLayer);
+    status_t createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w,
+                                    uint32_t h, uint32_t flags, PixelFormat& format,
+                                    sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp,
+                                    sp<Layer>* outLayer);
+
+    status_t createBufferStateLayer(const sp<Client>& client, const String8& name, uint32_t w,
+                                    uint32_t h, uint32_t flags, sp<IBinder>* outHandle,
+                                    sp<Layer>* outLayer);
 
     status_t createColorLayer(const sp<Client>& client, const String8& name,
             uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle,
@@ -547,25 +579,19 @@
 
     String8 getUniqueLayerName(const String8& name);
 
-    // called in response to the window-manager calling
-    // ISurfaceComposerClient::destroySurface()
-    status_t onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle);
-
     // called when all clients have released all their references to
     // this layer meaning it is entirely safe to destroy all
     // resources associated to this layer.
-    status_t onLayerDestroyed(const wp<Layer>& layer);
-
-    // remove a layer from SurfaceFlinger immediately
-    status_t removeLayer(const sp<Layer>& layer, bool topLevelOnly = false);
-    status_t removeLayerLocked(const Mutex&, const sp<Layer>& layer, bool topLevelOnly = false);
+    void onHandleDestroyed(sp<Layer>& layer);
+    void markLayerPendingRemovalLocked(const sp<Layer>& layer);
 
     // add a layer to SurfaceFlinger
     status_t addClientLayer(const sp<Client>& client,
             const sp<IBinder>& handle,
             const sp<IGraphicBufferProducer>& gbc,
             const sp<Layer>& lbc,
-            const sp<Layer>& parent);
+            const sp<Layer>& parent,
+            bool addToCurrentState);
 
     /* ------------------------------------------------------------------------
      * Boot animation, on/off animations and screen capture
@@ -574,18 +600,19 @@
     void startBootAnim();
 
     void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
-                                bool yswap, bool useIdentityTransform);
+                                ANativeWindowBuffer* buffer, bool useIdentityTransform,
+                                int* outSyncFd);
     status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
-                                 sp<GraphicBuffer>* outBuffer,
+                                 sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
                                  bool useIdentityTransform);
     status_t captureScreenImplLocked(const RenderArea& renderArea,
                                      TraverseLayersFunction traverseLayers,
                                      ANativeWindowBuffer* buffer, bool useIdentityTransform,
                                      bool forSystem, int* outSyncFd);
-    void traverseLayersInDisplay(const sp<const DisplayDevice>& display, int32_t minLayerZ,
-                                 int32_t maxLayerZ, const LayerVector::Visitor& visitor);
+    void traverseLayersInDisplay(const sp<const DisplayDevice>& display,
+                                 const LayerVector::Visitor& visitor);
 
-    sp<StartPropertySetThread> mStartPropertySetThread = nullptr;
+    sp<StartPropertySetThread> mStartPropertySetThread;
 
     /* ------------------------------------------------------------------------
      * Properties
@@ -604,155 +631,275 @@
     // called when starting, or restarting after system_server death
     void initializeDisplays();
 
-    sp<const DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) const {
-      Mutex::Autolock _l(mStateLock);
-      return getDisplayDeviceLocked(dpy);
+    sp<const DisplayDevice> getDisplayDevice(const wp<IBinder>& displayToken) const {
+        Mutex::Autolock _l(mStateLock);
+        return getDisplayDeviceLocked(displayToken);
     }
 
-    sp<DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) {
-      Mutex::Autolock _l(mStateLock);
-      return getDisplayDeviceLocked(dpy);
+    sp<DisplayDevice> getDisplayDevice(const wp<IBinder>& displayToken) {
+        Mutex::Autolock _l(mStateLock);
+        return getDisplayDeviceLocked(displayToken);
     }
 
     // NOTE: can only be called from the main thread or with mStateLock held
-    sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& dpy) const {
-        return mDisplays.valueFor(dpy);
+    sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const {
+        return const_cast<SurfaceFlinger*>(this)->getDisplayDeviceLocked(displayToken);
     }
 
     // NOTE: can only be called from the main thread or with mStateLock held
-    sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& dpy) {
-        return mDisplays.valueFor(dpy);
+    sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) {
+        const auto it = mDisplays.find(displayToken);
+        return it == mDisplays.end() ? nullptr : it->second;
     }
 
     sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const {
-        return getDisplayDeviceLocked(mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]);
+        return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked();
     }
 
-    int32_t getDisplayType(const sp<IBinder>& display) {
-        if (!display.get()) return NAME_NOT_FOUND;
-        for (int i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; ++i) {
-            if (display == mBuiltinDisplays[i]) {
-                return i;
-            }
+    sp<DisplayDevice> getDefaultDisplayDeviceLocked() {
+        if (const auto token = getInternalDisplayToken()) {
+            return getDisplayDeviceLocked(token);
         }
-        return NAME_NOT_FOUND;
+        return nullptr;
     }
 
     // mark a region of a layer stack dirty. this updates the dirty
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
 
+    // Initialize structures containing information about the internal
+    // display's native color coordinates using default data
+    void initDefaultDisplayNativePrimaries();
+
     /* ------------------------------------------------------------------------
      * H/W composer
      */
 
-    HWComposer& getHwComposer() const { return *getBE().mHwc; }
+    // The current hardware composer interface.
+    //
+    // The following thread safety rules apply when accessing mHwc, either
+    // directly or via getHwComposer():
+    //
+    // 1. When recreating mHwc, acquire mStateLock. We currently recreate mHwc
+    //    only when switching into and out of vr. Recreating mHwc must only be
+    //    done on the main thread.
+    //
+    // 2. When accessing mHwc on the main thread, it's not necessary to acquire
+    //    mStateLock.
+    //
+    // 3. When accessing mHwc on a thread other than the main thread, we always
+    //    need to acquire mStateLock. This is because the main thread could be
+    //    in the process of destroying the current mHwc instance.
+    //
+    // The above thread safety rules only apply to SurfaceFlinger.cpp. In
+    // SurfaceFlinger_hwc1.cpp we create mHwc at surface flinger init and never
+    // destroy it, so it's always safe to access mHwc from any thread without
+    // acquiring mStateLock.
+    HWComposer& getHwComposer() const;
 
     /* ------------------------------------------------------------------------
      * Compositing
      */
     void invalidateHwcGeometry();
-    void computeVisibleRegions(const sp<const DisplayDevice>& displayDevice,
-            Region& dirtyRegion, Region& opaqueRegion);
+    void computeVisibleRegions(const sp<const DisplayDevice>& display, Region& dirtyRegion,
+                               Region& opaqueRegion);
 
-    void preComposition(nsecs_t refreshStartTime);
-    void postComposition(nsecs_t refreshStartTime);
-    void updateCompositorTiming(
-            nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime,
-            std::shared_ptr<FenceTime>& presentFenceTime);
-    void setCompositorTimingSnapped(
-            nsecs_t vsyncPhase, nsecs_t vsyncInterval,
-            nsecs_t compositeToPresentLatency);
+    void preComposition();
+    void postComposition();
+    void getCompositorTiming(CompositorTiming* compositorTiming);
+    void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
+                                std::shared_ptr<FenceTime>& presentFenceTime);
+    void setCompositorTimingSnapped(const DisplayStatInfo& stats,
+                                    nsecs_t compositeToPresentLatency);
     void rebuildLayerStacks();
 
-    ui::Dataspace getBestDataspace(const sp<const DisplayDevice>& displayDevice,
+    ui::Dataspace getBestDataspace(const sp<const DisplayDevice>& display,
                                    ui::Dataspace* outHdrDataSpace) const;
 
     // Returns the appropriate ColorMode, Dataspace and RenderIntent for the
     // DisplayDevice. The function only returns the supported ColorMode,
     // Dataspace and RenderIntent.
-    void pickColorMode(const sp<DisplayDevice>& displayDevice,
-                       ui::ColorMode* outMode,
-                       ui::Dataspace* outDataSpace,
-                       ui::RenderIntent* outRenderIntent) const;
+    void pickColorMode(const sp<DisplayDevice>& display, ui::ColorMode* outMode,
+                       ui::Dataspace* outDataSpace, ui::RenderIntent* outRenderIntent) const;
 
-    void setUpHWComposer();
-    void doComposition();
-    void doDebugFlashRegions();
+    void calculateWorkingSet();
+    /*
+     * beginFrame - This function handles any pre-frame processing that needs to be
+     * prior to any CompositionInfo handling and is not dependent on data in
+     * CompositionInfo
+     */
+    void beginFrame(const sp<DisplayDevice>& display);
+    /* prepareFrame - This function will call into the DisplayDevice to prepare a
+     * frame after CompositionInfo has been programmed.   This provides a mechanism
+     * to prepare the hardware composer
+     */
+    void prepareFrame(const sp<DisplayDevice>& display);
+    void doComposition(const sp<DisplayDevice>& display, bool repainEverything);
+    void doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything);
     void doTracing(const char* where);
     void logLayerStats();
-    void doDisplayComposition(const sp<const DisplayDevice>& displayDevice, const Region& dirtyRegion);
+    void doDisplayComposition(const sp<DisplayDevice>& display, const Region& dirtyRegion);
 
-    // compose surfaces for display hw. this fails if using GL and the surface
-    // has been destroyed and is no longer valid.
-    bool doComposeSurfaces(const sp<const DisplayDevice>& displayDevice);
+    // This fails if using GL and the surface has been destroyed. readyFence
+    // will be populated if using GL and native fence sync is supported, to
+    // signal when drawing has completed.
+    bool doComposeSurfaces(const sp<DisplayDevice>& display, const Region& debugRegionm,
+                           base::unique_fd* readyFence);
 
-    void postFramebuffer();
-    void drawWormhole(const sp<const DisplayDevice>& displayDevice, const Region& region) const;
+    void postFramebuffer(const sp<DisplayDevice>& display);
+    void postFrame();
+    void drawWormhole(const Region& region) const;
 
     /* ------------------------------------------------------------------------
      * Display management
      */
-    DisplayDevice::DisplayType determineDisplayType(hwc2_display_t display,
-            HWC2::Connection connection) const;
-    sp<DisplayDevice> setupNewDisplayDeviceInternal(const wp<IBinder>& display, int hwcId,
-                                                    const DisplayDeviceState& state,
-                                                    const sp<DisplaySurface>& dispSurface,
-                                                    const sp<IGraphicBufferProducer>& producer);
+    sp<DisplayDevice> setupNewDisplayDeviceInternal(
+            const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
+            const DisplayDeviceState& state,
+            const sp<compositionengine::DisplaySurface>& dispSurface,
+            const sp<IGraphicBufferProducer>& producer);
     void processDisplayChangesLocked();
     void processDisplayHotplugEventsLocked();
 
+    void dispatchDisplayHotplugEvent(EventThread::DisplayType displayType, bool connected);
+
     /* ------------------------------------------------------------------------
      * VSync
      */
+    nsecs_t getVsyncPeriod() const REQUIRES(mStateLock);
     void enableHardwareVsync();
-    void resyncToHardwareVsync(bool makeAvailable);
+    void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
     void disableHardwareVsync(bool makeUnavailable);
 
-public:
-    void resyncWithRateLimit();
-    void getCompositorTiming(CompositorTiming* compositorTiming);
-private:
+    // Sets the refresh rate to newFps by switching active configs, if they are available for
+    // the desired refresh rate.
+    void setRefreshRateTo(float newFps);
 
-    /* ------------------------------------------------------------------------
+    using GetVsyncPeriod = std::function<nsecs_t()>;
+
+    // Stores per-display state about VSYNC.
+    struct VsyncState {
+        explicit VsyncState(SurfaceFlinger& flinger) : flinger(flinger) {}
+
+        void resync(const GetVsyncPeriod&);
+
+        SurfaceFlinger& flinger;
+        std::atomic<nsecs_t> lastResyncTime = 0;
+    };
+
+    const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)};
+
+    auto makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
+        std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
+        return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
+            if (const auto vsync = ptr.lock()) {
+                vsync->resync(getVsyncPeriod);
+            }
+        };
+    }
+
+    /*
+     * Display identification
+     */
+    sp<IBinder> getPhysicalDisplayToken(DisplayId displayId) const {
+        const auto it = mPhysicalDisplayTokens.find(displayId);
+        return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
+    }
+
+    std::optional<DisplayId> getPhysicalDisplayId(const sp<IBinder>& displayToken) const {
+        for (const auto& [id, token] : mPhysicalDisplayTokens) {
+            if (token == displayToken) {
+                return id;
+            }
+        }
+        return {};
+    }
+
+    // TODO(b/74619554): Remove special cases for primary display.
+    sp<IBinder> getInternalDisplayToken() const {
+        const auto displayId = getInternalDisplayId();
+        return displayId ? getPhysicalDisplayToken(*displayId) : nullptr;
+    }
+
+    std::optional<DisplayId> getInternalDisplayId() const {
+        const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
+        return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
+    }
+
+    // TODO(b/74619554): Remove special cases for external display.
+    std::optional<DisplayId> getExternalDisplayId() const {
+        const auto hwcDisplayId = getHwComposer().getExternalHwcDisplayId();
+        return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
+    }
+
+    /*
      * Debugging & dumpsys
      */
-public:
-    status_t dumpCritical(int fd, const Vector<String16>& /*args*/, bool asProto) {
-        return doDump(fd, Vector<String16>(), asProto);
-    }
-
-    status_t dumpAll(int fd, const Vector<String16>& args, bool asProto) {
-        return doDump(fd, args, asProto);
-    }
-
-private:
-    void listLayersLocked(const Vector<String16>& args, size_t& index, String8& result) const;
-    void dumpStatsLocked(const Vector<String16>& args, size_t& index, String8& result) const;
-    void clearStatsLocked(const Vector<String16>& args, size_t& index, String8& result);
-    void dumpAllLocked(const Vector<String16>& args, size_t& index, String8& result) const;
     bool startDdmConnection();
-    void appendSfConfigString(String8& result) const;
-    void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
-                         TraverseLayersFunction traverseLayers);
 
+    using DumpArgs = Vector<String16>;
+    using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
+
+    template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
+    static Dumper dumper(F&& dump) {
+        using namespace std::placeholders;
+        return std::bind(std::forward<F>(dump), _3);
+    }
+
+    template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+    Dumper dumper(F dump) {
+        using namespace std::placeholders;
+        return std::bind(dump, this, _3);
+    }
+
+    template <typename F>
+    Dumper argsDumper(F dump) {
+        using namespace std::placeholders;
+        return std::bind(dump, this, _1, _3);
+    }
+
+    template <typename F>
+    Dumper protoDumper(F dump) {
+        using namespace std::placeholders;
+        return std::bind(dump, this, _1, _2, _3);
+    }
+
+    void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+
+    void appendSfConfigString(std::string& result) const;
+    void listLayersLocked(std::string& result) const;
+    void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+    void clearStatsLocked(const DumpArgs& args, std::string& result);
+    void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
     void logFrameStats();
 
-    void dumpStaticScreenStats(String8& result) const;
+    void dumpVSync(std::string& result) const REQUIRES(mStateLock);
+    void dumpStaticScreenStats(std::string& result) const;
     // Not const because each Layer needs to query Fences and cache timestamps.
-    void dumpFrameEventsLocked(String8& result);
+    void dumpFrameEventsLocked(std::string& result);
 
     void recordBufferingStats(const char* layerName,
             std::vector<OccupancyTracker::Segment>&& history);
-    void dumpBufferingStats(String8& result) const;
-    void dumpWideColorInfo(String8& result) const;
+    void dumpBufferingStats(std::string& result) const;
+    void dumpDisplayIdentificationData(std::string& result) const;
+    void dumpWideColorInfo(std::string& result) const;
+    void dumpFrameCompositionInfo(std::string& result) const;
     LayersProto dumpProtoInfo(LayerVector::StateSet stateSet) const;
-    LayersProto dumpVisibleLayersProtoInfo(int32_t hwcId) const;
+    LayersProto dumpVisibleLayersProtoInfo(const DisplayDevice& display) const;
 
     bool isLayerTripleBufferingDisabled() const {
         return this->mLayerTripleBufferingDisabled;
     }
-    status_t doDump(int fd, const Vector<String16>& args, bool asProto);
+
+    status_t doDump(int fd, const DumpArgs& args, bool asProto);
+
+    status_t dumpCritical(int fd, const DumpArgs&, bool asProto) override {
+        return doDump(fd, DumpArgs(), asProto);
+    }
+
+    status_t dumpAll(int fd, const DumpArgs& args, bool asProto) override {
+        return doDump(fd, args, asProto);
+    }
 
     /* ------------------------------------------------------------------------
      * VrFlinger
@@ -768,10 +915,12 @@
      * Attributes
      */
 
+    surfaceflinger::Factory& mFactory;
+
     // access must be protected by mStateLock
     mutable Mutex mStateLock;
     State mCurrentState{LayerVector::StateSet::Current};
-    volatile int32_t mTransactionFlags;
+    std::atomic<int32_t> mTransactionFlags{0};
     Condition mTransactionCV;
     bool mTransactionPending;
     bool mAnimTransactionPending;
@@ -790,8 +939,7 @@
     bool mLayersRemoved;
     bool mLayersAdded;
 
-    // access must be protected by mInvalidateLock
-    volatile int32_t mRepaintEverything;
+    std::atomic<bool> mRepaintEverything{false};
 
     // constant members (no synchronization needed for access)
     nsecs_t mBootTime;
@@ -803,9 +951,12 @@
     std::unique_ptr<VSyncSource> mSfEventThreadSource;
     std::unique_ptr<InjectVSyncSource> mVSyncInjector;
     std::unique_ptr<EventControlThread> mEventControlThread;
-    sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES];
+    std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens;
 
+    // Calculates correct offsets.
     VSyncModulator mVsyncModulator;
+    // Keeps track of all available phase offsets for different refresh types.
+    std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
 
     // Can only accessed from the main thread, these members
     // don't need synchronization
@@ -825,7 +976,7 @@
     BootStage mBootStage;
 
     struct HotplugEvent {
-        hwc2_display_t display;
+        hwc2_display_t hwcDisplayId;
         HWC2::Connection connection = HWC2::Connection::Invalid;
     };
     // protected by mStateLock
@@ -833,7 +984,7 @@
 
     // this may only be written from the main thread with mStateLock held
     // it may be read from other threads with mStateLock held
-    DefaultKeyedVector< wp<IBinder>, sp<DisplayDevice> > mDisplays;
+    std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays;
 
     // don't use a lock for these, we don't care
     int mDebugRegion;
@@ -841,26 +992,27 @@
     int mDebugDisableHWC;
     int mDebugDisableTransformHint;
     volatile nsecs_t mDebugInSwapBuffers;
-    nsecs_t mLastSwapBufferTime;
     volatile nsecs_t mDebugInTransaction;
     nsecs_t mLastTransactionTime;
+    nsecs_t mPostFramebufferTime;
     bool mForceFullDamage;
     bool mPropagateBackpressure = true;
-    std::unique_ptr<SurfaceInterceptor> mInterceptor =
-            std::make_unique<impl::SurfaceInterceptor>(this);
+    std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)};
     SurfaceTracing mTracing;
     LayerStats mLayerStats;
-    TimeStats& mTimeStats = TimeStats::getInstance();
+    std::unique_ptr<TimeStats> mTimeStats;
     bool mUseHwcVirtualDisplays = false;
+    std::atomic<uint32_t> mFrameMissedCount{0};
+
+    TransactionCompletedThread mTransactionCompletedThread;
 
     // Restrict layers to use two buffers in their bufferqueues.
     bool mLayerTripleBufferingDisabled = false;
 
     // these are thread safe
-    mutable std::unique_ptr<MessageQueue> mEventQueue{std::make_unique<impl::MessageQueue>()};
+    mutable std::unique_ptr<MessageQueue> mEventQueue{mFactory.createMessageQueue()};
     FrameTracker mAnimFrameTracker;
-    DispSync mPrimaryDispSync;
-    int mPrimaryDisplayOrientation = DisplayState::eOrientationDefault;
+    std::unique_ptr<DispSync> mPrimaryDispSync;
 
     // protected by mDestroyedLayerLock;
     mutable Mutex mDestroyedLayerLock;
@@ -870,6 +1022,7 @@
     Mutex mHWVsyncLock;
     bool mPrimaryHWVsyncEnabled;
     bool mHWVsyncAvailable;
+    nsecs_t mRefreshStartTime;
 
     std::atomic<bool> mRefreshPending{false};
 
@@ -880,6 +1033,27 @@
     uint32_t mTexturePoolSize = 0;
     std::vector<uint32_t> mTexturePool;
 
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& strongPointer) const {
+            return std::hash<IBinder*>{}(strongPointer.get());
+        }
+    };
+    struct TransactionState {
+        TransactionState(const Vector<ComposerState>& composerStates,
+                         const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+                         int64_t desiredPresentTime)
+              : states(composerStates),
+                displays(displayStates),
+                flags(transactionFlags),
+                time(desiredPresentTime) {}
+
+        Vector<ComposerState> states;
+        Vector<DisplayState> displays;
+        uint32_t flags;
+        int64_t time;
+    };
+    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues;
+
     /* ------------------------------------------------------------------------
      * Feature prototyping
      */
@@ -901,20 +1075,30 @@
     std::thread::id mMainThreadId;
 
     DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::ENHANCED;
-    // Applied on Display P3 layers when the render intent is non-colorimetric.
-    mat4 mEnhancedSaturationMatrix;
 
-    using CreateBufferQueueFunction =
-            std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
-                               sp<IGraphicBufferConsumer>* /* outConsumer */,
-                               bool /* consumerIsSurfaceFlinger */)>;
-    CreateBufferQueueFunction mCreateBufferQueue;
-
-    using CreateNativeWindowSurfaceFunction =
-            std::function<std::unique_ptr<NativeWindowSurface>(const sp<IGraphicBufferProducer>&)>;
-    CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface;
+    ui::Dataspace mDefaultCompositionDataspace;
+    ui::Dataspace mWideColorGamutCompositionDataspace;
 
     SurfaceFlingerBE mBE;
+    std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
+
+    /* ------------------------------------------------------------------------
+     * Scheduler
+     */
+    bool mUseScheduler = false;
+    std::unique_ptr<Scheduler> mScheduler;
+    sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
+    sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
+    std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
+
+    /* ------------------------------------------------------------------------ */
+    sp<IInputFlinger> mInputFlinger;
+
+    InputWindowCommands mInputWindowCommands;
+
+    BufferStateLayerCache mBufferStateLayerCache;
+
+    ui::DisplayPrimaries mInternalDisplayPrimaries;
 };
 }; // namespace android
 
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
new file mode 100644
index 0000000..773a5d1
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/CompositionEngine.h>
+#include <ui/GraphicBuffer.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "ColorLayer.h"
+#include "ContainerLayer.h"
+#include "DisplayDevice.h"
+#include "Layer.h"
+#include "NativeWindowSurface.h"
+#include "StartPropertySetThread.h"
+#include "SurfaceFlinger.h"
+#include "SurfaceFlingerFactory.h"
+#include "SurfaceInterceptor.h"
+
+#include "DisplayHardware/ComposerHal.h"
+#include "Scheduler/DispSync.h"
+#include "Scheduler/EventControlThread.h"
+#include "Scheduler/MessageQueue.h"
+#include "Scheduler/PhaseOffsets.h"
+#include "Scheduler/Scheduler.h"
+#include "TimeStats/TimeStats.h"
+
+namespace android::surfaceflinger {
+
+sp<SurfaceFlinger> createSurfaceFlinger() {
+    class Factory final : public surfaceflinger::Factory {
+    public:
+        Factory() = default;
+        ~Factory() = default;
+
+        std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework,
+                                                 int64_t dispSyncPresentTimeOffset) override {
+            // Note: We create a local temporary with the real DispSync implementation
+            // type temporarily so we can initialize it with the configured values,
+            // before storing it for more generic use using the interface type.
+            auto primaryDispSync = std::make_unique<android::impl::DispSync>(name);
+            primaryDispSync->init(hasSyncFramework, dispSyncPresentTimeOffset);
+            return primaryDispSync;
+        }
+
+        std::unique_ptr<EventControlThread> createEventControlThread(
+                std::function<void(bool)> setVSyncEnabled) override {
+            return std::make_unique<android::impl::EventControlThread>(setVSyncEnabled);
+        }
+
+        std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override {
+            return std::make_unique<android::impl::HWComposer>(
+                    std::make_unique<Hwc2::impl::Composer>(serviceName));
+        }
+
+        std::unique_ptr<MessageQueue> createMessageQueue() override {
+            return std::make_unique<android::impl::MessageQueue>();
+        }
+
+        std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+            return std::make_unique<scheduler::impl::PhaseOffsets>();
+        }
+
+        std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) override {
+            return std::make_unique<Scheduler>(callback);
+        }
+
+        std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(
+                SurfaceFlinger* flinger) override {
+            return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+        }
+
+        sp<StartPropertySetThread> createStartPropertySetThread(
+                bool timestampPropertyValue) override {
+            return new StartPropertySetThread(timestampPropertyValue);
+        }
+
+        sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override {
+            return new DisplayDevice(std::move(creationArgs));
+        }
+
+        sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
+                                              uint32_t layerCount, uint64_t usage,
+                                              std::string requestorName) override {
+            return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
+        }
+
+        void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+                               sp<IGraphicBufferConsumer>* outConsumer,
+                               bool consumerIsSurfaceFlinger) override {
+            BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
+        }
+
+        std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+                const sp<IGraphicBufferProducer>& producer) override {
+            return surfaceflinger::impl::createNativeWindowSurface(producer);
+        }
+
+        std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override {
+            return compositionengine::impl::createCompositionEngine();
+        }
+
+        sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override {
+            return new ContainerLayer(args);
+        }
+
+        sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) override {
+            return new BufferQueueLayer(args);
+        }
+
+        sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override {
+            return new BufferStateLayer(args);
+        }
+
+        sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) override {
+            return new ColorLayer(args);
+        }
+
+        std::unique_ptr<TimeStats> createTimeStats() override {
+            return std::make_unique<TimeStats>();
+        }
+    };
+    static Factory factory;
+
+    return new SurfaceFlinger(factory);
+}
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
new file mode 100644
index 0000000..ac99e2a
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cinttypes>
+#include <functional>
+#include <memory>
+#include <string>
+
+#include <cutils/compiler.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+typedef int32_t PixelFormat;
+
+class BufferQueueLayer;
+class BufferStateLayer;
+class ColorLayer;
+class ContainerLayer;
+class DisplayDevice;
+class DispSync;
+class EventControlThread;
+class GraphicBuffer;
+class HWComposer;
+class IGraphicBufferConsumer;
+class IGraphicBufferProducer;
+class MessageQueue;
+class Scheduler;
+class StartPropertySetThread;
+class SurfaceFlinger;
+class SurfaceInterceptor;
+class TimeStats;
+
+struct DisplayDeviceCreationArgs;
+struct LayerCreationArgs;
+
+namespace compositionengine {
+class CompositionEngine;
+} // namespace compositionengine
+
+namespace scheduler {
+class PhaseOffsets;
+} // namespace scheduler
+namespace surfaceflinger {
+
+class NativeWindowSurface;
+
+// The interface that SurfaceFlinger uses to create all of the implementations
+// of each interface.
+class Factory {
+public:
+    virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework,
+                                                     int64_t dispSyncPresentTimeOffset) = 0;
+    virtual std::unique_ptr<EventControlThread> createEventControlThread(
+            std::function<void(bool)> setVSyncEnabled) = 0;
+    virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
+    virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
+    virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0;
+    virtual std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) = 0;
+    virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
+
+    virtual sp<StartPropertySetThread> createStartPropertySetThread(
+            bool timestampPropertyValue) = 0;
+    virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) = 0;
+    virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height,
+                                                  PixelFormat format, uint32_t layerCount,
+                                                  uint64_t usage, std::string requestorName) = 0;
+    virtual void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+                                   sp<IGraphicBufferConsumer>* outConsumer,
+                                   bool consumerIsSurfaceFlinger) = 0;
+    virtual std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+            const sp<IGraphicBufferProducer>&) = 0;
+
+    virtual std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() = 0;
+
+    virtual sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) = 0;
+    virtual sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
+    virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0;
+    virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
+
+    virtual std::unique_ptr<TimeStats> createTimeStats() = 0;
+
+protected:
+    ~Factory() = default;
+};
+
+ANDROID_API sp<SurfaceFlinger> createSurfaceFlinger();
+
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
new file mode 100644
index 0000000..b654ba7
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -0,0 +1,246 @@
+
+#include <sysprop/SurfaceFlingerProperties.sysprop.h>
+
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/types.h>
+#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
+#include <tuple>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace android {
+namespace sysprop {
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+using ::android::hardware::graphics::common::V1_2::Dataspace;
+using ::android::hardware::graphics::common::V1_2::PixelFormat;
+
+int64_t vsync_event_phase_offset_ns(int64_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::vsync_event_phase_offset_ns();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(
+            defaultValue);
+}
+
+int64_t vsync_sf_event_phase_offset_ns(int64_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::vsync_sf_event_phase_offset_ns();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(
+            defaultValue);
+}
+
+bool use_context_priority(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::use_context_priority();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useContextPriority>(
+            defaultValue);
+}
+
+int64_t max_frame_buffer_acquired_buffers(int64_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::max_frame_buffer_acquired_buffers();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(
+            defaultValue);
+}
+
+bool has_wide_color_display(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::has_wide_color_display();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(
+            defaultValue);
+}
+
+bool running_without_sync_framework(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::running_without_sync_framework();
+    if (temp.has_value()) {
+        return !(*temp);
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasSyncFramework>(defaultValue);
+}
+
+bool has_HDR_display(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::has_HDR_display();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(defaultValue);
+}
+
+int64_t present_time_offset_from_vsync_ns(int64_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::present_time_offset_from_vsync_ns();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(
+            defaultValue);
+}
+
+bool force_hwc_copy_for_virtual_displays(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::force_hwc_copy_for_virtual_displays();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useHwcForRGBtoYUV>(
+            defaultValue);
+}
+
+int64_t max_virtual_display_dimension(int64_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::max_virtual_display_dimension();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getUInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::maxVirtualDisplaySize>(
+            defaultValue);
+}
+
+bool use_vr_flinger(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::use_vr_flinger();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useVrFlinger>(defaultValue);
+}
+
+bool start_graphics_allocator_service(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::start_graphics_allocator_service();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(
+            defaultValue);
+}
+
+SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation(
+        SurfaceFlingerProperties::primary_display_orientation_values defaultValue) {
+    auto temp = SurfaceFlingerProperties::primary_display_orientation();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    auto configDefault = DisplayOrientation::ORIENTATION_0;
+    switch (defaultValue) {
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90:
+            configDefault = DisplayOrientation::ORIENTATION_90;
+            break;
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180:
+            configDefault = DisplayOrientation::ORIENTATION_180;
+            break;
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270:
+            configDefault = DisplayOrientation::ORIENTATION_270;
+            break;
+        default:
+            configDefault = DisplayOrientation::ORIENTATION_0;
+            break;
+    }
+    DisplayOrientation result =
+            getDisplayOrientation<V1_1::ISurfaceFlingerConfigs,
+                                  &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>(
+                    configDefault);
+    switch (result) {
+        case DisplayOrientation::ORIENTATION_90:
+            return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90;
+        case DisplayOrientation::ORIENTATION_180:
+            return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180;
+        case DisplayOrientation::ORIENTATION_270:
+            return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270;
+        default:
+            break;
+    }
+    return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0;
+}
+
+bool use_color_management(bool defaultValue) {
+    auto tmpuseColorManagement = SurfaceFlingerProperties::use_color_management();
+    auto tmpHasHDRDisplay = SurfaceFlingerProperties::has_HDR_display();
+    auto tmpHasWideColorDisplay = SurfaceFlingerProperties::has_wide_color_display();
+    if (tmpuseColorManagement.has_value() && tmpHasHDRDisplay.has_value() &&
+        tmpHasWideColorDisplay.has_value()) {
+        return *tmpuseColorManagement || *tmpHasHDRDisplay || *tmpHasWideColorDisplay;
+    }
+    auto surfaceFlingerConfigsServiceV1_2 = ISurfaceFlingerConfigs::getService();
+    if (surfaceFlingerConfigsServiceV1_2) {
+        return getBool<V1_2::ISurfaceFlingerConfigs,
+                       &V1_2::ISurfaceFlingerConfigs::useColorManagement>(defaultValue);
+    }
+    return defaultValue;
+}
+
+auto getCompositionPreference(sp<V1_2::ISurfaceFlingerConfigs> configsServiceV1_2) {
+    Dataspace defaultCompositionDataspace = Dataspace::V0_SRGB;
+    PixelFormat defaultCompositionPixelFormat = PixelFormat::RGBA_8888;
+    Dataspace wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
+    PixelFormat wideColorGamutCompositionPixelFormat = PixelFormat::RGBA_8888;
+    configsServiceV1_2->getCompositionPreference(
+            [&](auto tmpDefaultDataspace, auto tmpDefaultPixelFormat,
+                auto tmpWideColorGamutDataspace, auto tmpWideColorGamutPixelFormat) {
+                defaultCompositionDataspace = tmpDefaultDataspace;
+                defaultCompositionPixelFormat = tmpDefaultPixelFormat;
+                wideColorGamutCompositionDataspace = tmpWideColorGamutDataspace;
+                wideColorGamutCompositionPixelFormat = tmpWideColorGamutPixelFormat;
+            });
+    return std::tuple(defaultCompositionDataspace, defaultCompositionPixelFormat,
+                      wideColorGamutCompositionDataspace, wideColorGamutCompositionPixelFormat);
+}
+
+int64_t default_composition_dataspace(Dataspace defaultValue) {
+    auto temp = SurfaceFlingerProperties::default_composition_dataspace();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+    if (configsServiceV1_2) {
+        return static_cast<int64_t>(get<0>(getCompositionPreference(configsServiceV1_2)));
+    }
+    return static_cast<int64_t>(defaultValue);
+}
+
+int32_t default_composition_pixel_format(PixelFormat defaultValue) {
+    auto temp = SurfaceFlingerProperties::default_composition_pixel_format();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+    if (configsServiceV1_2) {
+        return static_cast<int32_t>(get<1>(getCompositionPreference(configsServiceV1_2)));
+    }
+    return static_cast<int32_t>(defaultValue);
+}
+
+int64_t wcg_composition_dataspace(Dataspace defaultValue) {
+    auto temp = SurfaceFlingerProperties::wcg_composition_dataspace();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+    if (configsServiceV1_2) {
+        return static_cast<int64_t>(get<2>(getCompositionPreference(configsServiceV1_2)));
+    }
+    return static_cast<int64_t>(defaultValue);
+}
+
+int32_t wcg_composition_pixel_format(PixelFormat defaultValue) {
+    auto temp = SurfaceFlingerProperties::wcg_composition_pixel_format();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+    if (configsServiceV1_2) {
+        return static_cast<int32_t>(get<3>(getCompositionPreference(configsServiceV1_2)));
+    }
+    return static_cast<int32_t>(defaultValue);
+}
+
+} // namespace sysprop
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
new file mode 100644
index 0000000..9b26883
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -0,0 +1,58 @@
+
+#ifndef SURFACEFLINGERPROPERTIES_H_
+#define SURFACEFLINGERPROPERTIES_H_
+
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
+#include <sysprop/SurfaceFlingerProperties.sysprop.h>
+
+#include <cstdint>
+#include <optional>
+#include <vector>
+
+namespace android {
+namespace sysprop {
+
+int64_t vsync_event_phase_offset_ns(int64_t defaultValue);
+
+int64_t vsync_sf_event_phase_offset_ns(int64_t defaultValue);
+
+bool use_context_priority(bool defaultValue);
+
+int64_t max_frame_buffer_acquired_buffers(int64_t defaultValue);
+
+bool has_wide_color_display(bool defaultValue);
+
+bool running_without_sync_framework(bool defaultValue);
+
+bool has_HDR_display(bool defaultValue);
+
+int64_t present_time_offset_from_vsync_ns(int64_t defaultValue);
+
+bool force_hwc_copy_for_virtual_displays(bool defaultValue);
+
+int64_t max_virtual_display_dimension(int64_t defaultValue);
+
+bool use_vr_flinger(bool defaultValue);
+
+bool start_graphics_allocator_service(bool defaultValue);
+
+SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation(
+        SurfaceFlingerProperties::primary_display_orientation_values defaultValue);
+
+bool use_color_management(bool defaultValue);
+
+int64_t default_composition_dataspace(
+        android::hardware::graphics::common::V1_2::Dataspace defaultValue);
+
+int32_t default_composition_pixel_format(
+        android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
+
+int64_t wcg_composition_dataspace(
+        android::hardware::graphics::common::V1_2::Dataspace defaultValue);
+
+int32_t wcg_composition_pixel_format(
+        android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
+} // namespace sysprop
+} // namespace android
+#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 4596a21..7bfe033 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -30,6 +30,7 @@
 namespace android {
 
 // ----------------------------------------------------------------------------
+// TODO(marissaw): add new layer state values to SurfaceInterceptor
 
 SurfaceInterceptor::~SurfaceInterceptor() = default;
 
@@ -95,22 +96,25 @@
         const sp<const Layer>& layer)
 {
     Transaction* transaction(increment->mutable_transaction());
-    transaction->set_synchronous(layer->mTransactionFlags & BnSurfaceComposer::eSynchronous);
-    transaction->set_animation(layer->mTransactionFlags & BnSurfaceComposer::eAnimation);
+    const uint32_t layerFlags = layer->getTransactionFlags();
+    transaction->set_synchronous(layerFlags & BnSurfaceComposer::eSynchronous);
+    transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation);
 
     const int32_t layerId(getLayerId(layer));
-    addPositionLocked(transaction, layerId, layer->mCurrentState.active.transform.tx(),
-            layer->mCurrentState.active.transform.ty());
+    addPositionLocked(transaction, layerId, layer->mCurrentState.active_legacy.transform.tx(),
+                      layer->mCurrentState.active_legacy.transform.ty());
     addDepthLocked(transaction, layerId, layer->mCurrentState.z);
     addAlphaLocked(transaction, layerId, layer->mCurrentState.color.a);
-    addTransparentRegionLocked(transaction, layerId, layer->mCurrentState.activeTransparentRegion);
+    addTransparentRegionLocked(transaction, layerId,
+                               layer->mCurrentState.activeTransparentRegion_legacy);
     addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
-    addCropLocked(transaction, layerId, layer->mCurrentState.crop);
-    if (layer->mCurrentState.barrierLayer != nullptr) {
-        addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.barrierLayer.promote(),
-                layer->mCurrentState.frameNumber);
+    addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
+    addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
+    if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
+        addDeferTransactionLocked(transaction, layerId,
+                                  layer->mCurrentState.barrierLayer_legacy.promote(),
+                                  layer->mCurrentState.frameNumber_legacy);
     }
-    addFinalCropLocked(transaction, layerId, layer->mCurrentState.finalCrop);
     addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
     addFlagsLocked(transaction, layerId, layer->mCurrentState.flags);
 }
@@ -122,10 +126,10 @@
     transaction->set_synchronous(false);
     transaction->set_animation(false);
 
-    addDisplaySurfaceLocked(transaction, display.displayId, display.surface);
-    addDisplayLayerStackLocked(transaction, display.displayId, display.layerStack);
-    addDisplaySizeLocked(transaction, display.displayId, display.width, display.height);
-    addDisplayProjectionLocked(transaction, display.displayId, display.orientation,
+    addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface);
+    addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
+    addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
+    addDisplayProjectionLocked(transaction, display.sequenceId, display.orientation,
             display.viewport, display.frame);
 }
 
@@ -177,10 +181,10 @@
 }
 
 DisplayChange* SurfaceInterceptor::createDisplayChangeLocked(Transaction* transaction,
-        int32_t displayId)
+        int32_t sequenceId)
 {
     DisplayChange* dispChange(transaction->add_display_change());
-    dispChange->set_id(displayId);
+    dispChange->set_id(sequenceId);
     return dispChange;
 }
 
@@ -286,13 +290,12 @@
     setProtoRectLocked(protoRect, rect);
 }
 
-void SurfaceInterceptor::addFinalCropLocked(Transaction* transaction, int32_t layerId,
-        const Rect& rect)
+void SurfaceInterceptor::addCornerRadiusLocked(Transaction* transaction, int32_t layerId,
+                                       float cornerRadius)
 {
     SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    FinalCropChange* finalCropChange(change->mutable_final_crop());
-    Rectangle* protoRect(finalCropChange->mutable_rectangle());
-    setProtoRectLocked(protoRect, rect);
+    CornerRadiusChange* cornerRadiusChange(change->mutable_corner_radius());
+    cornerRadiusChange->set_corner_radius(cornerRadius);
 }
 
 void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
@@ -353,25 +356,26 @@
     if (state.what & layer_state_t::eLayerStackChanged) {
         addLayerStackLocked(transaction, layerId, state.layerStack);
     }
-    if (state.what & layer_state_t::eCropChanged) {
-        addCropLocked(transaction, layerId, state.crop);
+    if (state.what & layer_state_t::eCropChanged_legacy) {
+        addCropLocked(transaction, layerId, state.crop_legacy);
     }
-    if (state.what & layer_state_t::eDeferTransaction) {
+    if (state.what & layer_state_t::eCornerRadiusChanged) {
+        addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
+    }
+    if (state.what & layer_state_t::eDeferTransaction_legacy) {
         sp<Layer> otherLayer = nullptr;
-        if (state.barrierHandle != nullptr) {
-            otherLayer = static_cast<Layer::Handle*>(state.barrierHandle.get())->owner.promote();
-        } else if (state.barrierGbp != nullptr) {
-            auto const& gbp = state.barrierGbp;
+        if (state.barrierHandle_legacy != nullptr) {
+            otherLayer =
+                    static_cast<Layer::Handle*>(state.barrierHandle_legacy.get())->owner.promote();
+        } else if (state.barrierGbp_legacy != nullptr) {
+            auto const& gbp = state.barrierGbp_legacy;
             if (mFlinger->authenticateSurfaceTextureLocked(gbp)) {
                 otherLayer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
             } else {
                 ALOGE("Attempt to defer transaction to to an unrecognized GraphicBufferProducer");
             }
         }
-        addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber);
-    }
-    if (state.what & layer_state_t::eFinalCropChanged) {
-        addFinalCropLocked(transaction, layerId, state.finalCrop);
+        addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber_legacy);
     }
     if (state.what & layer_state_t::eOverrideScalingModeChanged) {
         addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
@@ -379,19 +383,19 @@
 }
 
 void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
-        const DisplayState& state, int32_t displayId)
+        const DisplayState& state, int32_t sequenceId)
 {
     if (state.what & DisplayState::eSurfaceChanged) {
-        addDisplaySurfaceLocked(transaction, displayId, state.surface);
+        addDisplaySurfaceLocked(transaction, sequenceId, state.surface);
     }
     if (state.what & DisplayState::eLayerStackChanged) {
-        addDisplayLayerStackLocked(transaction, displayId, state.layerStack);
+        addDisplayLayerStackLocked(transaction, sequenceId, state.layerStack);
     }
     if (state.what & DisplayState::eDisplaySizeChanged) {
-        addDisplaySizeLocked(transaction, displayId, state.width, state.height);
+        addDisplaySizeLocked(transaction, sequenceId, state.width, state.height);
     }
     if (state.what & DisplayState::eDisplayProjectionChanged) {
-        addDisplayProjectionLocked(transaction, displayId, state.orientation, state.viewport,
+        addDisplayProjectionLocked(transaction, sequenceId, state.orientation, state.viewport,
                 state.frame);
     }
 }
@@ -411,7 +415,7 @@
         ssize_t dpyIdx = displays.indexOfKey(disp.token);
         if (dpyIdx >= 0) {
             const DisplayDeviceState& dispState(displays.valueAt(dpyIdx));
-            addDisplayChangesLocked(transaction, disp, dispState.displayId);
+            addDisplayChangesLocked(transaction, disp, dispState.sequenceId);
         }
     }
 }
@@ -422,8 +426,8 @@
     SurfaceCreation* creation(increment->mutable_surface_creation());
     creation->set_id(getLayerId(layer));
     creation->set_name(getLayerName(layer));
-    creation->set_w(layer->mCurrentState.active.w);
-    creation->set_h(layer->mCurrentState.active.h);
+    creation->set_w(layer->mCurrentState.active_legacy.w);
+    creation->set_h(layer->mCurrentState.active_legacy.h);
 }
 
 void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment,
@@ -448,7 +452,7 @@
     event->set_when(timestamp);
 }
 
-void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId,
+void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
         const sp<const IGraphicBufferProducer>& surface)
 {
     if (surface == nullptr) {
@@ -457,7 +461,7 @@
     uint64_t bufferQueueId = 0;
     status_t err(surface->getUniqueId(&bufferQueueId));
     if (err == NO_ERROR) {
-        DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+        DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
         DispSurfaceChange* surfaceChange(dispChange->mutable_surface());
         surfaceChange->set_buffer_queue_id(bufferQueueId);
         surfaceChange->set_buffer_queue_name(surface->getConsumerName().string());
@@ -469,26 +473,26 @@
 }
 
 void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction,
-        int32_t displayId, uint32_t layerStack)
+        int32_t sequenceId, uint32_t layerStack)
 {
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
     LayerStackChange* layerStackChange(dispChange->mutable_layer_stack());
     layerStackChange->set_layer_stack(layerStack);
 }
 
-void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t displayId,
+void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId,
         uint32_t w, uint32_t h)
 {
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
     SizeChange* sizeChange(dispChange->mutable_size());
     sizeChange->set_w(w);
     sizeChange->set_h(h);
 }
 
 void SurfaceInterceptor::addDisplayProjectionLocked(Transaction* transaction,
-        int32_t displayId, int32_t orientation, const Rect& viewport, const Rect& frame)
+        int32_t sequenceId, int32_t orientation, const Rect& viewport, const Rect& frame)
 {
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
     ProjectionChange* projectionChange(dispChange->mutable_projection());
     projectionChange->set_orientation(orientation);
     Rectangle* viewportRect(projectionChange->mutable_viewport());
@@ -501,22 +505,24 @@
         const DisplayDeviceState& info)
 {
     DisplayCreation* creation(increment->mutable_display_creation());
-    creation->set_id(info.displayId);
+    creation->set_id(info.sequenceId);
     creation->set_name(info.displayName);
-    creation->set_type(info.type);
     creation->set_is_secure(info.isSecure);
+    if (info.displayId) {
+        creation->set_display_id(info.displayId->value);
+    }
 }
 
-void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t displayId) {
+void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t sequenceId) {
     DisplayDeletion* deletion(increment->mutable_display_deletion());
-    deletion->set_id(displayId);
+    deletion->set_id(sequenceId);
 }
 
-void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t displayId,
+void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId,
         int32_t mode)
 {
     PowerModeUpdate* powerModeUpdate(increment->mutable_power_mode_update());
-    powerModeUpdate->set_id(displayId);
+    powerModeUpdate->set_id(sequenceId);
     powerModeUpdate->set_mode(mode);
 }
 
@@ -579,22 +585,22 @@
     addDisplayCreationLocked(createTraceIncrementLocked(), info);
 }
 
-void SurfaceInterceptor::saveDisplayDeletion(int32_t displayId) {
+void SurfaceInterceptor::saveDisplayDeletion(int32_t sequenceId) {
     if (!mEnabled) {
         return;
     }
     ATRACE_CALL();
     std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addDisplayDeletionLocked(createTraceIncrementLocked(), displayId);
+    addDisplayDeletionLocked(createTraceIncrementLocked(), sequenceId);
 }
 
-void SurfaceInterceptor::savePowerModeUpdate(int32_t displayId, int32_t mode) {
+void SurfaceInterceptor::savePowerModeUpdate(int32_t sequenceId, int32_t mode) {
     if (!mEnabled) {
         return;
     }
     ATRACE_CALL();
     std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addPowerModeUpdateLocked(createTraceIncrementLocked(), displayId, mode);
+    addPowerModeUpdateLocked(createTraceIncrementLocked(), sequenceId, mode);
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 96defcc..563a44c 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -66,8 +66,8 @@
 
     // Intercept display data
     virtual void saveDisplayCreation(const DisplayDeviceState& info) = 0;
-    virtual void saveDisplayDeletion(int32_t displayId) = 0;
-    virtual void savePowerModeUpdate(int32_t displayId, int32_t mode) = 0;
+    virtual void saveDisplayDeletion(int32_t sequenceId) = 0;
+    virtual void savePowerModeUpdate(int32_t sequenceId, int32_t mode) = 0;
     virtual void saveVSyncEvent(nsecs_t timestamp) = 0;
 };
 
@@ -101,8 +101,8 @@
 
     // Intercept display data
     void saveDisplayCreation(const DisplayDeviceState& info) override;
-    void saveDisplayDeletion(int32_t displayId) override;
-    void savePowerModeUpdate(int32_t displayId, int32_t mode) override;
+    void saveDisplayDeletion(int32_t sequenceId) override;
+    void savePowerModeUpdate(int32_t sequenceId, int32_t mode) override;
     void saveVSyncEvent(nsecs_t timestamp) override;
 
 private:
@@ -127,8 +127,8 @@
             uint32_t height, uint64_t frameNumber);
     void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
     void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
-    void addDisplayDeletionLocked(Increment* increment, int32_t displayId);
-    void addPowerModeUpdateLocked(Increment* increment, int32_t displayId, int32_t mode);
+    void addDisplayDeletionLocked(Increment* increment, int32_t sequenceId);
+    void addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId, int32_t mode);
 
     // Add surface transactions to the trace
     SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId);
@@ -144,9 +144,9 @@
     void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags);
     void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
     void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+    void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
     void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
             const sp<const Layer>& layer, uint64_t frameNumber);
-    void addFinalCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
     void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
             int32_t overrideScalingMode);
     void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
@@ -155,17 +155,17 @@
             const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
 
     // Add display transactions to the trace
-    DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t displayId);
-    void addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId,
+    DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
+    void addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
             const sp<const IGraphicBufferProducer>& surface);
-    void addDisplayLayerStackLocked(Transaction* transaction, int32_t displayId,
+    void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
             uint32_t layerStack);
-    void addDisplaySizeLocked(Transaction* transaction, int32_t displayId, uint32_t w,
+    void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w,
             uint32_t h);
-    void addDisplayProjectionLocked(Transaction* transaction, int32_t displayId,
+    void addDisplayProjectionLocked(Transaction* transaction, int32_t sequenceId,
             int32_t orientation, const Rect& viewport, const Rect& frame);
     void addDisplayChangesLocked(Transaction* transaction,
-            const DisplayState& state, int32_t displayId);
+            const DisplayState& state, int32_t sequenceId);
 
 
     bool mEnabled {false};
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index 67dcd06..b7e9a91 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -20,28 +20,55 @@
 #include "SurfaceTracing.h"
 
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <log/log.h>
 #include <utils/SystemClock.h>
 #include <utils/Trace.h>
 
 namespace android {
 
-void SurfaceTracing::enable() {
-    ATRACE_CALL();
+void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) {
+    // use the swap trick to make sure memory is released
+    std::queue<LayersTraceProto>().swap(mStorage);
+    mSizeInBytes = newSize;
+    mUsedInBytes = 0U;
+}
+
+void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) {
+    auto protoSize = proto.ByteSize();
+    while (mUsedInBytes + protoSize > mSizeInBytes) {
+        if (mStorage.empty()) {
+            return;
+        }
+        mUsedInBytes -= mStorage.front().ByteSize();
+        mStorage.pop();
+    }
+    mUsedInBytes += protoSize;
+    mStorage.emplace();
+    mStorage.back().Swap(&proto);
+}
+
+void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) {
+    fileProto->mutable_entry()->Reserve(mStorage.size());
+
+    while (!mStorage.empty()) {
+        auto entry = fileProto->add_entry();
+        entry->Swap(&mStorage.front());
+        mStorage.pop();
+    }
+}
+
+void SurfaceTracing::enable(size_t bufferSizeInByte) {
     std::lock_guard<std::mutex> protoGuard(mTraceMutex);
 
     if (mEnabled) {
         return;
     }
     mEnabled = true;
-
-    mTrace = std::make_unique<LayersTraceFileProto>();
-    mTrace->set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
-                             LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
+    mBuffer.reset(bufferSizeInByte);
 }
 
 status_t SurfaceTracing::disable() {
-    ATRACE_CALL();
     std::lock_guard<std::mutex> protoGuard(mTraceMutex);
 
     if (!mEnabled) {
@@ -51,7 +78,7 @@
     status_t err(writeProtoFileLocked());
     ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
     ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
-    mTrace.reset();
+    mBuffer.reset(0);
     return err;
 }
 
@@ -65,43 +92,42 @@
     if (!mEnabled) {
         return;
     }
-    LayersTraceProto* entry = mTrace->add_entry();
-    entry->set_elapsed_realtime_nanos(elapsedRealtimeNano());
-    entry->set_where(where);
-    entry->mutable_layers()->Swap(&layers);
 
-    constexpr int maxBufferedEntryCount = 3600;
-    if (mTrace->entry_size() >= maxBufferedEntryCount) {
-        // TODO: flush buffered entries without disabling tracing
-        ALOGE("too many buffered frames; force disable tracing");
-        mEnabled = false;
-        writeProtoFileLocked();
-        mTrace.reset();
-    }
+    LayersTraceProto entry;
+    entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
+    entry.set_where(where);
+    entry.mutable_layers()->Swap(&layers);
+
+    mBuffer.emplace(std::move(entry));
 }
 
 status_t SurfaceTracing::writeProtoFileLocked() {
     ATRACE_CALL();
 
-    if (!mTrace->IsInitialized()) {
-        return NOT_ENOUGH_DATA;
-    }
+    LayersTraceFileProto fileProto;
     std::string output;
-    if (!mTrace->SerializeToString(&output)) {
+
+    fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
+                               LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
+    mBuffer.flush(&fileProto);
+
+    if (!fileProto.SerializeToString(&output)) {
         return PERMISSION_DENIED;
     }
-    if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
+    if (!android::base::WriteStringToFile(output, kDefaultFileName, true)) {
         return PERMISSION_DENIED;
     }
 
     return NO_ERROR;
 }
 
-void SurfaceTracing::dump(String8& result) const {
+void SurfaceTracing::dump(std::string& result) const {
     std::lock_guard<std::mutex> protoGuard(mTraceMutex);
 
-    result.appendFormat("Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
-    result.appendFormat("  number of entries: %d\n", mTrace ? mTrace->entry_size() : 0);
+    base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+    base::StringAppendF(&result, "  number of entries: %zu (%.2fMB / %.2fMB)\n",
+                        mBuffer.frameCount(), float(mBuffer.used()) / float(1_MB),
+                        float(mBuffer.size()) / float(1_MB));
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index fd8cb82..fd919af 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -18,36 +18,57 @@
 
 #include <layerproto/LayerProtoHeader.h>
 #include <utils/Errors.h>
-#include <utils/String8.h>
 
 #include <memory>
 #include <mutex>
+#include <queue>
 
 using namespace android::surfaceflinger;
 
 namespace android {
 
+constexpr auto operator""_MB(unsigned long long const num) {
+    return num * 1024 * 1024;
+}
+
 /*
  * SurfaceTracing records layer states during surface flinging.
  */
 class SurfaceTracing {
 public:
-    void enable();
+    void enable() { enable(kDefaultBufferCapInByte); }
+    void enable(size_t bufferSizeInByte);
     status_t disable();
-    bool isEnabled() const;
-
     void traceLayers(const char* where, LayersProto);
-    void dump(String8& result) const;
+
+    bool isEnabled() const;
+    void dump(std::string& result) const;
 
 private:
-    static constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/layers_trace.pb";
+    static constexpr auto kDefaultBufferCapInByte = 100_MB;
+    static constexpr auto kDefaultFileName = "/data/misc/wmtrace/layers_trace.pb";
+
+    class LayersTraceBuffer { // ring buffer
+    public:
+        size_t size() const { return mSizeInBytes; }
+        size_t used() const { return mUsedInBytes; }
+        size_t frameCount() const { return mStorage.size(); }
+
+        void reset(size_t newSize);
+        void emplace(LayersTraceProto&& proto);
+        void flush(LayersTraceFileProto* fileProto);
+
+    private:
+        size_t mUsedInBytes = 0U;
+        size_t mSizeInBytes = 0U;
+        std::queue<LayersTraceProto> mStorage;
+    };
 
     status_t writeProtoFileLocked();
 
     bool mEnabled = false;
-    std::string mOutputFileName = DEFAULT_FILENAME;
     mutable std::mutex mTraceMutex;
-    std::unique_ptr<LayersTraceFileProto> mTrace;
+    LayersTraceBuffer mBuffer;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
new file mode 100644
index 0000000..cab33ae
--- /dev/null
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+      "name": "libsurfaceflinger_unittest"
+    },
+    {
+      "name": "libcompositionengine_test"
+    }
+  ]
+}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index d4f1e29..8d3776b 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -24,6 +24,7 @@
 #include <log/log.h>
 
 #include <utils/String8.h>
+#include <utils/Timers.h>
 #include <utils/Trace.h>
 
 #include <algorithm>
@@ -31,27 +32,12 @@
 
 namespace android {
 
-TimeStats& TimeStats::getInstance() {
-    static std::unique_ptr<TimeStats> sInstance;
-    static std::once_flag sOnceFlag;
-
-    std::call_once(sOnceFlag, [] { sInstance.reset(new TimeStats); });
-    return *sInstance.get();
-}
-
-void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index,
-                          String8& result) {
+void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
     ATRACE_CALL();
 
-    if (args.size() > index + 10) {
-        ALOGD("Invalid args count");
-        return;
-    }
-
     std::unordered_map<std::string, int32_t> argsMap;
-    while (index < args.size()) {
+    for (size_t index = 0; index < args.size(); ++index) {
         argsMap[std::string(String8(args[index]).c_str())] = index;
-        ++index;
     }
 
     if (argsMap.count("-disable")) {
@@ -85,7 +71,7 @@
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mMutex);
-    timeStats.totalFrames++;
+    mTimeStats.totalFrames++;
 }
 
 void TimeStats::incrementMissedFrames() {
@@ -94,7 +80,7 @@
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mMutex);
-    timeStats.missedFrames++;
+    mTimeStats.missedFrames++;
 }
 
 void TimeStats::incrementClientCompositionFrames() {
@@ -103,13 +89,13 @@
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mMutex);
-    timeStats.clientCompositionFrames++;
+    mTimeStats.clientCompositionFrames++;
 }
 
-bool TimeStats::recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord) {
+bool TimeStats::recordReadyLocked(int32_t layerID, TimeRecord* timeRecord) {
     if (!timeRecord->ready) {
-        ALOGV("[%s]-[%" PRIu64 "]-presentFence is still not received", layerName.c_str(),
-              timeRecord->frameNumber);
+        ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerID,
+              timeRecord->frameTime.frameNumber);
         return false;
     }
 
@@ -118,11 +104,11 @@
             return false;
         }
         if (timeRecord->acquireFence->getSignalTime() != Fence::SIGNAL_TIME_INVALID) {
-            timeRecord->acquireTime = timeRecord->acquireFence->getSignalTime();
+            timeRecord->frameTime.acquireTime = timeRecord->acquireFence->getSignalTime();
             timeRecord->acquireFence = nullptr;
         } else {
-            ALOGV("[%s]-[%" PRIu64 "]-acquireFence signal time is invalid", layerName.c_str(),
-                  timeRecord->frameNumber);
+            ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerID,
+                  timeRecord->frameTime.frameNumber);
         }
     }
 
@@ -131,11 +117,11 @@
             return false;
         }
         if (timeRecord->presentFence->getSignalTime() != Fence::SIGNAL_TIME_INVALID) {
-            timeRecord->presentTime = timeRecord->presentFence->getSignalTime();
+            timeRecord->frameTime.presentTime = timeRecord->presentFence->getSignalTime();
             timeRecord->presentFence = nullptr;
         } else {
-            ALOGV("[%s]-[%" PRIu64 "]-presentFence signal time invalid", layerName.c_str(),
-                  timeRecord->frameNumber);
+            ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerID,
+                  timeRecord->frameTime.frameNumber);
         }
     }
 
@@ -148,14 +134,15 @@
     return static_cast<int32_t>(delta);
 }
 
+// This regular expression captures the following for instance:
+// StatusBar in StatusBar#0
+// com.appname in com.appname/com.appname.activity#0
+// com.appname in SurfaceView - com.appname/com.appname.activity#0
+static const std::regex packageNameRegex("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+");
+
 static std::string getPackageName(const std::string& layerName) {
-    // This regular expression captures the following for instance:
-    // StatusBar in StatusBar#0
-    // com.appname in com.appname/com.appname.activity#0
-    // com.appname in SurfaceView - com.appname/com.appname.activity#0
-    const std::regex re("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+");
     std::smatch match;
-    if (std::regex_match(layerName.begin(), layerName.end(), match, re)) {
+    if (std::regex_match(layerName.begin(), layerName.end(), match, packageNameRegex)) {
         // There must be a match for group 1 otherwise the whole string is not
         // matched and the above will return false
         return match[1];
@@ -163,103 +150,129 @@
     return "";
 }
 
-void TimeStats::flushAvailableRecordsToStatsLocked(const std::string& layerName) {
+void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) {
     ATRACE_CALL();
 
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
     std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
     while (!timeRecords.empty()) {
-        if (!recordReadyLocked(layerName, &timeRecords[0])) break;
-        ALOGV("[%s]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerName.c_str(),
-              timeRecords[0].frameNumber, timeRecords[0].presentTime);
+        if (!recordReadyLocked(layerID, &timeRecords[0])) break;
+        ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID,
+              timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
 
+        const std::string& layerName = layerRecord.layerName;
         if (prevTimeRecord.ready) {
-            if (!timeStats.stats.count(layerName)) {
-                timeStats.stats[layerName].layerName = layerName;
-                timeStats.stats[layerName].packageName = getPackageName(layerName);
-                timeStats.stats[layerName].statsStart = static_cast<int64_t>(std::time(0));
+            if (!mTimeStats.stats.count(layerName)) {
+                mTimeStats.stats[layerName].layerName = layerName;
+                mTimeStats.stats[layerName].packageName = getPackageName(layerName);
             }
-            TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timeStats.stats[layerName];
+            TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
             timeStatsLayer.totalFrames++;
+            timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
+            layerRecord.droppedFrames = 0;
 
-            const int32_t postToPresentMs =
-                    msBetween(timeRecords[0].postTime, timeRecords[0].presentTime);
-            ALOGV("[%s]-[%" PRIu64 "]-post2present[%d]", layerName.c_str(),
-                  timeRecords[0].frameNumber, postToPresentMs);
+            const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime,
+                                                      timeRecords[0].frameTime.acquireTime);
+            ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerID,
+                  timeRecords[0].frameTime.frameNumber, postToAcquireMs);
+            timeStatsLayer.deltas["post2acquire"].insert(postToAcquireMs);
+
+            const int32_t postToPresentMs = msBetween(timeRecords[0].frameTime.postTime,
+                                                      timeRecords[0].frameTime.presentTime);
+            ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerID,
+                  timeRecords[0].frameTime.frameNumber, postToPresentMs);
             timeStatsLayer.deltas["post2present"].insert(postToPresentMs);
 
-            const int32_t acquireToPresentMs =
-                    msBetween(timeRecords[0].acquireTime, timeRecords[0].presentTime);
-            ALOGV("[%s]-[%" PRIu64 "]-acquire2present[%d]", layerName.c_str(),
-                  timeRecords[0].frameNumber, acquireToPresentMs);
+            const int32_t acquireToPresentMs = msBetween(timeRecords[0].frameTime.acquireTime,
+                                                         timeRecords[0].frameTime.presentTime);
+            ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerID,
+                  timeRecords[0].frameTime.frameNumber, acquireToPresentMs);
             timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs);
 
-            const int32_t latchToPresentMs =
-                    msBetween(timeRecords[0].latchTime, timeRecords[0].presentTime);
-            ALOGV("[%s]-[%" PRIu64 "]-latch2present[%d]", layerName.c_str(),
-                  timeRecords[0].frameNumber, latchToPresentMs);
+            const int32_t latchToPresentMs = msBetween(timeRecords[0].frameTime.latchTime,
+                                                       timeRecords[0].frameTime.presentTime);
+            ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerID,
+                  timeRecords[0].frameTime.frameNumber, latchToPresentMs);
             timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs);
 
-            const int32_t desiredToPresentMs =
-                    msBetween(timeRecords[0].desiredTime, timeRecords[0].presentTime);
-            ALOGV("[%s]-[%" PRIu64 "]-desired2present[%d]", layerName.c_str(),
-                  timeRecords[0].frameNumber, desiredToPresentMs);
+            const int32_t desiredToPresentMs = msBetween(timeRecords[0].frameTime.desiredTime,
+                                                         timeRecords[0].frameTime.presentTime);
+            ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerID,
+                  timeRecords[0].frameTime.frameNumber, desiredToPresentMs);
             timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs);
 
-            const int32_t presentToPresentMs =
-                    msBetween(prevTimeRecord.presentTime, timeRecords[0].presentTime);
-            ALOGV("[%s]-[%" PRIu64 "]-present2present[%d]", layerName.c_str(),
-                  timeRecords[0].frameNumber, presentToPresentMs);
+            const int32_t presentToPresentMs = msBetween(prevTimeRecord.frameTime.presentTime,
+                                                         timeRecords[0].frameTime.presentTime);
+            ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerID,
+                  timeRecords[0].frameTime.frameNumber, presentToPresentMs);
             timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
-
-            timeStats.stats[layerName].statsEnd = static_cast<int64_t>(std::time(0));
         }
+
+        // Output additional trace points to track frame time.
+        ATRACE_INT64(("TimeStats-Post - " + layerName).c_str(), timeRecords[0].frameTime.postTime);
+        ATRACE_INT64(("TimeStats-Acquire - " + layerName).c_str(),
+                     timeRecords[0].frameTime.acquireTime);
+        ATRACE_INT64(("TimeStats-Latch - " + layerName).c_str(),
+                     timeRecords[0].frameTime.latchTime);
+        ATRACE_INT64(("TimeStats-Desired - " + layerName).c_str(),
+                     timeRecords[0].frameTime.desiredTime);
+        ATRACE_INT64(("TimeStats-Present - " + layerName).c_str(),
+                     timeRecords[0].frameTime.presentTime);
+
         prevTimeRecord = timeRecords[0];
         timeRecords.pop_front();
         layerRecord.waitData--;
     }
 }
 
+// This regular expression captures the following layer names for instance:
+// 1) StatusBat#0
+// 2) NavigationBar#1
+// 3) co(m).*#0
+// 4) SurfaceView - co(m).*#0
+// Using [-\\s\t]+ for the conjunction part between SurfaceView and co(m).*
+// is a bit more robust in case there's a slight change.
+// The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases.
+static const std::regex layerNameRegex(
+        "(((SurfaceView[-\\s\\t]+)?com?\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+");
+
 static bool layerNameIsValid(const std::string& layerName) {
-    // This regular expression captures the following layer names for instance:
-    // 1) StatusBat#0
-    // 2) NavigationBar#1
-    // 3) com.*#0
-    // 4) SurfaceView - com.*#0
-    // Using [-\\s\t]+ for the conjunction part between SurfaceView and com.* is
-    // a bit more robust in case there's a slight change.
-    // The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases.
-    std::regex re("(((SurfaceView[-\\s\\t]+)?com\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+");
-    return std::regex_match(layerName.begin(), layerName.end(), re);
+    return std::regex_match(layerName.begin(), layerName.end(), layerNameRegex);
 }
 
-void TimeStats::setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime) {
+void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+                            nsecs_t postTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-PostTime[%" PRId64 "]", layerName.c_str(), frameNumber, postTime);
+    ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerID, frameNumber, layerName.c_str(),
+          postTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName) && !layerNameIsValid(layerName)) {
-        return;
+    if (!mTimeStatsTracker.count(layerID) && layerNameIsValid(layerName)) {
+        mTimeStatsTracker[layerID].layerName = layerName;
     }
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
-        ALOGV("[%s]-timeRecords is already at its maximum size[%zu]", layerName.c_str(),
-              MAX_NUM_TIME_RECORDS);
-        // TODO(zzyiwei): if this happens, there must be a present fence missing
-        // or waitData is not in the correct position. Need to think out a
-        // reasonable way to recover from this state.
+        ALOGE("[%d]-[%s]-timeRecords is at its maximum size[%zu]. Ignore this when unittesting.",
+              layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
+        mTimeStatsTracker.erase(layerID);
         return;
     }
     // For most media content, the acquireFence is invalid because the buffer is
     // ready at the queueBuffer stage. In this case, acquireTime should be given
     // a default value as postTime.
     TimeRecord timeRecord = {
-            .frameNumber = frameNumber,
-            .postTime = postTime,
-            .acquireTime = postTime,
+            .frameTime =
+                    {
+                            .frameNumber = frameNumber,
+                            .postTime = postTime,
+                            .latchTime = postTime,
+                            .acquireTime = postTime,
+                            .desiredTime = postTime,
+                    },
     };
     layerRecord.timeRecords.push_back(timeRecord);
     if (layerRecord.waitData < 0 ||
@@ -267,160 +280,234 @@
         layerRecord.waitData = layerRecord.timeRecords.size() - 1;
 }
 
-void TimeStats::setLatchTime(const std::string& layerName, uint64_t frameNumber,
-                             nsecs_t latchTime) {
+void TimeStats::setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerName.c_str(), frameNumber, latchTime);
+    ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerID, frameNumber, latchTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
-    if (timeRecord.frameNumber == frameNumber) {
-        timeRecord.latchTime = latchTime;
+    if (timeRecord.frameTime.frameNumber == frameNumber) {
+        timeRecord.frameTime.latchTime = latchTime;
     }
 }
 
-void TimeStats::setDesiredTime(const std::string& layerName, uint64_t frameNumber,
-                               nsecs_t desiredTime) {
+void TimeStats::setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerName.c_str(), frameNumber,
-          desiredTime);
+    ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerID, frameNumber, desiredTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
-    if (timeRecord.frameNumber == frameNumber) {
-        timeRecord.desiredTime = desiredTime;
+    if (timeRecord.frameTime.frameNumber == frameNumber) {
+        timeRecord.frameTime.desiredTime = desiredTime;
     }
 }
 
-void TimeStats::setAcquireTime(const std::string& layerName, uint64_t frameNumber,
-                               nsecs_t acquireTime) {
+void TimeStats::setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerName.c_str(), frameNumber,
-          acquireTime);
+    ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerID, frameNumber, acquireTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
-    if (timeRecord.frameNumber == frameNumber) {
-        timeRecord.acquireTime = acquireTime;
+    if (timeRecord.frameTime.frameNumber == frameNumber) {
+        timeRecord.frameTime.acquireTime = acquireTime;
     }
 }
 
-void TimeStats::setAcquireFence(const std::string& layerName, uint64_t frameNumber,
+void TimeStats::setAcquireFence(int32_t layerID, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& acquireFence) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+    ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerID, frameNumber,
           acquireFence->getSignalTime());
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
-    if (timeRecord.frameNumber == frameNumber) {
+    if (timeRecord.frameTime.frameNumber == frameNumber) {
         timeRecord.acquireFence = acquireFence;
     }
 }
 
-void TimeStats::setPresentTime(const std::string& layerName, uint64_t frameNumber,
-                               nsecs_t presentTime) {
+void TimeStats::setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerName.c_str(), frameNumber,
-          presentTime);
+    ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerID, frameNumber, presentTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
-    if (timeRecord.frameNumber == frameNumber) {
-        timeRecord.presentTime = presentTime;
+    if (timeRecord.frameTime.frameNumber == frameNumber) {
+        timeRecord.frameTime.presentTime = presentTime;
         timeRecord.ready = true;
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerName);
+    flushAvailableRecordsToStatsLocked(layerID);
 }
 
-void TimeStats::setPresentFence(const std::string& layerName, uint64_t frameNumber,
+void TimeStats::setPresentFence(int32_t layerID, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& presentFence) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+    ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerID, frameNumber,
           presentFence->getSignalTime());
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
-    if (timeRecord.frameNumber == frameNumber) {
+    if (timeRecord.frameTime.frameNumber == frameNumber) {
         timeRecord.presentFence = presentFence;
         timeRecord.ready = true;
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerName);
+    flushAvailableRecordsToStatsLocked(layerID);
 }
 
-void TimeStats::onDisconnect(const std::string& layerName) {
+void TimeStats::onDestroy(int32_t layerID) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-onDisconnect", layerName.c_str());
+    ALOGV("[%d]-onDestroy", layerID);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    flushAvailableRecordsToStatsLocked(layerName);
-    timeStatsTracker.erase(layerName);
+    if (!mTimeStatsTracker.count(layerID)) return;
+    mTimeStatsTracker.erase(layerID);
 }
 
-void TimeStats::clearLayerRecord(const std::string& layerName) {
+void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%s]-clearLayerRecord", layerName.c_str());
+    ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerID, frameNumber);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
-    layerRecord.timeRecords.clear();
-    layerRecord.prevTimeRecord.ready = false;
-    layerRecord.waitData = -1;
-}
-
-void TimeStats::removeTimeRecord(const std::string& layerName, uint64_t frameNumber) {
-    if (!mEnabled.load()) return;
-
-    ATRACE_CALL();
-    ALOGV("[%s]-[%" PRIu64 "]-removeTimeRecord", layerName.c_str(), frameNumber);
-
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (!timeStatsTracker.count(layerName)) return;
-    LayerRecord& layerRecord = timeStatsTracker[layerName];
+    if (!mTimeStatsTracker.count(layerID)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
     size_t removeAt = 0;
     for (const TimeRecord& record : layerRecord.timeRecords) {
-        if (record.frameNumber == frameNumber) break;
+        if (record.frameTime.frameNumber == frameNumber) break;
         removeAt++;
     }
     if (removeAt == layerRecord.timeRecords.size()) return;
     layerRecord.timeRecords.erase(layerRecord.timeRecords.begin() + removeAt);
     if (layerRecord.waitData > static_cast<int32_t>(removeAt)) {
-        --layerRecord.waitData;
+        layerRecord.waitData--;
     }
+    layerRecord.droppedFrames++;
+}
+
+void TimeStats::flushPowerTimeLocked() {
+    if (!mEnabled.load()) return;
+
+    nsecs_t curTime = systemTime();
+    // elapsedTime is in milliseconds.
+    int64_t elapsedTime = (curTime - mPowerTime.prevTime) / 1000000;
+
+    switch (mPowerTime.powerMode) {
+        case HWC_POWER_MODE_NORMAL:
+            mTimeStats.displayOnTime += elapsedTime;
+            break;
+        case HWC_POWER_MODE_OFF:
+        case HWC_POWER_MODE_DOZE:
+        case HWC_POWER_MODE_DOZE_SUSPEND:
+        default:
+            break;
+    }
+
+    mPowerTime.prevTime = curTime;
+}
+
+void TimeStats::setPowerMode(int32_t powerMode) {
+    if (!mEnabled.load()) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mPowerTime.powerMode = powerMode;
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (powerMode == mPowerTime.powerMode) return;
+
+    flushPowerTimeLocked();
+    mPowerTime.powerMode = powerMode;
+}
+
+void TimeStats::flushAvailableGlobalRecordsToStatsLocked() {
+    ATRACE_CALL();
+
+    while (!mGlobalRecord.presentFences.empty()) {
+        const nsecs_t curPresentTime = mGlobalRecord.presentFences.front()->getSignalTime();
+        if (curPresentTime == Fence::SIGNAL_TIME_PENDING) break;
+
+        if (curPresentTime == Fence::SIGNAL_TIME_INVALID) {
+            ALOGE("GlobalPresentFence is invalid!");
+            mGlobalRecord.prevPresentTime = 0;
+            mGlobalRecord.presentFences.pop_front();
+            continue;
+        }
+
+        ALOGV("GlobalPresentFenceTime[%" PRId64 "]",
+              mGlobalRecord.presentFences.front()->getSignalTime());
+
+        if (mGlobalRecord.prevPresentTime != 0) {
+            const int32_t presentToPresentMs =
+                    msBetween(mGlobalRecord.prevPresentTime, curPresentTime);
+            ALOGV("Global present2present[%d] prev[%" PRId64 "] curr[%" PRId64 "]",
+                  presentToPresentMs, mGlobalRecord.prevPresentTime, curPresentTime);
+            mTimeStats.presentToPresent.insert(presentToPresentMs);
+        }
+
+        mGlobalRecord.prevPresentTime = curPresentTime;
+        mGlobalRecord.presentFences.pop_front();
+    }
+}
+
+void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (presentFence == nullptr || !presentFence->isValid()) {
+        mGlobalRecord.prevPresentTime = 0;
+        return;
+    }
+
+    if (mPowerTime.powerMode != HWC_POWER_MODE_NORMAL) {
+        // Try flushing the last present fence on HWC_POWER_MODE_NORMAL.
+        flushAvailableGlobalRecordsToStatsLocked();
+        mGlobalRecord.presentFences.clear();
+        mGlobalRecord.prevPresentTime = 0;
+        return;
+    }
+
+    if (mGlobalRecord.presentFences.size() == MAX_NUM_TIME_RECORDS) {
+        // The front presentFence must be trapped in pending status in this
+        // case. Try dequeuing the front one to recover.
+        ALOGE("GlobalPresentFences is already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+        mGlobalRecord.prevPresentTime = 0;
+        mGlobalRecord.presentFences.pop_front();
+    }
+
+    mGlobalRecord.presentFences.emplace_back(presentFence);
+    flushAvailableGlobalRecordsToStatsLocked();
 }
 
 void TimeStats::enable() {
@@ -429,9 +516,10 @@
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mMutex);
-    ALOGD("Enabled");
     mEnabled.store(true);
-    timeStats.statsStart = static_cast<int64_t>(std::time(0));
+    mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
+    mPowerTime.prevTime = systemTime();
+    ALOGD("Enabled");
 }
 
 void TimeStats::disable() {
@@ -440,45 +528,54 @@
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mMutex);
-    ALOGD("Disabled");
+    flushPowerTimeLocked();
     mEnabled.store(false);
-    timeStats.statsEnd = static_cast<int64_t>(std::time(0));
+    mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+    ALOGD("Disabled");
 }
 
 void TimeStats::clear() {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStatsTracker.clear();
+    mTimeStats.stats.clear();
+    mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
+    mTimeStats.statsEnd = 0;
+    mTimeStats.totalFrames = 0;
+    mTimeStats.missedFrames = 0;
+    mTimeStats.clientCompositionFrames = 0;
+    mTimeStats.displayOnTime = 0;
+    mTimeStats.presentToPresent.hist.clear();
+    mPowerTime.prevTime = systemTime();
+    mGlobalRecord.prevPresentTime = 0;
+    mGlobalRecord.presentFences.clear();
     ALOGD("Cleared");
-    timeStats.stats.clear();
-    timeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
-    timeStats.statsEnd = 0;
-    timeStats.totalFrames = 0;
-    timeStats.missedFrames = 0;
-    timeStats.clientCompositionFrames = 0;
 }
 
 bool TimeStats::isEnabled() {
     return mEnabled.load();
 }
 
-void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, String8& result) {
+void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result) {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (timeStats.statsStart == 0) {
+    if (mTimeStats.statsStart == 0) {
         return;
     }
 
-    timeStats.statsEnd = static_cast<int64_t>(std::time(0));
+    mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+
+    flushPowerTimeLocked();
 
     if (asProto) {
         ALOGD("Dumping TimeStats as proto");
-        SFTimeStatsGlobalProto timeStatsProto = timeStats.toProto(maxLayers);
+        SFTimeStatsGlobalProto timeStatsProto = mTimeStats.toProto(maxLayers);
         result.append(timeStatsProto.SerializeAsString().c_str(), timeStatsProto.ByteSize());
     } else {
         ALOGD("Dumping TimeStats as text");
-        result.append(timeStats.toString(maxLayers).c_str());
+        result.append(mTimeStats.toString(maxLayers));
         result.append("\n");
     }
 }
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 8318210..e8fbcab 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -19,10 +19,11 @@
 #include <timestatsproto/TimeStatsHelper.h>
 #include <timestatsproto/TimeStatsProtoHeader.h>
 
+#include <hardware/hwcomposer_defs.h>
+
 #include <ui/FenceTime.h>
 
 #include <utils/String16.h>
-#include <utils/String8.h>
 #include <utils/Vector.h>
 
 #include <deque>
@@ -33,70 +34,96 @@
 using namespace android::surfaceflinger;
 
 namespace android {
-class String8;
 
 class TimeStats {
-    // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
-    // static const size_t MAX_NUM_LAYER_RECORDS = 200;
-    static const size_t MAX_NUM_TIME_RECORDS = 64;
-
-    struct TimeRecord {
-        bool ready = false;
+    struct FrameTime {
         uint64_t frameNumber = 0;
         nsecs_t postTime = 0;
         nsecs_t latchTime = 0;
         nsecs_t acquireTime = 0;
         nsecs_t desiredTime = 0;
         nsecs_t presentTime = 0;
+    };
+
+    struct TimeRecord {
+        bool ready = false;
+        FrameTime frameTime;
         std::shared_ptr<FenceTime> acquireFence;
         std::shared_ptr<FenceTime> presentFence;
     };
 
     struct LayerRecord {
+        std::string layerName;
         // This is the index in timeRecords, at which the timestamps for that
         // specific frame are still not fully received. This is not waiting for
         // fences to signal, but rather waiting to receive those fences/timestamps.
         int32_t waitData = -1;
+        uint32_t droppedFrames = 0;
         TimeRecord prevTimeRecord;
         std::deque<TimeRecord> timeRecords;
     };
 
+    struct PowerTime {
+        int32_t powerMode = HWC_POWER_MODE_OFF;
+        nsecs_t prevTime = 0;
+    };
+
+    struct GlobalRecord {
+        nsecs_t prevPresentTime = 0;
+        std::deque<std::shared_ptr<FenceTime>> presentFences;
+    };
+
 public:
-    static TimeStats& getInstance();
-    void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, String8& result);
+    TimeStats() = default;
+    ~TimeStats() = default;
+
+    void parseArgs(bool asProto, const Vector<String16>& args, std::string& result);
+    bool isEnabled();
+
     void incrementTotalFrames();
     void incrementMissedFrames();
     void incrementClientCompositionFrames();
 
-    void setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime);
-    void setLatchTime(const std::string& layerName, uint64_t frameNumber, nsecs_t latchTime);
-    void setDesiredTime(const std::string& layerName, uint64_t frameNumber, nsecs_t desiredTime);
-    void setAcquireTime(const std::string& layerName, uint64_t frameNumber, nsecs_t acquireTime);
-    void setAcquireFence(const std::string& layerName, uint64_t frameNumber,
+    void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+                     nsecs_t postTime);
+    void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime);
+    void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime);
+    void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime);
+    void setAcquireFence(int32_t layerID, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& acquireFence);
-    void setPresentTime(const std::string& layerName, uint64_t frameNumber, nsecs_t presentTime);
-    void setPresentFence(const std::string& layerName, uint64_t frameNumber,
+    void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime);
+    void setPresentFence(int32_t layerID, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence);
-    void onDisconnect(const std::string& layerName);
-    void clearLayerRecord(const std::string& layerName);
-    void removeTimeRecord(const std::string& layerName, uint64_t frameNumber);
+    // Clean up the layer record
+    void onDestroy(int32_t layerID);
+    // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
+    void removeTimeRecord(int32_t layerID, uint64_t frameNumber);
+
+    void setPowerMode(int32_t powerMode);
+    void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence);
+
+    // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
+    // static const size_t MAX_NUM_LAYER_RECORDS = 200;
+    static const size_t MAX_NUM_TIME_RECORDS = 64;
 
 private:
-    TimeStats() = default;
-
-    bool recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord);
-    void flushAvailableRecordsToStatsLocked(const std::string& layerName);
+    bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord);
+    void flushAvailableRecordsToStatsLocked(int32_t layerID);
+    void flushPowerTimeLocked();
+    void flushAvailableGlobalRecordsToStatsLocked();
 
     void enable();
     void disable();
     void clear();
-    bool isEnabled();
-    void dump(bool asProto, std::optional<uint32_t> maxLayers, String8& result);
+    void dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result);
 
     std::atomic<bool> mEnabled = false;
     std::mutex mMutex;
-    TimeStatsHelper::TimeStatsGlobal timeStats;
-    std::unordered_map<std::string, LayerRecord> timeStatsTracker;
+    TimeStatsHelper::TimeStatsGlobal mTimeStats;
+    // Hashmap for LayerRecord with layerID as the hash key
+    std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker;
+    PowerTime mPowerTime;
+    GlobalRecord mGlobalRecord;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 21f3ef3..75ce4be 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -13,8 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "timestatsproto/TimeStatsHelper.h"
+
 #include <android-base/stringprintf.h>
-#include <timestatsproto/TimeStatsHelper.h>
+#include <inttypes.h>
 
 #include <array>
 
@@ -39,17 +41,25 @@
     if (delta < 0) return;
     // std::lower_bound won't work on out of range values
     if (delta > histogramConfig[HISTOGRAM_SIZE - 1]) {
-        hist[histogramConfig[HISTOGRAM_SIZE - 1]]++;
+        hist[histogramConfig[HISTOGRAM_SIZE - 1]] += delta / histogramConfig[HISTOGRAM_SIZE - 1];
         return;
     }
     auto iter = std::lower_bound(histogramConfig.begin(), histogramConfig.end(), delta);
     hist[*iter]++;
 }
 
+int64_t TimeStatsHelper::Histogram::totalTime() const {
+    int64_t ret = 0;
+    for (const auto& ele : hist) {
+        ret += ele.first * ele.second;
+    }
+    return ret;
+}
+
 float TimeStatsHelper::Histogram::averageTime() const {
     int64_t ret = 0;
     int64_t count = 0;
-    for (auto& ele : hist) {
+    for (const auto& ele : hist) {
         count += ele.second;
         ret += ele.first * ele.second;
     }
@@ -68,19 +78,18 @@
 }
 
 std::string TimeStatsHelper::TimeStatsLayer::toString() const {
-    std::string result = "";
+    std::string result = "\n";
     StringAppendF(&result, "layerName = %s\n", layerName.c_str());
     StringAppendF(&result, "packageName = %s\n", packageName.c_str());
-    StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart));
-    StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd));
-    StringAppendF(&result, "totalFrames= %d\n", totalFrames);
-    auto iter = deltas.find("present2present");
+    StringAppendF(&result, "totalFrames = %d\n", totalFrames);
+    StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
+    const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
         StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
     }
-    for (auto& ele : deltas) {
+    for (const auto& ele : deltas) {
         StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str());
-        StringAppendF(&result, "%s", ele.second.toString().c_str());
+        result.append(ele.second.toString());
     }
 
     return result;
@@ -88,15 +97,18 @@
 
 std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> maxLayers) const {
     std::string result = "SurfaceFlinger TimeStats:\n";
-    StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart));
-    StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd));
-    StringAppendF(&result, "totalFrames= %d\n", totalFrames);
-    StringAppendF(&result, "missedFrames= %d\n", missedFrames);
-    StringAppendF(&result, "clientCompositionFrames= %d\n", clientCompositionFrames);
-    StringAppendF(&result, "TimeStats for each layer is as below:\n");
+    StringAppendF(&result, "statsStart = %" PRId64 "\n", statsStart);
+    StringAppendF(&result, "statsEnd = %" PRId64 "\n", statsEnd);
+    StringAppendF(&result, "totalFrames = %d\n", totalFrames);
+    StringAppendF(&result, "missedFrames = %d\n", missedFrames);
+    StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
+    StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
+    StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
+    StringAppendF(&result, "presentToPresent histogram is as below:\n");
+    result.append(presentToPresent.toString());
     const auto dumpStats = generateDumpStats(maxLayers);
-    for (auto& ele : dumpStats) {
-        StringAppendF(&result, "%s", ele->toString().c_str());
+    for (const auto& ele : dumpStats) {
+        result.append(ele->toString());
     }
 
     return result;
@@ -106,15 +118,14 @@
     SFTimeStatsLayerProto layerProto;
     layerProto.set_layer_name(layerName);
     layerProto.set_package_name(packageName);
-    layerProto.set_stats_start(statsStart);
-    layerProto.set_stats_end(statsEnd);
     layerProto.set_total_frames(totalFrames);
-    for (auto& ele : deltas) {
+    layerProto.set_dropped_frames(droppedFrames);
+    for (const auto& ele : deltas) {
         SFTimeStatsDeltaProto* deltaProto = layerProto.add_deltas();
         deltaProto->set_delta_name(ele.first);
-        for (auto& histEle : ele.second.hist) {
+        for (const auto& histEle : ele.second.hist) {
             SFTimeStatsHistogramBucketProto* histProto = deltaProto->add_histograms();
-            histProto->set_render_millis(histEle.first);
+            histProto->set_time_millis(histEle.first);
             histProto->set_frame_count(histEle.second);
         }
     }
@@ -129,8 +140,14 @@
     globalProto.set_total_frames(totalFrames);
     globalProto.set_missed_frames(missedFrames);
     globalProto.set_client_composition_frames(clientCompositionFrames);
+    globalProto.set_display_on_time(displayOnTime);
+    for (const auto& histEle : presentToPresent.hist) {
+        SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present();
+        histProto->set_time_millis(histEle.first);
+        histProto->set_frame_count(histEle.second);
+    }
     const auto dumpStats = generateDumpStats(maxLayers);
-    for (auto& ele : dumpStats) {
+    for (const auto& ele : dumpStats) {
         SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
         layerProto->CopyFrom(ele->toProto());
     }
@@ -140,7 +157,7 @@
 std::vector<TimeStatsHelper::TimeStatsLayer const*>
 TimeStatsHelper::TimeStatsGlobal::generateDumpStats(std::optional<uint32_t> maxLayers) const {
     std::vector<TimeStatsLayer const*> dumpStats;
-    for (auto& ele : stats) {
+    for (const auto& ele : stats) {
         dumpStats.push_back(&ele.second);
     }
 
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 1798555..5f40a1a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -34,6 +34,7 @@
         std::unordered_map<int32_t, int32_t> hist;
 
         void insert(int32_t delta);
+        int64_t totalTime() const;
         float averageTime() const;
         std::string toString() const;
     };
@@ -42,9 +43,8 @@
     public:
         std::string layerName;
         std::string packageName;
-        int64_t statsStart = 0;
-        int64_t statsEnd = 0;
         int32_t totalFrames = 0;
+        int32_t droppedFrames = 0;
         std::unordered_map<std::string, Histogram> deltas;
 
         std::string toString() const;
@@ -58,6 +58,8 @@
         int32_t totalFrames = 0;
         int32_t missedFrames = 0;
         int32_t clientCompositionFrames = 0;
+        int64_t displayOnTime = 0;
+        Histogram presentToPresent;
         std::unordered_map<std::string, TimeStatsLayer> stats;
 
         std::string toString(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index f29fbd1..377612a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -20,46 +20,63 @@
 
 option optimize_for = LITE_RUNTIME;
 
+// //depot/google3/wireless/android/graphics/surfaceflingerstats/proto/
+// timestats.proto is based on this proto. Please only make valid protobuf
+// changes to these messages, and keep google3 side proto messages in sync if
+// the end to end pipeline needs to be updated.
+
+// Next tag: 9
 message SFTimeStatsGlobalProto {
-  // The start & end timestamps in UTC as
-  // milliseconds since January 1, 1970
+  // The stats start time in UTC as seconds since January 1, 1970
   optional int64 stats_start = 1;
+  // The stats end time in UTC as seconds since January 1, 1970
   optional int64 stats_end = 2;
-  // Total frames
+  // Total number of frames presented during tracing period.
   optional int32 total_frames = 3;
   // Total missed frames of SurfaceFlinger.
   optional int32 missed_frames = 4;
   // Total frames fallback to client composition.
   optional int32 client_composition_frames = 5;
-
+  // Primary display on time in milliseconds.
+  optional int64 display_on_time = 7;
+  // Present to present histogram.
+  repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
+  // Stats per layer. Apps could have multiple layers.
   repeated SFTimeStatsLayerProto stats = 6;
 }
 
+// Next tag: 8
 message SFTimeStatsLayerProto {
-  // The layer name
+  // The name of the visible view layer.
   optional string layer_name = 1;
-  // The package name
+  // The package name of the application owning this layer.
   optional string package_name = 2;
-  // The start & end timestamps in UTC as
-  // milliseconds since January 1, 1970
+  // The stats start time in UTC as seconds since January 1, 1970
   optional int64 stats_start = 3;
+  // The stats end time in UTC as seconds since January 1, 1970
   optional int64 stats_end = 4;
-  // Distinct frame count.
+  // Total number of frames presented during tracing period.
   optional int32 total_frames = 5;
-
+  // Total number of frames dropped by SurfaceFlinger.
+  optional int32 dropped_frames = 7;
+  // There are multiple timestamps tracked in SurfaceFlinger, and these are the
+  // histograms of deltas between different combinations of those timestamps.
   repeated SFTimeStatsDeltaProto deltas = 6;
 }
 
+// Next tag: 3
 message SFTimeStatsDeltaProto {
   // Name of the time interval
   optional string delta_name = 1;
-  // Histogram of the delta time
+  // Histogram of the delta time. There should be at most 85 buckets ranging
+  // from [0ms, 1ms) to [1000ms, infinity)
   repeated SFTimeStatsHistogramBucketProto histograms = 2;
 }
 
+// Next tag: 3
 message SFTimeStatsHistogramBucketProto {
-  // Lower bound of render time in milliseconds.
-  optional int32 render_millis = 1;
+  // Lower bound of time interval in milliseconds.
+  optional int32 time_millis = 1;
   // Number of frames in the bucket.
   optional int32 frame_count = 2;
 }
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
new file mode 100644
index 0000000..d2b7fe0
--- /dev/null
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "TransactionCompletedThread"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "TransactionCompletedThread.h"
+
+#include <cinttypes>
+
+#include <binder/IInterface.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+TransactionCompletedThread::~TransactionCompletedThread() {
+    std::lock_guard lockThread(mThreadMutex);
+
+    {
+        std::lock_guard lock(mMutex);
+        mKeepRunning = false;
+        mConditionVariable.notify_all();
+    }
+
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+
+    {
+        std::lock_guard lock(mMutex);
+        for (const auto& [listener, listenerStats] : mListenerStats) {
+            listener->unlinkToDeath(mDeathRecipient);
+        }
+    }
+}
+
+void TransactionCompletedThread::run() {
+    std::lock_guard lock(mMutex);
+    if (mRunning || !mKeepRunning) {
+        return;
+    }
+    mDeathRecipient = new ThreadDeathRecipient();
+    mRunning = true;
+
+    std::lock_guard lockThread(mThreadMutex);
+    mThread = std::thread(&TransactionCompletedThread::threadMain, this);
+}
+
+void TransactionCompletedThread::registerPendingCallbackHandle(const sp<CallbackHandle>& handle) {
+    std::lock_guard lock(mMutex);
+
+    sp<IBinder> listener = IInterface::asBinder(handle->listener);
+    const auto& callbackIds = handle->callbackIds;
+
+    mPendingTransactions[listener][callbackIds]++;
+}
+
+void TransactionCompletedThread::addPresentedCallbackHandles(
+        const std::deque<sp<CallbackHandle>>& handles) {
+    std::lock_guard lock(mMutex);
+
+    for (const auto& handle : handles) {
+        auto listener = mPendingTransactions.find(IInterface::asBinder(handle->listener));
+        auto& pendingCallbacks = listener->second;
+        auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
+
+        if (pendingCallback != pendingCallbacks.end()) {
+            auto& pendingCount = pendingCallback->second;
+
+            // Decrease the pending count for this listener
+            if (--pendingCount == 0) {
+                pendingCallbacks.erase(pendingCallback);
+            }
+        } else {
+            ALOGE("there are more latched callbacks than there were registered callbacks");
+        }
+
+        addCallbackHandle(handle);
+    }
+}
+
+void TransactionCompletedThread::addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle) {
+    std::lock_guard lock(mMutex);
+    addCallbackHandle(handle);
+}
+
+void TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
+    const sp<IBinder> listener = IInterface::asBinder(handle->listener);
+
+    // If we don't already have a reference to this listener, linkToDeath so we get a notification
+    // if it dies.
+    if (mListenerStats.count(listener) == 0) {
+        status_t error = listener->linkToDeath(mDeathRecipient);
+        if (error != NO_ERROR) {
+            ALOGE("cannot add callback handle because linkToDeath failed, err: %d", error);
+            return;
+        }
+    }
+
+    auto& listenerStats = mListenerStats[listener];
+    listenerStats.listener = handle->listener;
+
+    auto& transactionStats = listenerStats.transactionStats[handle->callbackIds];
+    transactionStats.latchTime = handle->latchTime;
+    transactionStats.surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
+                                               handle->previousReleaseFence);
+}
+
+void TransactionCompletedThread::addPresentFence(const sp<Fence>& presentFence) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mPresentFence = presentFence;
+}
+
+void TransactionCompletedThread::sendCallbacks() {
+    std::lock_guard lock(mMutex);
+    if (mRunning) {
+        mConditionVariable.notify_all();
+    }
+}
+
+void TransactionCompletedThread::threadMain() {
+    std::lock_guard lock(mMutex);
+
+    while (mKeepRunning) {
+        mConditionVariable.wait(mMutex);
+
+        // For each listener
+        auto it = mListenerStats.begin();
+        while (it != mListenerStats.end()) {
+            auto& [listener, listenerStats] = *it;
+
+            // For each transaction
+            bool sendCallback = true;
+            for (auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+                // If we are still waiting on the callback handles for this transaction, skip it
+                if (mPendingTransactions[listener].count(callbackIds) != 0) {
+                    sendCallback = false;
+                    break;
+                }
+
+                // If the transaction has been latched
+                if (transactionStats.latchTime >= 0) {
+                    if (!mPresentFence) {
+                        sendCallback = false;
+                        break;
+                    }
+                    transactionStats.presentFence = mPresentFence;
+                }
+            }
+            // If the listener has no pending transactions and all latched transactions have been
+            // presented
+            if (sendCallback) {
+                // If the listener is still alive
+                if (listener->isBinderAlive()) {
+                    // Send callback
+                    listenerStats.listener->onTransactionCompleted(listenerStats);
+                    listener->unlinkToDeath(mDeathRecipient);
+                }
+                it = mListenerStats.erase(it);
+            } else {
+                it++;
+            }
+        }
+
+        if (mPresentFence) {
+            mPresentFence.clear();
+        }
+    }
+}
+
+// -----------------------------------------------------------------------
+
+CallbackHandle::CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
+                               const std::vector<CallbackId>& ids, const sp<IBinder>& sc)
+      : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {}
+
+} // namespace android
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
new file mode 100644
index 0000000..f49306d
--- /dev/null
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <deque>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+
+#include <binder/IBinder.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <ui/Fence.h>
+
+namespace android {
+
+class CallbackHandle : public RefBase {
+public:
+    CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
+                   const std::vector<CallbackId>& ids, const sp<IBinder>& sc);
+
+    sp<ITransactionCompletedListener> listener;
+    std::vector<CallbackId> callbackIds;
+    sp<IBinder> surfaceControl;
+
+    bool releasePreviousBuffer = false;
+    sp<Fence> previousReleaseFence;
+    nsecs_t acquireTime = -1;
+    nsecs_t latchTime = -1;
+};
+
+class TransactionCompletedThread {
+public:
+    ~TransactionCompletedThread();
+
+    void run();
+
+    // Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle
+    // that needs to be latched and presented this frame. This function should be called once the
+    // layer has received the CallbackHandle so the TransactionCompletedThread knows not to send
+    // a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
+    // presented.
+    void registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
+    // Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
+    void addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
+
+    // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
+    // presented this frame.
+    void addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
+
+    void addPresentFence(const sp<Fence>& presentFence);
+
+    void sendCallbacks();
+
+private:
+    void threadMain();
+
+    void addCallbackHandle(const sp<CallbackHandle>& handle) REQUIRES(mMutex);
+
+    class ThreadDeathRecipient : public IBinder::DeathRecipient {
+    public:
+        // This function is a no-op. isBinderAlive needs a linked DeathRecipient to work.
+        // Death recipients needs a binderDied function.
+        //
+        // (isBinderAlive checks if BpBinder's mAlive is 0. mAlive is only set to 0 in sendObituary.
+        // sendObituary is only called if linkToDeath was called with a DeathRecipient.)
+        void binderDied(const wp<IBinder>& /*who*/) override {}
+    };
+    sp<ThreadDeathRecipient> mDeathRecipient;
+
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& strongPointer) const {
+            return std::hash<IBinder*>{}(strongPointer.get());
+        }
+    };
+
+    // Protects the creation and destruction of mThread
+    std::mutex mThreadMutex;
+
+    std::thread mThread GUARDED_BY(mThreadMutex);
+
+    std::mutex mMutex;
+    std::condition_variable_any mConditionVariable;
+
+    std::unordered_map<
+            sp<IBinder /*listener*/>,
+            std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
+            IBinderHash>
+            mPendingTransactions GUARDED_BY(mMutex);
+    std::unordered_map<sp<IBinder /*listener*/>, ListenerStats, IBinderHash> mListenerStats
+            GUARDED_BY(mMutex);
+
+    bool mRunning GUARDED_BY(mMutex) = false;
+    bool mKeepRunning GUARDED_BY(mMutex) = true;
+
+    sp<Fence> mPresentFence GUARDED_BY(mMutex);
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
deleted file mode 100644
index b11d057..0000000
--- a/services/surfaceflinger/Transform.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_TRANSFORM_H
-#define ANDROID_TRANSFORM_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/Point.h>
-#include <ui/Rect.h>
-#include <math/vec2.h>
-#include <math/vec3.h>
-
-#include <gui/ISurfaceComposer.h>
-
-#include <hardware/hardware.h>
-
-namespace android {
-
-class Region;
-
-// ---------------------------------------------------------------------------
-
-class Transform
-{
-public:
-                    Transform();
-                    Transform(const Transform&  other);
-           explicit Transform(uint32_t orientation);
-                    ~Transform();
-
-            enum orientation_flags {
-                ROT_0   = 0x00000000,
-                FLIP_H  = HAL_TRANSFORM_FLIP_H,
-                FLIP_V  = HAL_TRANSFORM_FLIP_V,
-                ROT_90  = HAL_TRANSFORM_ROT_90,
-                ROT_180 = FLIP_H|FLIP_V,
-                ROT_270 = ROT_180|ROT_90,
-                ROT_INVALID = 0x80
-            };
-
-            static orientation_flags fromRotation(ISurfaceComposer::Rotation rotation);
-
-            enum type_mask {
-                IDENTITY            = 0,
-                TRANSLATE           = 0x1,
-                ROTATE              = 0x2,
-                SCALE               = 0x4,
-                UNKNOWN             = 0x8
-            };
-
-            // query the transform
-            bool        preserveRects() const;
-            uint32_t    getType() const;
-            uint32_t    getOrientation() const;
-
-            const vec3& operator [] (size_t i) const;  // returns column i
-            float   tx() const;
-            float   ty() const;
-
-            // modify the transform
-            void        reset();
-            void        set(float tx, float ty);
-            void        set(float a, float b, float c, float d);
-            status_t    set(uint32_t flags, float w, float h);
-
-            // transform data
-            Rect    makeBounds(int w, int h) const;
-            vec2    transform(int x, int y) const;
-            Region  transform(const Region& reg) const;
-            Rect    transform(const Rect& bounds,
-                    bool roundOutwards = false) const;
-            FloatRect transform(const FloatRect& bounds) const;
-            Transform operator * (const Transform& rhs) const;
-            // assumes the last row is < 0 , 0 , 1 >
-            vec2 transform(const vec2& v) const;
-            vec3 transform(const vec3& v) const;
-
-            Transform inverse() const;
-
-            // for debugging
-            void dump(const char* name) const;
-
-private:
-    struct mat33 {
-        vec3 v[3];
-        inline const vec3& operator [] (int i) const { return v[i]; }
-        inline vec3& operator [] (int i) { return v[i]; }
-    };
-
-    enum { UNKNOWN_TYPE = 0x80000000 };
-
-    uint32_t type() const;
-    static bool absIsOne(float f);
-    static bool isZero(float f);
-
-    mat33               mMatrix;
-    mutable uint32_t    mType;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* ANDROID_TRANSFORM_H */
diff --git a/services/surfaceflinger/clz.h b/services/surfaceflinger/clz.h
deleted file mode 100644
index a4c5262..0000000
--- a/services/surfaceflinger/clz.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACE_FLINGER_CLZ_H
-
-#include <stdint.h>
-
-namespace android {
-
-int inline clz(int32_t x) {
-    return __builtin_clz(x);
-}
-
-template <typename T>
-static inline T min(T a, T b) {
-    return a<b ? a : b;
-}
-template <typename T>
-static inline T min(T a, T b, T c) {
-    return min(a, min(b, c));
-}
-template <typename T>
-static inline T min(T a, T b, T c, T d) {
-    return min(a, b, min(c, d));
-}
-
-template <typename T>
-static inline T max(T a, T b) {
-    return a>b ? a : b;
-}
-template <typename T>
-static inline T max(T a, T b, T c) {
-    return max(a, max(b, c));
-}
-template <typename T>
-static inline T max(T a, T b, T c, T d) {
-    return max(a, b, max(c, d));
-}
-
-template <typename T>
-static inline
-void swap(T& a, T& b) {
-    T t(a);
-    a = b;
-    b = t;
-}
-
-
-}; // namespace android
-
-#endif /* ANDROID_SURFACE_FLINGER_CLZ_H */
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index ac147fe..cb368b0 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -1,6 +1,5 @@
 cc_library_shared {
     name: "liblayers_proto",
-    vendor_available: true,
     export_include_dirs: ["include"],
 
     srcs: [
@@ -11,6 +10,7 @@
 
     shared_libs: [
         "android.hardware.graphics.common@1.1",
+        "libgui",
         "libui",
         "libprotobuf-cpp-lite",
         "libbase",
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index fcf42f0..5c72fea 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -31,17 +31,12 @@
     int32_t lz = lhs->z;
     int32_t rz = rhs->z;
     if (lz != rz) {
-        return (lz > rz) ? 1 : -1;
+        return lz < rz;
     }
 
     return lhs->id < rhs->id;
 }
 
-bool sortLayerUniquePtrs(const std::unique_ptr<LayerProtoParser::Layer>& lhs,
-                   const std::unique_ptr<LayerProtoParser::Layer>& rhs) {
-    return sortLayers(lhs.get(), rhs.get());
-}
-
 const LayerProtoParser::LayerGlobal LayerProtoParser::generateLayerGlobalInfo(
         const LayersProto& layersProto) {
     LayerGlobal layerGlobal;
@@ -52,77 +47,85 @@
     return layerGlobal;
 }
 
-std::vector<std::unique_ptr<LayerProtoParser::Layer>> LayerProtoParser::generateLayerTree(
-        const LayersProto& layersProto) {
-    std::unordered_map<int32_t, LayerProtoParser::Layer*> layerMap = generateMap(layersProto);
-    std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers;
+LayerProtoParser::LayerTree LayerProtoParser::generateLayerTree(const LayersProto& layersProto) {
+    LayerTree layerTree;
+    layerTree.allLayers = generateLayerList(layersProto);
 
-    for (std::pair<int32_t, Layer*> kv : layerMap) {
-        if (kv.second->parent == nullptr) {
-            // Make unique_ptr for top level layers since they are not children. This ensures there
-            // will only be one unique_ptr made for each layer.
-            layers.push_back(std::unique_ptr<Layer>(kv.second));
+    // find and sort the top-level layers
+    for (Layer& layer : layerTree.allLayers) {
+        if (layer.parent == nullptr) {
+            layerTree.topLevelLayers.push_back(&layer);
         }
     }
+    std::sort(layerTree.topLevelLayers.begin(), layerTree.topLevelLayers.end(), sortLayers);
 
-    std::sort(layers.begin(), layers.end(), sortLayerUniquePtrs);
-    return layers;
+    return layerTree;
 }
 
-std::unordered_map<int32_t, LayerProtoParser::Layer*> LayerProtoParser::generateMap(
+std::vector<LayerProtoParser::Layer> LayerProtoParser::generateLayerList(
         const LayersProto& layersProto) {
+    std::vector<Layer> layerList;
     std::unordered_map<int32_t, Layer*> layerMap;
 
+    // build the layer list and the layer map
+    layerList.reserve(layersProto.layers_size());
+    layerMap.reserve(layersProto.layers_size());
     for (int i = 0; i < layersProto.layers_size(); i++) {
-        const LayerProto& layerProto = layersProto.layers(i);
-        layerMap[layerProto.id()] = generateLayer(layerProto);
+        layerList.emplace_back(generateLayer(layersProto.layers(i)));
+        // this works because layerList never changes capacity
+        layerMap[layerList.back().id] = &layerList.back();
     }
 
+    // fix up children and relatives
     for (int i = 0; i < layersProto.layers_size(); i++) {
-        const LayerProto& layerProto = layersProto.layers(i);
-        updateChildrenAndRelative(layerProto, layerMap);
+        updateChildrenAndRelative(layersProto.layers(i), layerMap);
     }
 
-    return layerMap;
+    return layerList;
 }
 
-LayerProtoParser::Layer* LayerProtoParser::generateLayer(const LayerProto& layerProto) {
-    Layer* layer = new Layer();
-    layer->id = layerProto.id();
-    layer->name = layerProto.name();
-    layer->type = layerProto.type();
-    layer->transparentRegion = generateRegion(layerProto.transparent_region());
-    layer->visibleRegion = generateRegion(layerProto.visible_region());
-    layer->damageRegion = generateRegion(layerProto.damage_region());
-    layer->layerStack = layerProto.layer_stack();
-    layer->z = layerProto.z();
-    layer->position = {layerProto.position().x(), layerProto.position().y()};
-    layer->requestedPosition = {layerProto.requested_position().x(),
+LayerProtoParser::Layer LayerProtoParser::generateLayer(const LayerProto& layerProto) {
+    Layer layer;
+    layer.id = layerProto.id();
+    layer.name = layerProto.name();
+    layer.type = layerProto.type();
+    layer.transparentRegion = generateRegion(layerProto.transparent_region());
+    layer.visibleRegion = generateRegion(layerProto.visible_region());
+    layer.damageRegion = generateRegion(layerProto.damage_region());
+    layer.layerStack = layerProto.layer_stack();
+    layer.z = layerProto.z();
+    layer.position = {layerProto.position().x(), layerProto.position().y()};
+    layer.requestedPosition = {layerProto.requested_position().x(),
                                 layerProto.requested_position().y()};
-    layer->size = {layerProto.size().w(), layerProto.size().h()};
-    layer->crop = generateRect(layerProto.crop());
-    layer->finalCrop = generateRect(layerProto.final_crop());
-    layer->isOpaque = layerProto.is_opaque();
-    layer->invalidate = layerProto.invalidate();
-    layer->dataspace = layerProto.dataspace();
-    layer->pixelFormat = layerProto.pixel_format();
-    layer->color = {layerProto.color().r(), layerProto.color().g(), layerProto.color().b(),
+    layer.size = {layerProto.size().w(), layerProto.size().h()};
+    layer.crop = generateRect(layerProto.crop());
+    layer.isOpaque = layerProto.is_opaque();
+    layer.invalidate = layerProto.invalidate();
+    layer.dataspace = layerProto.dataspace();
+    layer.pixelFormat = layerProto.pixel_format();
+    layer.color = {layerProto.color().r(), layerProto.color().g(), layerProto.color().b(),
                     layerProto.color().a()};
-    layer->requestedColor = {layerProto.requested_color().r(), layerProto.requested_color().g(),
+    layer.requestedColor = {layerProto.requested_color().r(), layerProto.requested_color().g(),
                              layerProto.requested_color().b(), layerProto.requested_color().a()};
-    layer->flags = layerProto.flags();
-    layer->transform = generateTransform(layerProto.transform());
-    layer->requestedTransform = generateTransform(layerProto.requested_transform());
-    layer->activeBuffer = generateActiveBuffer(layerProto.active_buffer());
-    layer->queuedFrames = layerProto.queued_frames();
-    layer->refreshPending = layerProto.refresh_pending();
-    layer->hwcFrame = generateRect(layerProto.hwc_frame());
-    layer->hwcCrop = generateFloatRect(layerProto.hwc_crop());
-    layer->hwcTransform = layerProto.hwc_transform();
-    layer->windowType = layerProto.window_type();
-    layer->appId = layerProto.app_id();
-    layer->hwcCompositionType = layerProto.hwc_composition_type();
-    layer->isProtected = layerProto.is_protected();
+    layer.flags = layerProto.flags();
+    layer.transform = generateTransform(layerProto.transform());
+    layer.requestedTransform = generateTransform(layerProto.requested_transform());
+    layer.activeBuffer = generateActiveBuffer(layerProto.active_buffer());
+    layer.bufferTransform = generateTransform(layerProto.buffer_transform());
+    layer.queuedFrames = layerProto.queued_frames();
+    layer.refreshPending = layerProto.refresh_pending();
+    layer.hwcFrame = generateRect(layerProto.hwc_frame());
+    layer.hwcCrop = generateFloatRect(layerProto.hwc_crop());
+    layer.hwcTransform = layerProto.hwc_transform();
+    layer.hwcCompositionType = layerProto.hwc_composition_type();
+    layer.isProtected = layerProto.is_protected();
+    layer.cornerRadius = layerProto.corner_radius();
+    for (const auto& entry : layerProto.metadata()) {
+        const std::string& dataStr = entry.second;
+        std::vector<uint8_t>& outData = layer.metadata.mMap[entry.first];
+        outData.resize(dataStr.size());
+        memcpy(outData.data(), dataStr.data(), dataStr.size());
+    }
 
     return layer;
 }
@@ -186,9 +189,7 @@
 
     for (int i = 0; i < layerProto.children_size(); i++) {
         if (layerMap.count(layerProto.children(i)) > 0) {
-            // Only make unique_ptrs for children since they are guaranteed to be unique, only one
-            // parent per child. This ensures there will only be one unique_ptr made for each layer.
-            currLayer->children.push_back(std::unique_ptr<Layer>(layerMap[layerProto.children(i)]));
+            currLayer->children.push_back(layerMap[layerProto.children(i)]);
         }
     }
 
@@ -211,29 +212,28 @@
     }
 }
 
-std::string LayerProtoParser::layersToString(
-        std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers) {
+std::string LayerProtoParser::layerTreeToString(const LayerTree& layerTree) {
     std::string result;
-    for (std::unique_ptr<LayerProtoParser::Layer>& layer : layers) {
+    for (const LayerProtoParser::Layer* layer : layerTree.topLevelLayers) {
         if (layer->zOrderRelativeOf != nullptr) {
             continue;
         }
-        result.append(layerToString(layer.get()).c_str());
+        result.append(layerToString(layer));
     }
 
     return result;
 }
 
-std::string LayerProtoParser::layerToString(LayerProtoParser::Layer* layer) {
+std::string LayerProtoParser::layerToString(const LayerProtoParser::Layer* layer) {
     std::string result;
 
     std::vector<Layer*> traverse(layer->relatives);
-    for (std::unique_ptr<LayerProtoParser::Layer>& child : layer->children) {
+    for (LayerProtoParser::Layer* child : layer->children) {
         if (child->zOrderRelativeOf != nullptr) {
             continue;
         }
 
-        traverse.push_back(child.get());
+        traverse.push_back(child);
     }
 
     std::sort(traverse.begin(), traverse.end(), sortLayers);
@@ -244,13 +244,13 @@
         if (relative->z >= 0) {
             break;
         }
-        result.append(layerToString(relative).c_str());
+        result.append(layerToString(relative));
     }
-    result.append(layer->to_string().c_str());
+    result.append(layer->to_string());
     result.append("\n");
     for (; i < traverse.size(); i++) {
         auto& relative = traverse[i];
-        result.append(layerToString(relative).c_str());
+        result.append(layerToString(relative));
     }
 
     return result;
@@ -298,8 +298,8 @@
                   z, static_cast<double>(position.x), static_cast<double>(position.y), size.x,
                   size.y);
 
-    StringAppendF(&result, "crop=%s, finalCrop=%s, ", crop.to_string().c_str(),
-                  finalCrop.to_string().c_str());
+    StringAppendF(&result, "crop=%s, ", crop.to_string().c_str());
+    StringAppendF(&result, "cornerRadius=%f, ", cornerRadius);
     StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
     StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
     StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str());
@@ -312,8 +312,16 @@
     StringAppendF(&result, "      zOrderRelativeOf=%s\n",
                   zOrderRelativeOf == nullptr ? "none" : zOrderRelativeOf->name.c_str());
     StringAppendF(&result, "      activeBuffer=%s,", activeBuffer.to_string().c_str());
+    StringAppendF(&result, " tr=%s", bufferTransform.to_string().c_str());
     StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d,", queuedFrames, refreshPending);
-    StringAppendF(&result, " windowType=%d, appId=%d", windowType, appId);
+    StringAppendF(&result, " metadata={");
+    bool first = true;
+    for (const auto& entry : metadata.mMap) {
+        if (!first) result.append(", ");
+        first = false;
+        result.append(metadata.itemToString(entry.first, ":"));
+    }
+    result.append("}");
 
     return result;
 }
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 74a6f28..d1b2b1f 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -17,6 +17,7 @@
 
 #include <layerproto/LayerProtoHeader.h>
 
+#include <gui/LayerMetadata.h>
 #include <math/vec4.h>
 
 #include <memory>
@@ -80,7 +81,7 @@
     public:
         int32_t id;
         std::string name;
-        std::vector<std::unique_ptr<Layer>> children;
+        std::vector<Layer*> children;
         std::vector<Layer*> relatives;
         std::string type;
         LayerProtoParser::Region transparentRegion;
@@ -92,7 +93,6 @@
         float2 requestedPosition;
         int2 size;
         LayerProtoParser::Rect crop;
-        LayerProtoParser::Rect finalCrop;
         bool isOpaque;
         bool invalidate;
         std::string dataspace;
@@ -105,15 +105,16 @@
         Layer* parent = 0;
         Layer* zOrderRelativeOf = 0;
         LayerProtoParser::ActiveBuffer activeBuffer;
+        Transform bufferTransform;
         int32_t queuedFrames;
         bool refreshPending;
         LayerProtoParser::Rect hwcFrame;
         LayerProtoParser::FloatRect hwcCrop;
         int32_t hwcTransform;
-        int32_t windowType;
-        int32_t appId;
         int32_t hwcCompositionType;
         bool isProtected;
+        float cornerRadius;
+        LayerMetadata metadata;
 
         std::string to_string() const;
     };
@@ -126,13 +127,22 @@
         int32_t globalTransform;
     };
 
+    class LayerTree {
+    public:
+        // all layers in LayersProto and in the original order
+        std::vector<Layer> allLayers;
+
+        // pointers to top-level layers in allLayers
+        std::vector<Layer*> topLevelLayers;
+    };
+
     static const LayerGlobal generateLayerGlobalInfo(const LayersProto& layersProto);
-    static std::vector<std::unique_ptr<Layer>> generateLayerTree(const LayersProto& layersProto);
-    static std::string layersToString(std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers);
+    static LayerTree generateLayerTree(const LayersProto& layersProto);
+    static std::string layerTreeToString(const LayerTree& layerTree);
 
 private:
-    static std::unordered_map<int32_t, Layer*> generateMap(const LayersProto& layersProto);
-    static LayerProtoParser::Layer* generateLayer(const LayerProto& layerProto);
+    static std::vector<Layer> generateLayerList(const LayersProto& layersProto);
+    static LayerProtoParser::Layer generateLayer(const LayerProto& layerProto);
     static LayerProtoParser::Region generateRegion(const RegionProto& regionProto);
     static LayerProtoParser::Rect generateRect(const RectProto& rectProto);
     static LayerProtoParser::FloatRect generateFloatRect(const FloatRectProto& rectProto);
@@ -142,7 +152,7 @@
     static void updateChildrenAndRelative(const LayerProto& layerProto,
                                           std::unordered_map<int32_t, Layer*>& layerMap);
 
-    static std::string layerToString(LayerProtoParser::Layer* layer);
+    static std::string layerToString(const LayerProtoParser::Layer* layer);
 };
 
 } // namespace surfaceflinger
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index edf56ab..4c756d9 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -41,7 +41,7 @@
   // The layer's crop in it's own bounds.
   optional RectProto crop = 14;
   // The layer's crop in it's parent's bounds.
-  optional RectProto final_crop = 15;
+  optional RectProto final_crop = 15 [deprecated=true];
   optional bool is_opaque = 16;
   optional bool invalidate = 17;
   optional string dataspace = 18;
@@ -74,8 +74,8 @@
   optional FloatRectProto hwc_crop = 31;
   // The layer's composer backend transform
   optional int32 hwc_transform = 32;
-  optional int32 window_type = 33;
-  optional int32 app_id = 34;
+  optional int32 window_type = 33 [deprecated=true];
+  optional int32 app_id = 34 [deprecated=true];
   // The layer's composition type
   optional int32 hwc_composition_type = 35;
   // If it's a buffer layer, indicate if the content is protected
@@ -84,6 +84,13 @@
   optional uint64 curr_frame = 37;
   // A list of barriers that the layer is waiting to update state.
   repeated BarrierLayerProto barrier_layer = 38;
+  // If active_buffer is not null, record its transform.
+  optional TransformProto buffer_transform = 39;
+  optional int32 effective_scaling_mode = 40;
+  // Layer's corner radius.
+  optional float corner_radius = 41;
+  // Metadata map. May be empty.
+  map<int32, bytes> metadata = 42;
 }
 
 message PositionProto {
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index aefe704..e7986d3 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -21,30 +21,35 @@
 #include <android/frameworks/displayservice/1.0/IDisplayService.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/graphics/allocator/2.0/IAllocator.h>
-#include <binder/IServiceManager.h>
+#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
 #include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <configstore/Utils.h>
 #include <displayservice/DisplayService.h>
 #include <hidl/LegacySupport.h>
 #include <processgroup/sched_policy.h>
-#include <configstore/Utils.h>
 #include "SurfaceFlinger.h"
+#include "SurfaceFlingerFactory.h"
+#include "SurfaceFlingerProperties.h"
 
 using namespace android;
 
 static status_t startGraphicsAllocatorService() {
     using android::hardware::configstore::getBool;
     using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs;
-    if (!getBool<ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(false)) {
+    if (!android::sysprop::start_graphics_allocator_service(false)) {
         return OK;
     }
 
-    using android::hardware::graphics::allocator::V2_0::IAllocator;
+    status_t result = hardware::registerPassthroughServiceImplementation<
+            android::hardware::graphics::allocator::V3_0::IAllocator>();
+    if (result == OK) {
+        return OK;
+    }
 
-    status_t result =
-        hardware::registerPassthroughServiceImplementation<IAllocator>();
+    result = hardware::registerPassthroughServiceImplementation<
+            android::hardware::graphics::allocator::V2_0::IAllocator>();
     if (result != OK) {
         ALOGE("could not start graphics allocator service");
         return result;
@@ -84,7 +89,7 @@
     ps->startThreadPool();
 
     // instantiate surfaceflinger
-    sp<SurfaceFlinger> flinger = new SurfaceFlinger();
+    sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
 
     setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
 
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
new file mode 100644
index 0000000..cc7b280
--- /dev/null
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -0,0 +1,252 @@
+# Copyright (C) 2019 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.
+
+module: "android.sysprop.SurfaceFlingerProperties"
+owner: Platform
+
+# The following two propertiess 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 may 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 must 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
+prop {
+    api_name: "vsync_event_phase_offset_ns"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.vsync_event_phase_offset_ns"
+}
+
+prop {
+    api_name: "vsync_sf_event_phase_offset_ns"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.vsync_sf_event_phase_offset_ns"
+}
+
+# Instruct the Render Engine to use EGL_IMG_context_priority hint if available.
+prop {
+    api_name: "use_context_priority"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_context_priority"
+}
+
+# Controls the number of buffers SurfaceFlinger will allocate for use in FramebufferSurface.
+prop {
+    api_name: "max_frame_buffer_acquired_buffers"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
+}
+
+# hasWideColorDisplay indicates that the device has
+# or can support a wide-color display, e.g. color space
+# greater than sRGB. Typical display may have same
+# color primaries as DCI-P3.
+# Indicate support for this feature by setting
+# TARGET_HAS_WIDE_COLOR_DISPLAY to true in BoardConfig.mk
+# This also means that the device is color managed.
+# A color managed device will use the appropriate
+# display mode depending on the content on the screen.
+# Default is sRGB.
+prop {
+    api_name: "has_wide_color_display"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.has_wide_color_display"
+}
+
+# Indicates if Sync framework is available. Sync framework provides fence
+# mechanism which significantly reduces buffer processing latency.
+prop {
+    api_name: "running_without_sync_framework"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.running_without_sync_framework"
+}
+
+# hwHDRDisplay indicates that the device has an High Dynamic Range display.
+# A display is considered High Dynamic Range if it
+#
+#     1. is a wide color gamut display, typically DCI-P3 or lager
+#     2. has high luminance capability, typically 540 nits or higher at 10% OPR
+#
+# Indicate support for this feature by setting
+# ro.surface_flinger.has_HDR_display to true in device.mk
+# ro.surface_flinger.has_wide_color_display must be set to true when
+# ro.surface_flinger.has_HDR_display is true.
+prop {
+    api_name: "has_HDR_display"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.has_HDR_display"
+}
+
+# Specify the offset in nanoseconds to add to vsync time when timestamping present fences.
+prop {
+    api_name: "present_time_offset_from_vsync_ns"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.present_time_offset_from_vsync_ns"
+}
+
+# Some hardware can do RGB->YUV conversion more efficiently in hardware
+# controlled by HWC than in hardware controlled by the video encoder.
+# This instruct VirtualDisplaySurface to use HWC for such conversion on
+# GL composition.
+prop {
+    api_name: "force_hwc_copy_for_virtual_displays"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays"
+}
+
+# Maximum dimension supported by HWC for virtual display.
+# Must be equals to min(max_width, max_height).
+prop {
+    api_name: "max_virtual_display_dimension"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.max_virtual_display_dimension"
+}
+
+# Return true if surface flinger should use vr flinger for compatible vr
+# apps, false otherwise. Devices that will never be running vr apps should
+# return false to avoid extra resource usage. Daydream ready devices must
+# return true for full vr support.
+prop {
+    api_name: "use_vr_flinger"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_vr_flinger"
+}
+
+# Returns true if surface flinger should start
+# hardware.graphics.allocator@2.0::IAllocator service.
+prop {
+    api_name: "start_graphics_allocator_service"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.start_graphics_allocator_service"
+}
+
+# Returns the orientation of the primary display device.
+prop {
+    api_name: "primary_display_orientation"
+    type: Enum
+    enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270"
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.primary_display_orientation"
+}
+
+# useColorManagement indicates whether SurfaceFlinger should manage color
+# by switching to appropriate color mode automatically depending on the
+# Dataspace of the surfaces on screen.
+prop {
+    api_name: "use_color_management"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_color_management"
+}
+
+# The following four propertiess define:
+# Returns the default data space and pixel format that SurfaceFlinger
+# expects to receive and output as well as the wide color gamut data space
+# and pixel format for wide color gamut surfaces.
+# To determine the data space and pixel format, there are a few things
+# we recommend to consider:
+#
+#   1. Hardware composer's capability to composite contents with the chosen
+#      data space and pixel format efficiently;
+#   2. Hardware composer's ability to composite contents when sRGB contents
+#      and the chosen wide color gamut data space contents coexist;
+#   3. For better blending, consider using pixel format where the alpha
+#      channel has as many bits as the RGB color channel.
+#   4. Memory consumption and efficient buffer compression when considering
+#      more bits in pixel format.
+
+# dataspace is the default data space that SurfaceFlinger expects.
+# The data space must not be Dataspace::UNKNOWN, if unspecified,
+# the default data space is Dataspace::V0_SRGB;
+prop {
+    api_name: "default_composition_dataspace"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.default_composition_dataspace"
+}
+
+# pixelFormat is the default pixel format that SurfaceFlinger
+# expects. If unspecified, the default pixel format is
+# PixelFormat::RGBA_8888.
+prop {
+    api_name: "default_composition_pixel_format"
+    type: Integer
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.default_composition_pixel_format"
+}
+
+# wcgDataspace is the data space that SurfaceFlinger expects for
+# wide color gamut surfaces.
+# When hasWideColorDisplay returns true, this API must return a
+# valid wide color gamut data space.
+# The data space must not be UNKNOWN, if unspecified, the data space
+# is V0_SRGB by default, which essentially indicates there's no wide
+# color gamut, meaning hasWideColorDisplay returns false.
+prop {
+    api_name: "wcg_composition_dataspace"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.wcg_composition_dataspace"
+}
+
+# wcgPixelFormat is the pixel format that SurfaceFlinger expects for
+# wide color gamut surfaces. If unspecified, the pixel format is
+# PixelFormat::RGBA_8888 by default.
+prop {
+    api_name: "wcg_composition_pixel_format"
+    type: Integer
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.wcg_composition_pixel_format"
+}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index c511c5e..f121a95 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -17,6 +17,8 @@
     defaults: ["surfaceflinger_defaults"],
     test_suites: ["device-tests"],
     srcs: [
+        "BufferGenerator.cpp",
+        "Credentials_test.cpp",
         "Stress_test.cpp",
         "SurfaceInterceptor_test.cpp",
         "Transaction_test.cpp",
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
new file mode 100644
index 0000000..8ddda60
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
+
+#include <GLES3/gl3.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include "BufferGenerator.h"
+#include "BufferGeneratorShader.h"
+
+namespace android {
+
+/* 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 SurfaceManager {
+public:
+    /* Returns a fence from egl */
+    using BufferCallback = std::function<void(const sp<GraphicBuffer>& buffer, int32_t fence)>;
+
+    /* 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, BufferCallback callback)
+              : mConsumer(consumer), mCallback(callback) {}
+
+        void onFrameAvailable(const BufferItem& /*item*/) {
+            BufferItem item;
+
+            if (mConsumer->acquireBuffer(&item, 0)) return;
+            if (mConsumer->detachBuffer(item.mSlot)) return;
+
+            mCallback(item.mGraphicBuffer, item.mFence->dup());
+        }
+
+    private:
+        sp<IGraphicBufferConsumer> mConsumer;
+        BufferCallback mCallback;
+    };
+
+    /* Creates a buffer listener that waits on a new frame from the buffer
+     * queue. */
+    void initialize(uint32_t width, uint32_t height, android_pixel_format_t format,
+                    BufferCallback callback) {
+        sp<IGraphicBufferProducer> producer;
+        sp<IGraphicBufferConsumer> consumer;
+        BufferQueue::createBufferQueue(&producer, &consumer);
+
+        consumer->setDefaultBufferSize(width, height);
+        consumer->setDefaultBufferFormat(format);
+
+        mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
+
+        mListener = new BufferListener(consumer, callback);
+        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 EglManager {
+public:
+    EglManager()
+          : mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT) {}
+
+    ~EglManager() { 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, 3, 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 majorVersion;
+        if (!eglQueryContext(mEglDisplay, mEglContext, EGL_CONTEXT_CLIENT_VERSION, &majorVersion)) {
+            ALOGW("Could not query EGL version");
+            cleanup();
+            return false;
+        }
+
+        if (majorVersion != 3) {
+            ALOGW("Unsupported EGL version");
+            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;
+};
+
+class Program {
+public:
+    ~Program() {
+        if (mInitialized) {
+            glDetachShader(mProgram, mVertexShader);
+            glDetachShader(mProgram, mFragmentShader);
+
+            glDeleteShader(mVertexShader);
+            glDeleteShader(mFragmentShader);
+
+            glDeleteProgram(mProgram);
+        }
+    }
+
+    bool initialize(const char* vertex, const char* fragment) {
+        mVertexShader = buildShader(vertex, GL_VERTEX_SHADER);
+        if (!mVertexShader) {
+            return false;
+        }
+
+        mFragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
+        if (!mFragmentShader) {
+            return false;
+        }
+
+        mProgram = glCreateProgram();
+        glAttachShader(mProgram, mVertexShader);
+        glAttachShader(mProgram, mFragmentShader);
+
+        glLinkProgram(mProgram);
+
+        GLint status;
+        glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
+        if (status != GL_TRUE) {
+            GLint length = 0;
+            glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &length);
+            if (length > 1) {
+                GLchar log[length];
+                glGetProgramInfoLog(mProgram, length, nullptr, &log[0]);
+                ALOGE("%s", log);
+            }
+            ALOGE("Error while linking shaders");
+            return false;
+        }
+        mInitialized = true;
+        return true;
+    }
+
+    void use() const { glUseProgram(mProgram); }
+
+    void bindVec4(GLint location, vec4 v) const { glUniform4f(location, v.x, v.y, v.z, v.w); }
+
+    void bindVec3(GLint location, const vec3* v, uint32_t count) const {
+        glUniform3fv(location, count, &(v->x));
+    }
+
+    void bindFloat(GLint location, float v) { glUniform1f(location, v); }
+
+private:
+    GLuint buildShader(const char* source, GLenum type) const {
+        GLuint shader = glCreateShader(type);
+        glShaderSource(shader, 1, &source, nullptr);
+        glCompileShader(shader);
+
+        GLint status;
+        glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+        if (status != GL_TRUE) {
+            ALOGE("Error while compiling shader of type 0x%x:\n===\n%s\n===", type, source);
+            // Some drivers return wrong values for GL_INFO_LOG_LENGTH
+            // use a fixed size instead
+            GLchar log[512];
+            glGetShaderInfoLog(shader, sizeof(log), nullptr, &log[0]);
+            ALOGE("Shader info log: %s", log);
+            return 0;
+        }
+
+        return shader;
+    }
+
+    GLuint mProgram = 0;
+    GLuint mVertexShader = 0;
+    GLuint mFragmentShader = 0;
+    bool mInitialized = false;
+};
+
+BufferGenerator::BufferGenerator()
+      : mSurfaceManager(new SurfaceManager), mEglManager(new EglManager), mProgram(new Program) {
+    const float width = 1000.0;
+    const float height = 1000.0;
+
+    auto setBufferWithContext =
+            std::bind(setBuffer, std::placeholders::_1, std::placeholders::_2, this);
+    mSurfaceManager->initialize(width, height, HAL_PIXEL_FORMAT_RGBA_8888, setBufferWithContext);
+
+    if (!mEglManager->initialize(mSurfaceManager->getSurface())) return;
+
+    mEglManager->makeCurrent();
+
+    if (!mProgram->initialize(VERTEX_SHADER, FRAGMENT_SHADER)) return;
+    mProgram->use();
+    mProgram->bindVec4(0, vec4{width, height, 1.0f / width, 1.0f / height});
+    mProgram->bindVec3(2, &SPHERICAL_HARMONICS[0], 4);
+
+    glEnableVertexAttribArray(0);
+    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, &TRIANGLE[0]);
+
+    mInitialized = true;
+}
+
+BufferGenerator::~BufferGenerator() {
+    mEglManager->makeCurrent();
+}
+
+status_t BufferGenerator::get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+    // mMutex is used to protect get() from getting called by multiple threads at the same time
+    static std::mutex mMutex;
+    std::lock_guard lock(mMutex);
+
+    if (!mInitialized) {
+        if (outBuffer) {
+            *outBuffer = nullptr;
+        }
+        if (*outFence) {
+            *outFence = nullptr;
+        }
+        return -EINVAL;
+    }
+
+    // Generate a buffer and fence. They will be returned through the setBuffer callback
+    mEglManager->makeCurrent();
+
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    const std::chrono::duration<float> time = std::chrono::steady_clock::now() - mEpoch;
+    mProgram->bindFloat(1, time.count());
+
+    glDrawArrays(GL_TRIANGLES, 0, 3);
+
+    mPending = true;
+    mEglManager->present();
+
+    // Wait for the setBuffer callback
+    if (!mConditionVariable.wait_for(mMutex, std::chrono::seconds(2),
+                                     [this] { return !mPending; })) {
+        ALOGE("failed to set buffer and fence");
+        return -ETIME;
+    }
+
+    // Return buffer and fence
+    if (outBuffer) {
+        *outBuffer = mGraphicBuffer;
+    }
+    if (outFence) {
+        *outFence = new Fence(mFence);
+    } else {
+        close(mFence);
+    }
+    mGraphicBuffer = nullptr;
+    mFence = -1;
+
+    return NO_ERROR;
+}
+
+// static
+void BufferGenerator::setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence,
+                                void* bufferGenerator) {
+    BufferGenerator* generator = static_cast<BufferGenerator*>(bufferGenerator);
+    generator->mGraphicBuffer = buffer;
+    generator->mFence = fence;
+    generator->mPending = false;
+    generator->mConditionVariable.notify_all();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/BufferGenerator.h b/services/surfaceflinger/tests/BufferGenerator.h
new file mode 100644
index 0000000..a3ffe86
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGenerator.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <mutex>
+
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+
+class SurfaceManager;
+class EglManager;
+class Program;
+
+class BufferGenerator {
+public:
+    BufferGenerator();
+    ~BufferGenerator();
+
+    /* Get a new fence */
+    status_t get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+
+    /* Static callback that sets the fence on a particular instance */
+    static void setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence, void* fenceGenerator);
+
+private:
+    bool mInitialized = false;
+
+    std::unique_ptr<SurfaceManager> mSurfaceManager;
+    std::unique_ptr<EglManager> mEglManager;
+    std::unique_ptr<Program> mProgram;
+
+    std::condition_variable_any mConditionVariable;
+
+    sp<GraphicBuffer> mGraphicBuffer;
+    int32_t mFence = -1;
+    bool mPending = false;
+
+    using Epoch = std::chrono::time_point<std::chrono::steady_clock>;
+    Epoch mEpoch = std::chrono::steady_clock::now();
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/BufferGeneratorShader.h b/services/surfaceflinger/tests/BufferGeneratorShader.h
new file mode 100644
index 0000000..564cda3
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGeneratorShader.h
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <GLES3/gl3.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+static const char* VERTEX_SHADER = R"SHADER__(#version 300 es
+precision highp float;
+
+layout(location = 0) in vec4 mesh_position;
+
+void main() {
+    gl_Position = mesh_position;
+}
+)SHADER__";
+
+static const char* FRAGMENT_SHADER = R"SHADER__(#version 300 es
+precision highp float;
+
+layout(location = 0) uniform vec4 resolution;
+layout(location = 1) uniform float time;
+layout(location = 2) uniform vec3[4] SPHERICAL_HARMONICS;
+
+layout(location = 0) out vec4 fragColor;
+
+#define saturate(x) clamp(x, 0.0, 1.0)
+#define PI 3.14159265359
+
+//------------------------------------------------------------------------------
+// Distance field functions
+//------------------------------------------------------------------------------
+
+float sdPlane(in vec3 p) {
+    return p.y;
+}
+
+float sdSphere(in vec3 p, float s) {
+    return length(p) - s;
+}
+
+float sdTorus(in vec3 p, in vec2 t) {
+    return length(vec2(length(p.xz) - t.x, p.y)) - t.y;
+}
+
+vec2 opUnion(vec2 d1, vec2 d2) {
+    return d1.x < d2.x ? d1 : d2;
+}
+
+vec2 scene(in vec3 position) {
+    vec2 scene = opUnion(
+          vec2(sdPlane(position), 1.0),
+          vec2(sdSphere(position - vec3(0.0, 0.4, 0.0), 0.4), 12.0)
+    );
+    return scene;
+}
+
+//------------------------------------------------------------------------------
+// Ray casting
+//------------------------------------------------------------------------------
+
+float shadow(in vec3 origin, in vec3 direction, in float tmin, in float tmax) {
+    float hit = 1.0;
+
+    for (float t = tmin; t < tmax; ) {
+        float h = scene(origin + direction * t).x;
+        if (h < 0.001) return 0.0;
+        t += h;
+        hit = min(hit, 10.0 * h / t);
+    }
+
+    return clamp(hit, 0.0, 1.0);
+}
+
+vec2 traceRay(in vec3 origin, in vec3 direction) {
+    float tmin = 0.02;
+    float tmax = 20.0;
+
+    float material = -1.0;
+    float t = tmin;
+
+    for ( ; t < tmax; ) {
+        vec2 hit = scene(origin + direction * t);
+        if (hit.x < 0.002 || t > tmax) break;
+        t += hit.x;
+        material = hit.y;
+    }
+
+    if (t > tmax) {
+        material = -1.0;
+    }
+
+    return vec2(t, material);
+}
+
+vec3 normal(in vec3 position) {
+    vec3 epsilon = vec3(0.001, 0.0, 0.0);
+    vec3 n = vec3(
+          scene(position + epsilon.xyy).x - scene(position - epsilon.xyy).x,
+          scene(position + epsilon.yxy).x - scene(position - epsilon.yxy).x,
+          scene(position + epsilon.yyx).x - scene(position - epsilon.yyx).x);
+    return normalize(n);
+}
+
+//------------------------------------------------------------------------------
+// BRDF
+//------------------------------------------------------------------------------
+
+float pow5(float x) {
+    float x2 = x * x;
+    return x2 * x2 * x;
+}
+
+float D_GGX(float linearRoughness, float NoH, const vec3 h) {
+    // Walter et al. 2007, "Microfacet Models for Refraction through Rough Surfaces"
+    float oneMinusNoHSquared = 1.0 - NoH * NoH;
+    float a = NoH * linearRoughness;
+    float k = linearRoughness / (oneMinusNoHSquared + a * a);
+    float d = k * k * (1.0 / PI);
+    return d;
+}
+
+float V_SmithGGXCorrelated(float linearRoughness, float NoV, float NoL) {
+    // Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"
+    float a2 = linearRoughness * linearRoughness;
+    float GGXV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2);
+    float GGXL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2);
+    return 0.5 / (GGXV + GGXL);
+}
+
+vec3 F_Schlick(const vec3 f0, float VoH) {
+    // Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"
+    return f0 + (vec3(1.0) - f0) * pow5(1.0 - VoH);
+}
+
+float F_Schlick(float f0, float f90, float VoH) {
+    return f0 + (f90 - f0) * pow5(1.0 - VoH);
+}
+
+float Fd_Burley(float linearRoughness, float NoV, float NoL, float LoH) {
+    // Burley 2012, "Physically-Based Shading at Disney"
+    float f90 = 0.5 + 2.0 * linearRoughness * LoH * LoH;
+    float lightScatter = F_Schlick(1.0, f90, NoL);
+    float viewScatter  = F_Schlick(1.0, f90, NoV);
+    return lightScatter * viewScatter * (1.0 / PI);
+}
+
+float Fd_Lambert() {
+    return 1.0 / PI;
+}
+
+//------------------------------------------------------------------------------
+// Indirect lighting
+//------------------------------------------------------------------------------
+
+vec3 Irradiance_SphericalHarmonics(const vec3 n) {
+    return max(
+          SPHERICAL_HARMONICS[0]
+        + SPHERICAL_HARMONICS[1] * (n.y)
+        + SPHERICAL_HARMONICS[2] * (n.z)
+        + SPHERICAL_HARMONICS[3] * (n.x)
+        , 0.0);
+}
+
+vec2 PrefilteredDFG_Karis(float roughness, float NoV) {
+    // Karis 2014, "Physically Based Material on Mobile"
+    const vec4 c0 = vec4(-1.0, -0.0275, -0.572,  0.022);
+    const vec4 c1 = vec4( 1.0,  0.0425,  1.040, -0.040);
+
+    vec4 r = roughness * c0 + c1;
+    float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
+
+    return vec2(-1.04, 1.04) * a004 + r.zw;
+}
+
+//------------------------------------------------------------------------------
+// Tone mapping and transfer functions
+//------------------------------------------------------------------------------
+
+vec3 Tonemap_ACES(const vec3 x) {
+    // Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
+    const float a = 2.51;
+    const float b = 0.03;
+    const float c = 2.43;
+    const float d = 0.59;
+    const float e = 0.14;
+    return (x * (a * x + b)) / (x * (c * x + d) + e);
+}
+
+vec3 OECF_sRGBFast(const vec3 linear) {
+    return pow(linear, vec3(1.0 / 2.2));
+}
+
+//------------------------------------------------------------------------------
+// Rendering
+//------------------------------------------------------------------------------
+
+vec3 render(in vec3 origin, in vec3 direction, out float distance) {
+    // Sky gradient
+    vec3 color = vec3(0.65, 0.85, 1.0) + direction.y * 0.72;
+
+    // (distance, material)
+    vec2 hit = traceRay(origin, direction);
+    distance = hit.x;
+    float material = hit.y;
+
+    // We've hit something in the scene
+    if (material > 0.0) {
+        vec3 position = origin + distance * direction;
+
+        vec3 v = normalize(-direction);
+        vec3 n = normal(position);
+        vec3 l = normalize(vec3(0.6, 0.7, -0.7));
+        vec3 h = normalize(v + l);
+        vec3 r = normalize(reflect(direction, n));
+
+        float NoV = abs(dot(n, v)) + 1e-5;
+        float NoL = saturate(dot(n, l));
+        float NoH = saturate(dot(n, h));
+        float LoH = saturate(dot(l, h));
+
+        vec3 baseColor = vec3(0.0);
+        float roughness = 0.0;
+        float metallic = 0.0;
+
+        float intensity = 2.0;
+        float indirectIntensity = 0.64;
+
+        if (material < 4.0)  {
+            // Checkerboard floor
+            float f = mod(floor(6.0 * position.z) + floor(6.0 * position.x), 2.0);
+            baseColor = 0.4 + f * vec3(0.6);
+            roughness = 0.1;
+        } else if (material < 16.0) {
+            // Metallic objects
+            baseColor = vec3(0.3, 0.0, 0.0);
+            roughness = 0.2;
+        }
+
+        float linearRoughness = roughness * roughness;
+        vec3 diffuseColor = (1.0 - metallic) * baseColor.rgb;
+        vec3 f0 = 0.04 * (1.0 - metallic) + baseColor.rgb * metallic;
+
+        float attenuation = shadow(position, l, 0.02, 2.5);
+
+        // specular BRDF
+        float D = D_GGX(linearRoughness, NoH, h);
+        float V = V_SmithGGXCorrelated(linearRoughness, NoV, NoL);
+        vec3  F = F_Schlick(f0, LoH);
+        vec3 Fr = (D * V) * F;
+
+        // diffuse BRDF
+        vec3 Fd = diffuseColor * Fd_Burley(linearRoughness, NoV, NoL, LoH);
+
+        color = Fd + Fr;
+        color *= (intensity * attenuation * NoL) * vec3(0.98, 0.92, 0.89);
+
+        // diffuse indirect
+        vec3 indirectDiffuse = Irradiance_SphericalHarmonics(n) * Fd_Lambert();
+
+        vec2 indirectHit = traceRay(position, r);
+        vec3 indirectSpecular = vec3(0.65, 0.85, 1.0) + r.y * 0.72;
+        if (indirectHit.y > 0.0) {
+            if (indirectHit.y < 4.0)  {
+                vec3 indirectPosition = position + indirectHit.x * r;
+                // Checkerboard floor
+                float f = mod(floor(6.0 * indirectPosition.z) + floor(6.0 * indirectPosition.x), 2.0);
+                indirectSpecular = 0.4 + f * vec3(0.6);
+            } else if (indirectHit.y < 16.0) {
+                // Metallic objects
+                indirectSpecular = vec3(0.3, 0.0, 0.0);
+            }
+        }
+
+        // indirect contribution
+        vec2 dfg = PrefilteredDFG_Karis(roughness, NoV);
+        vec3 specularColor = f0 * dfg.x + dfg.y;
+        vec3 ibl = diffuseColor * indirectDiffuse + indirectSpecular * specularColor;
+
+        color += ibl * indirectIntensity;
+    }
+
+    return color;
+}
+
+//------------------------------------------------------------------------------
+// Setup and execution
+//------------------------------------------------------------------------------
+
+mat3 setCamera(in vec3 origin, in vec3 target, float rotation) {
+    vec3 forward = normalize(target - origin);
+    vec3 orientation = vec3(sin(rotation), cos(rotation), 0.0);
+    vec3 left = normalize(cross(forward, orientation));
+    vec3 up = normalize(cross(left, forward));
+    return mat3(left, up, forward);
+}
+
+void main() {
+    // Normalized coordinates
+    vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
+    // Aspect ratio
+    p.x *= resolution.x / resolution.y;
+
+    // Camera position and "look at"
+    vec3 origin = vec3(0.0, 1.0, 0.0);
+    vec3 target = vec3(0.0);
+
+    origin.x += 2.0 * cos(time * 0.2);
+    origin.z += 2.0 * sin(time * 0.2);
+
+    mat3 toWorld = setCamera(origin, target, 0.0);
+    vec3 direction = toWorld * normalize(vec3(p.xy, 2.0));
+
+    // Render scene
+    float distance;
+    vec3 color = render(origin, direction, distance);
+
+    // Tone mapping
+    color = Tonemap_ACES(color);
+
+    // Exponential distance fog
+    color = mix(color, 0.8 * vec3(0.7, 0.8, 1.0), 1.0 - exp2(-0.011 * distance * distance));
+
+    // Gamma compression
+    color = OECF_sRGBFast(color);
+
+    fragColor = vec4(color, 1.0);
+}
+)SHADER__";
+
+static const android::vec3 SPHERICAL_HARMONICS[4] =
+        {{0.754554516862612, 0.748542953903366, 0.790921515418539},
+         {-0.083856548007422, 0.092533500963210, 0.322764661032516},
+         {0.308152705331738, 0.366796330467391, 0.466698181299906},
+         {-0.188884931542396, -0.277402551592231, -0.377844212327557}};
+
+static const android::vec4 TRIANGLE[3] = {{-1.0f, -1.0f, 1.0f, 1.0f},
+                                          {3.0f, -1.0f, 1.0f, 1.0f},
+                                          {-1.0f, 3.0f, 1.0f, 1.0f}};
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
new file mode 100644
index 0000000..f021d3d
--- /dev/null
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -0,0 +1,357 @@
+#include <algorithm>
+#include <functional>
+#include <limits>
+#include <ostream>
+
+#include <gtest/gtest.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/gui/ComposerService.h>
+
+#include <ui/DisplayInfo.h>
+#include <utils/String8.h>
+
+namespace android {
+
+using Transaction = SurfaceComposerClient::Transaction;
+using ui::ColorMode;
+
+namespace {
+const String8 DISPLAY_NAME("Credentials Display Test");
+const String8 SURFACE_NAME("Test Surface Name");
+const uint32_t ROTATION = 0;
+const float FRAME_SCALE = 1.0f;
+} // namespace
+
+/**
+ * This class tests the CheckCredentials method in SurfaceFlinger.
+ * Methods like EnableVsyncInjections and InjectVsync are not tested since they do not
+ * return anything meaningful.
+ */
+class CredentialsTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        // Start the tests as root.
+        seteuid(AID_ROOT);
+
+        ASSERT_NO_FATAL_FAILURE(initClient());
+    }
+
+    void TearDown() override {
+        mComposerClient->dispose();
+        mBGSurfaceControl.clear();
+        mComposerClient.clear();
+        // Finish the tests as root.
+        seteuid(AID_ROOT);
+    }
+
+    sp<IBinder> mDisplay;
+    sp<IBinder> mVirtualDisplay;
+    sp<SurfaceComposerClient> mComposerClient;
+    sp<SurfaceControl> mBGSurfaceControl;
+    sp<SurfaceControl> mVirtualSurfaceControl;
+
+    void initClient() {
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+    }
+
+    void setupBackgroundSurface() {
+        mDisplay = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+        DisplayInfo info;
+        SurfaceComposerClient::getDisplayInfo(mDisplay, &info);
+        const ssize_t displayWidth = info.w;
+        const ssize_t displayHeight = info.h;
+
+        // Background surface
+        mBGSurfaceControl =
+                mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
+                                               PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(mBGSurfaceControl != nullptr);
+        ASSERT_TRUE(mBGSurfaceControl->isValid());
+
+        Transaction t;
+        t.setDisplayLayerStack(mDisplay, 0);
+        ASSERT_EQ(NO_ERROR,
+                  t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply());
+    }
+
+    void setupVirtualDisplay() {
+        mVirtualDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
+        const ssize_t displayWidth = 100;
+        const ssize_t displayHeight = 100;
+
+        // Background surface
+        mVirtualSurfaceControl =
+                mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
+                                               PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(mVirtualSurfaceControl != nullptr);
+        ASSERT_TRUE(mVirtualSurfaceControl->isValid());
+
+        Transaction t;
+        t.setDisplayLayerStack(mVirtualDisplay, 0);
+        ASSERT_EQ(NO_ERROR,
+                  t.setLayer(mVirtualSurfaceControl, INT_MAX - 3)
+                          .show(mVirtualSurfaceControl)
+                          .apply());
+    }
+
+    /**
+     * Sets UID to imitate Graphic's process.
+     */
+    void setGraphicsUID() {
+        seteuid(AID_ROOT);
+        seteuid(AID_GRAPHICS);
+    }
+
+    /**
+     * Sets UID to imitate System's process.
+     */
+    void setSystemUID() {
+        seteuid(AID_ROOT);
+        seteuid(AID_SYSTEM);
+    }
+
+    /**
+     * Sets UID to imitate a process that doesn't have any special privileges in
+     * our code.
+     */
+    void setBinUID() {
+        seteuid(AID_ROOT);
+        seteuid(AID_BIN);
+    }
+
+    /**
+     * Template function the check a condition for different types of users: root
+     * graphics, system, and non-supported user. Root, graphics, and system should
+     * always equal privilegedValue, and non-supported user should equal unprivilegedValue.
+     */
+    template <typename T>
+    void checkWithPrivileges(std::function<T()> condition, T privilegedValue, T unprivilegedValue) {
+        // Check with root.
+        seteuid(AID_ROOT);
+        ASSERT_EQ(privilegedValue, condition());
+
+        // Check as a Graphics user.
+        setGraphicsUID();
+        ASSERT_EQ(privilegedValue, condition());
+
+        // Check as a system user.
+        setSystemUID();
+        ASSERT_EQ(privilegedValue, condition());
+
+        // Check as a non-supported user.
+        setBinUID();
+        ASSERT_EQ(unprivilegedValue, condition());
+    }
+};
+
+TEST_F(CredentialsTest, ClientInitTest) {
+    // Root can init can init the client.
+    ASSERT_NO_FATAL_FAILURE(initClient());
+
+    // Graphics can init the client.
+    setGraphicsUID();
+    ASSERT_NO_FATAL_FAILURE(initClient());
+
+    // System can init the client.
+    setSystemUID();
+    ASSERT_NO_FATAL_FAILURE(initClient());
+
+    // Anyone else can init the client.
+    setBinUID();
+    mComposerClient = new SurfaceComposerClient;
+    ASSERT_NO_FATAL_FAILURE(initClient());
+}
+
+TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
+    std::function<bool()> condition = [=]() {
+        sp<IBinder> display(
+                SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+        return (display != nullptr);
+    };
+    // Anyone can access display information.
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
+}
+
+TEST_F(CredentialsTest, AllowedGetterMethodsTest) {
+    // The following methods are tested with a UID that is not root, graphics,
+    // or system, to show that anyone can access them.
+    setBinUID();
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    ASSERT_TRUE(display != nullptr);
+
+    DisplayInfo info;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+
+    Vector<DisplayInfo> configs;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display));
+
+    ASSERT_NE(static_cast<ui::ColorMode>(BAD_VALUE),
+              SurfaceComposerClient::getActiveColorMode(display));
+}
+
+TEST_F(CredentialsTest, GetDisplayColorModesTest) {
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    std::function<status_t()> condition = [=]() {
+        Vector<ui::ColorMode> outColorModes;
+        return SurfaceComposerClient::getDisplayColorModes(display, &outColorModes);
+    };
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
+}
+
+TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) {
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    std::function<status_t()> condition = [=]() {
+        ui::DisplayPrimaries primaries;
+        return SurfaceComposerClient::getDisplayNativePrimaries(display, primaries);
+    };
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
+}
+
+TEST_F(CredentialsTest, SetActiveConfigTest) {
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    std::function<status_t()> condition = [=]() {
+        return SurfaceComposerClient::setActiveConfig(display, 0);
+    };
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
+}
+
+TEST_F(CredentialsTest, SetActiveColorModeTest) {
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    std::function<status_t()> condition = [=]() {
+        return SurfaceComposerClient::setActiveColorMode(display, ui::ColorMode::NATIVE);
+    };
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
+}
+
+TEST_F(CredentialsTest, CreateDisplayTest) {
+    std::function<bool()> condition = [=]() {
+        sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
+        return testDisplay.get() != nullptr;
+    };
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
+
+    condition = [=]() {
+        sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
+        return testDisplay.get() != nullptr;
+    };
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
+}
+
+TEST_F(CredentialsTest, DISABLED_DestroyDisplayTest) {
+    setupVirtualDisplay();
+
+    DisplayInfo info;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
+    SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+    // This test currently fails. TODO(b/112002626): Find a way to properly create
+    // a display in the test environment, so that destroy display can remove it.
+    ASSERT_EQ(NAME_NOT_FOUND, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
+}
+
+TEST_F(CredentialsTest, CaptureTest) {
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    std::function<status_t()> condition = [=]() {
+        sp<GraphicBuffer> outBuffer;
+        return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB,
+                                         ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/,
+                                         0 /*reqHeight*/, false, ROTATION, &outBuffer);
+    };
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
+}
+
+TEST_F(CredentialsTest, CaptureLayersTest) {
+    setupBackgroundSurface();
+    sp<GraphicBuffer> outBuffer;
+    std::function<status_t()> condition = [=]() {
+        sp<GraphicBuffer> outBuffer;
+        return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
+                                               ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+                                               Rect(), FRAME_SCALE, &outBuffer);
+    };
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
+}
+
+/**
+ * The following tests are for methods accessible directly through SurfaceFlinger.
+ */
+
+/**
+ * An app can pass a buffer queue to the media server and ask the media server to decode a DRM video
+ * to that buffer queue. The media server is the buffer producer in this case. Because the app may create
+ * its own buffer queue and act as the buffer consumer, the media server wants to be careful to avoid
+ * sending decoded video frames to the app. This is where authenticateSurfaceTexture call comes in, to check
+ * the consumer of a buffer queue is SurfaceFlinger.
+ */
+TEST_F(CredentialsTest, AuthenticateSurfaceTextureTest) {
+    setupBackgroundSurface();
+    sp<IGraphicBufferProducer> producer =
+            mBGSurfaceControl->getSurface()->getIGraphicBufferProducer();
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+
+    std::function<bool()> condition = [=]() { return sf->authenticateSurfaceTexture(producer); };
+    // Anyone should be able to check if the consumer of the buffer queue is SF.
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
+}
+
+TEST_F(CredentialsTest, GetLayerDebugInfo) {
+    setupBackgroundSurface();
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+
+    // Historically, only root and shell can access the getLayerDebugInfo which
+    // is called when we call dumpsys. I don't see a reason why we should change this.
+    std::vector<LayerDebugInfo> outLayers;
+    // Check with root.
+    seteuid(AID_ROOT);
+    ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers));
+
+    // Check as a shell.
+    seteuid(AID_SHELL);
+    ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers));
+
+    // Check as anyone else.
+    seteuid(AID_ROOT);
+    seteuid(AID_BIN);
+    ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers));
+}
+
+TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) {
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    bool result = false;
+    status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
+    ASSERT_EQ(NO_ERROR, error);
+    bool hasWideColorMode = false;
+    Vector<ColorMode> colorModes;
+    SurfaceComposerClient::getDisplayColorModes(display, &colorModes);
+    for (ColorMode colorMode : colorModes) {
+        switch (colorMode) {
+            case ColorMode::DISPLAY_P3:
+            case ColorMode::ADOBE_RGB:
+            case ColorMode::DCI_P3:
+                hasWideColorMode = true;
+                break;
+            default:
+                break;
+        }
+    }
+    ASSERT_EQ(hasWideColorMode, result);
+}
+
+TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) {
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    std::function<status_t()> condition = [=]() {
+        bool result = false;
+        return SurfaceComposerClient::isWideColorDisplay(display, &result);
+    };
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp
index 4577153..89c26f4 100644
--- a/services/surfaceflinger/tests/Stress_test.cpp
+++ b/services/surfaceflinger/tests/Stress_test.cpp
@@ -34,7 +34,7 @@
             auto surf = client->createSurface(String8("t"), 100, 100,
                     PIXEL_FORMAT_RGBA_8888, 0);
             ASSERT_TRUE(surf != nullptr);
-            client->destroySurface(surf->getHandle());
+            surf->clear();
         }
     };
 
@@ -101,10 +101,7 @@
     for (int i = 0; i < 100000; i++) {
         surfaceflinger::LayersProto layersProto = generateLayerProto();
         auto layerTree = surfaceflinger::LayerProtoParser::generateLayerTree(layersProto);
-        // Allow some layerTrees to just fall out of scope (instead of std::move)
-        if (i % 2) {
-            surfaceflinger::LayerProtoParser::layersToString(std::move(layerTree));
-        }
+        surfaceflinger::LayerProtoParser::layerTreeToString(layerTree);
     }
     system(cmd.c_str());
 }
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index cca84e5..91999ae 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
 {
         "presubmit": {
-            "filter": "LayerTransactionTest.*:LayerUpdateTest.*:ChildLayerTest.*:SurfaceFlingerStress.*:CropLatchingTest.*:GeometryLatchingTest.*:ScreenCaptureTest.*:DereferenceSurfaceControlTest.*:-CropLatchingTest.FinalCropLatchingBufferOldSize"
+            "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*"
         }
 }
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index de78c3f..8ec3e15 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -42,13 +42,17 @@
 constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
 constexpr uint32_t SIZE_UPDATE = 134;
 constexpr uint32_t STACK_UPDATE = 1;
-constexpr uint64_t DEFERRED_UPDATE = 13;
+constexpr uint64_t DEFERRED_UPDATE = 0;
 constexpr float ALPHA_UPDATE = 0.29f;
+constexpr float CORNER_RADIUS_UPDATE = 0.2f;
 constexpr float POSITION_UPDATE = 121;
 const Rect CROP_UPDATE(16, 16, 32, 32);
 
 const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
+constexpr auto TEST_SURFACE_NAME = "BG Interceptor Test Surface";
+constexpr auto UNIQUE_TEST_SURFACE_NAME = "BG Interceptor Test Surface#0";
 constexpr auto LAYER_NAME = "Layer Create and Delete Test";
+constexpr auto UNIQUE_LAYER_NAME = "Layer Create and Delete Test#0";
 
 constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
 
@@ -94,30 +98,21 @@
     system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
 }
 
-int32_t getSurfaceId(const std::string& surfaceName) {
-    enableInterceptor();
-    disableInterceptor();
-    Trace capturedTrace;
-    readProtoFile(&capturedTrace);
+int32_t getSurfaceId(const Trace& capturedTrace, const std::string& surfaceName) {
     int32_t layerId = 0;
-    for (const auto& increment : *capturedTrace.mutable_increment()) {
+    for (const auto& increment : capturedTrace.increment()) {
         if (increment.increment_case() == increment.kSurfaceCreation) {
             if (increment.surface_creation().name() == surfaceName) {
                 layerId = increment.surface_creation().id();
-                break;
             }
         }
     }
     return layerId;
 }
 
-int32_t getDisplayId(const std::string& displayName) {
-    enableInterceptor();
-    disableInterceptor();
-    Trace capturedTrace;
-    readProtoFile(&capturedTrace);
+int32_t getDisplayId(const Trace& capturedTrace, const std::string& displayName) {
     int32_t displayId = 0;
-    for (const auto& increment : *capturedTrace.mutable_increment()) {
+    for (const auto& increment : capturedTrace.increment()) {
         if (increment.increment_case() == increment.kDisplayCreation) {
             if (increment.display_creation().name() == displayName) {
                 displayId = increment.display_creation().id();
@@ -130,36 +125,15 @@
 
 class SurfaceInterceptorTest : public ::testing::Test {
 protected:
-    virtual void SetUp() {
+    void SetUp() override {
         // Allow SurfaceInterceptor write to /data
         system("setenforce 0");
 
         mComposerClient = new SurfaceComposerClient;
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-        sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
-                ISurfaceComposer::eDisplayIdMain));
-        DisplayInfo info;
-        SurfaceComposerClient::getDisplayInfo(display, &info);
-        ssize_t displayWidth = info.w;
-        ssize_t displayHeight = info.h;
-
-        // Background surface
-        mBGSurfaceControl = mComposerClient->createSurface(
-                String8("BG Interceptor Test Surface"), displayWidth, displayHeight,
-                PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(mBGSurfaceControl != nullptr);
-        ASSERT_TRUE(mBGSurfaceControl->isValid());
-        mBGLayerId = getSurfaceId("BG Interceptor Test Surface");
-
-        Transaction t;
-        t.setDisplayLayerStack(display, 0);
-        ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX-3)
-                .show(mBGSurfaceControl)
-                .apply());
     }
 
-    virtual void TearDown() {
+    void TearDown() override {
         mComposerClient->dispose();
         mBGSurfaceControl.clear();
         mComposerClient.clear();
@@ -168,18 +142,25 @@
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mBGSurfaceControl;
     int32_t mBGLayerId;
-    // Used to verify creation and destruction of surfaces and displays
-    int32_t mTargetId;
 
 public:
-    void captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
-            bool (SurfaceInterceptorTest::* verification)(Trace *));
-    void captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
-            SurfaceChange::SurfaceChangeCase changeCase);
-    void captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
-            Increment::IncrementCase incrementCase);
-    void runInTransaction(void (SurfaceInterceptorTest::* action)(Transaction&),
-            bool intercepted = false);
+    using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&);
+    using TestAction = void (SurfaceInterceptorTest::*)();
+    using TestBooleanVerification = bool (SurfaceInterceptorTest::*)(const Trace&);
+    using TestVerification = void (SurfaceInterceptorTest::*)(const Trace&);
+
+    void setupBackgroundSurface();
+    void preProcessTrace(const Trace& trace);
+
+    // captureTest will enable SurfaceInterceptor, setup background surface,
+    // disable SurfaceInterceptor, collect the trace and process the trace for
+    // id of background surface before further verification.
+    void captureTest(TestTransactionAction action, TestBooleanVerification verification);
+    void captureTest(TestTransactionAction action, SurfaceChange::SurfaceChangeCase changeCase);
+    void captureTest(TestTransactionAction action, Increment::IncrementCase incrementCase);
+    void captureTest(TestAction action, TestBooleanVerification verification);
+    void captureTest(TestAction action, TestVerification verification);
+    void runInTransaction(TestTransactionAction action);
 
     // Verification of changes to a surface
     bool positionUpdateFound(const SurfaceChange& change, bool foundPosition);
@@ -187,7 +168,7 @@
     bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
     bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
     bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
-    bool finalCropUpdateFound(const SurfaceChange& change, bool foundFinalCrop);
+    bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
     bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
     bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
     bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
@@ -196,18 +177,22 @@
     bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
     bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
     bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred);
-    bool surfaceUpdateFound(Trace* trace, SurfaceChange::SurfaceChangeCase changeCase);
-    void assertAllUpdatesFound(Trace* trace);
+    bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
+
+    // Find all of the updates in the single trace
+    void assertAllUpdatesFound(const Trace& trace);
 
     // Verification of creation and deletion of a surface
     bool surfaceCreationFound(const Increment& increment, bool foundSurface);
-    bool surfaceDeletionFound(const Increment& increment, bool foundSurface);
+    bool surfaceDeletionFound(const Increment& increment, const int32_t targetId,
+            bool foundSurface);
     bool displayCreationFound(const Increment& increment, bool foundDisplay);
-    bool displayDeletionFound(const Increment& increment, bool foundDisplay);
-    bool singleIncrementFound(Trace* trace, Increment::IncrementCase incrementCase);
+    bool displayDeletionFound(const Increment& increment, const int32_t targetId,
+            bool foundDisplay);
+    bool singleIncrementFound(const Trace& trace, Increment::IncrementCase incrementCase);
 
     // Verification of buffer updates
-    bool bufferUpdatesFound(Trace* trace);
+    bool bufferUpdatesFound(const Trace& trace);
 
     // Perform each of the possible changes to a surface
     void positionUpdate(Transaction&);
@@ -215,7 +200,7 @@
     void alphaUpdate(Transaction&);
     void layerUpdate(Transaction&);
     void cropUpdate(Transaction&);
-    void finalCropUpdate(Transaction&);
+    void cornerRadiusUpdate(Transaction&);
     void matrixUpdate(Transaction&);
     void overrideScalingModeUpdate(Transaction&);
     void transparentRegionHintUpdate(Transaction&);
@@ -230,48 +215,93 @@
 
     void nBufferUpdates();
     void runAllUpdates();
+
+private:
+    void captureInTransaction(TestTransactionAction action, Trace*);
+    void capture(TestAction action, Trace*);
 };
 
-void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
-        bool (SurfaceInterceptorTest::* verification)(Trace *))
-{
-    runInTransaction(action, true);
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    ASSERT_TRUE((this->*verification)(&capturedTrace));
+void SurfaceInterceptorTest::captureInTransaction(TestTransactionAction action, Trace* outTrace) {
+    enableInterceptor();
+    setupBackgroundSurface();
+    runInTransaction(action);
+    disableInterceptor();
+    ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
+    preProcessTrace(*outTrace);
 }
 
-void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
-        Increment::IncrementCase incrementCase)
-{
-    runInTransaction(action, true);
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    ASSERT_TRUE(singleIncrementFound(&capturedTrace, incrementCase));
+void SurfaceInterceptorTest::capture(TestAction action, Trace* outTrace) {
+    enableInterceptor();
+    setupBackgroundSurface();
+    (this->*action)();
+    disableInterceptor();
+    ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
+    preProcessTrace(*outTrace);
 }
 
-void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
-        SurfaceChange::SurfaceChangeCase changeCase)
-{
-    runInTransaction(action, true);
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    ASSERT_TRUE(surfaceUpdateFound(&capturedTrace, changeCase));
+void SurfaceInterceptorTest::setupBackgroundSurface() {
+    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
+                ISurfaceComposer::eDisplayIdMain));
+    DisplayInfo info;
+    SurfaceComposerClient::getDisplayInfo(display, &info);
+    ssize_t displayWidth = info.w;
+    ssize_t displayHeight = info.h;
+
+    // Background surface
+    mBGSurfaceControl = mComposerClient->createSurface(
+            String8(TEST_SURFACE_NAME), displayWidth, displayHeight,
+            PIXEL_FORMAT_RGBA_8888, 0);
+    ASSERT_TRUE(mBGSurfaceControl != nullptr);
+    ASSERT_TRUE(mBGSurfaceControl->isValid());
+
+    Transaction t;
+    t.setDisplayLayerStack(display, 0);
+    ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX-3)
+            .show(mBGSurfaceControl)
+            .apply());
 }
 
-void SurfaceInterceptorTest::runInTransaction(void (SurfaceInterceptorTest::* action)(Transaction&),
-        bool intercepted)
-{
-    if (intercepted) {
-        enableInterceptor();
-    }
+void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) {
+    mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_SURFACE_NAME);
+}
+
+void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
+        TestBooleanVerification verification) {
+    Trace capturedTrace;
+    captureInTransaction(action, &capturedTrace);
+    ASSERT_TRUE((this->*verification)(capturedTrace));
+}
+
+void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
+        Increment::IncrementCase incrementCase) {
+    Trace capturedTrace;
+    captureInTransaction(action, &capturedTrace);
+    ASSERT_TRUE(singleIncrementFound(capturedTrace, incrementCase));
+}
+
+void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
+        SurfaceChange::SurfaceChangeCase changeCase) {
+    Trace capturedTrace;
+    captureInTransaction(action, &capturedTrace);
+    ASSERT_TRUE(surfaceUpdateFound(capturedTrace, changeCase));
+}
+
+void SurfaceInterceptorTest::captureTest(TestAction action, TestBooleanVerification verification) {
+    Trace capturedTrace;
+    capture(action, &capturedTrace);
+    ASSERT_TRUE((this->*verification)(capturedTrace));
+}
+
+void SurfaceInterceptorTest::captureTest(TestAction action, TestVerification verification) {
+    Trace capturedTrace;
+    capture(action, &capturedTrace);
+    (this->*verification)(capturedTrace);
+}
+
+void SurfaceInterceptorTest::runInTransaction(TestTransactionAction action) {
     Transaction t;
     (this->*action)(t);
     t.apply(true);
-
-    if (intercepted) {
-        disableInterceptor();
-    }
 }
 
 void SurfaceInterceptorTest::positionUpdate(Transaction& t) {
@@ -286,16 +316,16 @@
     t.setAlpha(mBGSurfaceControl, ALPHA_UPDATE);
 }
 
+void SurfaceInterceptorTest::cornerRadiusUpdate(Transaction& t) {
+    t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
+}
+
 void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
     t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
 }
 
 void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
-    t.setCrop(mBGSurfaceControl, CROP_UPDATE);
-}
-
-void SurfaceInterceptorTest::finalCropUpdate(Transaction& t) {
-    t.setFinalCrop(mBGSurfaceControl, CROP_UPDATE);
+    t.setCrop_legacy(mBGSurfaceControl, CROP_UPDATE);
 }
 
 void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
@@ -328,7 +358,8 @@
 }
 
 void SurfaceInterceptorTest::deferredTransactionUpdate(Transaction& t) {
-    t.deferTransactionUntil(mBGSurfaceControl, mBGSurfaceControl->getHandle(), DEFERRED_UPDATE);
+    t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl->getHandle(),
+                                   DEFERRED_UPDATE);
 }
 
 void SurfaceInterceptorTest::displayCreation(Transaction&) {
@@ -338,7 +369,6 @@
 
 void SurfaceInterceptorTest::displayDeletion(Transaction&) {
     sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
-    mTargetId = getDisplayId(DISPLAY_NAME.string());
     SurfaceComposerClient::destroyDisplay(testDisplay);
 }
 
@@ -346,9 +376,9 @@
     runInTransaction(&SurfaceInterceptorTest::positionUpdate);
     runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
     runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
+    runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
     runInTransaction(&SurfaceInterceptorTest::layerUpdate);
     runInTransaction(&SurfaceInterceptorTest::cropUpdate);
-    runInTransaction(&SurfaceInterceptorTest::finalCropUpdate);
     runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
     runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate);
     runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
@@ -380,9 +410,8 @@
     bool hasY(change.position().y() == POSITION_UPDATE);
     if (hasX && hasY && !foundPosition) {
         foundPosition = true;
-    }
-    // Failed because the position update was found a second time
-    else if (hasX && hasY && foundPosition) {
+    } else if (hasX && hasY && foundPosition) {
+        // Failed because the position update was found a second time
         [] () { FAIL(); }();
     }
     return foundPosition;
@@ -393,8 +422,7 @@
     bool hasHeight(change.size().w() == SIZE_UPDATE);
     if (hasWidth && hasHeight && !foundSize) {
         foundSize = true;
-    }
-    else if (hasWidth && hasHeight && foundSize) {
+    } else if (hasWidth && hasHeight && foundSize) {
         [] () { FAIL(); }();
     }
     return foundSize;
@@ -404,19 +432,28 @@
     bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE);
     if (hasAlpha && !foundAlpha) {
         foundAlpha = true;
-    }
-    else if (hasAlpha && foundAlpha) {
+    } else if (hasAlpha && foundAlpha) {
         [] () { FAIL(); }();
     }
     return foundAlpha;
 }
 
+bool SurfaceInterceptorTest::cornerRadiusUpdateFound(const SurfaceChange &change,
+                                                     bool foundCornerRadius) {
+    bool hasCornerRadius(change.corner_radius().corner_radius() == CORNER_RADIUS_UPDATE);
+    if (hasCornerRadius && !foundCornerRadius) {
+        foundCornerRadius = true;
+    } else if (hasCornerRadius && foundCornerRadius) {
+        [] () { FAIL(); }();
+    }
+    return foundCornerRadius;
+}
+
 bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
     bool hasLayer(change.layer().layer() == LAYER_UPDATE);
     if (hasLayer && !foundLayer) {
         foundLayer = true;
-    }
-    else if (hasLayer && foundLayer) {
+    } else if (hasLayer && foundLayer) {
         [] () { FAIL(); }();
     }
     return foundLayer;
@@ -429,59 +466,38 @@
     bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom);
     if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) {
         foundCrop = true;
-    }
-    else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
+    } else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
         [] () { FAIL(); }();
     }
     return foundCrop;
 }
 
-bool SurfaceInterceptorTest::finalCropUpdateFound(const SurfaceChange& change,
-        bool foundFinalCrop)
-{
-    bool hasLeft(change.final_crop().rectangle().left() == CROP_UPDATE.left);
-    bool hasTop(change.final_crop().rectangle().top() == CROP_UPDATE.top);
-    bool hasRight(change.final_crop().rectangle().right() == CROP_UPDATE.right);
-    bool hasBottom(change.final_crop().rectangle().bottom() == CROP_UPDATE.bottom);
-    if (hasLeft && hasRight && hasTop && hasBottom && !foundFinalCrop) {
-        foundFinalCrop = true;
-    }
-    else if (hasLeft && hasRight && hasTop && hasBottom && foundFinalCrop) {
-        [] () { FAIL(); }();
-    }
-    return foundFinalCrop;
-}
-
 bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) {
     bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2);
     bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2);
-    bool hasSy((float)change.matrix().dsdy() == (float)-M_SQRT1_2);
-    bool hasTy((float)change.matrix().dtdy() == (float)M_SQRT1_2);
+    bool hasSy((float)change.matrix().dsdy() == (float)M_SQRT1_2);
+    bool hasTy((float)change.matrix().dtdy() == (float)-M_SQRT1_2);
     if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) {
         foundMatrix = true;
-    }
-    else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
+    } else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
         [] () { FAIL(); }();
     }
     return foundMatrix;
 }
 
 bool SurfaceInterceptorTest::scalingModeUpdateFound(const SurfaceChange& change,
-        bool foundScalingMode)
-{
+        bool foundScalingMode) {
     bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE);
     if (hasScalingUpdate && !foundScalingMode) {
         foundScalingMode = true;
-    }
-    else if (hasScalingUpdate && foundScalingMode) {
+    } else if (hasScalingUpdate && foundScalingMode) {
         [] () { FAIL(); }();
     }
     return foundScalingMode;
 }
 
 bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
-        bool foundTransparentRegion)
-{
+        bool foundTransparentRegion) {
     auto traceRegion = change.transparent_region_hint().region(0);
     bool hasLeft(traceRegion.left() == CROP_UPDATE.left);
     bool hasTop(traceRegion.top() == CROP_UPDATE.top);
@@ -489,84 +505,72 @@
     bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom);
     if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) {
         foundTransparentRegion = true;
-    }
-    else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
+    } else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
         [] () { FAIL(); }();
     }
     return foundTransparentRegion;
 }
 
 bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change,
-        bool foundLayerStack)
-{
+        bool foundLayerStack) {
     bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE);
     if (hasLayerStackUpdate && !foundLayerStack) {
         foundLayerStack = true;
-    }
-    else if (hasLayerStackUpdate && foundLayerStack) {
+    } else if (hasLayerStackUpdate && foundLayerStack) {
         [] () { FAIL(); }();
     }
     return foundLayerStack;
 }
 
 bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change,
-        bool foundHiddenFlag)
-{
+        bool foundHiddenFlag) {
     bool hasHiddenFlag(change.hidden_flag().hidden_flag());
     if (hasHiddenFlag && !foundHiddenFlag) {
         foundHiddenFlag = true;
-    }
-    else if (hasHiddenFlag && foundHiddenFlag) {
+    } else if (hasHiddenFlag && foundHiddenFlag) {
         [] () { FAIL(); }();
     }
     return foundHiddenFlag;
 }
 
 bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change,
-        bool foundOpaqueFlag)
-{
+        bool foundOpaqueFlag) {
     bool hasOpaqueFlag(change.opaque_flag().opaque_flag());
     if (hasOpaqueFlag && !foundOpaqueFlag) {
         foundOpaqueFlag = true;
-    }
-    else if (hasOpaqueFlag && foundOpaqueFlag) {
+    } else if (hasOpaqueFlag && foundOpaqueFlag) {
         [] () { FAIL(); }();
     }
     return foundOpaqueFlag;
 }
 
 bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change,
-        bool foundSecureFlag)
-{
+        bool foundSecureFlag) {
     bool hasSecureFlag(change.secure_flag().secure_flag());
     if (hasSecureFlag && !foundSecureFlag) {
         foundSecureFlag = true;
-    }
-    else if (hasSecureFlag && foundSecureFlag) {
+    } else if (hasSecureFlag && foundSecureFlag) {
         [] () { FAIL(); }();
     }
     return foundSecureFlag;
 }
 
 bool SurfaceInterceptorTest::deferredTransactionUpdateFound(const SurfaceChange& change,
-        bool foundDeferred)
-{
+        bool foundDeferred) {
     bool hasId(change.deferred_transaction().layer_id() == mBGLayerId);
     bool hasFrameNumber(change.deferred_transaction().frame_number() == DEFERRED_UPDATE);
     if (hasId && hasFrameNumber && !foundDeferred) {
         foundDeferred = true;
-    }
-    else if (hasId && hasFrameNumber && foundDeferred) {
+    } else if (hasId && hasFrameNumber && foundDeferred) {
         [] () { FAIL(); }();
     }
     return foundDeferred;
 }
 
-bool SurfaceInterceptorTest::surfaceUpdateFound(Trace* trace,
-        SurfaceChange::SurfaceChangeCase changeCase)
-{
+bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
+        SurfaceChange::SurfaceChangeCase changeCase) {
     bool foundUpdate = false;
-    for (const auto& increment : *trace->mutable_increment()) {
+    for (const auto& increment : trace.increment()) {
         if (increment.increment_case() == increment.kTransaction) {
             for (const auto& change : increment.transaction().surface_change()) {
                 if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) {
@@ -587,8 +591,8 @@
                         case SurfaceChange::SurfaceChangeCase::kCrop:
                             foundUpdate = cropUpdateFound(change, foundUpdate);
                             break;
-                        case SurfaceChange::SurfaceChangeCase::kFinalCrop:
-                            foundUpdate = finalCropUpdateFound(change, foundUpdate);
+                        case SurfaceChange::SurfaceChangeCase::kCornerRadius:
+                            foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
                             break;
                         case SurfaceChange::SurfaceChangeCase::kMatrix:
                             foundUpdate = matrixUpdateFound(change, foundUpdate);
@@ -624,13 +628,12 @@
     return foundUpdate;
 }
 
-void SurfaceInterceptorTest::assertAllUpdatesFound(Trace* trace) {
+void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) {
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kFinalCrop));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOverrideScalingMode));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
@@ -642,24 +645,23 @@
 }
 
 bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
-    bool isMatch(increment.surface_creation().name() == LAYER_NAME &&
+    bool isMatch(increment.surface_creation().name() == UNIQUE_LAYER_NAME &&
             increment.surface_creation().w() == SIZE_UPDATE &&
             increment.surface_creation().h() == SIZE_UPDATE);
     if (isMatch && !foundSurface) {
         foundSurface = true;
-    }
-    else if (isMatch && foundSurface) {
+    } else if (isMatch && foundSurface) {
         [] () { FAIL(); }();
     }
     return foundSurface;
 }
 
-bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment, bool foundSurface) {
-    bool isMatch(increment.surface_deletion().id() == mTargetId);
+bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment,
+        const int32_t targetId, bool foundSurface) {
+    bool isMatch(increment.surface_deletion().id() == targetId);
     if (isMatch && !foundSurface) {
         foundSurface = true;
-    }
-    else if (isMatch && foundSurface) {
+    } else if (isMatch && foundSurface) {
         [] () { FAIL(); }();
     }
     return foundSurface;
@@ -670,42 +672,45 @@
             increment.display_creation().is_secure());
     if (isMatch && !foundDisplay) {
         foundDisplay = true;
-    }
-    else if (isMatch && foundDisplay) {
+    } else if (isMatch && foundDisplay) {
         [] () { FAIL(); }();
     }
     return foundDisplay;
 }
 
-bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment, bool foundDisplay) {
-    bool isMatch(increment.display_deletion().id() == mTargetId);
+bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment,
+        const int32_t targetId, bool foundDisplay) {
+    bool isMatch(increment.display_deletion().id() == targetId);
     if (isMatch && !foundDisplay) {
         foundDisplay = true;
-    }
-    else if (isMatch && foundDisplay) {
+    } else if (isMatch && foundDisplay) {
         [] () { FAIL(); }();
     }
     return foundDisplay;
 }
 
-bool SurfaceInterceptorTest::singleIncrementFound(Trace* trace,
-        Increment::IncrementCase incrementCase)
-{
+bool SurfaceInterceptorTest::singleIncrementFound(const Trace& trace,
+        Increment::IncrementCase incrementCase) {
     bool foundIncrement = false;
-    for (const auto& increment : *trace->mutable_increment()) {
+    for (const auto& increment : trace.increment()) {
         if (increment.increment_case() == incrementCase) {
+            int32_t targetId = 0;
             switch (incrementCase) {
                 case Increment::IncrementCase::kSurfaceCreation:
                     foundIncrement = surfaceCreationFound(increment, foundIncrement);
                     break;
                 case Increment::IncrementCase::kSurfaceDeletion:
-                    foundIncrement = surfaceDeletionFound(increment, foundIncrement);
+                    // Find the id of created surface.
+                    targetId = getSurfaceId(trace, UNIQUE_LAYER_NAME);
+                    foundIncrement = surfaceDeletionFound(increment, targetId, foundIncrement);
                     break;
                 case Increment::IncrementCase::kDisplayCreation:
                     foundIncrement = displayCreationFound(increment, foundIncrement);
                     break;
                 case Increment::IncrementCase::kDisplayDeletion:
-                    foundIncrement = displayDeletionFound(increment, foundIncrement);
+                    // Find the id of created display.
+                    targetId = getDisplayId(trace, DISPLAY_NAME.string());
+                    foundIncrement = displayDeletionFound(increment, targetId, foundIncrement);
                     break;
                 default:
                     /* code */
@@ -716,9 +721,9 @@
     return foundIncrement;
 }
 
-bool SurfaceInterceptorTest::bufferUpdatesFound(Trace* trace) {
+bool SurfaceInterceptorTest::bufferUpdatesFound(const Trace& trace) {
     uint32_t updates = 0;
-    for (const auto& inc : *trace->mutable_increment()) {
+    for (const auto& inc : trace.increment()) {
         if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) {
             updates++;
         }
@@ -747,9 +752,9 @@
     captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
 }
 
-TEST_F(SurfaceInterceptorTest, InterceptFinalCropUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::finalCropUpdate,
-            SurfaceChange::SurfaceChangeCase::kFinalCrop);
+TEST_F(SurfaceInterceptorTest, InterceptCornerRadiusUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::cornerRadiusUpdate,
+            SurfaceChange::SurfaceChangeCase::kCornerRadius);
 }
 
 TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
@@ -792,14 +797,8 @@
 }
 
 TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
-    enableInterceptor();
-    runAllUpdates();
-    disableInterceptor();
-
-    // Find all of the updates in the single trace
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    assertAllUpdatesFound(&capturedTrace);
+    captureTest(&SurfaceInterceptorTest::runAllUpdates,
+                &SurfaceInterceptorTest::assertAllUpdatesFound);
 }
 
 TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) {
@@ -807,40 +806,30 @@
             Increment::IncrementCase::kSurfaceCreation);
 }
 
-TEST_F(SurfaceInterceptorTest, InterceptSurfaceDeletionWorks) {
-    sp<SurfaceControl> layerToDelete = mComposerClient->createSurface(String8(LAYER_NAME),
-            SIZE_UPDATE, SIZE_UPDATE, PIXEL_FORMAT_RGBA_8888, 0);
-    this->mTargetId = getSurfaceId(LAYER_NAME);
-    enableInterceptor();
-    mComposerClient->destroySurface(layerToDelete->getHandle());
-    disableInterceptor();
-
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceDeletion));
-}
-
 TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) {
     captureTest(&SurfaceInterceptorTest::displayCreation,
             Increment::IncrementCase::kDisplayCreation);
 }
 
 TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) {
-    captureTest(&SurfaceInterceptorTest::displayDeletion,
-            Increment::IncrementCase::kDisplayDeletion);
+    enableInterceptor();
+    runInTransaction(&SurfaceInterceptorTest::displayDeletion);
+    disableInterceptor();
+    Trace capturedTrace;
+    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+    ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kDisplayDeletion));
 }
 
 TEST_F(SurfaceInterceptorTest, InterceptBufferUpdateWorks) {
-    nBufferUpdates();
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    ASSERT_TRUE(bufferUpdatesFound(&capturedTrace));
+    captureTest(&SurfaceInterceptorTest::nBufferUpdates,
+            &SurfaceInterceptorTest::bufferUpdatesFound);
 }
 
 // If the interceptor is enabled while buffer updates are being pushed, the interceptor should
 // first create a snapshot of the existing displays and surfaces and then start capturing
 // the buffer updates
 TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) {
+    setupBackgroundSurface();
     std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
     enableInterceptor();
     disableInterceptor();
@@ -854,6 +843,7 @@
 
 TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) {
     enableInterceptor();
+    setupBackgroundSurface();
     std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
     std::thread surfaceUpdates(&SurfaceInterceptorTest::runAllUpdates, this);
     runInTransaction(&SurfaceInterceptorTest::surfaceCreation);
@@ -863,10 +853,11 @@
 
     Trace capturedTrace;
     ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+    preProcessTrace(capturedTrace);
 
-    assertAllUpdatesFound(&capturedTrace);
-    ASSERT_TRUE(bufferUpdatesFound(&capturedTrace));
-    ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceCreation));
+    assertAllUpdatesFound(capturedTrace);
+    ASSERT_TRUE(bufferUpdatesFound(capturedTrace));
+    ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kSurfaceCreation));
 }
 
 }
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 79f27df..56d3bd4 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -15,21 +15,26 @@
  */
 
 #include <algorithm>
+#include <chrono>
+#include <cinttypes>
 #include <functional>
 #include <limits>
 #include <ostream>
+#include <thread>
 
 #include <gtest/gtest.h>
 
 #include <android/native_window.h>
 
+#include <binder/ProcessState.h>
+#include <gui/BufferItemConsumer.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerState.h>
-
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
 
+#include <ui/ColorSpace.h>
 #include <ui/DisplayInfo.h>
 #include <ui/Rect.h>
 #include <utils/String8.h>
@@ -37,6 +42,8 @@
 #include <math.h>
 #include <math/vec3.h>
 
+#include "BufferGenerator.h"
+
 namespace android {
 
 namespace {
@@ -62,38 +69,29 @@
 const Color Color::BLACK{0, 0, 0, 255};
 const Color Color::TRANSPARENT{0, 0, 0, 0};
 
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using namespace std::chrono_literals;
+
 std::ostream& operator<<(std::ostream& os, const Color& color) {
     os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a);
     return os;
 }
 
 // Fill a region with the specified color.
-void fillBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect, const Color& color) {
-    int32_t x = rect.left;
-    int32_t y = rect.top;
-    int32_t width = rect.right - rect.left;
-    int32_t height = rect.bottom - rect.top;
-
-    if (x < 0) {
-        width += x;
-        x = 0;
-    }
-    if (y < 0) {
-        height += y;
-        y = 0;
-    }
-    if (x + width > buffer.width) {
-        x = std::min(x, buffer.width);
-        width = buffer.width - x;
-    }
-    if (y + height > buffer.height) {
-        y = std::min(y, buffer.height);
-        height = buffer.height - y;
+void fillANativeWindowBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect,
+                                  const Color& color) {
+    Rect r(0, 0, buffer.width, buffer.height);
+    if (!r.intersect(rect, &r)) {
+        return;
     }
 
-    for (int32_t j = 0; j < height; j++) {
-        uint8_t* dst = static_cast<uint8_t*>(buffer.bits) + (buffer.stride * (y + j) + x) * 4;
-        for (int32_t i = 0; i < width; i++) {
+    int32_t width = r.right - r.left;
+    int32_t height = r.bottom - r.top;
+
+    for (int32_t row = 0; row < height; row++) {
+        uint8_t* dst =
+                static_cast<uint8_t*>(buffer.bits) + (buffer.stride * (r.top + row) + r.left) * 4;
+        for (int32_t column = 0; column < width; column++) {
             dst[0] = color.r;
             dst[1] = color.g;
             dst[2] = color.b;
@@ -103,6 +101,33 @@
     }
 }
 
+// Fill a region with the specified color.
+void fillGraphicBufferColor(const sp<GraphicBuffer>& buffer, const Rect& rect, const Color& color) {
+    Rect r(0, 0, buffer->width, buffer->height);
+    if (!r.intersect(rect, &r)) {
+        return;
+    }
+
+    int32_t width = r.right - r.left;
+    int32_t height = r.bottom - r.top;
+
+    uint8_t* pixels;
+    buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                 reinterpret_cast<void**>(&pixels));
+
+    for (int32_t row = 0; row < height; row++) {
+        uint8_t* dst = pixels + (buffer->getStride() * (r.top + row) + r.left) * 4;
+        for (int32_t column = 0; column < width; column++) {
+            dst[0] = color.r;
+            dst[1] = color.g;
+            dst[2] = color.b;
+            dst[3] = color.a;
+            dst += 4;
+        }
+    }
+    buffer->unlock();
+}
+
 // Check if a region has the specified color.
 void expectBufferColor(const sp<GraphicBuffer>& outBuffer, uint8_t* pixels, const Rect& rect,
                        const Color& color, uint8_t tolerance) {
@@ -169,17 +194,15 @@
 // individual pixel values for testing purposes.
 class ScreenCapture : public RefBase {
 public:
-    static void captureScreen(sp<ScreenCapture>* sc, int32_t minLayerZ = 0,
-                              int32_t maxLayerZ = std::numeric_limits<int32_t>::max()) {
+    static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
         sp<ISurfaceComposer> sf(ComposerService::getComposerService());
         sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
         SurfaceComposerClient::Transaction().apply(true);
 
         sp<GraphicBuffer> outBuffer;
         ASSERT_EQ(NO_ERROR,
-                  sf->captureScreen(display, &outBuffer, Rect(), 0, 0, minLayerZ, maxLayerZ,
-                                    false));
-        *sc = new ScreenCapture(outBuffer);
+                  sf->captureScreen(display, &outBuffer, Rect(), 0, 0, false));
+        *sc = std::make_unique<ScreenCapture>(outBuffer);
     }
 
     static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
@@ -299,18 +322,28 @@
         ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient";
 
         ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
+
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        sp<IBinder> binder = sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+        ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
     }
 
-    sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
-                                   uint32_t flags = 0) {
-        auto layer =
-                mClient->createSurface(String8(name), width, height, PIXEL_FORMAT_RGBA_8888, flags);
-        EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
+    virtual void TearDown() {
+        mBlackBgSurface = 0;
+        mClient->dispose();
+        mClient = 0;
+    }
 
-        status_t error = Transaction()
-                                 .setLayerStack(layer, mDisplayLayerStack)
-                                 .setLayer(layer, mLayerZBase)
-                                 .apply();
+    virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
+                                           const char* name, uint32_t width, uint32_t height,
+                                           uint32_t flags = 0, SurfaceControl* parent = nullptr) {
+        auto layer =
+                createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags, parent);
+
+        Transaction t;
+        t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
+
+        status_t error = t.apply();
         if (error != NO_ERROR) {
             ADD_FAILURE() << "failed to initialize SurfaceControl";
             layer.clear();
@@ -319,7 +352,21 @@
         return layer;
     }
 
-    ANativeWindow_Buffer getLayerBuffer(const sp<SurfaceControl>& layer) {
+    virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client,
+                                             const char* name, uint32_t width, uint32_t height,
+                                             PixelFormat format, uint32_t flags,
+                                             SurfaceControl* parent = nullptr) {
+        auto layer = client->createSurface(String8(name), width, height, format, flags, parent);
+        EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
+        return layer;
+    }
+
+    virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+                                           uint32_t flags = 0, SurfaceControl* parent = nullptr) {
+        return createLayer(mClient, name, width, height, flags, parent);
+    }
+
+    ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
         // wait for previous transactions (such as setSize) to complete
         Transaction().apply(true);
 
@@ -329,53 +376,130 @@
         return buffer;
     }
 
-    void postLayerBuffer(const sp<SurfaceControl>& layer) {
+    void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
         ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
 
         // wait for the newly posted buffer to be latched
         waitForLayerBuffers();
     }
 
-    void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color) {
+    virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+                                           int32_t bufferWidth, int32_t bufferHeight) {
         ANativeWindow_Buffer buffer;
-        ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer));
-        fillBufferColor(buffer, Rect(0, 0, buffer.width, buffer.height), color);
-        postLayerBuffer(layer);
+        ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+        fillANativeWindowBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color);
+        postBufferQueueLayerBuffer(layer);
     }
 
-    void fillLayerQuadrant(const sp<SurfaceControl>& layer, const Color& topLeft,
+    virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+                                           int32_t bufferWidth, int32_t bufferHeight) {
+        sp<GraphicBuffer> buffer =
+                new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
+                                  BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                          BufferUsage::COMPOSER_OVERLAY,
+                                  "test");
+        fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color);
+        Transaction().setBuffer(layer, buffer).apply();
+    }
+
+    void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color,
+                        int32_t bufferWidth, int32_t bufferHeight) {
+        switch (mLayerType) {
+            case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+                fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
+                break;
+            case ISurfaceComposerClient::eFXSurfaceBufferState:
+                fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight);
+                break;
+            default:
+                ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
+        }
+    }
+
+    void fillLayerQuadrant(uint32_t mLayerType, const sp<SurfaceControl>& layer,
+                           int32_t bufferWidth, int32_t bufferHeight, const Color& topLeft,
                            const Color& topRight, const Color& bottomLeft,
                            const Color& bottomRight) {
-        ANativeWindow_Buffer buffer;
-        ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer));
-        ASSERT_TRUE(buffer.width % 2 == 0 && buffer.height % 2 == 0);
-
-        const int32_t halfW = buffer.width / 2;
-        const int32_t halfH = buffer.height / 2;
-        fillBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
-        fillBufferColor(buffer, Rect(halfW, 0, buffer.width, halfH), topRight);
-        fillBufferColor(buffer, Rect(0, halfH, halfW, buffer.height), bottomLeft);
-        fillBufferColor(buffer, Rect(halfW, halfH, buffer.width, buffer.height), bottomRight);
-
-        postLayerBuffer(layer);
+        switch (mLayerType) {
+            case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+                fillBufferQueueLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
+                                             bottomLeft, bottomRight);
+                break;
+            case ISurfaceComposerClient::eFXSurfaceBufferState:
+                fillBufferStateLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
+                                             bottomLeft, bottomRight);
+                break;
+            default:
+                ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
+        }
     }
 
-    sp<ScreenCapture> screenshot() {
-        sp<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot, mLayerZBase);
+    virtual void fillBufferQueueLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+                                              int32_t bufferHeight, const Color& topLeft,
+                                              const Color& topRight, const Color& bottomLeft,
+                                              const Color& bottomRight) {
+        ANativeWindow_Buffer buffer;
+        ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+        ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
+
+        const int32_t halfW = bufferWidth / 2;
+        const int32_t halfH = bufferHeight / 2;
+        fillANativeWindowBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
+        fillANativeWindowBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), topRight);
+        fillANativeWindowBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), bottomLeft);
+        fillANativeWindowBufferColor(buffer, Rect(halfW, halfH, bufferWidth, bufferHeight),
+                                     bottomRight);
+
+        postBufferQueueLayerBuffer(layer);
+    }
+
+    virtual void fillBufferStateLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+                                              int32_t bufferHeight, const Color& topLeft,
+                                              const Color& topRight, const Color& bottomLeft,
+                                              const Color& bottomRight) {
+        sp<GraphicBuffer> buffer =
+                new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
+                                  BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                          BufferUsage::COMPOSER_OVERLAY,
+                                  "test");
+
+        ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
+
+        const int32_t halfW = bufferWidth / 2;
+        const int32_t halfH = bufferHeight / 2;
+        fillGraphicBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
+        fillGraphicBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), topRight);
+        fillGraphicBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), bottomLeft);
+        fillGraphicBufferColor(buffer, Rect(halfW, halfH, bufferWidth, bufferHeight), bottomRight);
+
+        Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply();
+    }
+
+    std::unique_ptr<ScreenCapture> screenshot() {
+        std::unique_ptr<ScreenCapture> screenshot;
+        ScreenCapture::captureScreen(&screenshot);
         return screenshot;
     }
 
+    static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+        static BufferGenerator bufferGenerator;
+        return bufferGenerator.get(outBuffer, outFence);
+    }
+
     sp<SurfaceComposerClient> mClient;
 
     sp<IBinder> mDisplay;
     uint32_t mDisplayWidth;
     uint32_t mDisplayHeight;
     uint32_t mDisplayLayerStack;
+    Rect mDisplayRect = Rect::INVALID_RECT;
 
     // leave room for ~256 layers
     const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
 
+    sp<SurfaceControl> mBlackBgSurface;
+    bool mColorManagementUsed;
+
 private:
     void SetUpDisplay() {
         mDisplay = mClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
@@ -386,6 +510,8 @@
         SurfaceComposerClient::getDisplayInfo(mDisplay, &info);
         mDisplayWidth = info.w;
         mDisplayHeight = info.h;
+        mDisplayRect =
+                Rect(static_cast<int32_t>(mDisplayWidth), static_cast<int32_t>(mDisplayHeight));
 
         // After a new buffer is queued, SurfaceFlinger is notified and will
         // latch the new buffer on next vsync.  Let's heuristically wait for 3
@@ -393,42 +519,210 @@
         mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
 
         mDisplayLayerStack = 0;
+
+        mBlackBgSurface =
+                createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
+                              PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+
         // set layer stack (b/68888219)
         Transaction t;
         t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
+        t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
+        t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
+        t.setColor(mBlackBgSurface, half3{0, 0, 0});
+        t.setLayer(mBlackBgSurface, mLayerZBase);
         t.apply();
     }
 
-    void waitForLayerBuffers() { usleep(mBufferPostDelay); }
+    void waitForLayerBuffers() {
+        // Request an empty transaction to get applied synchronously to ensure the buffer is
+        // latched.
+        Transaction().apply(true);
+        usleep(mBufferPostDelay);
+    }
 
     int32_t mBufferPostDelay;
+
+    friend class LayerRenderPathTestHarness;
+};
+enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
+
+class LayerRenderPathTestHarness {
+public:
+    LayerRenderPathTestHarness(LayerTransactionTest* delegate, RenderPath renderPath)
+          : mDelegate(delegate), mRenderPath(renderPath) {}
+
+    std::unique_ptr<ScreenCapture> getScreenCapture() {
+        switch (mRenderPath) {
+            case RenderPath::SCREENSHOT:
+                return mDelegate->screenshot();
+            case RenderPath::VIRTUAL_DISPLAY:
+
+                sp<IBinder> mainDisplay =
+                        SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+                DisplayInfo mainDisplayInfo;
+                SurfaceComposerClient::getDisplayInfo(mainDisplay, &mainDisplayInfo);
+
+                sp<IBinder> vDisplay;
+                sp<IGraphicBufferProducer> producer;
+                sp<IGraphicBufferConsumer> consumer;
+                sp<BufferItemConsumer> itemConsumer;
+                BufferQueue::createBufferQueue(&producer, &consumer);
+
+                consumer->setConsumerName(String8("Virtual disp consumer"));
+                consumer->setDefaultBufferSize(mainDisplayInfo.w, mainDisplayInfo.h);
+
+                itemConsumer = new BufferItemConsumer(consumer,
+                                                      // Sample usage bits from screenrecord
+                                                      GRALLOC_USAGE_HW_VIDEO_ENCODER |
+                                                              GRALLOC_USAGE_SW_READ_OFTEN);
+
+                vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
+                                                                false /*secure*/);
+
+                SurfaceComposerClient::Transaction t;
+                t.setDisplaySurface(vDisplay, producer);
+                t.setDisplayLayerStack(vDisplay, 0);
+                t.setDisplayProjection(vDisplay, mainDisplayInfo.orientation,
+                                       Rect(mainDisplayInfo.viewportW, mainDisplayInfo.viewportH),
+                                       Rect(mainDisplayInfo.w, mainDisplayInfo.h));
+                t.apply();
+                SurfaceComposerClient::Transaction().apply(true);
+                BufferItem item;
+                itemConsumer->acquireBuffer(&item, 0, true);
+                auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer);
+                itemConsumer->releaseBuffer(item);
+                SurfaceComposerClient::destroyDisplay(vDisplay);
+                return sc;
+        }
+    }
+
+protected:
+    LayerTransactionTest* mDelegate;
+    RenderPath mRenderPath;
 };
 
-TEST_F(LayerTransactionTest, SetPositionBasic) {
+class LayerTypeTransactionHarness : public LayerTransactionTest {
+public:
+    LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {}
+
+    sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+                                   uint32_t flags = 0, SurfaceControl* parent = nullptr) {
+        // if the flags already have a layer type specified, return an error
+        if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
+            return nullptr;
+        }
+        return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent);
+    }
+
+    void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
+                        int32_t bufferHeight) {
+        ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color,
+                                                                     bufferWidth, bufferHeight));
+    }
+
+    void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+                           int32_t bufferHeight, const Color& topLeft, const Color& topRight,
+                           const Color& bottomLeft, const Color& bottomRight) {
+        ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer,
+                                                                        bufferWidth, bufferHeight,
+                                                                        topLeft, topRight,
+                                                                        bottomLeft, bottomRight));
+    }
+
+protected:
+    uint32_t mLayerType;
+};
+
+class LayerTypeTransactionTest : public LayerTypeTransactionHarness,
+                                 public ::testing::WithParamInterface<uint32_t> {
+public:
+    LayerTypeTransactionTest() : LayerTypeTransactionHarness(GetParam()) {}
+};
+
+class LayerTypeAndRenderTypeTransactionTest
+      : public LayerTypeTransactionHarness,
+        public ::testing::WithParamInterface<std::tuple<uint32_t, RenderPath>> {
+public:
+    LayerTypeAndRenderTypeTransactionTest()
+          : LayerTypeTransactionHarness(std::get<0>(GetParam())),
+            mRenderPathHarness(LayerRenderPathTestHarness(this, std::get<1>(GetParam()))) {}
+
+    std::unique_ptr<ScreenCapture> getScreenCapture() {
+        return mRenderPathHarness.getScreenCapture();
+    }
+
+protected:
+    LayerRenderPathTestHarness mRenderPathHarness;
+};
+
+// Environment for starting up binder threads. This is required for testing
+// virtual displays, as BufferQueue parameters may be queried over binder.
+class BinderEnvironment : public ::testing::Environment {
+public:
+    void SetUp() override { ProcessState::self()->startThreadPool(); }
+};
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class LayerRenderTypeTransactionTest : public LayerTransactionTest,
+                                       public ::testing::WithParamInterface<RenderPath> {
+public:
+    LayerRenderTypeTransactionTest() : mHarness(LayerRenderPathTestHarness(this, GetParam())) {}
+
+    std::unique_ptr<ScreenCapture> getScreenCapture() { return mHarness.getScreenCapture(); }
+    void setRelativeZBasicHelper(uint32_t layerType);
+    void setRelativeZGroupHelper(uint32_t layerType);
+    void setAlphaBasicHelper(uint32_t layerType);
+
+protected:
+    LayerRenderPathTestHarness mHarness;
+};
+
+INSTANTIATE_TEST_CASE_P(
+        LayerTypeAndRenderTypeTransactionTests, LayerTypeAndRenderTypeTransactionTest,
+        ::testing::Combine(
+                ::testing::Values(
+                        static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
+                        static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)),
+                ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT)));
+
+INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest,
+                        ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT));
+
+INSTANTIATE_TEST_CASE_P(
+        LayerTypeTransactionTests, LayerTypeTransactionTest,
+        ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
+                          static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)));
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionBasic_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     {
         SCOPED_TRACE("default position");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
-        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+        const Rect rect(0, 0, 32, 32);
+        auto shot = getScreenCapture();
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
     }
 
     Transaction().setPosition(layer, 5, 10).apply();
     {
         SCOPED_TRACE("new position");
-        auto shot = screenshot();
-        shot->expectColor(Rect(5, 10, 37, 42), Color::RED);
-        shot->expectBorder(Rect(5, 10, 37, 42), Color::BLACK);
+        const Rect rect(5, 10, 37, 42);
+        auto shot = getScreenCapture();
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionRounding) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     // GLES requires only 4 bits of subpixel precision during rasterization
     // XXX GLES composition does not match HWC composition due to precision
@@ -437,113 +731,114 @@
     Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
     {
         SCOPED_TRACE("rounding down");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply();
     {
         SCOPED_TRACE("rounding up");
-        screenshot()->expectColor(Rect(1, 1, 33, 33), Color::RED);
+        getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionOutOfBounds) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     Transaction().setPosition(layer, -32, -32).apply();
     {
         SCOPED_TRACE("negative coordinates");
-        screenshot()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
     }
 
     Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply();
     {
         SCOPED_TRACE("positive coordinates");
-        screenshot()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionPartiallyOutOfBounds) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     // partially out of bounds
     Transaction().setPosition(layer, -30, -30).apply();
     {
         SCOPED_TRACE("negative coordinates");
-        screenshot()->expectColor(Rect(0, 0, 2, 2), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED);
     }
 
     Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply();
     {
         SCOPED_TRACE("positive coordinates");
-        screenshot()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
-                                       mDisplayHeight),
-                                  Color::RED);
+        getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
+                                             mDisplayHeight),
+                                        Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionWithResize) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     // setPosition is applied immediately by default, with or without resize
     // pending
     Transaction().setPosition(layer, 5, 10).setSize(layer, 64, 64).apply();
     {
         SCOPED_TRACE("resize pending");
-        auto shot = screenshot();
-        shot->expectColor(Rect(5, 10, 37, 42), Color::RED);
-        shot->expectBorder(Rect(5, 10, 37, 42), Color::BLACK);
+        auto shot = getScreenCapture();
+        const Rect rect(5, 10, 37, 42);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
     {
         SCOPED_TRACE("resize applied");
-        screenshot()->expectColor(Rect(5, 10, 69, 74), Color::RED);
+        getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionWithNextResize) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResize_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     // request setPosition to be applied with the next resize
     Transaction().setPosition(layer, 5, 10).setGeometryAppliesWithResize(layer).apply();
     {
         SCOPED_TRACE("new position pending");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setPosition(layer, 15, 20).apply();
     {
         SCOPED_TRACE("pending new position modified");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setSize(layer, 64, 64).apply();
     {
         SCOPED_TRACE("resize pending");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     // finally resize and latch the buffer
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
     {
         SCOPED_TRACE("new position applied");
-        screenshot()->expectColor(Rect(15, 20, 79, 84), Color::RED);
+        getScreenCapture()->expectColor(Rect(15, 20, 79, 84), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetPositionWithNextResizeScaleToWindow) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResizeScaleToWindow_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     // setPosition is not immediate even with SCALE_TO_WINDOW override
     Transaction()
@@ -554,116 +849,137 @@
             .apply();
     {
         SCOPED_TRACE("new position pending");
-        screenshot()->expectColor(Rect(0, 0, 64, 64), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
     {
         SCOPED_TRACE("new position applied");
-        screenshot()->expectColor(Rect(5, 10, 69, 74), Color::RED);
+        getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetSizeBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     Transaction().setSize(layer, 64, 64).apply();
     {
         SCOPED_TRACE("resize pending");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
-        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+        auto shot = getScreenCapture();
+        const Rect rect(0, 0, 32, 32);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
     {
         SCOPED_TRACE("resize applied");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
-        shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+        auto shot = getScreenCapture();
+        const Rect rect(0, 0, 64, 64);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetSizeInvalid) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) {
     // cannot test robustness against invalid sizes (zero or really huge)
 }
 
-TEST_F(LayerTransactionTest, SetSizeWithScaleToWindow) {
+TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition
     Transaction()
             .setSize(layer, 64, 64)
             .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
             .apply();
-    screenshot()->expectColor(Rect(0, 0, 64, 64), Color::RED);
+    getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
 }
 
-TEST_F(LayerTransactionTest, SetZBasic) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZBasic) {
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
     ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
 
     Transaction().setLayer(layerR, mLayerZBase + 1).apply();
     {
         SCOPED_TRACE("layerR");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setLayer(layerG, mLayerZBase + 2).apply();
     {
         SCOPED_TRACE("layerG");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
     }
 }
 
-TEST_F(LayerTransactionTest, SetZNegative) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZNegative) {
+    sp<SurfaceControl> parent =
+            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+                                              ISurfaceComposerClient::eFXSurfaceContainer);
+    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
     ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
 
+    Transaction()
+            .reparent(layerR, parent->getHandle())
+            .reparent(layerG, parent->getHandle())
+            .apply();
     Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply();
     {
         SCOPED_TRACE("layerR");
-        sp<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot, -2, -1);
-        screenshot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setLayer(layerR, -3).apply();
     {
         SCOPED_TRACE("layerG");
-        sp<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot, -3, -1);
-        screenshot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
     }
 }
 
-TEST_F(LayerTransactionTest, SetRelativeZBasic) {
+void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
-    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
 
-    Transaction()
-            .setPosition(layerG, 16, 16)
-            .setRelativeLayer(layerG, layerR->getHandle(), 1)
-            .apply();
+    switch (layerType) {
+        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+            Transaction()
+                    .setPosition(layerG, 16, 16)
+                    .setRelativeLayer(layerG, layerR->getHandle(), 1)
+                    .apply();
+            break;
+        case ISurfaceComposerClient::eFXSurfaceBufferState:
+            Transaction()
+                    .setFrame(layerR, Rect(0, 0, 32, 32))
+                    .setFrame(layerG, Rect(16, 16, 48, 48))
+                    .setRelativeLayer(layerG, layerR->getHandle(), 1)
+                    .apply();
+            break;
+        default:
+            ASSERT_FALSE(true) << "Unsupported layer type";
+    }
     {
         SCOPED_TRACE("layerG above");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
         shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
     }
@@ -671,53 +987,86 @@
     Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
     {
         SCOPED_TRACE("layerG below");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
         shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN);
     }
 }
 
-TEST_F(LayerTransactionTest, SetRelativeZNegative) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferQueue) {
+    ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferState) {
+    ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) {
+    sp<SurfaceControl> parent =
+            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+                                              ISurfaceComposerClient::eFXSurfaceContainer);
+    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     sp<SurfaceControl> layerB;
     ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
     ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
     ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32));
+
+    Transaction()
+            .reparent(layerB, parent->getHandle())
+            .apply();
 
     // layerR = mLayerZBase, layerG = layerR - 1, layerB = -2
     Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply();
 
-    sp<ScreenCapture> screenshot;
+    std::unique_ptr<ScreenCapture> screenshot;
     // only layerB is in this range
-    ScreenCapture::captureScreen(&screenshot, -2, -1);
+    sp<IBinder> parentHandle = parent->getHandle();
+    ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
     screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
 }
 
-TEST_F(LayerTransactionTest, SetRelativeZGroup) {
+void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     sp<SurfaceControl> layerB;
-    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
-    ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE));
+    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerB, Color::BLUE, 32, 32));
 
     // layerR = 0, layerG = layerR + 3, layerB = 2
-    Transaction()
-            .setPosition(layerG, 8, 8)
-            .setRelativeLayer(layerG, layerR->getHandle(), 3)
-            .setPosition(layerB, 16, 16)
-            .setLayer(layerB, mLayerZBase + 2)
-            .apply();
+    switch (layerType) {
+        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+            Transaction()
+                    .setPosition(layerG, 8, 8)
+                    .setRelativeLayer(layerG, layerR->getHandle(), 3)
+                    .setPosition(layerB, 16, 16)
+                    .setLayer(layerB, mLayerZBase + 2)
+                    .apply();
+            break;
+        case ISurfaceComposerClient::eFXSurfaceBufferState:
+            Transaction()
+                    .setFrame(layerR, Rect(0, 0, 32, 32))
+                    .setFrame(layerG, Rect(8, 8, 40, 40))
+                    .setRelativeLayer(layerG, layerR->getHandle(), 3)
+                    .setFrame(layerB, Rect(16, 16, 48, 48))
+                    .setLayer(layerB, mLayerZBase + 2)
+                    .apply();
+            break;
+        default:
+            ASSERT_FALSE(true) << "Unsupported layer type";
+    }
+
     {
         SCOPED_TRACE("(layerR < layerG) < layerB");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
         shot->expectColor(Rect(8, 8, 16, 16), Color::GREEN);
         shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
@@ -727,7 +1076,7 @@
     Transaction().setLayer(layerR, mLayerZBase + 4).apply();
     {
         SCOPED_TRACE("layerB < (layerR < layerG)");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
         shot->expectColor(Rect(8, 8, 40, 40), Color::GREEN);
         shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
@@ -737,7 +1086,7 @@
     Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
     {
         SCOPED_TRACE("layerB < (layerG < layerR)");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
         shot->expectColor(Rect(32, 32, 40, 40), Color::GREEN);
         shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
@@ -748,7 +1097,7 @@
     Transaction().setLayer(layerG, mLayerZBase).apply();
     {
         SCOPED_TRACE("layerG < layerB < layerR");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
         shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE);
     }
@@ -758,57 +1107,65 @@
     Transaction().setLayer(layerR, mLayerZBase + 1).apply();
     {
         SCOPED_TRACE("layerG < layerR < layerB");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
         shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
     }
 }
 
-TEST_F(LayerTransactionTest, SetRelativeZBug64572777) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferQueue) {
+    ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferState) {
+    ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetRelativeZBug64572777) {
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
 
     ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
     ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
 
     Transaction()
             .setPosition(layerG, 16, 16)
             .setRelativeLayer(layerG, layerR->getHandle(), 1)
             .apply();
 
-    mClient->destroySurface(layerG->getHandle());
+    layerG->clear();
     // layerG should have been removed
-    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
 }
 
-TEST_F(LayerTransactionTest, SetFlagsHidden) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
 
     Transaction().setFlags(layer, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden).apply();
     {
         SCOPED_TRACE("layer hidden");
-        screenshot()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
     }
 
     Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply();
     {
         SCOPED_TRACE("layer shown");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetFlagsOpaque) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsOpaque) {
     const Color translucentRed = {100, 0, 0, 100};
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed, 32, 32));
     ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
 
     Transaction()
             .setLayer(layerR, mLayerZBase + 1)
@@ -816,21 +1173,21 @@
             .apply();
     {
         SCOPED_TRACE("layerR opaque");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
     }
 
     Transaction().setFlags(layerR, 0, layer_state_t::eLayerOpaque).apply();
     {
         SCOPED_TRACE("layerR translucent");
         const uint8_t g = uint8_t(255 - translucentRed.a);
-        screenshot()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
     }
 }
 
-TEST_F(LayerTransactionTest, SetFlagsSecure) {
+TEST_P(LayerTypeTransactionTest, SetFlagsSecure) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
 
     sp<ISurfaceComposer> composer = ComposerService::getComposerService();
     sp<GraphicBuffer> outBuffer;
@@ -838,31 +1195,29 @@
             .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
             .apply(true);
     ASSERT_EQ(PERMISSION_DENIED,
-              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, mLayerZBase, mLayerZBase,
-                                      false));
+              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
 
     Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
     ASSERT_EQ(NO_ERROR,
-              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, mLayerZBase, mLayerZBase,
-                                      false));
+              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
 }
 
-TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferQueue) {
     const Rect top(0, 0, 32, 16);
     const Rect bottom(0, 16, 32, 32);
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
 
     ANativeWindow_Buffer buffer;
-    ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer));
-    ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, top, Color::TRANSPARENT));
-    ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, bottom, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+    ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, top, Color::TRANSPARENT));
+    ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, bottom, Color::RED));
     // setTransparentRegionHint always applies to the following buffer
     Transaction().setTransparentRegionHint(layer, Region(top)).apply();
-    ASSERT_NO_FATAL_FAILURE(postLayerBuffer(layer));
+    ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
     {
         SCOPED_TRACE("top transparent");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(top, Color::BLACK);
         shot->expectColor(bottom, Color::RED);
     }
@@ -870,24 +1225,75 @@
     Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
     {
         SCOPED_TRACE("transparent region hint pending");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(top, Color::BLACK);
         shot->expectColor(bottom, Color::RED);
     }
 
-    ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer));
-    ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, top, Color::RED));
-    ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, bottom, Color::TRANSPARENT));
-    ASSERT_NO_FATAL_FAILURE(postLayerBuffer(layer));
+    ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+    ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, top, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, bottom, Color::TRANSPARENT));
+    ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
     {
         SCOPED_TRACE("bottom transparent");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(top, Color::RED);
         shot->expectColor(bottom, Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetTransparentRegionHintOutOfBounds) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState) {
+    const Rect top(0, 0, 32, 16);
+    const Rect bottom(0, 16, 32, 32);
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+
+    ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, top, Color::TRANSPARENT));
+    ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, bottom, Color::RED));
+    Transaction()
+            .setTransparentRegionHint(layer, Region(top))
+            .setBuffer(layer, buffer)
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .apply();
+    {
+        SCOPED_TRACE("top transparent");
+        auto shot = getScreenCapture();
+        shot->expectColor(top, Color::BLACK);
+        shot->expectColor(bottom, Color::RED);
+    }
+
+    Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
+    {
+        SCOPED_TRACE("transparent region hint intermediate");
+        auto shot = getScreenCapture();
+        shot->expectColor(top, Color::BLACK);
+        shot->expectColor(bottom, Color::BLACK);
+    }
+
+    buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                               BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                       BufferUsage::COMPOSER_OVERLAY,
+                               "test");
+
+    ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, top, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, bottom, Color::TRANSPARENT));
+    Transaction().setBuffer(layer, buffer).apply();
+    {
+        SCOPED_TRACE("bottom transparent");
+        auto shot = getScreenCapture();
+        shot->expectColor(top, Color::RED);
+        shot->expectColor(bottom, Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) {
     sp<SurfaceControl> layerTransparent;
     sp<SurfaceControl> layerR;
     ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
@@ -895,32 +1301,152 @@
 
     // check that transparent region hint is bound by the layer size
     Transaction()
-            .setTransparentRegionHint(layerTransparent,
-                                      Region(Rect(0, 0, mDisplayWidth, mDisplayHeight)))
+            .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
             .setPosition(layerR, 16, 16)
             .setLayer(layerR, mLayerZBase + 1)
             .apply();
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerTransparent, Color::TRANSPARENT));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
-    screenshot()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+    ASSERT_NO_FATAL_FAILURE(
+            fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerR, Color::RED, 32, 32));
+    getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
 }
 
-TEST_F(LayerTransactionTest, SetAlphaBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) {
+    sp<SurfaceControl> layerTransparent;
+    sp<SurfaceControl> layerR;
+    ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(
+            layerR = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    // check that transparent region hint is bound by the layer size
+    Transaction()
+            .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
+            .setFrame(layerR, Rect(16, 16, 48, 48))
+            .setLayer(layerR, mLayerZBase + 1)
+            .apply();
+    ASSERT_NO_FATAL_FAILURE(
+            fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32));
+    getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorAlpha_Color_NoEffect) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 0, 0, ISurfaceComposerClient::eFXSurfaceColor));
+
+    half3 color;
+    color.r = 1.0f;
+    color.g = 0.0f;
+    color.b = 0.0f;
+    Transaction()
+            .setCrop_legacy(layer, Rect(0, 0, 32, 32))
+            .setAlpha(layer, 1.0f)
+            .setColor(layer, color)
+            .setColorAlpha(layer, 0.5f)
+            .apply();
+
+    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorAlpha_BufferQueue_NoEffect) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().setAlpha(layer, 1.0f).setColorAlpha(layer, 0.5f).apply();
+
+    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorAlpha_BufferState_ColorLayer) {
+    sp<SurfaceControl> bgLayer;
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(bgLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bgLayer, Color::RED, 32, 32));
+
+    // create color layer
+    half3 color;
+    color.r = 0.0f;
+    color.g = 1.0f;
+    color.b = 0.0f;
+    Transaction().setFrame(layer, Rect(0, 0, 32, 32)).setColor(layer, color).apply();
+
+    {
+        SCOPED_TRACE("before alpha");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    // apply alpha
+    Transaction().setAlpha(layer, 0.0f).apply();
+    {
+        SCOPED_TRACE("set alpha");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorAlpha_BufferState_NoColorLayer) {
+    sp<SurfaceControl> bgLayer;
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(bgLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bgLayer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("before alpha");
+        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    // setting alpha without creating color layer should have no effect
+    Transaction().setFrame(layer, Rect(0, 0, 32, 32)).setAlpha(layer, 0.5f).apply();
+    {
+        SCOPED_TRACE("alpha");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
     sp<SurfaceControl> layer1;
     sp<SurfaceControl> layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer("test 2", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer1, {64, 0, 0, 255}));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer2, {0, 64, 0, 255}));
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer("test 2", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer1, {64, 0, 0, 255}, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer2, {0, 64, 0, 255}, 32, 32));
 
-    Transaction()
-            .setAlpha(layer1, 0.25f)
-            .setAlpha(layer2, 0.75f)
-            .setPosition(layer2, 16, 0)
-            .setLayer(layer2, mLayerZBase + 1)
-            .apply();
+    switch (layerType) {
+        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+            Transaction()
+                    .setAlpha(layer1, 0.25f)
+                    .setAlpha(layer2, 0.75f)
+                    .setPosition(layer2, 16, 0)
+                    .setLayer(layer2, mLayerZBase + 1)
+                    .apply();
+            break;
+        case ISurfaceComposerClient::eFXSurfaceBufferState:
+            Transaction()
+                    .setAlpha(layer1, 0.25f)
+                    .setAlpha(layer2, 0.75f)
+                    .setFrame(layer1, Rect(0, 0, 32, 32))
+                    .setFrame(layer2, Rect(16, 0, 48, 32))
+                    .setLayer(layer2, mLayerZBase + 1)
+                    .apply();
+            break;
+        default:
+            ASSERT_FALSE(true) << "Unsupported layer type";
+    }
     {
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         uint8_t r = 16; // 64 * 0.25f
         uint8_t g = 48; // 64 * 0.75f
         shot->expectColor(Rect(0, 0, 16, 32), {r, 0, 0, 255});
@@ -931,37 +1457,102 @@
     }
 }
 
-TEST_F(LayerTransactionTest, SetAlphaClamped) {
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferQueue) {
+    ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferState) {
+    ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetAlphaClamped) {
     const Color color = {64, 0, 0, 255};
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color, 32, 32));
 
     Transaction().setAlpha(layer, 2.0f).apply();
     {
         SCOPED_TRACE("clamped to 1.0f");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), color);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color);
     }
 
     Transaction().setAlpha(layer, -1.0f).apply();
     {
         SCOPED_TRACE("clamped to 0.0f");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetColorBasic) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) {
+    sp<SurfaceControl> layer;
+    const uint8_t size = 64;
+    const uint8_t testArea = 4;
+    const float cornerRadius = 20.0f;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
+
+    Transaction()
+            .setCornerRadius(layer, cornerRadius)
+            .apply();
+    {
+        const uint8_t bottom = size - 1;
+        const uint8_t right = size - 1;
+        auto shot = getScreenCapture();
+        // Transparent corners
+        shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+        shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
+        shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
+        shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) {
+    sp<SurfaceControl> parent;
+    sp<SurfaceControl> child;
+    const uint8_t size = 64;
+    const uint8_t testArea = 4;
+    const float cornerRadius = 20.0f;
+    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size));
+    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size / 2));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2));
+
+    Transaction()
+            .setCornerRadius(parent, cornerRadius)
+            .reparent(child, parent->getHandle())
+            .setPosition(child, 0, size / 2)
+            .apply();
+    {
+        const uint8_t bottom = size - 1;
+        const uint8_t right = size - 1;
+        auto shot = getScreenCapture();
+        // Top edge of child should not have rounded corners because it's translated in the parent
+        shot->expectColor(Rect(0, size / 2, right, static_cast<int>(bottom - cornerRadius)),
+            Color::GREEN);
+        // But bottom edges should have been clipped according to parent bounds
+        shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
+        shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorBasic) {
     sp<SurfaceControl> bufferLayer;
     sp<SurfaceControl> colorLayer;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED));
-    ASSERT_NO_FATAL_FAILURE(
-            colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceColor));
 
-    Transaction().setLayer(colorLayer, mLayerZBase + 1).apply();
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(colorLayer, mLayerZBase + 1)
+            .apply();
+
     {
         SCOPED_TRACE("default color");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
     const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
@@ -972,26 +1563,103 @@
     Transaction().setColor(colorLayer, color).apply();
     {
         SCOPED_TRACE("new color");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
     }
 }
 
-TEST_F(LayerTransactionTest, SetColorClamped) {
-    sp<SurfaceControl> colorLayer;
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferState_NoPriorColor) {
+    sp<SurfaceControl> bufferQueueLayer;
+    sp<SurfaceControl> bufferStateLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferQueueLayer = createLayer("test bg", 32, 32));
     ASSERT_NO_FATAL_FAILURE(
-            colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+            bufferStateLayer =
+                    createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
-    Transaction().setColor(colorLayer, half3(2.0f, -1.0f, 0.0f)).apply();
-    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferQueueLayer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("default color");
+        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    half3 color;
+    color.r = 0.0f;
+    color.g = 1.0f;
+    color.b = 0.0f;
+    Transaction()
+            .setFrame(bufferStateLayer, Rect(0, 0, 32, 32))
+            .setLayer(bufferStateLayer, mLayerZBase + 1)
+            .setColor(bufferStateLayer, color)
+            .apply();
+
+    {
+        SCOPED_TRACE("set color");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
 }
 
-TEST_F(LayerTransactionTest, SetColorWithAlpha) {
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferState_PriorColor) {
+    sp<SurfaceControl> bufferQueueLayer;
+    sp<SurfaceControl> bufferStateLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferQueueLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(
+            bufferStateLayer =
+                    createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferQueueLayer, Color::RED, 32, 32));
+
+    half3 color;
+    color.r = 0.0f;
+    color.g = 1.0f;
+    color.b = 0.0f;
+    Transaction()
+            .setFrame(bufferStateLayer, Rect(0, 0, 32, 32))
+            .setLayer(bufferStateLayer, mLayerZBase + 1)
+            .setColor(bufferStateLayer, color)
+            .apply();
+    {
+        SCOPED_TRACE("default color");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    color.r = 0.0f;
+    color.g = 0.0f;
+    color.b = 1.0f;
+    Transaction().setColor(bufferStateLayer, color).apply();
+    {
+        SCOPED_TRACE("new color");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorClamped) {
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceColor));
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setColor(colorLayer, half3(2.0f, -1.0f, 0.0f))
+            .apply();
+
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithAlpha) {
     sp<SurfaceControl> bufferLayer;
     sp<SurfaceControl> colorLayer;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED));
-    ASSERT_NO_FATAL_FAILURE(
-            colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceColor));
+    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
 
     const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
     const float alpha = 0.25f;
@@ -1004,20 +1672,21 @@
             .setAlpha(colorLayer, alpha)
             .setLayer(colorLayer, mLayerZBase + 1)
             .apply();
-    screenshot()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
-                              tolerance);
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+                                    tolerance);
 }
 
-TEST_F(LayerTransactionTest, SetColorWithParentAlpha_Bug74220420) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithParentAlpha_Bug74220420) {
     sp<SurfaceControl> bufferLayer;
     sp<SurfaceControl> parentLayer;
     sp<SurfaceControl> colorLayer;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
     ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED));
-    ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer(
-            "childWithColor", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
-
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
+                                                     0 /* buffer height */,
+                                                     ISurfaceComposerClient::eFXSurfaceColor));
+    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
     const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
     const float alpha = 0.25f;
     const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
@@ -1030,91 +1699,137 @@
             .setAlpha(parentLayer, alpha)
             .setLayer(parentLayer, mLayerZBase + 1)
             .apply();
-    screenshot()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
-                              tolerance);
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+                                    tolerance);
 }
 
-TEST_F(LayerTransactionTest, SetColorWithBuffer) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
     sp<SurfaceControl> bufferLayer;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32));
 
     // color is ignored
     Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply();
-    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
 }
 
-TEST_F(LayerTransactionTest, SetLayerStackBasic) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
 
     Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
     {
         SCOPED_TRACE("non-existing layer stack");
-        screenshot()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
     }
 
     Transaction().setLayerStack(layer, mDisplayLayerStack).apply();
     {
         SCOPED_TRACE("original layer stack");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetMatrixBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(
-            fillLayerQuadrant(layer, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
 
     Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 0, 0).apply();
     {
         SCOPED_TRACE("IDENTITY");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
-                                     Color::WHITE);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
     }
 
     Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 32, 0).apply();
     {
         SCOPED_TRACE("FLIP_H");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
-                                     Color::BLUE);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED,
+                                           Color::WHITE, Color::BLUE);
     }
 
     Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).setPosition(layer, 0, 32).apply();
     {
         SCOPED_TRACE("FLIP_V");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
-                                     Color::GREEN);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE,
+                                           Color::RED, Color::GREEN);
     }
 
     Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).setPosition(layer, 32, 0).apply();
     {
         SCOPED_TRACE("ROT_90");
-        screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
-                                     Color::GREEN);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED,
+                                           Color::WHITE, Color::GREEN);
     }
 
     Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setPosition(layer, 0, 0).apply();
     {
         SCOPED_TRACE("SCALE");
-        screenshot()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN, Color::BLUE,
-                                     Color::WHITE, true /* filtered */);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE, true /* filtered */);
     }
 }
 
-TEST_F(LayerTransactionTest, SetMatrixRot45) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
+
+    Transaction()
+            .setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f)
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .apply();
+    {
+        SCOPED_TRACE("IDENTITY");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
+    }
+
+    Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply();
+    {
+        SCOPED_TRACE("FLIP_H");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
+    }
+
+    Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply();
+    {
+        SCOPED_TRACE("FLIP_V");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
+    }
+
+    Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply();
+    {
+        SCOPED_TRACE("ROT_90");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
+    }
+
+    Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply();
+    {
+        SCOPED_TRACE("SCALE");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixRot45_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(
-            fillLayerQuadrant(layer, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
 
     const float rot = M_SQRT1_2; // 45 degrees
     const float trans = M_SQRT2 * 16.0f;
     Transaction().setMatrix(layer, rot, rot, -rot, rot).setPosition(layer, trans, 0).apply();
 
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     // check a 8x8 region inside each color
     auto get8x8Rect = [](int32_t centerX, int32_t centerY) {
         const int32_t halfL = 4;
@@ -1127,31 +1842,33 @@
     shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE);
 }
 
-TEST_F(LayerTransactionTest, SetMatrixWithResize) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     // setMatrix is applied after any pending resize, unlike setPosition
     Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply();
     {
         SCOPED_TRACE("resize pending");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
-        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+        auto shot = getScreenCapture();
+        const Rect rect(0, 0, 32, 32);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
     {
         SCOPED_TRACE("resize applied");
-        screenshot()->expectColor(Rect(0, 0, 128, 128), Color::RED);
+        const Rect rect(0, 0, 128, 128);
+        getScreenCapture()->expectColor(rect, Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetMatrixWithScaleToWindow) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition
     Transaction()
@@ -1159,14 +1876,14 @@
             .setSize(layer, 64, 64)
             .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
             .apply();
-    screenshot()->expectColor(Rect(0, 0, 128, 128), Color::RED);
+    getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
 }
 
-TEST_F(LayerTransactionTest, SetOverrideScalingModeBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(
-            fillLayerQuadrant(layer, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
 
     // XXX SCALE_CROP is not respected; calling setSize and
     // setOverrideScalingMode in separate transactions does not work
@@ -1177,335 +1894,2041 @@
             .apply();
     {
         SCOPED_TRACE("SCALE_TO_WINDOW");
-        screenshot()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN, Color::BLUE,
-                                     Color::WHITE, true /* filtered */);
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE, true /* filtered */);
     }
 }
 
-TEST_F(LayerTransactionTest, SetCropBasic) {
+TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+
+    sp<IBinder> handle = layer->getHandle();
+    ASSERT_TRUE(handle != nullptr);
+
+    FrameStats frameStats;
+    mClient->getLayerFrameStats(handle, &frameStats);
+
+    ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
     const Rect crop(8, 8, 24, 24);
 
-    Transaction().setCrop(layer, crop).apply();
-    auto shot = screenshot();
+    Transaction().setCrop_legacy(layer, crop).apply();
+    auto shot = getScreenCapture();
     shot->expectColor(crop, Color::RED);
     shot->expectBorder(crop, Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetCropEmpty) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    const Rect crop(8, 8, 24, 24);
+
+    Transaction().setCrop(layer, crop).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("empty rect");
+        Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    {
+        SCOPED_TRACE("negative rect");
+        Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
 
     {
         SCOPED_TRACE("empty rect");
         Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     {
         SCOPED_TRACE("negative rect");
         Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 }
 
-TEST_F(LayerTransactionTest, SetCropOutOfBounds) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    Transaction().setCrop(layer, Rect(-128, -64, 128, 64)).apply();
-    auto shot = screenshot();
+    Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetCropWithTranslation) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", mDisplayWidth, mDisplayHeight / 2,
+                                                ISurfaceComposerClient::eFXSurfaceBufferState));
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(mDisplayWidth, mDisplayHeight / 2, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    fillGraphicBufferColor(buffer, Rect(0, 0, mDisplayWidth, mDisplayHeight / 4), Color::BLUE);
+    fillGraphicBufferColor(buffer, Rect(0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2),
+                           Color::RED);
+
+    Transaction().setBuffer(layer, buffer).apply();
+
+    // Partially out of bounds in the negative (upper left) direction
+    Transaction().setCrop(layer, Rect(-128, -128, mDisplayWidth, mDisplayHeight / 4)).apply();
+    {
+        SCOPED_TRACE("out of bounds, negative (upper left) direction");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLACK);
+    }
+
+    // Partially out of bounds in the positive (lower right) direction
+    Transaction()
+            .setCrop(layer, Rect(0, mDisplayHeight / 4, mDisplayWidth + 1, mDisplayHeight))
+            .apply();
+    {
+        SCOPED_TRACE("out of bounds, positive (lower right) direction");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::RED);
+        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLACK);
+    }
+
+    // Fully out of buffer space bounds
+    Transaction().setCrop(layer, Rect(-128, -128, -1, -1)).apply();
+    {
+        SCOPED_TRACE("Fully out of bounds");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 4), Color::BLUE);
+        shot->expectColor(Rect(0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2),
+                          Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     const Point position(32, 32);
     const Rect crop(8, 8, 24, 24);
-    Transaction().setPosition(layer, position.x, position.y).setCrop(layer, crop).apply();
-    auto shot = screenshot();
+    Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
+    auto shot = getScreenCapture();
     shot->expectColor(crop + position, Color::RED);
     shot->expectBorder(crop + position, Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetCropWithScale) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+    const Rect frame(32, 32, 64, 64);
+    const Rect crop(8, 8, 24, 24);
+    Transaction().setFrame(layer, frame).setCrop(layer, crop).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(frame, Color::RED);
+    shot->expectBorder(frame, Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    // crop is affected by matrix
+    // crop_legacy is affected by matrix
     Transaction()
             .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
-            .setCrop(layer, Rect(8, 8, 24, 24))
+            .setCrop_legacy(layer, Rect(8, 8, 24, 24))
             .apply();
-    auto shot = screenshot();
+    auto shot = getScreenCapture();
     shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
     shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetCropWithResize) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithResize_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    // setCrop is applied immediately by default, with or without resize pending
-    Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
+    // setCrop_legacy is applied immediately by default, with or without resize pending
+    Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
     {
         SCOPED_TRACE("resize pending");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
         shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
     {
         SCOPED_TRACE("resize applied");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
         shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetCropWithNextResize) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResize_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    // request setCrop to be applied with the next resize
-    Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setGeometryAppliesWithResize(layer).apply();
+    // request setCrop_legacy to be applied with the next resize
+    Transaction()
+            .setCrop_legacy(layer, Rect(8, 8, 24, 24))
+            .setGeometryAppliesWithResize(layer)
+            .apply();
     {
         SCOPED_TRACE("waiting for next resize");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
-    Transaction().setCrop(layer, Rect(4, 4, 12, 12)).apply();
+    Transaction().setCrop_legacy(layer, Rect(4, 4, 12, 12)).apply();
     {
         SCOPED_TRACE("pending crop modified");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     Transaction().setSize(layer, 16, 16).apply();
     {
         SCOPED_TRACE("resize pending");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     // finally resize
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
     {
         SCOPED_TRACE("new crop applied");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
         shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResizeScaleToWindow_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    // setCrop is not immediate even with SCALE_TO_WINDOW override
+    // setCrop_legacy is not immediate even with SCALE_TO_WINDOW override
     Transaction()
-            .setCrop(layer, Rect(4, 4, 12, 12))
+            .setCrop_legacy(layer, Rect(4, 4, 12, 12))
             .setSize(layer, 16, 16)
             .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
             .setGeometryAppliesWithResize(layer)
             .apply();
     {
         SCOPED_TRACE("new crop pending");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
         shot->expectBorder(Rect(0, 0, 16, 16), Color::BLACK);
     }
 
     // XXX crop is never latched without other geometry change (b/69315677)
     Transaction().setPosition(layer, 1, 0).setGeometryAppliesWithResize(layer).apply();
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
     Transaction().setPosition(layer, 0, 0).apply();
     {
         SCOPED_TRACE("new crop applied");
-        auto shot = screenshot();
+        auto shot = getScreenCapture();
         shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
         shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetFinalCropBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
-    const Rect crop(8, 8, 24, 24);
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    const Rect frame(8, 8, 24, 24);
 
-    // same as in SetCropBasic
-    Transaction().setFinalCrop(layer, crop).apply();
-    auto shot = screenshot();
-    shot->expectColor(crop, Color::RED);
-    shot->expectBorder(crop, Color::BLACK);
+    Transaction().setFrame(layer, frame).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(frame, Color::RED);
+    shot->expectBorder(frame, Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetFinalCropEmpty) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_BufferState) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
 
-    // same as in SetCropEmpty
     {
         SCOPED_TRACE("empty rect");
-        Transaction().setFinalCrop(layer, Rect(8, 8, 8, 8)).apply();
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
     {
         SCOPED_TRACE("negative rect");
-        Transaction().setFinalCrop(layer, Rect(8, 8, 0, 0)).apply();
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetFinalCropOutOfBounds) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
+
+    // A parentless layer will default to a frame with the same size as the buffer
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 10, 10), Color::RED);
+    shot->expectBorder(Rect(0, 0, 10, 10), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) {
+    sp<SurfaceControl> parent, child;
+    ASSERT_NO_FATAL_FAILURE(
+            parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+    Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
+
+    ASSERT_NO_FATAL_FAILURE(
+            child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+
+    Transaction().reparent(child, parent->getHandle()).apply();
+
+    // A layer will default to the frame of its parent
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) {
+    sp<SurfaceControl> parent, child;
+    ASSERT_NO_FATAL_FAILURE(parent = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32));
+
+    ASSERT_NO_FATAL_FAILURE(
+            child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+
+    Transaction().reparent(child, parent->getHandle()).apply();
+
+    // A layer will default to the frame of its parent
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameUpdate_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    Transaction().setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+    std::this_thread::sleep_for(500ms);
+
+    Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
+    shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) {
+    sp<SurfaceControl> parent, child;
+    ASSERT_NO_FATAL_FAILURE(
+            parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(
+            child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    Transaction().reparent(child, parent->getHandle()).apply();
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+    Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+    Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 16), Color::RED);
+    shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("set buffer 1");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
+
+    {
+        SCOPED_TRACE("set buffer 2");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("set buffer 3");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) {
+    sp<SurfaceControl> layer1;
+    ASSERT_NO_FATAL_FAILURE(
+            layer1 = createLayer("test", 64, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<SurfaceControl> layer2;
+    ASSERT_NO_FATAL_FAILURE(
+            layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64));
+
+    Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply();
+    {
+        SCOPED_TRACE("set layer 1 buffer red");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32));
+
+    Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply();
+    {
+        SCOPED_TRACE("set layer 2 buffer blue");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+        shot->expectColor(Rect(0, 32, 64, 64), Color::RED);
+        shot->expectColor(Rect(0, 32, 32, 64), Color::RED);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64));
+    {
+        SCOPED_TRACE("set layer 1 buffer green");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+        shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
+        shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32));
+
+    {
+        SCOPED_TRACE("set layer 2 buffer white");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::WHITE);
+        shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
+        shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformRotate90_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
+
+    Transaction()
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90)
+            .apply();
+
+    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
+                                       Color::GREEN, true /* filtered */);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipH_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
+
+    Transaction()
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H)
+            .apply();
+
+    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
+                                       Color::BLUE, true /* filtered */);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
+
+    Transaction()
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V)
+            .apply();
+
+    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
+                                       Color::GREEN, true /* filtered */);
+}
+
+TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction().setTransformToDisplayInverse(layer, false).apply();
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32));
+
+    Transaction().setTransformToDisplayInverse(layer, true).apply();
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    Transaction transaction;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    sp<Fence> fence;
+    if (getBuffer(nullptr, &fence) != NO_ERROR) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+
+    status_t status = fence->wait(1000);
+    ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
+    std::this_thread::sleep_for(200ms);
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    sp<Fence> fence = Fence::NO_FENCE;
+
+    Transaction()
+            .setBuffer(layer, buffer)
+            .setAcquireFence(layer, fence)
+            .apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorDataspace_ColorLayer_NoEffect) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 0, 0, ISurfaceComposerClient::eFXSurfaceColor));
+    half3 color;
+    color.r = 1.0f;
+    color.g = 0.0f;
+    color.b = 0.0f;
+    Transaction()
+            .setCrop_legacy(layer, Rect(0, 0, 32, 32))
+            .setColor(layer, color)
+            .setDataspace(layer, ui::Dataspace::UNKNOWN)
+            .setColorDataspace(layer, ui::Dataspace::BT2020_ITU)
+            .apply();
+
+    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorDataspace_BufferQueue_NoEffect) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    // same as in SetCropOutOfBounds
-    Transaction().setFinalCrop(layer, Rect(-128, -64, 128, 64)).apply();
+    Transaction()
+            .setDataspace(layer, ui::Dataspace::UNKNOWN)
+            .setColorDataspace(layer, ui::Dataspace::BT2020_ITU)
+            .apply();
+
+    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorDataspace_BufferState_ColorLayer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    half3 color;
+    color.r = 1.0f;
+    color.g = 0.0f;
+    color.b = 0.0f;
+    Transaction()
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .setColor(layer, color)
+            .setColorDataspace(layer, ui::Dataspace::BT2020_ITU)
+            .apply();
     auto shot = screenshot();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
-TEST_F(LayerTransactionTest, SetFinalCropWithTranslation) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorDataspace_BufferState_NoColorLayer) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
 
-    // final crop is applied post-translation
-    Transaction().setPosition(layer, 16, 16).setFinalCrop(layer, Rect(8, 8, 24, 24)).apply();
+    Transaction()
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .setDataspace(layer, ui::Dataspace::UNKNOWN)
+            .setColorDataspace(layer, ui::Dataspace::DCI_P3)
+            .apply();
+
+    screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    Transaction()
+            .setBuffer(layer, buffer)
+            .setDataspace(layer, ui::Dataspace::UNKNOWN)
+            .apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    HdrMetadata hdrMetadata;
+    hdrMetadata.validTypes = 0;
+    Transaction()
+            .setBuffer(layer, buffer)
+            .setHdrMetadata(layer, hdrMetadata)
+            .apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    Region region;
+    region.set(32, 32);
+    Transaction()
+            .setBuffer(layer, buffer)
+            .setSurfaceDamageRegion(layer, region)
+            .apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    Transaction()
+            .setBuffer(layer, buffer)
+            .setApi(layer, NATIVE_WINDOW_API_CPU)
+            .apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_F(LayerTransactionTest, SetSidebandStreamNull_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    // verify this doesn't cause a crash
+    Transaction().setSidebandStream(layer, nullptr).apply();
+}
+
+TEST_F(LayerTransactionTest, CacheBuffer_BufferState) {
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
+
+    ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+}
+
+TEST_F(LayerTransactionTest, CacheBuffers_BufferState) {
+    std::vector<int32_t> bufferIds;
+    int32_t bufferCount = 20;
+
+    for (int i = 0; i < bufferCount; i++) {
+        sp<GraphicBuffer> buffer =
+                new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                                  BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                          BufferUsage::COMPOSER_OVERLAY,
+                                  "test");
+        int32_t bufferId = -1;
+        ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+        if (bufferId < 0) {
+            EXPECT_GE(bufferId, 0);
+            break;
+        }
+        bufferIds.push_back(bufferId);
+    }
+
+    for (int32_t bufferId : bufferIds) {
+        ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+    }
+}
+
+TEST_F(LayerTransactionTest, CacheBufferInvalid_BufferState) {
+    sp<GraphicBuffer> buffer = nullptr;
+
+    int32_t bufferId = -1;
+    ASSERT_NE(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_LT(bufferId, 0);
+}
+
+TEST_F(LayerTransactionTest, UncacheBufferTwice_BufferState) {
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
+
+    ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+    mClient->uncacheBuffer(bufferId);
+}
+
+TEST_F(LayerTransactionTest, UncacheBufferInvalidId_BufferState) {
+    mClient->uncacheBuffer(-1);
+    mClient->uncacheBuffer(0);
+    mClient->uncacheBuffer(1);
+    mClient->uncacheBuffer(5);
+    mClient->uncacheBuffer(1000);
+}
+
+TEST_F(LayerTransactionTest, SetCachedBuffer_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
+
+    Transaction().setCachedBuffer(layer, bufferId).apply();
+
     auto shot = screenshot();
-    shot->expectColor(Rect(16, 16, 24, 24), Color::RED);
-    shot->expectBorder(Rect(16, 16, 24, 24), Color::BLACK);
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+
+    ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
 }
 
-TEST_F(LayerTransactionTest, SetFinalCropWithScale) {
+TEST_F(LayerTransactionTest, SetCachedBufferDelayed_BufferState) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
-    // final crop is not affected by matrix
-    Transaction()
-            .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
-            .setFinalCrop(layer, Rect(8, 8, 24, 24))
-            .apply();
-    auto shot = screenshot();
-    shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
-    shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
-}
+    sp<GraphicBuffer> cachedBuffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(cachedBuffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
 
-TEST_F(LayerTransactionTest, SetFinalCropWithResize) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
-
-    // same as in SetCropWithResize
-    Transaction().setFinalCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::BLUE);
+    Transaction().setBuffer(layer, buffer).apply();
     {
-        SCOPED_TRACE("resize pending");
+        SCOPED_TRACE("Uncached buffer");
+
         auto shot = screenshot();
-        shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
-        shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    fillGraphicBufferColor(cachedBuffer, Rect(0, 0, 32, 32), Color::RED);
+    Transaction().setCachedBuffer(layer, bufferId).apply();
     {
-        SCOPED_TRACE("resize applied");
+        SCOPED_TRACE("Cached buffer");
+
         auto shot = screenshot();
-        shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
-        shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+}
+
+TEST_F(LayerTransactionTest, ReparentToSelf) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+    Transaction().reparent(layer, layer->getHandle()).apply();
+
+    {
+        // We expect the transaction to be silently dropped, but for SurfaceFlinger
+        // to still be functioning.
+        SCOPED_TRACE("after reparent to self");
+        const Rect rect(0, 0, 32, 32);
+        auto shot = screenshot();
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
     }
 }
 
-TEST_F(LayerTransactionTest, SetFinalCropWithNextResize) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+class ColorTransformHelper {
+public:
+    static void DegammaColorSingle(half& s) {
+        if (s <= 0.03928f)
+            s = s / 12.92f;
+        else
+            s = pow((s + 0.055f) / 1.055f, 2.4f);
+    }
 
-    // same as in SetCropWithNextResize
+    static void DegammaColor(half3& color) {
+        DegammaColorSingle(color.r);
+        DegammaColorSingle(color.g);
+        DegammaColorSingle(color.b);
+    }
+
+    static void GammaColorSingle(half& s) {
+        if (s <= 0.0031308f) {
+            s = s * 12.92f;
+        } else {
+            s = 1.055f * pow(s, (1.0f / 2.4f)) - 0.055f;
+        }
+    }
+
+    static void GammaColor(half3& color) {
+        GammaColorSingle(color.r);
+        GammaColorSingle(color.g);
+        GammaColorSingle(color.b);
+    }
+
+    static void applyMatrix(half3& color, const mat3& mat) {
+        half3 ret = half3(0);
+
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++) {
+                ret[i] = ret[i] + color[j] * mat[j][i];
+            }
+        }
+        color = ret;
+    }
+};
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceColor));
     Transaction()
-            .setFinalCrop(layer, Rect(8, 8, 24, 24))
-            .setGeometryAppliesWithResize(layer)
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(colorLayer, mLayerZBase + 1)
             .apply();
     {
-        SCOPED_TRACE("waiting for next resize");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        SCOPED_TRACE("default color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
-    Transaction().setFinalCrop(layer, Rect(4, 4, 12, 12)).apply();
-    {
-        SCOPED_TRACE("pending final crop modified");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+    half3 expected = color;
+    mat3 matrix;
+    matrix[0][0] = 0.3; matrix[1][0] = 0.59; matrix[2][0] = 0.11;
+    matrix[0][1] = 0.3; matrix[1][1] = 0.59; matrix[2][1] = 0.11;
+    matrix[0][2] = 0.3; matrix[1][2] = 0.59; matrix[2][2] = 0.11;
+
+    // degamma before applying the matrix
+    if (mColorManagementUsed) {
+        ColorTransformHelper::DegammaColor(expected);
     }
 
-    Transaction().setSize(layer, 16, 16).apply();
-    {
-        SCOPED_TRACE("resize pending");
-        screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    ColorTransformHelper::applyMatrix(expected, matrix);
+
+    if (mColorManagementUsed) {
+        ColorTransformHelper::GammaColor(expected);
     }
 
-    // finally resize
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+    const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+                                 uint8_t(expected.b * 255), 255};
+
+    // this is handwavy, but the precison loss scaled by 255 (8-bit per
+    // channel) should be less than one
+    const uint8_t tolerance = 1;
+
+    Transaction().setColor(colorLayer, color)
+        .setColorTransform(colorLayer, matrix, vec3()).apply();
     {
-        SCOPED_TRACE("new final crop applied");
-        auto shot = screenshot();
-        shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
-        shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
+        SCOPED_TRACE("new color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
     }
 }
 
-TEST_F(LayerTransactionTest, SetFinalCropWithNextResizeScaleToWindow) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) {
+    sp<SurfaceControl> parentLayer;
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
+                                                      0 /* buffer height */,
+                                                      ISurfaceComposerClient::eFXSurfaceContainer));
+    ASSERT_NO_FATAL_FAILURE(
+            colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                     ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get()));
 
-    // same as in SetCropWithNextResizeScaleToWindow
     Transaction()
-            .setFinalCrop(layer, Rect(4, 4, 12, 12))
-            .setSize(layer, 16, 16)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .setGeometryAppliesWithResize(layer)
+            .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(parentLayer, mLayerZBase + 1)
             .apply();
     {
-        SCOPED_TRACE("new final crop pending");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
-        shot->expectBorder(Rect(0, 0, 16, 16), Color::BLACK);
+        SCOPED_TRACE("default color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
-    // XXX final crop is never latched without other geometry change (b/69315677)
-    Transaction().setPosition(layer, 1, 0).setGeometryAppliesWithResize(layer).apply();
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
-    Transaction().setPosition(layer, 0, 0).apply();
-    {
-        SCOPED_TRACE("new final crop applied");
-        auto shot = screenshot();
-        shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
-        shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
+    const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+    half3 expected = color;
+    mat3 matrix;
+    matrix[0][0] = 0.3; matrix[1][0] = 0.59; matrix[2][0] = 0.11;
+    matrix[0][1] = 0.3; matrix[1][1] = 0.59; matrix[2][1] = 0.11;
+    matrix[0][2] = 0.3; matrix[1][2] = 0.59; matrix[2][2] = 0.11;
+
+    // degamma before applying the matrix
+    if (mColorManagementUsed) {
+        ColorTransformHelper::DegammaColor(expected);
     }
+
+    ColorTransformHelper::applyMatrix(expected, matrix);
+
+    if (mColorManagementUsed) {
+        ColorTransformHelper::GammaColor(expected);
+    }
+
+    const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+                                 uint8_t(expected.b * 255), 255};
+
+    // this is handwavy, but the precison loss scaled by 255 (8-bit per
+    // channel) should be less than one
+    const uint8_t tolerance = 1;
+
+    Transaction()
+            .setColor(colorLayer, color)
+            .setColorTransform(parentLayer, matrix, vec3())
+            .apply();
+    {
+        SCOPED_TRACE("new color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) {
+    sp<SurfaceControl> parentLayer;
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
+                                                      0 /* buffer height */,
+                                                      ISurfaceComposerClient::eFXSurfaceContainer));
+    ASSERT_NO_FATAL_FAILURE(
+            colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                     ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get()));
+
+    Transaction()
+            .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(parentLayer, mLayerZBase + 1)
+            .apply();
+    {
+        SCOPED_TRACE("default color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+    half3 expected = color;
+    mat3 matrixChild;
+    matrixChild[0][0] = 0.3; matrixChild[1][0] = 0.59; matrixChild[2][0] = 0.11;
+    matrixChild[0][1] = 0.3; matrixChild[1][1] = 0.59; matrixChild[2][1] = 0.11;
+    matrixChild[0][2] = 0.3; matrixChild[1][2] = 0.59; matrixChild[2][2] = 0.11;
+    mat3 matrixParent;
+    matrixParent[0][0] = 0.2; matrixParent[1][0] = 0.4; matrixParent[2][0] = 0.10;
+    matrixParent[0][1] = 0.2; matrixParent[1][1] = 0.4; matrixParent[2][1] = 0.10;
+    matrixParent[0][2] = 0.2; matrixParent[1][2] = 0.4; matrixParent[2][2] = 0.10;
+
+    // degamma before applying the matrix
+    if (mColorManagementUsed) {
+        ColorTransformHelper::DegammaColor(expected);
+    }
+
+    ColorTransformHelper::applyMatrix(expected, matrixChild);
+    ColorTransformHelper::applyMatrix(expected, matrixParent);
+
+    if (mColorManagementUsed) {
+        ColorTransformHelper::GammaColor(expected);
+    }
+
+    const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+                                 uint8_t(expected.b * 255), 255};
+
+    // this is handwavy, but the precison loss scaled by 255 (8-bit per
+    // channel) should be less than one
+    const uint8_t tolerance = 1;
+
+    Transaction()
+            .setColor(colorLayer, color)
+            .setColorTransform(parentLayer, matrixParent, vec3())
+            .setColorTransform(colorLayer, matrixChild, vec3())
+            .apply();
+    {
+        SCOPED_TRACE("new color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+    }
+}
+
+struct CallbackData {
+    CallbackData() = default;
+    CallbackData(nsecs_t time, const sp<Fence>& fence,
+                 const std::vector<SurfaceControlStats>& stats)
+          : latchTime(time), presentFence(fence), surfaceControlStats(stats) {}
+
+    nsecs_t latchTime;
+    sp<Fence> presentFence;
+    std::vector<SurfaceControlStats> surfaceControlStats;
+};
+
+class ExpectedResult {
+public:
+    enum Transaction {
+        NOT_PRESENTED = 0,
+        PRESENTED,
+    };
+
+    enum Buffer {
+        NOT_ACQUIRED = 0,
+        ACQUIRED,
+    };
+
+    enum PreviousBuffer {
+        NOT_RELEASED = 0,
+        RELEASED,
+        UNKNOWN,
+    };
+
+    void reset() {
+        mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+        mExpectedSurfaceResults.clear();
+    }
+
+    void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer,
+                    ExpectedResult::Buffer bufferResult = ACQUIRED,
+                    ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+        mTransactionResult = transactionResult;
+        mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer),
+                                        std::forward_as_tuple(bufferResult, previousBufferResult));
+    }
+
+    void addSurfaces(ExpectedResult::Transaction transactionResult,
+                     const std::vector<sp<SurfaceControl>>& layers,
+                     ExpectedResult::Buffer bufferResult = ACQUIRED,
+                     ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+        for (const auto& layer : layers) {
+            addSurface(transactionResult, layer, bufferResult, previousBufferResult);
+        }
+    }
+
+    void addExpectedPresentTime(nsecs_t expectedPresentTime) {
+        mExpectedPresentTime = expectedPresentTime;
+    }
+
+    void verifyCallbackData(const CallbackData& callbackData) const {
+        const auto& [latchTime, presentFence, surfaceControlStats] = callbackData;
+        if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
+            ASSERT_GE(latchTime, 0) << "bad latch time";
+            ASSERT_NE(presentFence, nullptr);
+            if (mExpectedPresentTime >= 0) {
+                ASSERT_EQ(presentFence->wait(3000), NO_ERROR);
+                ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6));
+                // if the panel is running at 30 hz, at the worst case, our expected time just
+                // misses vsync and we have to wait another 33.3ms
+                ASSERT_LE(presentFence->getSignalTime(),
+                          mExpectedPresentTime + nsecs_t(66.666666 * 1e6));
+            }
+        } else {
+            ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
+            ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
+        }
+
+        ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size())
+                << "wrong number of surfaces";
+
+        for (const auto& stats : surfaceControlStats) {
+            ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control";
+
+            const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl);
+            ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end())
+                    << "unexpected surface control";
+            expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime);
+        }
+    }
+
+private:
+    class ExpectedSurfaceResult {
+    public:
+        ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult,
+                              ExpectedResult::PreviousBuffer previousBufferResult)
+              : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
+
+        void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
+                                       nsecs_t latchTime) const {
+            const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceControlStats;
+
+            ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
+                    << "bad acquire time";
+            ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time";
+
+            if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) {
+                ASSERT_NE(previousReleaseFence, nullptr)
+                        << "failed to set release prev buffer fence";
+            } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) {
+                ASSERT_EQ(previousReleaseFence, nullptr)
+                        << "should not have set released prev buffer fence";
+            }
+        }
+
+    private:
+        ExpectedResult::Buffer mBufferResult;
+        ExpectedResult::PreviousBuffer mPreviousBufferResult;
+    };
+
+    struct SCHash {
+        std::size_t operator()(const sp<SurfaceControl>& sc) const {
+            return std::hash<IBinder*>{}(sc->getHandle().get());
+        }
+    };
+    ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+    nsecs_t mExpectedPresentTime = -1;
+    std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults;
+};
+
+class CallbackHelper {
+public:
+    static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence,
+                         const std::vector<SurfaceControlStats>& stats) {
+        if (!callbackContext) {
+            ALOGE("failed to get callback context");
+        }
+        CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext);
+        std::lock_guard lock(helper->mMutex);
+        helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats);
+        helper->mConditionVariable.notify_all();
+    }
+
+    void getCallbackData(CallbackData* outData) {
+        std::unique_lock lock(mMutex);
+
+        if (mCallbackDataQueue.empty()) {
+            ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)),
+                      std::cv_status::timeout)
+                    << "did not receive callback";
+        }
+
+        *outData = std::move(mCallbackDataQueue.front());
+        mCallbackDataQueue.pop();
+    }
+
+    void verifyFinalState() {
+        // Wait to see if there are extra callbacks
+        std::this_thread::sleep_for(500ms);
+
+        std::lock_guard lock(mMutex);
+        EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+        mCallbackDataQueue = {};
+    }
+
+    void* getContext() { return static_cast<void*>(this); }
+
+    std::mutex mMutex;
+    std::condition_variable mConditionVariable;
+    std::queue<CallbackData> mCallbackDataQueue;
+};
+
+class LayerCallbackTest : public LayerTransactionTest {
+public:
+    virtual sp<SurfaceControl> createBufferStateLayer() {
+        return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
+    }
+
+    static int fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
+                               const sp<SurfaceControl>& layer = nullptr) {
+        if (layer) {
+            sp<GraphicBuffer> buffer;
+            sp<Fence> fence;
+            int err = getBuffer(&buffer, &fence);
+            if (err != NO_ERROR) {
+                return err;
+            }
+
+            transaction.setBuffer(layer, buffer).setAcquireFence(layer, fence);
+        }
+
+        transaction.addTransactionCompletedCallback(callbackHelper->function,
+                                                    callbackHelper->getContext());
+        return NO_ERROR;
+    }
+
+    static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+                                bool finalState = false) {
+        CallbackData callbackData;
+        ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+        EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
+
+        if (finalState) {
+            ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+        }
+    }
+
+    static void waitForCallbacks(CallbackHelper& helper,
+                                 const std::vector<ExpectedResult>& expectedResults,
+                                 bool finalState = false) {
+        for (const auto& expectedResult : expectedResults) {
+            waitForCallback(helper, expectedResult);
+        }
+        if (finalState) {
+            ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+        }
+    }
+};
+
+TEST_F(LayerCallbackTest, Basic) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoBuffer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoStateChange) {
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.apply();
+
+    ExpectedResult expected;
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, OffScreen) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_SameCallback) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction1, &callback, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_SameLayer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    int err = fillTransaction(transaction1, &callback1, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_DifferentClients) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        int err = fillTransaction(transaction, &callback, layer);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction.apply();
+
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::ACQUIRED,
+                            (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                     : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        ExpectedResult expected;
+
+        if (i == 0) {
+            int err = fillTransaction(transaction, &callback, layer);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
+            expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+        } else {
+            int err = fillTransaction(transaction, &callback);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
+        }
+
+        transaction.apply();
+
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        if (i == 0) {
+            int err = fillTransaction(transaction, &callback, layer);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
+        } else {
+            int err = fillTransaction(transaction, &callback);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
+        }
+
+        transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+        ExpectedResult expected;
+        expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
+                                     : ExpectedResult::Transaction::NOT_PRESENTED,
+                            layer,
+                            (i == 0) ? ExpectedResult::Buffer::ACQUIRED
+                                     : ExpectedResult::Buffer::NOT_ACQUIRED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    for (size_t i = 0; i < 10; i++) {
+        int err = fillTransaction(transaction1, &callback1, layer1);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+        err = fillTransaction(transaction2, &callback2, layer2);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+        transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+        ExpectedResult expected;
+        expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+                             ExpectedResult::Buffer::ACQUIRED,
+                             (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                      : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+    ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    for (size_t i = 0; i < 10; i++) {
+        int err = fillTransaction(transaction1, &callback1, layer1);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+        err = fillTransaction(transaction2, &callback2, layer2);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+        transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+        ExpectedResult expected;
+        expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+                             ExpectedResult::Buffer::ACQUIRED,
+                             (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                      : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+    ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+
+    // Normal call to set up test
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+    expected.reset();
+
+    // Test
+    err = fillTransaction(transaction1, &callback1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+
+    // Normal call to set up test
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+    expected.reset();
+
+    // Test
+    err = fillTransaction(transaction1, &callback1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    std::vector<ExpectedResult> expectedResults(50);
+    for (auto& expected : expectedResults) {
+        expected.reset();
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::ACQUIRED,
+                            ExpectedResult::PreviousBuffer::UNKNOWN);
+
+        int err = fillTransaction(transaction, &callback, layer);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction.apply();
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    // Normal call to set up test
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+
+    // Test
+    std::vector<ExpectedResult> expectedResults(50);
+    for (auto& expected : expectedResults) {
+        expected.reset();
+
+        err = fillTransaction(transaction, &callback);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction.apply();
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    // Normal call to set up test
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+    ExpectedResult expectedResult;
+    expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expectedResult, true));
+
+    // Test
+    std::vector<ExpectedResult> expectedResults(50);
+    for (auto& expected : expectedResults) {
+        expected.reset();
+        expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED);
+
+        err = fillTransaction(transaction, &callback);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected.addExpectedPresentTime(time);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback1;
+    int err = fillTransaction(transaction, &callback1, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected1;
+    expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected1.addExpectedPresentTime(time);
+
+    CallbackHelper callback2;
+    err = fillTransaction(transaction, &callback2, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 33ms after the first frame
+    time += (33.3 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected2;
+    expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                         ExpectedResult::Buffer::ACQUIRED,
+                         ExpectedResult::PreviousBuffer::RELEASED);
+    expected2.addExpectedPresentTime(time);
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback1;
+    int err = fillTransaction(transaction, &callback1, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected1;
+    expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected1.addExpectedPresentTime(time);
+
+    CallbackHelper callback2;
+    err = fillTransaction(transaction, &callback2, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 33ms before the previous frame
+    time -= (33.3 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected2;
+    expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                         ExpectedResult::Buffer::ACQUIRED,
+                         ExpectedResult::PreviousBuffer::RELEASED);
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the past
+    nsecs_t time = systemTime() - (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected.addExpectedPresentTime(systemTime());
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
 }
 
 class LayerUpdateTest : public LayerTransactionTest {
 protected:
     virtual void SetUp() {
-        mComposerClient = new SurfaceComposerClient;
-        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
         sp<IBinder> display(
                 SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
@@ -1516,24 +3939,22 @@
         ssize_t displayHeight = info.h;
 
         // Background surface
-        mBGSurfaceControl =
-                mComposerClient->createSurface(String8("BG Test Surface"), displayWidth,
-                                               displayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+        mBGSurfaceControl = createLayer(String8("BG Test Surface"), displayWidth,
+                                               displayHeight, 0);
         ASSERT_TRUE(mBGSurfaceControl != nullptr);
         ASSERT_TRUE(mBGSurfaceControl->isValid());
         fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
 
         // Foreground surface
-        mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
-                                                           PIXEL_FORMAT_RGBA_8888, 0);
+        mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
+
         ASSERT_TRUE(mFGSurfaceControl != nullptr);
         ASSERT_TRUE(mFGSurfaceControl->isValid());
 
         fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
 
         // Synchronization surface
-        mSyncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
-                                                             PIXEL_FORMAT_RGBA_8888, 0);
+        mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0);
         ASSERT_TRUE(mSyncSurfaceControl != nullptr);
         ASSERT_TRUE(mSyncSurfaceControl->isValid());
 
@@ -1555,11 +3976,10 @@
     }
 
     virtual void TearDown() {
-        mComposerClient->dispose();
+        LayerTransactionTest::TearDown();
         mBGSurfaceControl = 0;
         mFGSurfaceControl = 0;
         mSyncSurfaceControl = 0;
-        mComposerClient = 0;
     }
 
     void waitForPostedBuffers() {
@@ -1578,7 +3998,6 @@
         t.apply(true);
     }
 
-    sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mBGSurfaceControl;
     sp<SurfaceControl> mFGSurfaceControl;
 
@@ -1588,10 +4007,10 @@
 };
 
 TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
-    sp<ScreenCapture> sc;
 
-    sp<SurfaceControl> relative = mComposerClient->createSurface(String8("relativeTestSurface"), 10,
-                                                                 10, PIXEL_FORMAT_RGBA_8888, 0);
+    std::unique_ptr<ScreenCapture> sc;
+
+    sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
     fillSurfaceRGBA8(relative, 10, 10, 10);
     waitForPostedBuffers();
 
@@ -1649,13 +4068,12 @@
         asTransaction([&](Transaction& t) {
             t.setSize(mFGSurfaceControl, 64, 64);
             t.setPosition(mFGSurfaceControl, 64, 64);
-            t.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64));
-            t.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1));
+            t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
         });
 
         EXPECT_INITIAL_STATE("After restoring initial state");
     }
-    sp<ScreenCapture> sc;
+    std::unique_ptr<ScreenCapture> sc;
 };
 
 class CropLatchingTest : public GeometryLatchingTest {
@@ -1679,45 +4097,8 @@
     }
 };
 
-// In this test we ensure that setGeometryAppliesWithResize actually demands
-// a buffer of the new size, and not just any size.
-TEST_F(CropLatchingTest, FinalCropLatchingBufferOldSize) {
-    EXPECT_INITIAL_STATE("before anything");
-    // Normally the crop applies immediately even while a resize is pending.
-    asTransaction([&](Transaction& t) {
-        t.setSize(mFGSurfaceControl, 128, 128);
-        t.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
-    });
-
-    EXPECT_CROPPED_STATE("after setting crop (without geometryAppliesWithResize)");
-
-    restoreInitialState();
-
-    // In order to prepare to submit a buffer at the wrong size, we acquire it prior to
-    // initiating the resize.
-    lockAndFillFGBuffer();
-
-    asTransaction([&](Transaction& t) {
-        t.setSize(mFGSurfaceControl, 128, 128);
-        t.setGeometryAppliesWithResize(mFGSurfaceControl);
-        t.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
-    });
-
-    EXPECT_INITIAL_STATE("after setting crop (with geometryAppliesWithResize)");
-
-    // We now submit our old buffer, at the old size, and ensure it doesn't
-    // trigger geometry latching.
-    unlockFGBuffer();
-
-    EXPECT_INITIAL_STATE("after unlocking FG buffer (with geometryAppliesWithResize)");
-
-    completeFGResize();
-
-    EXPECT_CROPPED_STATE("after the resize finishes");
-}
-
 TEST_F(LayerUpdateTest, DeferredTransactionTest) {
-    sp<ScreenCapture> sc;
+    std::unique_ptr<ScreenCapture> sc;
     {
         SCOPED_TRACE("before anything");
         ScreenCapture::captureScreen(&sc);
@@ -1729,14 +4110,14 @@
     // set up two deferred transactions on different frames
     asTransaction([&](Transaction& t) {
         t.setAlpha(mFGSurfaceControl, 0.75);
-        t.deferTransactionUntil(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
-                                mSyncSurfaceControl->getSurface()->getNextFrameNumber());
+        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+                                       mSyncSurfaceControl->getSurface()->getNextFrameNumber());
     });
 
     asTransaction([&](Transaction& t) {
         t.setPosition(mFGSurfaceControl, 128, 128);
-        t.deferTransactionUntil(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
-                                mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+                                       mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
     });
 
     {
@@ -1772,26 +4153,27 @@
 }
 
 TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) {
-    sp<ScreenCapture> sc;
+    std::unique_ptr<ScreenCapture> sc;
 
     sp<SurfaceControl> childNoBuffer =
-            mComposerClient->createSurface(String8("Bufferless child"), 10, 10,
-                                           PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    sp<SurfaceControl> childBuffer =
-            mComposerClient->createSurface(String8("Buffered child"), 20, 20,
-                                           PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
+            createSurface(mClient, "Bufferless child", 0 /* buffer width */, 0 /* buffer height */,
+                          PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    sp<SurfaceControl> childBuffer = createSurface(mClient, "Buffered child", 20, 20,
+                                                   PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
     fillSurfaceRGBA8(childBuffer, 200, 200, 200);
-
-    SurfaceComposerClient::Transaction{}.show(childNoBuffer).show(childBuffer).apply(true);
-
+    SurfaceComposerClient::Transaction{}
+            .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
+            .show(childNoBuffer)
+            .show(childBuffer)
+            .apply(true);
     {
         ScreenCapture::captureScreen(&sc);
         sc->expectChildColor(73, 73);
         sc->expectFGColor(74, 74);
     }
-
-    SurfaceComposerClient::Transaction{}.setSize(childNoBuffer, 20, 20).apply(true);
-
+    SurfaceComposerClient::Transaction{}
+            .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
+            .apply(true);
     {
         ScreenCapture::captureScreen(&sc);
         sc->expectChildColor(73, 73);
@@ -1800,7 +4182,7 @@
 }
 
 TEST_F(LayerUpdateTest, MergingTransactions) {
-    sp<ScreenCapture> sc;
+    std::unique_ptr<ScreenCapture> sc;
     {
         SCOPED_TRACE("before move");
         ScreenCapture::captureScreen(&sc);
@@ -1827,13 +4209,13 @@
 protected:
     void SetUp() override {
         LayerUpdateTest::SetUp();
-        mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
-                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+        mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+                               mFGSurfaceControl.get());
         fillSurfaceRGBA8(mChild, 200, 200, 200);
 
         {
             SCOPED_TRACE("before anything");
-            ScreenCapture::captureScreen(&mCapture);
+            mCapture = screenshot();
             mCapture->expectChildColor(64, 64);
         }
     }
@@ -1843,7 +4225,7 @@
     }
 
     sp<SurfaceControl> mChild;
-    sp<ScreenCapture> mCapture;
+    std::unique_ptr<ScreenCapture> mCapture;
 };
 
 TEST_F(ChildLayerTest, ChildLayerPositioning) {
@@ -1854,7 +4236,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Top left of foreground must now be visible
         mCapture->expectFGColor(64, 64);
         // But 10 pixels in we should see the child surface
@@ -1866,7 +4248,7 @@
     asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Top left of foreground should now be at 0, 0
         mCapture->expectFGColor(0, 0);
         // But 10 pixels in we should see the child surface
@@ -1881,27 +4263,11 @@
         t.show(mChild);
         t.setPosition(mChild, 0, 0);
         t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
+        t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(4, 4);
-        mCapture->expectBGColor(5, 5);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildLayerFinalCropping) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setFinalCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
-    });
-
-    {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         mCapture->expectChildColor(0, 0);
         mCapture->expectChildColor(4, 4);
         mCapture->expectBGColor(5, 5);
@@ -1916,7 +4282,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         mCapture->expectFGColor(0, 0);
         // Last pixel in foreground should now be the child.
         mCapture->expectChildColor(63, 63);
@@ -1931,7 +4297,7 @@
 
     // Find the boundary between the parent and child
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         mCapture->expectChildColor(9, 9);
         mCapture->expectFGColor(10, 10);
     }
@@ -1941,7 +4307,7 @@
     // The boundary should be twice as far from the origin now.
     // The pixels from the last test should all be child now
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         mCapture->expectChildColor(9, 9);
         mCapture->expectChildColor(10, 10);
         mCapture->expectChildColor(19, 19);
@@ -1962,7 +4328,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Unblended child color
         mCapture->checkPixel(0, 0, 0, 254, 0);
     }
@@ -1970,7 +4336,7 @@
     asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Child and BG blended.
         mCapture->checkPixel(0, 0, 127, 127, 0);
     }
@@ -1978,7 +4344,7 @@
     asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Child and BG blended.
         mCapture->checkPixel(0, 0, 95, 64, 95);
     }
@@ -1992,7 +4358,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Top left of foreground must now be visible
         mCapture->expectFGColor(64, 64);
         // But 10 pixels in we should see the child surface
@@ -2006,7 +4372,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         mCapture->expectFGColor(64, 64);
         // In reparenting we should have exposed the entire foreground surface.
         mCapture->expectFGColor(74, 74);
@@ -2017,6 +4383,36 @@
     }
 }
 
+TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) {
+    sp<SurfaceControl> mGrandChild =
+            createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+    fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
+
+    {
+        SCOPED_TRACE("Grandchild visible");
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->checkPixel(64, 64, 111, 111, 111);
+    }
+
+    mChild->clear();
+
+    {
+        SCOPED_TRACE("After destroying child");
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectFGColor(64, 64);
+    }
+
+    asTransaction([&](Transaction& t) {
+         t.reparent(mGrandChild, mFGSurfaceControl->getHandle());
+    });
+
+    {
+        SCOPED_TRACE("After reparenting grandchild");
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->checkPixel(64, 64, 111, 111, 111);
+    }
+}
+
 TEST_F(ChildLayerTest, DetachChildrenSameClient) {
     asTransaction([&](Transaction& t) {
         t.show(mChild);
@@ -2025,7 +4421,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Top left of foreground must now be visible
         mCapture->expectFGColor(64, 64);
         // But 10 pixels in we should see the child surface
@@ -2034,6 +4430,7 @@
         mCapture->expectFGColor(84, 84);
     }
 
+
     asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
 
     asTransaction([&](Transaction& t) { t.hide(mChild); });
@@ -2041,7 +4438,7 @@
     // Since the child has the same client as the parent, it will not get
     // detached and will be hidden.
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         mCapture->expectFGColor(64, 64);
         mCapture->expectFGColor(74, 74);
         mCapture->expectFGColor(84, 84);
@@ -2051,10 +4448,9 @@
 TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
     sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
     sp<SurfaceControl> mChildNewClient =
-            mNewComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
-                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+            createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
+                          PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
 
-    ASSERT_TRUE(mChildNewClient != nullptr);
     ASSERT_TRUE(mChildNewClient->isValid());
 
     fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
@@ -2067,7 +4463,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Top left of foreground must now be visible
         mCapture->expectFGColor(64, 64);
         // But 10 pixels in we should see the child surface
@@ -2082,13 +4478,68 @@
 
     // Nothing should have changed.
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         mCapture->expectFGColor(64, 64);
         mCapture->expectChildColor(74, 74);
         mCapture->expectFGColor(84, 84);
     }
 }
 
+TEST_F(ChildLayerTest, DetachChildrenThenAttach) {
+    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> childNewClient =
+            newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+    ASSERT_TRUE(childNewClient != nullptr);
+    ASSERT_TRUE(childNewClient->isValid());
+
+    fillSurfaceRGBA8(childNewClient, 200, 200, 200);
+
+    Transaction()
+            .hide(mChild)
+            .show(childNewClient)
+            .setPosition(childNewClient, 10, 10)
+            .setPosition(mFGSurfaceControl, 64, 64)
+            .apply();
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    Transaction().detachChildren(mFGSurfaceControl).apply();
+    Transaction().hide(childNewClient).apply();
+
+    // Nothing should have changed.
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(64, 64);
+        mCapture->expectChildColor(74, 74);
+        mCapture->expectFGColor(84, 84);
+    }
+
+    sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0);
+    fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32,
+                   32);
+    Transaction()
+            .setLayer(newParentSurface, INT32_MAX - 1)
+            .show(newParentSurface)
+            .setPosition(newParentSurface, 20, 20)
+            .reparent(childNewClient, newParentSurface->getHandle())
+            .apply();
+    {
+        mCapture = screenshot();
+        // Child is now hidden.
+        mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED);
+    }
+}
+
 TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
     asTransaction([&](Transaction& t) {
         t.show(mChild);
@@ -2097,7 +4548,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // We've positioned the child in the top left.
         mCapture->expectChildColor(0, 0);
         // But it's only 10x10.
@@ -2111,7 +4562,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // We've positioned the child in the top left.
         mCapture->expectChildColor(0, 0);
         mCapture->expectChildColor(10, 10);
@@ -2130,7 +4581,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // We've positioned the child in the top left.
         mCapture->expectChildColor(0, 0);
         // But it's only 10x10.
@@ -2149,7 +4600,7 @@
     {
         // The child should still be in the same place and not have any strange scaling as in
         // b/37673612.
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         mCapture->expectChildColor(0, 0);
         mCapture->expectFGColor(10, 10);
     }
@@ -2160,14 +4611,13 @@
     mChild.clear();
 
     // Now recreate it as hidden
-    mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
-                                            PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
-                                            mFGSurfaceControl.get());
+    mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888,
+                           ISurfaceComposerClient::eHidden, mFGSurfaceControl.get());
 
     // Show the child layer in a deferred transaction
     asTransaction([&](Transaction& t) {
-        t.deferTransactionUntil(mChild, mFGSurfaceControl->getHandle(),
-                                mFGSurfaceControl->getSurface()->getNextFrameNumber());
+        t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
+                                       mFGSurfaceControl->getSurface()->getNextFrameNumber());
         t.show(mChild);
     });
 
@@ -2194,7 +4644,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Top left of foreground must now be visible
         mCapture->expectFGColor(64, 64);
         // But 10 pixels in we should see the child surface
@@ -2206,7 +4656,7 @@
     asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         mCapture->expectFGColor(64, 64);
         // In reparenting we should have exposed the entire foreground surface.
         mCapture->expectFGColor(74, 74);
@@ -2225,7 +4675,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Top left of foreground must now be visible
         mCapture->expectFGColor(64, 64);
         // But 10 pixels in we should see the child surface
@@ -2235,17 +4685,16 @@
     }
     asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); });
     {
-        ScreenCapture::captureScreen(&mCapture);
-        // Nothing should have changed.
+        mCapture = screenshot();
+        // The surface should now be offscreen.
         mCapture->expectFGColor(64, 64);
-        mCapture->expectChildColor(74, 74);
+        mCapture->expectFGColor(74, 74);
         mCapture->expectFGColor(84, 84);
     }
 }
 
 TEST_F(ChildLayerTest, ReparentFromNoParent) {
-    sp<SurfaceControl> newSurface = mComposerClient->createSurface(String8("New Surface"), 10, 10,
-                                                                   PIXEL_FORMAT_RGBA_8888, 0);
+    sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0);
     ASSERT_TRUE(newSurface != nullptr);
     ASSERT_TRUE(newSurface->isValid());
 
@@ -2259,7 +4708,7 @@
     });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Top left of foreground must now be visible
         mCapture->expectFGColor(64, 64);
         // At 10, 10 we should see the new surface
@@ -2269,7 +4718,7 @@
     asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); });
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // newSurface will now be a child of mFGSurface so it will be 10, 10 offset from
         // mFGSurface, putting it at 74, 74.
         mCapture->expectFGColor(64, 64);
@@ -2279,13 +4728,12 @@
 }
 
 TEST_F(ChildLayerTest, NestedChildren) {
-    sp<SurfaceControl> grandchild =
-            mComposerClient->createSurface(String8("Grandchild surface"), 10, 10,
-                                           PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
     fillSurfaceRGBA8(grandchild, 50, 50, 50);
 
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         // Expect the grandchild to begin at 64, 64 because it's a child of mChild layer
         // which begins at 64, 64
         mCapture->checkPixel(64, 64, 50, 50, 50);
@@ -2293,8 +4741,7 @@
 }
 
 TEST_F(ChildLayerTest, ChildLayerRelativeLayer) {
-    sp<SurfaceControl> relative = mComposerClient->createSurface(String8("Relative surface"), 128,
-                                                                 128, PIXEL_FORMAT_RGBA_8888, 0);
+    sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0);
     fillSurfaceRGBA8(relative, 255, 255, 255);
 
     Transaction t;
@@ -2306,12 +4753,212 @@
     // We expect that the child should have been elevated above our
     // INT_MAX layer even though it's not a child of it.
     {
-        ScreenCapture::captureScreen(&mCapture);
+        mCapture = screenshot();
         mCapture->expectChildColor(0, 0);
         mCapture->expectChildColor(9, 9);
         mCapture->checkPixel(10, 10, 255, 255, 255);
     }
 }
+class BoundlessLayerTest : public LayerUpdateTest {
+protected:
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+// Verify setting a size on a buffer layer has no effect.
+TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) {
+    sp<SurfaceControl> bufferLayer =
+            createSurface(mClient, "BufferLayer", 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
+                          mFGSurfaceControl.get());
+    ASSERT_TRUE(bufferLayer->isValid());
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30));
+    asTransaction([&](Transaction& t) { t.show(bufferLayer); });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 94, 94), Color::BLACK);
+        // Buffer layer should not extend past buffer bounds
+        mCapture->expectFGColor(95, 95);
+    }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has a buffer size
+// which will crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) {
+    sp<SurfaceControl> colorLayer =
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has
+// a crop which will be used to crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) {
+    sp<SurfaceControl> cropLayer = createSurface(mClient, "CropLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                                 0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(cropLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get());
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(cropLayer);
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // 5 pixels from the foreground we should see the child surface
+        mCapture->expectColor(Rect(69, 69, 74, 74), Color::BLACK);
+        // 10 pixels from the foreground we should be back to the foreground surface
+        mCapture->expectFGColor(74, 74);
+    }
+}
+
+// Verify for boundless layer with no children, their transforms have no effect.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) {
+    sp<SurfaceControl> colorLayer =
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setPosition(colorLayer, 320, 320);
+        t.setMatrix(colorLayer, 2, 0, 0, 2);
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify for boundless layer with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) {
+    sp<SurfaceControl> boundlessLayerRightShift =
+            createSurface(mClient, "BoundlessLayerRightShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(boundlessLayerRightShift->isValid());
+    sp<SurfaceControl> boundlessLayerDownShift =
+            createSurface(mClient, "BoundlessLayerLeftShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          0 /* flags */, boundlessLayerRightShift.get());
+    ASSERT_TRUE(boundlessLayerDownShift->isValid());
+    sp<SurfaceControl> colorLayer =
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceColor, boundlessLayerDownShift.get());
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setPosition(boundlessLayerRightShift, 32, 0);
+        t.show(boundlessLayerRightShift);
+        t.setPosition(boundlessLayerDownShift, 0, 32);
+        t.show(boundlessLayerDownShift);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(96, 96, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify child layers do not get clipped if they temporarily move into the negative
+// coordinate space as the result of an intermediate transformation.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) {
+    sp<SurfaceControl> boundlessLayer =
+            mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(boundlessLayer != nullptr);
+    ASSERT_TRUE(boundlessLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor, boundlessLayer.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        // shift child layer off bounds. If this layer was not boundless, we will
+        // expect the child layer to be cropped.
+        t.setPosition(boundlessLayer, 32, 32);
+        t.show(boundlessLayer);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        // undo shift by parent
+        t.setPosition(colorLayer, -32, -32);
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify for boundless root layers with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) {
+    sp<SurfaceControl> rootBoundlessLayer = createSurface(mClient, "RootBoundlessLayer", 0, 0,
+                                                          PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
+    ASSERT_TRUE(rootBoundlessLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceColor, rootBoundlessLayer.get());
+
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
+        t.setPosition(rootBoundlessLayer, 32, 32);
+        t.show(rootBoundlessLayer);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+        t.hide(mFGSurfaceControl);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectBGColor(31, 31);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(32, 32, 96, 96), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(97, 97);
+    }
+}
 
 class ScreenCaptureTest : public LayerUpdateTest {
 protected:
@@ -2329,9 +4976,8 @@
 TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
     auto fgHandle = mFGSurfaceControl->getHandle();
 
-    sp<SurfaceControl> child =
-            mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
 
     SurfaceComposerClient::Transaction().show(child).apply(true);
@@ -2345,9 +4991,8 @@
 TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
     auto fgHandle = mFGSurfaceControl->getHandle();
 
-    sp<SurfaceControl> child =
-            mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
 
     SurfaceComposerClient::Transaction().show(child).apply(true);
@@ -2359,9 +5004,8 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureTransparent) {
-    sp<SurfaceControl> child =
-            mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
 
     fillSurfaceRGBA8(child, 200, 200, 200);
 
@@ -2379,11 +5023,10 @@
 TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
     auto fgHandle = mFGSurfaceControl->getHandle();
 
-    sp<SurfaceControl> child =
-            mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
-    sp<SurfaceControl> relative = mComposerClient->createSurface(String8("Relative surface"), 10,
-                                                                 10, PIXEL_FORMAT_RGBA_8888, 0);
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    ASSERT_NE(nullptr, child.get()) << "failed to create surface";
+    sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
     fillSurfaceRGBA8(child, 200, 200, 200);
     fillSurfaceRGBA8(relative, 100, 100, 100);
 
@@ -2403,12 +5046,10 @@
 TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
     auto fgHandle = mFGSurfaceControl->getHandle();
 
-    sp<SurfaceControl> child =
-            mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
-    sp<SurfaceControl> relative =
-            mComposerClient->createSurface(String8("Relative surface"), 10, 10,
-                                           PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
+                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
     fillSurfaceRGBA8(relative, 100, 100, 100);
 
@@ -2437,9 +5078,8 @@
     void SetUp() override {
         LayerUpdateTest::SetUp();
 
-        mChild =
-            mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                    0, mFGSurfaceControl.get());
+        mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+                               mFGSurfaceControl.get());
         fillSurfaceRGBA8(mChild, 200, 200, 200);
 
         SurfaceComposerClient::Transaction().show(mChild).apply(true);
@@ -2465,8 +5105,9 @@
 }
 
 TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
-
-    SurfaceComposerClient::Transaction().setCrop(mFGSurfaceControl, Rect(0, 0, 1, 1)).apply(true);
+    SurfaceComposerClient::Transaction()
+            .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
+            .apply(true);
 
     // Even though the parent is cropped out we should still capture the child.
     verify();
@@ -2494,14 +5135,12 @@
 TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
     auto fgHandle = mFGSurfaceControl->getHandle();
 
-    sp<SurfaceControl> child =
-            mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
 
-    sp<SurfaceControl> grandchild =
-            mComposerClient->createSurface(String8("Grandchild surface"), 5, 5,
-                                           PIXEL_FORMAT_RGBA_8888, 0, child.get());
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
 
     fillSurfaceRGBA8(grandchild, 50, 50, 50);
     SurfaceComposerClient::Transaction()
@@ -2518,9 +5157,8 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureChildOnly) {
-    sp<SurfaceControl> child =
-            mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
     auto childHandle = child->getHandle();
 
@@ -2533,15 +5171,13 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
-    sp<SurfaceControl> child =
-            mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
-                                           0, mFGSurfaceControl.get());
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
     fillSurfaceRGBA8(child, 200, 200, 200);
     auto childHandle = child->getHandle();
 
-    sp<SurfaceControl> grandchild =
-            mComposerClient->createSurface(String8("Grandchild surface"), 5, 5,
-                                           PIXEL_FORMAT_RGBA_8888, 0, child.get());
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
     fillSurfaceRGBA8(grandchild, 50, 50, 50);
 
     SurfaceComposerClient::Transaction()
@@ -2559,14 +5195,12 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureCrop) {
-    sp<SurfaceControl> redLayer = mComposerClient->createSurface(String8("Red surface"), 60, 60,
-                                                                 PIXEL_FORMAT_RGBA_8888, 0);
-    sp<SurfaceControl> blueLayer =
-            mComposerClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888,
-                                           0, redLayer.get());
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
 
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(redLayer, Color::RED));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(blueLayer, Color::BLUE));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
 
     SurfaceComposerClient::Transaction()
             .setLayer(redLayer, INT32_MAX - 1)
@@ -2584,7 +5218,7 @@
     // red area to the right of the blue area
     mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
 
-    Rect crop = Rect(0, 0, 30, 30);
+    const Rect crop = Rect(0, 0, 30, 30);
     ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop);
     // Capturing the cropped screen, cropping out the shown red area, should leave only the blue
     // area visible.
@@ -2593,14 +5227,12 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureSize) {
-    sp<SurfaceControl> redLayer = mComposerClient->createSurface(String8("Red surface"), 60, 60,
-                                                                 PIXEL_FORMAT_RGBA_8888, 0);
-    sp<SurfaceControl> blueLayer =
-            mComposerClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888,
-                                           0, redLayer.get());
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
 
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(redLayer, Color::RED));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(blueLayer, Color::BLUE));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
 
     SurfaceComposerClient::Transaction()
             .setLayer(redLayer, INT32_MAX - 1)
@@ -2629,13 +5261,12 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
-    sp<SurfaceControl> redLayer = mComposerClient->createSurface(String8("Red surface"), 60, 60,
-                                                                 PIXEL_FORMAT_RGBA_8888, 0);
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
 
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(redLayer, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
 
     auto redLayerHandle = redLayer->getHandle();
-    mComposerClient->destroySurface(redLayerHandle);
+    redLayer->clear();
     SurfaceComposerClient::Transaction().apply(true);
 
     sp<GraphicBuffer> outBuffer;
@@ -2651,9 +5282,9 @@
     void SetUp() override {
         LayerTransactionTest::SetUp();
         bgLayer = createLayer("BG layer", 20, 20);
-        fillLayerColor(bgLayer, Color::RED);
+        fillBufferQueueLayerColor(bgLayer, Color::RED, 20, 20);
         fgLayer = createLayer("FG layer", 20, 20);
-        fillLayerColor(fgLayer, Color::BLUE);
+        fillBufferQueueLayerColor(fgLayer, Color::BLUE, 20, 20);
         Transaction().setLayer(fgLayer, mLayerZBase + 1).apply();
         {
             SCOPED_TRACE("before anything");
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 6ad1b87..a2c0611 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -11,6 +11,7 @@
     shared_libs: [
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.mapper@2.0",
+        "android.hardware.graphics.mapper@3.0",
         "android.hardware.power@1.3",
         "libbase",
         "libbinder",
@@ -30,8 +31,9 @@
         "libutils",
     ],
     static_libs: [
+        "libgmock",
+        "librenderengine",
         "libtrace_proto",
-        "libgmock"
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 9b31985..16e0891 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -624,7 +624,7 @@
     {
         TransactionScope ts(*sFakeComposer);
         Rect cropRect(16, 16, 32, 32);
-        ts.setCrop(mFGSurfaceControl, cropRect);
+        ts.setCrop_legacy(mFGSurfaceControl, cropRect);
     }
     ASSERT_EQ(2, sFakeComposer->getFrameCount());
 
@@ -634,40 +634,6 @@
     EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
 }
 
-TEST_F(TransactionTest, LayerFinalCrop) {
-    // TODO: Add scaling to confirm that crop happens in display space?
-    {
-        TransactionScope ts(*sFakeComposer);
-        Rect cropRect(32, 32, 32 + 64, 32 + 64);
-        ts.setFinalCrop(mFGSurfaceControl, cropRect);
-    }
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-    // In display space we are cropping with [32, 32, 96, 96] against display rect
-    // [64, 64, 128, 128]. Should yield display rect [64, 64, 96, 96]
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 32, 64 + 32};
-
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-
-TEST_F(TransactionTest, LayerFinalCropEmpty) {
-    // TODO: Add scaling to confirm that crop happens in display space?
-    {
-        TransactionScope ts(*sFakeComposer);
-        Rect cropRect(16, 16, 32, 32);
-        ts.setFinalCrop(mFGSurfaceControl, cropRect);
-    }
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-    // In display space we are cropping with [16, 16, 32, 32] against display rect
-    // [64, 64, 128, 128]. The intersection is empty and only the background layer is composited.
-    std::vector<RenderState> referenceFrame(1);
-    referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-
 TEST_F(TransactionTest, LayerSetLayer) {
     {
         TransactionScope ts(*sFakeComposer);
@@ -847,18 +813,16 @@
     {
         TransactionScope ts(*sFakeComposer);
         ts.setAlpha(mFGSurfaceControl, 0.75);
-        ts.deferTransactionUntil(mFGSurfaceControl, 
-                syncSurfaceControl->getHandle(),
-                syncSurfaceControl->getSurface()->getNextFrameNumber());
+        ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+                                        syncSurfaceControl->getSurface()->getNextFrameNumber());
     }
     EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
 
     {
         TransactionScope ts(*sFakeComposer);
         ts.setPosition(mFGSurfaceControl, 128, 128);
-        ts.deferTransactionUntil(mFGSurfaceControl,
-                syncSurfaceControl->getHandle(),
-                syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+        ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+                                        syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
     }
     EXPECT_EQ(4, sFakeComposer->getFrameCount());
     EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
@@ -981,7 +945,7 @@
         ts.show(mChild);
         ts.setPosition(mChild, 0, 0);
         ts.setPosition(mFGSurfaceControl, 0, 0);
-        ts.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
+        ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
     }
     // NOTE: The foreground surface would be occluded by the child
     // now, but is included in the stack because the child is
@@ -994,22 +958,6 @@
     EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
 }
 
-TEST_F(ChildLayerTest, FinalCropping) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 0, 0);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-        ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
-    }
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
-    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
-    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-
 TEST_F(ChildLayerTest, Constraints) {
     {
         TransactionScope ts(*sFakeComposer);
@@ -1095,7 +1043,7 @@
     EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
 }
 
-TEST_F(ChildLayerTest, DetachChildren) {
+TEST_F(ChildLayerTest, DetachChildrenSameClient) {
     {
         TransactionScope ts(*sFakeComposer);
         ts.show(mChild);
@@ -1111,14 +1059,59 @@
 
     {
         TransactionScope ts(*sFakeComposer);
+        ts.setPosition(mFGSurfaceControl, 0, 0);
         ts.detachChildren(mFGSurfaceControl);
     }
 
     {
         TransactionScope ts(*sFakeComposer);
+        ts.setPosition(mFGSurfaceControl, 64, 64);
         ts.hide(mChild);
     }
 
+    std::vector<RenderState> refFrame(2);
+    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+    refFrame[FG_LAYER] = mBaseFrame[FG_LAYER];
+
+    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
+    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> childNewClient =
+            newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    ASSERT_TRUE(childNewClient != nullptr);
+    ASSERT_TRUE(childNewClient->isValid());
+    fillSurfaceRGBA8(childNewClient, LIGHT_GRAY);
+
+    {
+        TransactionScope ts(*sFakeComposer);
+        ts.hide(mChild);
+        ts.show(childNewClient);
+        ts.setPosition(childNewClient, 10, 10);
+        ts.setPosition(mFGSurfaceControl, 64, 64);
+    }
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame =
+            hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        TransactionScope ts(*sFakeComposer);
+        ts.detachChildren(mFGSurfaceControl);
+        ts.setPosition(mFGSurfaceControl, 0, 0);
+    }
+
+    {
+        TransactionScope ts(*sFakeComposer);
+        ts.setPosition(mFGSurfaceControl, 64, 64);
+        ts.setPosition(childNewClient, 0, 0);
+        ts.hide(childNewClient);
+    }
+
     // Nothing should have changed. The child control becomes a no-op
     // zombie on detach. See comments for detachChildren in the
     // SurfaceControl.h file.
@@ -1193,8 +1186,8 @@
     // Show the child layer in a deferred transaction
     {
         TransactionScope ts(*sFakeComposer);
-        ts.deferTransactionUntil(mChild, mFGSurfaceControl->getHandle(), 
-                                      mFGSurfaceControl->getSurface()->getNextFrameNumber());
+        ts.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
+                                        mFGSurfaceControl->getSurface()->getNextFrameNumber());
         ts.show(mChild);
     }
 
@@ -1217,6 +1210,82 @@
     sFakeComposer->runVSyncAndWait();
 }
 
+class ChildColorLayerTest : public ChildLayerTest {
+protected:
+    void SetUp() override {
+        TransactionTest::SetUp();
+        mChild = mComposerClient->createSurface(String8("Child surface"), 0, 0,
+                                                PIXEL_FORMAT_RGBA_8888,
+                                                ISurfaceComposerClient::eFXSurfaceColor,
+                                                mFGSurfaceControl.get());
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setColor(mChild,
+                        {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
+            ts.setCrop_legacy(mChild, Rect(0, 0, 10, 10));
+        }
+
+        sFakeComposer->runVSyncAndWait();
+        mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+        mBaseFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f};
+        mBaseFrame[CHILD_LAYER].mSwapCount = 0;
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+        ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+    }
+};
+
+TEST_F(ChildColorLayerTest, LayerAlpha) {
+    {
+        TransactionScope ts(*sFakeComposer);
+        ts.show(mChild);
+        ts.setPosition(mChild, 0, 0);
+        ts.setPosition(mFGSurfaceControl, 0, 0);
+        ts.setAlpha(mChild, 0.5);
+    }
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+    referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        TransactionScope ts(*sFakeComposer);
+        ts.setAlpha(mFGSurfaceControl, 0.5);
+    }
+
+    auto referenceFrame2 = referenceFrame;
+    referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f;
+    referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildColorLayerTest, LayerZeroAlpha) {
+    {
+        TransactionScope ts(*sFakeComposer);
+        ts.show(mChild);
+        ts.setPosition(mChild, 0, 0);
+        ts.setPosition(mFGSurfaceControl, 0, 0);
+        ts.setAlpha(mChild, 0.5);
+    }
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+    referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        TransactionScope ts(*sFakeComposer);
+        ts.setAlpha(mFGSurfaceControl, 0.0f);
+    }
+
+    std::vector<RenderState> refFrame(1);
+    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+
+    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
 class LatchingTest : public TransactionTest {
 protected:
     void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); }
@@ -1235,8 +1304,7 @@
         TransactionScope ts(*sFakeComposer);
         ts.setSize(mFGSurfaceControl, 64, 64);
         ts.setPosition(mFGSurfaceControl, 64, 64);
-        ts.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64));
-        ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1));
+        ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
     }
 };
 
@@ -1280,7 +1348,7 @@
     {
         TransactionScope ts(*sFakeComposer);
         ts.setSize(mFGSurfaceControl, 128, 128);
-        ts.setCrop(mFGSurfaceControl, Rect(0, 0, 63, 63));
+        ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63));
     }
 
     auto referenceFrame1 = mBaseFrame;
@@ -1294,7 +1362,7 @@
         TransactionScope ts(*sFakeComposer);
         ts.setSize(mFGSurfaceControl, 128, 128);
         ts.setGeometryAppliesWithResize(mFGSurfaceControl);
-        ts.setCrop(mFGSurfaceControl, Rect(0, 0, 63, 63));
+        ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63));
     }
     EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
 
@@ -1307,111 +1375,6 @@
     EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
 }
 
-TEST_F(LatchingTest, FinalCropLatching) {
-    // Normally the crop applies immediately even while a resize is pending.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 128);
-        ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
-    }
-
-    auto referenceFrame1 = mBaseFrame;
-    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
-    referenceFrame1[FG_LAYER].mSourceCrop =
-            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
-    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
-
-    restoreInitialState();
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 128);
-        ts.setGeometryAppliesWithResize(mFGSurfaceControl);
-        ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
-    }
-    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-
-    completeFGResize();
-
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
-    referenceFrame2[FG_LAYER].mSourceCrop =
-            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
-    referenceFrame2[FG_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
-}
-
-// In this test we ensure that setGeometryAppliesWithResize actually demands
-// a buffer of the new size, and not just any size.
-TEST_F(LatchingTest, FinalCropLatchingBufferOldSize) {
-    // Normally the crop applies immediately even while a resize is pending.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 128);
-        ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
-    }
-
-    auto referenceFrame1 = mBaseFrame;
-    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
-    referenceFrame1[FG_LAYER].mSourceCrop =
-            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
-    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
-
-    restoreInitialState();
-
-    // In order to prepare to submit a buffer at the wrong size, we acquire it prior to
-    // initiating the resize.
-    lockAndFillFGBuffer();
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 128);
-        ts.setGeometryAppliesWithResize(mFGSurfaceControl);
-        ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
-    }
-    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-
-    // We now submit our old buffer, at the old size, and ensure it doesn't
-    // trigger geometry latching.
-    unlockFGBuffer();
-
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2[FG_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
-
-    completeFGResize();
-    auto referenceFrame3 = referenceFrame2;
-    referenceFrame3[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
-    referenceFrame3[FG_LAYER].mSourceCrop =
-            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
-    referenceFrame3[FG_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(referenceFrame3, sFakeComposer->getLatestFrame()));
-}
-
-TEST_F(LatchingTest, FinalCropLatchingRegressionForb37531386) {
-    // In this scenario, we attempt to set the final crop a second time while the resize
-    // is still pending, and ensure we are successful. Success meaning the second crop
-    // is the one which eventually latches and not the first.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 128);
-        ts.setGeometryAppliesWithResize(mFGSurfaceControl);
-        ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
-    }
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1));
-    }
-    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-
-    completeFGResize();
-
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-
 } // namespace
 
 int main(int argc, char** argv) {
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 9949bfa..b2bcb45 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 The Android Open Source Project
+// Copyright 2018 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,26 +16,55 @@
     name: "libsurfaceflinger_unittest",
     defaults: ["libsurfaceflinger_defaults"],
     test_suites: ["device-tests"],
+    sanitize: {
+        // Using the address sanitizer not only helps uncover issues in the code
+        // covered by the tests, but also covers some of the tricky injection of
+        // fakes the unit tests currently do.
+        //
+        // Note: If you get an runtime link error like:
+        //
+        //   CANNOT LINK EXECUTABLE "/data/local/tmp/libsurfaceflinger_unittest": library "libclang_rt.asan-aarch64-android.so" not found
+        //
+        // it is because the address sanitizer shared objects are not installed
+        // by default in the system image.
+        //
+        // You can either "make dist tests" before flashing, or set this
+        // option to false temporarily.
+        address: true,
+    },
     srcs: [
         ":libsurfaceflinger_sources",
+        "libsurfaceflinger_unittest_main.cpp",
+        "CompositionTest.cpp",
+        "DisplayIdentificationTest.cpp",
         "DisplayTransactionTest.cpp",
         "EventControlThreadTest.cpp",
         "EventThreadTest.cpp",
+        "IdleTimerTest.cpp",
+        "LayerHistoryTest.cpp",
+        "LayerMetadataTest.cpp",
+        "SchedulerTest.cpp",
+        "SchedulerUtilsTest.cpp",
+        "RefreshRateStatsTest.cpp",
+        "TimeStatsTest.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
-        "mock/DisplayHardware/MockDisplaySurface.cpp",
+        "mock/DisplayHardware/MockDisplay.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
         "mock/gui/MockGraphicBufferConsumer.cpp",
         "mock/gui/MockGraphicBufferProducer.cpp",
+        "mock/MockDispSync.cpp",
         "mock/MockEventControlThread.cpp",
         "mock/MockEventThread.cpp",
         "mock/MockMessageQueue.cpp",
         "mock/MockNativeWindowSurface.cpp",
         "mock/MockSurfaceInterceptor.cpp",
-        "mock/RenderEngine/MockRenderEngine.cpp",
         "mock/system/window/MockNativeWindow.cpp",
     ],
     static_libs: [
         "libgmock",
+        "libcompositionengine",
+        "libcompositionengine_mocks",
+        "librenderengine_mocks",
     ],
     header_libs: [
         "libsurfaceflinger_headers",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
new file mode 100644
index 0000000..1c4a661
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -0,0 +1,1278 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "CompositionTest"
+
+#include <compositionengine/mock/DisplaySurface.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/IProducerListener.h>
+#include <log/log.h>
+#include <renderengine/mock/Framebuffer.h>
+#include <renderengine/mock/Image.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <system/window.h>
+#include <utils/String8.h>
+
+#include "BufferQueueLayer.h"
+#include "ColorLayer.h"
+#include "Layer.h"
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockDispSync.h"
+#include "mock/MockEventControlThread.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+namespace {
+
+using testing::_;
+using testing::AtLeast;
+using testing::Between;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Field;
+using testing::Invoke;
+using testing::IsNull;
+using testing::Mock;
+using testing::NotNull;
+using testing::Ref;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+
+using android::Hwc2::Error;
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+using android::Hwc2::Transform;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+constexpr hwc2_display_t HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
+constexpr hwc2_layer_t HWC_LAYER = 5000;
+constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
+
+constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+
+constexpr int DEFAULT_CONFIG_ID = 0;
+constexpr int DEFAULT_TEXTURE_ID = 6000;
+constexpr int DEFAULT_LAYER_STACK = 7000;
+
+constexpr int DEFAULT_DISPLAY_MAX_LUMINANCE = 500;
+
+constexpr int DEFAULT_SIDEBAND_STREAM = 51;
+
+class CompositionTest : public testing::Test {
+public:
+    CompositionTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+        mFlinger.mutableEventControlThread().reset(mEventControlThread);
+        mFlinger.mutableEventThread().reset(mEventThread);
+        mFlinger.mutableEventQueue().reset(mMessageQueue);
+
+        mFlinger.mutablePrimaryDispSync().reset(mPrimaryDispSync);
+        EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*mPrimaryDispSync, getPeriod())
+                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+
+        mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+        setupComposer(0);
+    }
+
+    ~CompositionTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    void setupComposer(int virtualDisplayCount) {
+        mComposer = new Hwc2::mock::Composer();
+        EXPECT_CALL(*mComposer, getCapabilities())
+                .WillOnce(Return(std::vector<IComposer::Capability>()));
+        EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+        mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+        Mock::VerifyAndClear(mComposer);
+    }
+
+    void setupForceGeometryDirty() {
+        // TODO: This requires the visible region and other related
+        // state to be set, and is problematic for BufferLayers since they are
+        // not visible without a buffer (and setting up a buffer looks like a
+        // pain)
+        // mFlinger.mutableVisibleRegionsDirty() = true;
+
+        mFlinger.mutableGeometryInvalid() = true;
+    }
+
+    template <typename Case>
+    void displayRefreshCompositionDirtyGeometry();
+
+    template <typename Case>
+    void displayRefreshCompositionDirtyFrame();
+
+    template <typename Case>
+    void captureScreenComposition();
+
+    std::unordered_set<HWC2::Capability> mDefaultCapabilities = {HWC2::Capability::SidebandStream};
+
+    TestableSurfaceFlinger mFlinger;
+    sp<DisplayDevice> mDisplay;
+    sp<DisplayDevice> mExternalDisplay;
+    sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
+            new compositionengine::mock::DisplaySurface();
+    mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
+
+    sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+    ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer();
+
+    mock::EventThread* mEventThread = new mock::EventThread();
+    mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
+
+    Hwc2::mock::Composer* mComposer = nullptr;
+    renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+    mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+    renderengine::mock::Framebuffer* mReFrameBuffer = new renderengine::mock::Framebuffer();
+
+    sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
+
+    sp<GraphicBuffer> mCaptureScreenBuffer;
+};
+
+template <typename LayerCase>
+void CompositionTest::displayRefreshCompositionDirtyGeometry() {
+    setupForceGeometryDirty();
+    LayerCase::setupForDirtyGeometry(this);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
+    mFlinger.onMessageReceived(MessageQueue::REFRESH);
+
+    LayerCase::cleanup(this);
+}
+
+template <typename LayerCase>
+void CompositionTest::displayRefreshCompositionDirtyFrame() {
+    LayerCase::setupForDirtyFrame(this);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
+    mFlinger.onMessageReceived(MessageQueue::REFRESH);
+
+    LayerCase::cleanup(this);
+}
+
+template <typename LayerCase>
+void CompositionTest::captureScreenComposition() {
+    LayerCase::setupForScreenCapture(this);
+
+    const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
+    constexpr bool useIdentityTransform = true;
+    constexpr bool forSystem = true;
+
+    DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH,
+                                 DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB,
+                                 ui::Transform::ROT_0);
+
+    auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
+        return mFlinger.traverseLayersInDisplay(mDisplay, visitor);
+    };
+
+    // TODO: Eliminate expensive/real allocation if possible.
+    const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+            GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+    mCaptureScreenBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
+                                             HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
+
+    int fd = -1;
+    status_t result =
+            mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(),
+                                             useIdentityTransform, forSystem, &fd);
+    if (fd >= 0) {
+        close(fd);
+    }
+
+    EXPECT_EQ(NO_ERROR, result);
+
+    LayerCase::cleanup(this);
+}
+
+/* ------------------------------------------------------------------------
+ * Variants for each display configuration which can be tested
+ */
+
+template <typename Derived>
+struct BaseDisplayVariant {
+    static constexpr bool IS_SECURE = true;
+    static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_NORMAL;
+
+    static void setupPreconditions(CompositionTest* test) {
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+
+        FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, HWC2::DisplayType::Physical,
+                               true /* isPrimary */)
+                .setCapabilities(&test->mDefaultCapabilities)
+                .inject(&test->mFlinger, test->mComposer);
+
+        test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, DEFAULT_DISPLAY_ID,
+                                                   false /* isVirtual */, true /* isPrimary */)
+                                 .setDisplaySurface(test->mDisplaySurface)
+                                 .setNativeWindow(test->mNativeWindow)
+                                 .setSecure(Derived::IS_SECURE)
+                                 .setPowerMode(Derived::INIT_POWER_MODE)
+                                 .inject();
+        test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK);
+    }
+
+    template <typename Case>
+    static void setupCommonCompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer,
+                    setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
+                .Times(1);
+        EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
+        EXPECT_CALL(*test->mComposer, getDisplayRequests(HWC_DISPLAY, _, _, _)).Times(1);
+        EXPECT_CALL(*test->mComposer, acceptDisplayChanges(HWC_DISPLAY)).Times(1);
+        EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1);
+        EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1);
+
+        EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
+        // TODO: remove once we verify that we can just grab the fence from the
+        // FramebufferSurface.
+        EXPECT_CALL(*test->mRenderEngine, flush()).WillRepeatedly(Invoke([]() {
+            return base::unique_fd();
+        }));
+
+        EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1);
+        EXPECT_CALL(*test->mDisplaySurface, advanceFrame()).Times(1);
+
+        Case::CompositionType::setupHwcSetCallExpectations(test);
+        Case::CompositionType::setupHwcGetCallExpectations(test);
+    }
+
+    template <typename Case>
+    static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mRenderEngine, drawLayers)
+                .WillRepeatedly(
+                        [](const renderengine::DisplaySettings& displaySettings,
+                           const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
+                           ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+                            EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                      displaySettings.physicalDisplay);
+                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                      displaySettings.clip);
+                            return NO_ERROR;
+                        });
+    }
+
+    static void setupNonEmptyFrameCompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mDisplaySurface, beginFrame(true)).Times(1);
+    }
+
+    static void setupEmptyFrameCompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mDisplaySurface, beginFrame(false)).Times(1);
+    }
+
+    static void setupHwcCompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mDisplaySurface,
+                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_HWC))
+                .Times(1);
+    }
+
+    static void setupRECompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mDisplaySurface,
+                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GLES))
+                .Times(1);
+        EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
+                .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
+
+        EXPECT_CALL(*test->mNativeWindow, queueBuffer(_, _)).WillOnce(Return(0));
+        EXPECT_CALL(*test->mNativeWindow, dequeueBuffer(_, _))
+                .WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
+                                Return(0)));
+        EXPECT_CALL(*test->mRenderEngine, drawLayers)
+                .WillRepeatedly(
+                        [](const renderengine::DisplaySettings& displaySettings,
+                           const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
+                           ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+                            EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                      displaySettings.physicalDisplay);
+                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                      displaySettings.clip);
+                            EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
+                            return NO_ERROR;
+                        });
+    }
+
+    template <typename Case>
+    static void setupRELayerCompositionCallExpectations(CompositionTest* test) {
+        Case::Layer::setupRECompositionCallExpectations(test);
+    }
+
+    template <typename Case>
+    static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) {
+        Case::Layer::setupREScreenshotCompositionCallExpectations(test);
+    }
+};
+
+struct DefaultDisplaySetupVariant : public BaseDisplayVariant<DefaultDisplaySetupVariant> {};
+
+struct InsecureDisplaySetupVariant : public BaseDisplayVariant<InsecureDisplaySetupVariant> {
+    static constexpr bool IS_SECURE = false;
+
+    template <typename Case>
+    static void setupRELayerCompositionCallExpectations(CompositionTest* test) {
+        Case::Layer::setupInsecureRECompositionCallExpectations(test);
+    }
+
+    template <typename Case>
+    static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) {
+        Case::Layer::setupInsecureREScreenshotCompositionCallExpectations(test);
+    }
+};
+
+struct PoweredOffDisplaySetupVariant : public BaseDisplayVariant<PoweredOffDisplaySetupVariant> {
+    static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_OFF;
+
+    template <typename Case>
+    static void setupCommonCompositionCallExpectations(CompositionTest* test) {
+        // TODO: This seems like an unnecessary call if display is powered off.
+        EXPECT_CALL(*test->mComposer,
+                    setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
+                .Times(1);
+
+        // TODO: This seems like an unnecessary call if display is powered off.
+        Case::CompositionType::setupHwcSetCallExpectations(test);
+    }
+
+    static void setupHwcCompositionCallExpectations(CompositionTest*) {}
+
+    static void setupRECompositionCallExpectations(CompositionTest* test) {
+        // TODO: This seems like an unnecessary call if display is powered off.
+        EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
+                .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
+    }
+
+    template <typename Case>
+    static void setupRELayerCompositionCallExpectations(CompositionTest*) {}
+};
+
+/* ------------------------------------------------------------------------
+ * Variants for each layer configuration which can be tested
+ */
+
+template <typename LayerProperties>
+struct BaseLayerProperties {
+    static constexpr uint32_t WIDTH = 100;
+    static constexpr uint32_t HEIGHT = 100;
+    static constexpr PixelFormat FORMAT = PIXEL_FORMAT_RGBA_8888;
+    static constexpr uint64_t USAGE =
+            GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_SW_WRITE_NEVER;
+    static constexpr android_dataspace DATASPACE = HAL_DATASPACE_UNKNOWN;
+    static constexpr uint32_t SCALING_MODE = 0;
+    static constexpr uint32_t TRANSFORM = 0;
+    static constexpr uint32_t LAYER_FLAGS = 0;
+    static constexpr float COLOR[] = {1.f, 1.f, 1.f, 1.f};
+    static constexpr IComposerClient::BlendMode BLENDMODE =
+            IComposerClient::BlendMode::PREMULTIPLIED;
+
+    static void enqueueBuffer(CompositionTest*, sp<BufferQueueLayer> layer) {
+        auto producer = layer->getProducer();
+
+        IGraphicBufferProducer::QueueBufferOutput qbo;
+        status_t result = producer->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &qbo);
+        if (result != NO_ERROR) {
+            ALOGE("Failed to connect() (%d)", result);
+            return;
+        }
+
+        int slot;
+        sp<Fence> fence;
+        result = producer->dequeueBuffer(&slot, &fence, LayerProperties::WIDTH,
+                                         LayerProperties::HEIGHT, LayerProperties::FORMAT,
+                                         LayerProperties::USAGE, nullptr, nullptr);
+        if (result != IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+            ALOGE("Failed to dequeueBuffer() (%d)", result);
+            return;
+        }
+
+        sp<GraphicBuffer> buffer;
+        result = producer->requestBuffer(slot, &buffer);
+        if (result != NO_ERROR) {
+            ALOGE("Failed to requestBuffer() (%d)", result);
+            return;
+        }
+
+        IGraphicBufferProducer::QueueBufferInput qbi(systemTime(), false /* isAutoTimestamp */,
+                                                     LayerProperties::DATASPACE,
+                                                     Rect(LayerProperties::WIDTH,
+                                                          LayerProperties::HEIGHT),
+                                                     LayerProperties::SCALING_MODE,
+                                                     LayerProperties::TRANSFORM, Fence::NO_FENCE);
+        result = producer->queueBuffer(slot, qbi, &qbo);
+        if (result != NO_ERROR) {
+            ALOGE("Failed to queueBuffer (%d)", result);
+            return;
+        }
+    }
+
+    static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) {
+        // TODO: Eliminate the complexity of actually creating a buffer
+        EXPECT_CALL(*test->mRenderEngine, getMaxTextureSize()).WillOnce(Return(16384));
+        EXPECT_CALL(*test->mRenderEngine, getMaxViewportDims()).WillOnce(Return(16384));
+        status_t err =
+                layer->setDefaultBufferProperties(LayerProperties::WIDTH, LayerProperties::HEIGHT,
+                                                  LayerProperties::FORMAT);
+        ASSERT_EQ(NO_ERROR, err);
+        Mock::VerifyAndClear(test->mRenderEngine);
+
+        EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
+        enqueueBuffer(test, layer);
+        Mock::VerifyAndClear(test->mMessageQueue);
+
+        EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
+        bool ignoredRecomputeVisibleRegions;
+        layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, Fence::NO_FENCE);
+        Mock::VerifyAndClear(test->mRenderEngine);
+    }
+
+    static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
+        setupLatchedBuffer(test, layer);
+    }
+
+    static void setupBufferLayerPostFrameCallExpectations(CompositionTest* test) {
+        // BufferLayer::onPostComposition(), when there is no present fence
+        EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY, _))
+                .WillOnce(DoAll(SetArgPointee<1>(DEFAULT_CONFIG_ID), Return(Error::NONE)));
+    }
+
+    static void setupHwcSetGeometryCallExpectations(CompositionTest* test) {
+        // TODO: Coverage of other values
+        EXPECT_CALL(*test->mComposer,
+                    setLayerBlendMode(HWC_DISPLAY, HWC_LAYER, LayerProperties::BLENDMODE))
+                .Times(1);
+        // TODO: Coverage of other values for origin
+        EXPECT_CALL(*test->mComposer,
+                    setLayerDisplayFrame(HWC_DISPLAY, HWC_LAYER,
+                                         IComposerClient::Rect({0, 0, LayerProperties::WIDTH,
+                                                                LayerProperties::HEIGHT})))
+                .Times(1);
+        EXPECT_CALL(*test->mComposer,
+                    setLayerPlaneAlpha(HWC_DISPLAY, HWC_LAYER, LayerProperties::COLOR[3]))
+                .Times(1);
+        // TODO: Coverage of other values
+        EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1);
+        // TODO: Coverage of other values
+        EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1);
+
+        // These expectations retire on saturation as the code path these
+        // expectations are for appears to make an extra call to them.
+        // TODO: Investigate this extra call
+        EXPECT_CALL(*test->mComposer, setLayerTransform(HWC_DISPLAY, HWC_LAYER, DEFAULT_TRANSFORM))
+                .Times(AtLeast(1))
+                .RetiresOnSaturation();
+    }
+
+    static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer,
+                    setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+                                       IComposerClient::FRect({0.f, 0.f, LayerProperties::WIDTH,
+                                                               LayerProperties::HEIGHT})))
+                .Times(1);
+    }
+
+    static void setupHwcSetSourceCropColorCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer,
+                    setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+                                       IComposerClient::FRect({0.f, 0.f, 0.f, 0.f})))
+                .Times(1);
+    }
+
+    static void setupHwcSetPerFrameCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer,
+                    setLayerVisibleRegion(HWC_DISPLAY, HWC_LAYER,
+                                          std::vector<IComposerClient::Rect>({IComposerClient::Rect(
+                                                  {0, 0, LayerProperties::WIDTH,
+                                                   LayerProperties::HEIGHT})})))
+                .Times(1);
+    }
+
+    static void setupHwcSetPerFrameColorCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
+
+        // TODO: use COLOR
+        EXPECT_CALL(*test->mComposer,
+                    setLayerColor(HWC_DISPLAY, HWC_LAYER,
+                                  IComposerClient::Color({0xff, 0xff, 0xff, 0xff})))
+                .Times(1);
+
+    }
+
+    static void setupHwcSetPerFrameBufferCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
+        EXPECT_CALL(*test->mComposer, setLayerBuffer(HWC_DISPLAY, HWC_LAYER, _, _, _)).Times(1);
+
+        setupBufferLayerPostFrameCallExpectations(test);
+    }
+
+    static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mRenderEngine, drawLayers)
+                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+                    EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.physicalDisplay);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.clip);
+                    // screen capture adds an additional color layer as an alpha
+                    // prefill, so gtet the back layer.
+                    renderengine::LayerSettings layer = layerSettings.back();
+                    EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull()));
+                    EXPECT_THAT(layer.source.buffer.fence, Not(IsNull()));
+                    EXPECT_EQ(renderengine::Buffer::CachingHint::NO_CACHE,
+                              layer.source.buffer.cacheHint);
+                    EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName);
+                    EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
+                    EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
+                    EXPECT_EQ(false, layer.source.buffer.isOpaque);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+                    return NO_ERROR;
+                });
+    }
+
+    static void setupREBufferCompositionCallExpectations(CompositionTest* test) {
+        LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
+    }
+
+    static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) {
+        setupREBufferCompositionCallExpectations(test);
+    }
+
+    static void setupREBufferScreenshotCompositionCallExpectations(CompositionTest* test) {
+        LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
+    }
+
+    static void setupInsecureREBufferScreenshotCompositionCallExpectations(CompositionTest* test) {
+        LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
+    }
+
+    static void setupREColorCompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mRenderEngine, drawLayers)
+                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+                    EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.physicalDisplay);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.clip);
+                    // screen capture adds an additional color layer as an alpha
+                    // prefill, so get the back layer.
+                    renderengine::LayerSettings layer = layerSettings.back();
+                    EXPECT_THAT(layer.source.buffer.buffer, IsNull());
+                    EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
+                                    LayerProperties::COLOR[2]),
+                              layer.source.solidColor);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+                    return NO_ERROR;
+                });
+    }
+
+    static void setupREColorScreenshotCompositionCallExpectations(CompositionTest* test) {
+        setupREColorCompositionCallExpectations(test);
+    }
+};
+
+struct DefaultLayerProperties : public BaseLayerProperties<DefaultLayerProperties> {};
+
+struct ColorLayerProperties : public BaseLayerProperties<ColorLayerProperties> {};
+
+struct SidebandLayerProperties : public BaseLayerProperties<SidebandLayerProperties> {
+    using Base = BaseLayerProperties<SidebandLayerProperties>;
+    static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE;
+
+    static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
+        sp<NativeHandle> stream =
+                NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
+                                     false);
+        test->mFlinger.setLayerSidebandStream(layer, stream);
+    }
+
+    static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer,
+                    setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+                                       IComposerClient::FRect({0.f, 0.f, -1.f, -1.f})))
+                .Times(1);
+    }
+
+    static void setupHwcSetPerFrameBufferCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer,
+                    setLayerSidebandStream(HWC_DISPLAY, HWC_LAYER,
+                                           reinterpret_cast<native_handle_t*>(
+                                                   DEFAULT_SIDEBAND_STREAM)))
+                .WillOnce(Return(Error::NONE));
+
+        EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
+    }
+
+    static void setupREBufferCompositionCommonCallExpectations(CompositionTest* /*test*/) {}
+};
+
+struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> {
+    using Base = BaseLayerProperties<SecureLayerProperties>;
+
+    static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
+
+    static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mRenderEngine, drawLayers)
+                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             ANativeWindowBuffer*, base::unique_fd*) -> status_t {
+                    EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.physicalDisplay);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.clip);
+                    // screen capture adds an additional color layer as an alpha
+                    // prefill, so get the back layer.
+                    renderengine::LayerSettings layer = layerSettings.back();
+                    EXPECT_THAT(layer.source.buffer.buffer, IsNull());
+                    EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(1.0f, layer.alpha);
+                    return NO_ERROR;
+                });
+    }
+
+    static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) {
+        setupInsecureREBufferCompositionCommonCallExpectations(test);
+        Base::setupBufferLayerPostFrameCallExpectations(test);
+    }
+
+    static void setupInsecureREBufferScreenshotCompositionCallExpectations(CompositionTest* test) {
+        setupInsecureREBufferCompositionCommonCallExpectations(test);
+    }
+};
+
+struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> {
+    using Base = BaseLayerProperties<CursorLayerProperties>;
+
+    static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
+        Base::setupLayerState(test, layer);
+        test->mFlinger.setLayerPotentialCursor(layer, true);
+    }
+};
+
+struct NoLayerVariant {
+    using FlingerLayerType = sp<BufferQueueLayer>;
+
+    static FlingerLayerType createLayer(CompositionTest*) { return FlingerLayerType(); }
+    static void injectLayer(CompositionTest*, FlingerLayerType) {}
+    static void cleanupInjectedLayers(CompositionTest*) {}
+
+    static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {}
+    static void setupCallExpectationsForDirtyFrame(CompositionTest*) {}
+};
+
+template <typename LayerProperties>
+struct BaseLayerVariant {
+    template <typename L, typename F>
+    static sp<L> createLayerWithFactory(CompositionTest* test, F factory) {
+        EXPECT_CALL(*test->mMessageQueue, postMessage(_, 0)).Times(0);
+
+        sp<L> layer = factory();
+
+        Mock::VerifyAndClear(test->mComposer);
+        Mock::VerifyAndClear(test->mRenderEngine);
+        Mock::VerifyAndClear(test->mMessageQueue);
+
+        auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
+        layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
+        layerDrawingState.active.w = 100;
+        layerDrawingState.active.h = 100;
+        layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
+                                        LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
+
+        layer->setVisibleRegion(Region(Rect(0, 0, 100, 100)));
+
+        return layer;
+    }
+
+    static void injectLayer(CompositionTest* test, sp<Layer> layer) {
+        EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _))
+                .WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE)));
+
+        const auto displayId = test->mDisplay->getId();
+        ASSERT_TRUE(displayId);
+        layer->createHwcLayer(&test->mFlinger.getHwComposer(), *displayId);
+
+        Mock::VerifyAndClear(test->mComposer);
+
+        Vector<sp<Layer>> layers;
+        layers.add(layer);
+        test->mDisplay->setVisibleLayersSortedByZ(layers);
+        test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
+    }
+
+    static void cleanupInjectedLayers(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, destroyLayer(HWC_DISPLAY, HWC_LAYER))
+                .WillOnce(Return(Error::NONE));
+        const auto displayId = test->mDisplay->getId();
+        ASSERT_TRUE(displayId);
+        for (auto layer : test->mFlinger.mutableDrawingState().layersSortedByZ) {
+            layer->destroyHwcLayer(*displayId);
+        }
+        test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
+    }
+};
+
+template <typename LayerProperties>
+struct ColorLayerVariant : public BaseLayerVariant<LayerProperties> {
+    using Base = BaseLayerVariant<LayerProperties>;
+    using FlingerLayerType = sp<ColorLayer>;
+
+    static FlingerLayerType createLayer(CompositionTest* test) {
+        FlingerLayerType layer = Base::template createLayerWithFactory<ColorLayer>(test, [test]() {
+            return new ColorLayer(LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(),
+                                                    String8("test-layer"), LayerProperties::WIDTH,
+                                                    LayerProperties::HEIGHT,
+                                                    LayerProperties::LAYER_FLAGS));
+        });
+
+        auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
+        layerDrawingState.crop_legacy = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+        return layer;
+    }
+
+    static void setupRECompositionCallExpectations(CompositionTest* test) {
+        LayerProperties::setupREColorCompositionCallExpectations(test);
+    }
+
+    static void setupREScreenshotCompositionCallExpectations(CompositionTest* test) {
+        LayerProperties::setupREColorScreenshotCompositionCallExpectations(test);
+    }
+
+    static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) {
+        LayerProperties::setupHwcSetGeometryCallExpectations(test);
+        LayerProperties::setupHwcSetSourceCropColorCallExpectations(test);
+    }
+
+    static void setupCallExpectationsForDirtyFrame(CompositionTest* test) {
+        LayerProperties::setupHwcSetPerFrameCallExpectations(test);
+        LayerProperties::setupHwcSetPerFrameColorCallExpectations(test);
+    }
+};
+
+template <typename LayerProperties>
+struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> {
+    using Base = BaseLayerVariant<LayerProperties>;
+    using FlingerLayerType = sp<BufferQueueLayer>;
+
+    static FlingerLayerType createLayer(CompositionTest* test) {
+        test->mFlinger.mutableTexturePool().push_back(DEFAULT_TEXTURE_ID);
+
+        FlingerLayerType layer =
+                Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() {
+                    return new BufferQueueLayer(
+                            LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(),
+                                              String8("test-layer"), LayerProperties::WIDTH,
+                                              LayerProperties::HEIGHT,
+                                              LayerProperties::LAYER_FLAGS));
+                });
+
+        LayerProperties::setupLayerState(test, layer);
+
+        return layer;
+    }
+
+    static void cleanupInjectedLayers(CompositionTest* test) {
+        EXPECT_CALL(*test->mMessageQueue, postMessage(_, 0)).Times(2);
+        Base::cleanupInjectedLayers(test);
+    }
+
+    static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) {
+        LayerProperties::setupHwcSetGeometryCallExpectations(test);
+        LayerProperties::setupHwcSetSourceCropBufferCallExpectations(test);
+    }
+
+    static void setupCallExpectationsForDirtyFrame(CompositionTest* test) {
+        LayerProperties::setupHwcSetPerFrameCallExpectations(test);
+        LayerProperties::setupHwcSetPerFrameBufferCallExpectations(test);
+    }
+
+    static void setupRECompositionCallExpectations(CompositionTest* test) {
+        LayerProperties::setupREBufferCompositionCallExpectations(test);
+    }
+
+    static void setupInsecureRECompositionCallExpectations(CompositionTest* test) {
+        LayerProperties::setupInsecureREBufferCompositionCallExpectations(test);
+    }
+
+    static void setupREScreenshotCompositionCallExpectations(CompositionTest* test) {
+        LayerProperties::setupREBufferScreenshotCompositionCallExpectations(test);
+    }
+
+    static void setupInsecureREScreenshotCompositionCallExpectations(CompositionTest* test) {
+        LayerProperties::setupInsecureREBufferScreenshotCompositionCallExpectations(test);
+    }
+};
+
+/* ------------------------------------------------------------------------
+ * Variants to control how the composition type is changed
+ */
+
+struct NoCompositionTypeVariant {
+    static void setupHwcSetCallExpectations(CompositionTest*) {}
+
+    static void setupHwcGetCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _)).Times(1);
+    }
+};
+
+template <IComposerClient::Composition CompositionType>
+struct KeepCompositionTypeVariant {
+    static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(CompositionType);
+
+    static void setupHwcSetCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer,
+                    setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, CompositionType))
+                .Times(1);
+    }
+
+    static void setupHwcGetCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _)).Times(1);
+    }
+};
+
+template <IComposerClient::Composition InitialCompositionType,
+          IComposerClient::Composition FinalCompositionType>
+struct ChangeCompositionTypeVariant {
+    static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(FinalCompositionType);
+
+    static void setupHwcSetCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer,
+                    setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, InitialCompositionType))
+                .Times(1);
+    }
+
+    static void setupHwcGetCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::Layer>{
+                                        static_cast<Hwc2::Layer>(HWC_LAYER)}),
+                                SetArgPointee<2>(std::vector<IComposerClient::Composition>{
+                                        FinalCompositionType}),
+                                Return(Error::NONE)));
+    }
+};
+
+/* ------------------------------------------------------------------------
+ * Variants to select how the composition is expected to be handled
+ */
+
+struct CompositionResultBaseVariant {
+    static void setupLayerState(CompositionTest*, sp<Layer>) {}
+
+    template <typename Case>
+    static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) {
+        Case::Layer::setupCallExpectationsForDirtyGeometry(test);
+    }
+
+    template <typename Case>
+    static void setupCallExpectationsForDirtyFrame(CompositionTest* test) {
+        Case::Layer::setupCallExpectationsForDirtyFrame(test);
+    }
+};
+
+struct NoCompositionResultVariant : public CompositionResultBaseVariant {
+    template <typename Case>
+    static void setupCallExpectations(CompositionTest* test) {
+        Case::Display::setupEmptyFrameCompositionCallExpectations(test);
+        Case::Display::setupHwcCompositionCallExpectations(test);
+    }
+};
+
+struct HwcCompositionResultVariant : public CompositionResultBaseVariant {
+    template <typename Case>
+    static void setupCallExpectations(CompositionTest* test) {
+        Case::Display::setupNonEmptyFrameCompositionCallExpectations(test);
+        Case::Display::setupHwcCompositionCallExpectations(test);
+    }
+};
+
+struct RECompositionResultVariant : public CompositionResultBaseVariant {
+    template <typename Case>
+    static void setupCallExpectations(CompositionTest* test) {
+        Case::Display::setupNonEmptyFrameCompositionCallExpectations(test);
+        Case::Display::setupRECompositionCallExpectations(test);
+        Case::Display::template setupRELayerCompositionCallExpectations<Case>(test);
+    }
+};
+
+struct ForcedClientCompositionResultVariant : public RECompositionResultVariant {
+    static void setupLayerState(CompositionTest*, sp<Layer> layer) {
+        layer->forceClientComposition(DEFAULT_DISPLAY_ID);
+    }
+
+    template <typename Case>
+    static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {}
+
+    template <typename Case>
+    static void setupCallExpectationsForDirtyFrame(CompositionTest*) {}
+};
+
+struct EmptyScreenshotResultVariant {
+    static void setupLayerState(CompositionTest*, sp<Layer>) {}
+
+    template <typename Case>
+    static void setupCallExpectations(CompositionTest*) {}
+};
+
+struct REScreenshotResultVariant : public EmptyScreenshotResultVariant {
+    using Base = EmptyScreenshotResultVariant;
+
+    template <typename Case>
+    static void setupCallExpectations(CompositionTest* test) {
+        Base::template setupCallExpectations<Case>(test);
+        Case::Display::template setupRELayerScreenshotCompositionCallExpectations<Case>(test);
+    }
+};
+
+/* ------------------------------------------------------------------------
+ * Composition test case, containing all the variants being tested
+ */
+
+template <typename DisplayCase, typename LayerCase, typename CompositionTypeCase,
+          typename CompositionResultCase>
+struct CompositionCase {
+    using ThisCase =
+            CompositionCase<DisplayCase, LayerCase, CompositionTypeCase, CompositionResultCase>;
+    using Display = DisplayCase;
+    using Layer = LayerCase;
+    using CompositionType = CompositionTypeCase;
+    using CompositionResult = CompositionResultCase;
+
+    static void setupCommon(CompositionTest* test) {
+        Display::setupPreconditions(test);
+
+        auto layer = Layer::createLayer(test);
+        Layer::injectLayer(test, layer);
+        CompositionResult::setupLayerState(test, layer);
+    }
+
+    static void setupForDirtyGeometry(CompositionTest* test) {
+        setupCommon(test);
+
+        Display::template setupCommonCompositionCallExpectations<ThisCase>(test);
+        CompositionResult::template setupCallExpectationsForDirtyGeometry<ThisCase>(test);
+        CompositionResult::template setupCallExpectationsForDirtyFrame<ThisCase>(test);
+        CompositionResult::template setupCallExpectations<ThisCase>(test);
+    }
+
+    static void setupForDirtyFrame(CompositionTest* test) {
+        setupCommon(test);
+
+        Display::template setupCommonCompositionCallExpectations<ThisCase>(test);
+        CompositionResult::template setupCallExpectationsForDirtyFrame<ThisCase>(test);
+        CompositionResult::template setupCallExpectations<ThisCase>(test);
+    }
+
+    static void setupForScreenCapture(CompositionTest* test) {
+        setupCommon(test);
+
+        Display::template setupCommonScreensCaptureCallExpectations<ThisCase>(test);
+        CompositionResult::template setupCallExpectations<ThisCase>(test);
+    }
+
+    static void cleanup(CompositionTest* test) {
+        Layer::cleanupInjectedLayers(test);
+
+        for (auto& hwcDisplay : test->mFlinger.mFakeHwcDisplays) {
+            hwcDisplay->mutableLayers().clear();
+        }
+
+        test->mDisplay->setVisibleLayersSortedByZ(Vector<sp<android::Layer>>());
+    }
+};
+
+/* ------------------------------------------------------------------------
+ * Composition cases to test
+ */
+
+TEST_F(CompositionTest, noLayersDoesMinimalWorkWithDirtyGeometry) {
+    displayRefreshCompositionDirtyGeometry<
+            CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant,
+                            NoCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, noLayersDoesMinimalWorkWithDirtyFrame) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant,
+                            NoCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, noLayersDoesMinimalWorkToCaptureScreen) {
+    captureScreenComposition<
+            CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant,
+                            EmptyScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ *  Simple buffer layers
+ */
+
+TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyGeometry) {
+    displayRefreshCompositionDirtyGeometry<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+                            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyFrame) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+                            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, REComposedNormalBufferLayer) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+                            ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
+                                                         IComposerClient::Composition::CLIENT>,
+                            RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenNormalBufferLayer) {
+    captureScreenComposition<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+                            NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ *  Single-color layers
+ */
+
+TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyGeometry) {
+    displayRefreshCompositionDirtyGeometry<
+            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
+                            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyFrame) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
+                            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, REComposedColorLayer) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+                            ChangeCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR,
+                                                         IComposerClient::Composition::CLIENT>,
+                            RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenColorLayer) {
+    captureScreenComposition<
+            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+                            NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ *  Layers with sideband buffers
+ */
+
+TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyGeometry) {
+    displayRefreshCompositionDirtyGeometry<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>,
+                            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyFrame) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>,
+                            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, REComposedSidebandBufferLayer) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+                            ChangeCompositionTypeVariant<IComposerClient::Composition::SIDEBAND,
+                                                         IComposerClient::Composition::CLIENT>,
+                            RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenSidebandBufferLayer) {
+    captureScreenComposition<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+                            NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ *  Layers with ISurfaceComposerClient::eSecure, on a secure display
+ */
+
+TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyGeometry) {
+    displayRefreshCompositionDirtyGeometry<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+                            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyFrame) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+                            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, REComposedSecureBufferLayer) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+                            ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
+                                                         IComposerClient::Composition::CLIENT>,
+                            RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenSecureBufferLayerOnSecureDisplay) {
+    captureScreenComposition<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+                            NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ *  Layers with ISurfaceComposerClient::eSecure, on a non-secure display
+ */
+
+TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyGeometry) {
+    displayRefreshCompositionDirtyGeometry<
+            CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+                            ForcedClientCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyFrame) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+                            ForcedClientCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenSecureBufferLayerOnInsecureDisplay) {
+    captureScreenComposition<
+            CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+                            NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ *  Cursor layers
+ */
+
+TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyGeometry) {
+    displayRefreshCompositionDirtyGeometry<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>,
+                            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyFrame) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>,
+                            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, REComposedCursorLayer) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+                            ChangeCompositionTypeVariant<IComposerClient::Composition::CURSOR,
+                                                         IComposerClient::Composition::CLIENT>,
+                            RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenCursorLayer) {
+    captureScreenComposition<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+                            NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ *  Simple buffer layer on a display which is powered off.
+ */
+
+TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyGeometry) {
+    displayRefreshCompositionDirtyGeometry<CompositionCase<
+            PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyFrame) {
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+            KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+            HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, displayOffREComposedNormalBufferLayer) {
+    displayRefreshCompositionDirtyFrame<CompositionCase<
+            PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+            ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
+                                         IComposerClient::Composition::CLIENT>,
+            RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenNormalBufferLayerOnPoweredOffDisplay) {
+    captureScreenComposition<CompositionCase<
+            PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+            NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
new file mode 100644
index 0000000..55995d0
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android {
+namespace {
+
+const unsigned char kInternalEdid[] =
+        "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00"
+        "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27"
+        "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+        "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30"
+        "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53"
+        "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe"
+        "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45";
+
+const unsigned char kExternalEdid[] =
+        "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x01\x01\x01\x01"
+        "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25"
+        "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+        "\x01\x01\x01\x01\x01\x01\xe2\x68\x00\xa0\xa0\x40\x2e\x60\x30\x20"
+        "\x36\x00\x81\x90\x21\x00\x00\x1a\xbc\x1b\x00\xa0\x50\x20\x17\x30"
+        "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48"
+        "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\xff"
+        "\x00\x43\x4e\x34\x32\x30\x32\x31\x33\x37\x51\x0a\x20\x20\x00\x71";
+
+// Extended EDID with timing extension.
+const unsigned char kExternalEedid[] =
+        "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00"
+        "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26"
+        "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
+        "\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
+        "\x45\x00\xa0\x5a\x00\x00\x00\x1e\x66\x21\x56\xaa\x51\x00\x1e\x30"
+        "\x46\x8f\x33\x00\xa0\x5a\x00\x00\x00\x1e\x00\x00\x00\xfd\x00\x18"
+        "\x4b\x0f\x51\x17\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc"
+        "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x1d"
+        "\x02\x03\x1f\xf1\x47\x90\x04\x05\x03\x20\x22\x07\x23\x09\x07\x07"
+        "\x83\x01\x00\x00\xe2\x00\x0f\x67\x03\x0c\x00\x20\x00\xb8\x2d\x01"
+        "\x1d\x80\x18\x71\x1c\x16\x20\x58\x2c\x25\x00\xa0\x5a\x00\x00\x00"
+        "\x9e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\xa0\x5a\x00"
+        "\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\xa0"
+        "\x5a\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6";
+
+template <size_t N>
+DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&bytes)[N]) {
+    return DisplayIdentificationData(bytes, bytes + N - 1);
+}
+
+} // namespace
+
+const DisplayIdentificationData& getInternalEdid() {
+    static const DisplayIdentificationData data = asDisplayIdentificationData(kInternalEdid);
+    return data;
+}
+
+const DisplayIdentificationData& getExternalEdid() {
+    static const DisplayIdentificationData data = asDisplayIdentificationData(kExternalEdid);
+    return data;
+}
+
+const DisplayIdentificationData& getExternalEedid() {
+    static const DisplayIdentificationData data = asDisplayIdentificationData(kExternalEedid);
+    return data;
+}
+
+TEST(DisplayIdentificationTest, isEdid) {
+    EXPECT_FALSE(isEdid({}));
+
+    EXPECT_TRUE(isEdid(getInternalEdid()));
+    EXPECT_TRUE(isEdid(getExternalEdid()));
+    EXPECT_TRUE(isEdid(getExternalEedid()));
+}
+
+TEST(DisplayIdentificationTest, parseEdid) {
+    auto edid = parseEdid(getInternalEdid());
+    ASSERT_TRUE(edid);
+    EXPECT_EQ(0x4ca3u, edid->manufacturerId);
+    EXPECT_STREQ("SEC", edid->pnpId.data());
+    // ASCII text should be used as fallback if display name and serial number are missing.
+    EXPECT_EQ("121AT11-801", edid->displayName);
+
+    edid = parseEdid(getExternalEdid());
+    ASSERT_TRUE(edid);
+    EXPECT_EQ(0x22f0u, edid->manufacturerId);
+    EXPECT_STREQ("HWP", edid->pnpId.data());
+    EXPECT_EQ("HP ZR30w", edid->displayName);
+
+    edid = parseEdid(getExternalEedid());
+    ASSERT_TRUE(edid);
+    EXPECT_EQ(0x4c2du, edid->manufacturerId);
+    EXPECT_STREQ("SAM", edid->pnpId.data());
+    EXPECT_EQ("SAMSUNG", edid->displayName);
+}
+
+TEST(DisplayIdentificationTest, parseInvalidEdid) {
+    EXPECT_FALSE(isEdid({}));
+    EXPECT_FALSE(parseEdid({}));
+
+    // Display name must be printable.
+    auto data = getExternalEdid();
+    data[97] = '\x1b';
+    auto edid = parseEdid(data);
+    ASSERT_TRUE(edid);
+    // Serial number should be used as fallback if display name is invalid.
+    EXPECT_EQ("CN4202137Q", edid->displayName);
+
+    // Parsing should succeed even if EDID is truncated.
+    data.pop_back();
+    edid = parseEdid(data);
+    ASSERT_TRUE(edid);
+    EXPECT_EQ("CN4202137Q", edid->displayName);
+}
+
+TEST(DisplayIdentificationTest, getPnpId) {
+    EXPECT_FALSE(getPnpId(0));
+    EXPECT_FALSE(getPnpId(static_cast<uint16_t>(-1)));
+
+    EXPECT_STREQ("SEC", getPnpId(0x4ca3u).value_or(PnpId{}).data());
+    EXPECT_STREQ("HWP", getPnpId(0x22f0u).value_or(PnpId{}).data());
+    EXPECT_STREQ("SAM", getPnpId(0x4c2du).value_or(PnpId{}).data());
+}
+
+TEST(DisplayIdentificationTest, parseDisplayIdentificationData) {
+    const auto primaryInfo = parseDisplayIdentificationData(0, getInternalEdid());
+    ASSERT_TRUE(primaryInfo);
+
+    const auto secondaryInfo = parseDisplayIdentificationData(1, getExternalEdid());
+    ASSERT_TRUE(secondaryInfo);
+
+    const auto tertiaryInfo = parseDisplayIdentificationData(2, getExternalEedid());
+    ASSERT_TRUE(tertiaryInfo);
+
+    // Display IDs should be unique.
+    EXPECT_NE(primaryInfo->id, secondaryInfo->id);
+    EXPECT_NE(primaryInfo->id, tertiaryInfo->id);
+    EXPECT_NE(secondaryInfo->id, tertiaryInfo->id);
+}
+
+TEST(DisplayIdentificationTest, getFallbackDisplayId) {
+    // Manufacturer ID should be invalid.
+    ASSERT_FALSE(getPnpId(getFallbackDisplayId(0)));
+    ASSERT_FALSE(getPnpId(getFallbackDisplayId(0xffu)));
+}
+
+TEST(DisplayIdentificationTest, getVirtualDisplayId) {
+    // Manufacturer ID should be invalid.
+    ASSERT_FALSE(getPnpId(getVirtualDisplayId(0)));
+    ASSERT_FALSE(getPnpId(getVirtualDisplayId(0xffff'ffffu)));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h
index e6ac6bf..1c8e5cc 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+
+#include "DisplayHardware/DisplayIdentification.h"
 
 namespace android {
-namespace mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+const DisplayIdentificationData& getInternalEdid();
+const DisplayIdentificationData& getExternalEdid();
+const DisplayIdentificationData& getExternalEedid();
 
-} // namespace mock
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 9b308bf..4cb79ab 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -17,20 +17,26 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <type_traits>
+
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
 #include <log/log.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/DebugUtils.h>
 
+#include "DisplayIdentificationTest.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "mock/MockDispSync.h"
 #include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
 #include "mock/MockNativeWindowSurface.h"
 #include "mock/MockSurfaceInterceptor.h"
-#include "mock/RenderEngine/MockRenderEngine.h"
 #include "mock/gui/MockGraphicBufferConsumer.h"
 #include "mock/gui/MockGraphicBufferProducer.h"
 #include "mock/system/window/MockNativeWindow.h"
@@ -73,9 +79,11 @@
 
 #define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
 
-BOOL_SUBSTITUTE(Critical);
 BOOL_SUBSTITUTE(Async);
+BOOL_SUBSTITUTE(Critical);
+BOOL_SUBSTITUTE(Primary);
 BOOL_SUBSTITUTE(Secure);
+BOOL_SUBSTITUTE(Virtual);
 
 /* ------------------------------------------------------------------------
  *
@@ -96,7 +104,7 @@
     // --------------------------------------------------------------------
     // Postcondition helpers
 
-    bool hasHwcDisplay(hwc2_display_t displayId);
+    bool hasPhysicalHwcDisplay(hwc2_display_t hwcDisplayId);
     bool hasTransactionFlagSet(int flag);
     bool hasDisplayDevice(sp<IBinder> displayToken);
     sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
@@ -110,22 +118,24 @@
 
     TestableSurfaceFlinger mFlinger;
     mock::EventThread* mEventThread = new mock::EventThread();
+    mock::EventThread* mSFEventThread = new mock::EventThread();
     mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
+    sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
+    sp<GraphicBuffer> mBuffer = new GraphicBuffer();
 
     // These mocks are created by the test, but are destroyed by SurfaceFlinger
     // by virtue of being stored into a std::unique_ptr. However we still need
     // to keep a reference to them for use in setting up call expectations.
-    RE::mock::RenderEngine* mRenderEngine = new RE::mock::RenderEngine();
+    renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
     Hwc2::mock::Composer* mComposer = nullptr;
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
     mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor();
+    mock::DispSync* mPrimaryDispSync = new mock::DispSync();
 
     // These mocks are created only when expected to be created via a factory.
     sp<mock::GraphicBufferConsumer> mConsumer;
     sp<mock::GraphicBufferProducer> mProducer;
-    mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
-    sp<mock::NativeWindow> mNativeWindow;
-    RE::mock::Surface* mRenderSurface = nullptr;
+    surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
 };
 
 DisplayTransactionTest::DisplayTransactionTest() {
@@ -135,6 +145,7 @@
 
     // Default to no wide color display support configured
     mFlinger.mutableHasWideColorDisplay() = false;
+    mFlinger.mutableUseColorManagement() = false;
     mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
 
     // Default to using HWC virtual displays
@@ -151,9 +162,11 @@
 
     mFlinger.mutableEventControlThread().reset(mEventControlThread);
     mFlinger.mutableEventThread().reset(mEventThread);
+    mFlinger.mutableSFEventThread().reset(mSFEventThread);
     mFlinger.mutableEventQueue().reset(mMessageQueue);
-    mFlinger.setupRenderEngine(std::unique_ptr<RE::RenderEngine>(mRenderEngine));
+    mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
     mFlinger.mutableInterceptor().reset(mSurfaceInterceptor);
+    mFlinger.mutablePrimaryDispSync().reset(mPrimaryDispSync);
 
     injectMockComposer(0);
 }
@@ -191,15 +204,15 @@
     // This setup is only expected once per test.
     ASSERT_TRUE(mNativeWindowSurface == nullptr);
 
-    mNativeWindowSurface = new mock::NativeWindowSurface();
-    mNativeWindow = new mock::NativeWindow();
+    mNativeWindowSurface = new surfaceflinger::mock::NativeWindowSurface();
 
-    mFlinger.setCreateNativeWindowSurface(
-            [this](auto) { return std::unique_ptr<NativeWindowSurface>(mNativeWindowSurface); });
+    mFlinger.setCreateNativeWindowSurface([this](auto) {
+        return std::unique_ptr<surfaceflinger::NativeWindowSurface>(mNativeWindowSurface);
+    });
 }
 
-bool DisplayTransactionTest::hasHwcDisplay(hwc2_display_t displayId) {
-    return mFlinger.mutableHwcDisplaySlots().count(displayId) == 1;
+bool DisplayTransactionTest::hasPhysicalHwcDisplay(hwc2_display_t hwcDisplayId) {
+    return mFlinger.mutableHwcPhysicalDisplayIdMap().count(hwcDisplayId) == 1;
 }
 
 bool DisplayTransactionTest::hasTransactionFlagSet(int flag) {
@@ -207,11 +220,11 @@
 }
 
 bool DisplayTransactionTest::hasDisplayDevice(sp<IBinder> displayToken) {
-    return mFlinger.mutableDisplays().indexOfKey(displayToken) >= 0;
+    return mFlinger.mutableDisplays().count(displayToken) == 1;
 }
 
 sp<DisplayDevice> DisplayTransactionTest::getDisplayDevice(sp<IBinder> displayToken) {
-    return mFlinger.mutableDisplays().valueFor(displayToken);
+    return mFlinger.mutableDisplays()[displayToken];
 }
 
 bool DisplayTransactionTest::hasCurrentDisplayState(sp<IBinder> displayToken) {
@@ -234,18 +247,67 @@
  *
  */
 
-template <DisplayDevice::DisplayType type, DisplayDevice::DisplayType hwcId, int width, int height,
-          Critical critical, Async async, Secure secure, int grallocUsage>
+template <typename PhysicalDisplay>
+struct PhysicalDisplayId {};
+
+template <DisplayId::Type displayId>
+using VirtualDisplayId = std::integral_constant<DisplayId::Type, displayId>;
+
+struct NoDisplayId {};
+
+template <typename>
+struct IsPhysicalDisplayId : std::bool_constant<false> {};
+
+template <typename PhysicalDisplay>
+struct IsPhysicalDisplayId<PhysicalDisplayId<PhysicalDisplay>> : std::bool_constant<true> {};
+
+template <typename>
+struct DisplayIdGetter;
+
+template <typename PhysicalDisplay>
+struct DisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
+    static std::optional<DisplayId> get() {
+        if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+            return getFallbackDisplayId(static_cast<bool>(PhysicalDisplay::PRIMARY)
+                                                ? HWC_DISPLAY_PRIMARY
+                                                : HWC_DISPLAY_EXTERNAL);
+        }
+
+        const auto info =
+                parseDisplayIdentificationData(PhysicalDisplay::PORT,
+                                               PhysicalDisplay::GET_IDENTIFICATION_DATA());
+        return info ? std::make_optional(info->id) : std::nullopt;
+    }
+};
+
+template <DisplayId::Type displayId>
+struct DisplayIdGetter<VirtualDisplayId<displayId>> {
+    static std::optional<DisplayId> get() { return DisplayId{displayId}; }
+};
+
+template <>
+struct DisplayIdGetter<NoDisplayId> {
+    static std::optional<DisplayId> get() { return {}; }
+};
+
+// DisplayIdType can be:
+//     1) PhysicalDisplayId<...> for generated ID of physical display backed by HWC.
+//     2) VirtualDisplayId<...> for hard-coded ID of virtual display backed by HWC.
+//     3) NoDisplayId for virtual display without HWC backing.
+template <typename DisplayIdType, int width, int height, Critical critical, Async async,
+          Secure secure, Primary primary, int grallocUsage>
 struct DisplayVariant {
+    using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
+
     // The display width and height
     static constexpr int WIDTH = width;
     static constexpr int HEIGHT = height;
 
     static constexpr int GRALLOC_USAGE = grallocUsage;
 
-    // The type for this display
-    static constexpr DisplayDevice::DisplayType TYPE = type;
-    static constexpr DisplayDevice::DisplayType HWCOMPOSER_ID = hwcId;
+    // Whether the display is virtual or physical
+    static constexpr Virtual VIRTUAL =
+            IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
 
     // When creating native window surfaces for the framebuffer, whether those should be critical
     static constexpr Critical CRITICAL = critical;
@@ -256,9 +318,28 @@
     // Whether the display should be treated as secure
     static constexpr Secure SECURE = secure;
 
+    // Whether the display is primary
+    static constexpr Primary PRIMARY = primary;
+
+    static constexpr auto displayType() {
+        return static_cast<bool>(PRIMARY) ? EventThread::DisplayType::Primary
+                                          : EventThread::DisplayType::External;
+    }
+
     static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
-        auto injector = FakeDisplayDeviceInjector(test->mFlinger, TYPE, HWCOMPOSER_ID);
+        auto injector =
+                FakeDisplayDeviceInjector(test->mFlinger, DISPLAY_ID::get(),
+                                          static_cast<bool>(VIRTUAL), static_cast<bool>(PRIMARY));
+
         injector.setSecure(static_cast<bool>(SECURE));
+        injector.setNativeWindow(test->mNativeWindow);
+
+        // Creating a DisplayDevice requires getting default dimensions from the
+        // native window.
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
         return injector;
     }
 
@@ -266,19 +347,11 @@
     static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
                 .WillOnce(Return(test->mNativeWindow));
-        EXPECT_CALL(*test->mNativeWindow, perform(19)).WillRepeatedly(Return(NO_ERROR));
 
-        // For simplicity, we only expect to create a single render surface for
-        // each test.
-        ASSERT_TRUE(test->mRenderSurface == nullptr);
-        test->mRenderSurface = new RE::mock::Surface();
-        EXPECT_CALL(*test->mRenderEngine, createSurface())
-                .WillOnce(Return(ByMove(std::unique_ptr<RE::Surface>(test->mRenderSurface))));
-        EXPECT_CALL(*test->mRenderSurface, setAsync(static_cast<bool>(ASYNC))).Times(1);
-        EXPECT_CALL(*test->mRenderSurface, setCritical(static_cast<bool>(CRITICAL))).Times(1);
-        EXPECT_CALL(*test->mRenderSurface, setNativeWindow(test->mNativeWindow.get())).Times(1);
-        EXPECT_CALL(*test->mRenderSurface, queryWidth()).WillOnce(Return(WIDTH));
-        EXPECT_CALL(*test->mRenderSurface, queryHeight()).WillOnce(Return(HEIGHT));
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+        EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
     }
 
     static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
@@ -297,7 +370,8 @@
     }
 };
 
-template <hwc2_display_t hwcDisplayId, HWC2::DisplayType hwcDisplayType, typename DisplayVariant>
+template <hwc2_display_t hwcDisplayId, HWC2::DisplayType hwcDisplayType, typename DisplayVariant,
+          typename PhysicalDisplay = void>
 struct HwcDisplayVariant {
     // The display id supplied by the HWC
     static constexpr hwc2_display_t HWC_DISPLAY_ID = hwcDisplayId;
@@ -316,7 +390,10 @@
 
     // Called by tests to inject a HWC display setup
     static void injectHwcDisplay(DisplayTransactionTest* test) {
-        FakeHwcDisplayInjector(DisplayVariant::TYPE, HWC_DISPLAY_TYPE)
+        const auto displayId = DisplayVariant::DISPLAY_ID::get();
+        ASSERT_TRUE(displayId);
+        FakeHwcDisplayInjector(*displayId, HWC_DISPLAY_TYPE,
+                               static_cast<bool>(DisplayVariant::PRIMARY))
                 .setHwcDisplayId(HWC_DISPLAY_ID)
                 .setWidth(DisplayVariant::WIDTH)
                 .setHeight(DisplayVariant::HEIGHT)
@@ -353,6 +430,16 @@
                     getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
                                         IComposerClient::Attribute::DPI_Y, _))
                 .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+
+        if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+            EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+                    .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT),
+                                    SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()),
+                                    Return(Error::NONE)));
+        } else {
+            EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+                    .WillOnce(Return(Error::UNSUPPORTED));
+        }
     }
 
     // Called by tests to set up HWC call expectations
@@ -362,55 +449,67 @@
     }
 };
 
-struct NonHwcDisplayVariant {
-    static void injectHwcDisplay(DisplayTransactionTest*) {}
-
-    static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
-    }
-};
-
 // Physical displays are expected to be synchronous, secure, and have a HWC display for output.
 constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
         GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
 
-template <hwc2_display_t hwcDisplayId, DisplayDevice::DisplayType type, int width, int height,
+template <hwc2_display_t hwcDisplayId, typename PhysicalDisplay, int width, int height,
           Critical critical>
 struct PhysicalDisplayVariant
-      : public DisplayVariant<type, type, width, height, critical, Async::FALSE, Secure::TRUE,
-                              GRALLOC_USAGE_PHYSICAL_DISPLAY>,
-        public HwcDisplayVariant<hwcDisplayId, HWC2::DisplayType::Physical,
-                                 DisplayVariant<type, type, width, height, critical, Async::FALSE,
-                                                Secure::TRUE, GRALLOC_USAGE_PHYSICAL_DISPLAY>> {};
+      : DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height, critical, Async::FALSE,
+                       Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+        HwcDisplayVariant<hwcDisplayId, HWC2::DisplayType::Physical,
+                          DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height,
+                                         critical, Async::FALSE, Secure::TRUE,
+                                         PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+                          PhysicalDisplay> {};
 
-// An invalid display
-using InvalidDisplayVariant =
-        DisplayVariant<DisplayDevice::DISPLAY_ID_INVALID, DisplayDevice::DISPLAY_ID_INVALID, 0, 0,
-                       Critical::FALSE, Async::FALSE, Secure::FALSE, 0>;
+template <bool hasIdentificationData>
+struct PrimaryDisplay {
+    static constexpr Primary PRIMARY = Primary::TRUE;
+    static constexpr uint8_t PORT = 255;
+    static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+    static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
+};
+
+template <bool hasIdentificationData>
+struct ExternalDisplay {
+    static constexpr Primary PRIMARY = Primary::FALSE;
+    static constexpr uint8_t PORT = 254;
+    static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+    static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+};
+
+struct TertiaryDisplay {
+    static constexpr Primary PRIMARY = Primary::FALSE;
+};
 
 // A primary display is a physical display that is critical
 using PrimaryDisplayVariant =
-        PhysicalDisplayVariant<1001, DisplayDevice::DISPLAY_PRIMARY, 3840, 2160, Critical::TRUE>;
+        PhysicalDisplayVariant<1001, PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
 
 // An external display is physical display that is not critical.
 using ExternalDisplayVariant =
-        PhysicalDisplayVariant<1002, DisplayDevice::DISPLAY_EXTERNAL, 1920, 1280, Critical::FALSE>;
+        PhysicalDisplayVariant<1002, ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
 
 using TertiaryDisplayVariant =
-        PhysicalDisplayVariant<1003, DisplayDevice::DISPLAY_EXTERNAL, 1600, 1200, Critical::FALSE>;
+        PhysicalDisplayVariant<1003, TertiaryDisplay, 1600, 1200, Critical::FALSE>;
 
 // A virtual display not supported by the HWC.
 constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
 
 template <int width, int height, Secure secure>
 struct NonHwcVirtualDisplayVariant
-      : public DisplayVariant<DisplayDevice::DISPLAY_VIRTUAL, DisplayDevice::DISPLAY_ID_INVALID,
-                              width, height, Critical::FALSE, Async::TRUE, secure,
-                              GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>,
-        public NonHwcDisplayVariant {
-    using Base = DisplayVariant<DisplayDevice::DISPLAY_VIRTUAL, DisplayDevice::DISPLAY_ID_INVALID,
-                                width, height, Critical::FALSE, Async::TRUE, secure,
-                                GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+      : DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
+                       Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
+    using Base = DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
+                                Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+
+    static void injectHwcDisplay(DisplayTransactionTest*) {}
+
+    static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
+    }
 
     static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
         Base::setupNativeWindowSurfaceCreationCallExpectations(test);
@@ -423,14 +522,14 @@
 
 template <int width, int height, Secure secure>
 struct HwcVirtualDisplayVariant
-      : public DisplayVariant<DisplayDevice::DISPLAY_VIRTUAL, DisplayDevice::DISPLAY_VIRTUAL, width,
-                              height, Critical::FALSE, Async::TRUE, secure,
-                              GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
-        public HwcDisplayVariant<1010, HWC2::DisplayType::Virtual,
-                                 NonHwcVirtualDisplayVariant<width, height, secure>> {
-    using Base =
-            DisplayVariant<DisplayDevice::DISPLAY_VIRTUAL, DisplayDevice::DISPLAY_VIRTUAL, width,
-                           height, Critical::FALSE, Async::TRUE, secure, GRALLOC_USAGE_HW_COMPOSER>;
+      : DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE, secure,
+                       Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
+        HwcDisplayVariant<
+                1010, HWC2::DisplayType::Virtual,
+                DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
+                               secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
+    using Base = DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
+                                secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
     using Self = HwcVirtualDisplayVariant<width, height, secure>;
 
     static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
@@ -453,6 +552,7 @@
 
     static void injectConfigChange(DisplayTransactionTest* test) {
         test->mFlinger.mutableHasWideColorDisplay() = false;
+        test->mFlinger.mutableUseColorManagement() = false;
         test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
     }
 
@@ -471,6 +571,7 @@
     static constexpr bool WIDE_COLOR_SUPPORTED = true;
 
     static void injectConfigChange(DisplayTransactionTest* test) {
+        test->mFlinger.mutableUseColorManagement() = true;
         test->mFlinger.mutableHasWideColorDisplay() = true;
         test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
     }
@@ -499,6 +600,7 @@
     static constexpr bool WIDE_COLOR_SUPPORTED = false;
 
     static void injectConfigChange(DisplayTransactionTest* test) {
+        test->mFlinger.mutableUseColorManagement() = true;
         test->mFlinger.mutableHasWideColorDisplay() = true;
     }
 
@@ -512,6 +614,7 @@
 // For this variant, the display is not a HWC display, so no HDR support should
 // be configured.
 struct NonHwcDisplayHdrSupportVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = false;
     static constexpr bool HDR10_SUPPORTED = false;
     static constexpr bool HDR_HLG_SUPPORTED = false;
     static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
@@ -520,10 +623,27 @@
     }
 };
 
+template <typename Display>
+struct Hdr10PlusSupportedVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = true;
+    static constexpr bool HDR10_SUPPORTED = true;
+    static constexpr bool HDR_HLG_SUPPORTED = false;
+    static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({
+                                        Hdr::HDR10_PLUS,
+                                        Hdr::HDR10,
+                                })),
+                                Return(Error::NONE)));
+    }
+};
+
 // For this variant, the composer should respond with a non-empty list of HDR
 // modes containing HDR10, so HDR10 support should be configured.
 template <typename Display>
 struct Hdr10SupportedVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = false;
     static constexpr bool HDR10_SUPPORTED = true;
     static constexpr bool HDR_HLG_SUPPORTED = false;
     static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
@@ -538,6 +658,7 @@
 // modes containing HLG, so HLG support should be configured.
 template <typename Display>
 struct HdrHlgSupportedVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = false;
     static constexpr bool HDR10_SUPPORTED = false;
     static constexpr bool HDR_HLG_SUPPORTED = true;
     static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
@@ -552,6 +673,7 @@
 // modes containing DOLBY_VISION, so DOLBY_VISION support should be configured.
 template <typename Display>
 struct HdrDolbyVisionSupportedVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = false;
     static constexpr bool HDR10_SUPPORTED = false;
     static constexpr bool HDR_HLG_SUPPORTED = false;
     static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true;
@@ -566,6 +688,7 @@
 // modes, so no HDR support should be configured.
 template <typename Display>
 struct HdrNotSupportedVariant {
+    static constexpr bool HDR10_PLUS_SUPPORTED = false;
     static constexpr bool HDR10_SUPPORTED = false;
     static constexpr bool HDR_HLG_SUPPORTED = false;
     static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
@@ -578,7 +701,7 @@
 struct NonHwcPerFrameMetadataSupportVariant {
     static constexpr int PER_FRAME_METADATA_KEYS = 0;
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_, _)).Times(0);
+        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0);
     }
 };
 
@@ -586,9 +709,8 @@
 struct NoPerFrameMetadataSupportVariant {
     static constexpr int PER_FRAME_METADATA_KEYS = 0;
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>()),
-                                Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+                .WillOnce(Return(std::vector<PerFrameMetadataKey>()));
     }
 };
 
@@ -596,20 +718,19 @@
 struct Smpte2086PerFrameMetadataSupportVariant {
     static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({
-                                        PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
-                                        PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
-                                        PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
-                                        PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
-                                        PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
-                                        PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
-                                        PerFrameMetadataKey::WHITE_POINT_X,
-                                        PerFrameMetadataKey::WHITE_POINT_Y,
-                                        PerFrameMetadataKey::MAX_LUMINANCE,
-                                        PerFrameMetadataKey::MIN_LUMINANCE,
-                                })),
-                                Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+                .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+                        PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
+                        PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
+                        PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
+                        PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
+                        PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
+                        PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
+                        PerFrameMetadataKey::WHITE_POINT_X,
+                        PerFrameMetadataKey::WHITE_POINT_Y,
+                        PerFrameMetadataKey::MAX_LUMINANCE,
+                        PerFrameMetadataKey::MIN_LUMINANCE,
+                })));
     }
 };
 
@@ -617,15 +738,24 @@
 struct Cta861_3_PerFrameMetadataSupportVariant {
     static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({
-                                        PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
-                                        PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
-                                })),
-                                Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+                .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+                        PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
+                        PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+                })));
     }
 };
 
+template <typename Display>
+struct Hdr10_Plus_PerFrameMetadataSupportVariant {
+    static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS;
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+                .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+                        PerFrameMetadataKey::HDR10_PLUS_SEI,
+                })));
+    }
+};
 /* ------------------------------------------------------------------------
  * Typical display configurations to test
  */
@@ -664,6 +794,10 @@
         Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
              HdrNotSupportedVariant<PrimaryDisplayVariant>,
              NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using Hdr10PlusDisplayCase =
+        Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+             Hdr10SupportedVariant<PrimaryDisplayVariant>,
+             Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
 using Hdr10DisplayCase =
         Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
              Hdr10SupportedVariant<PrimaryDisplayVariant>,
@@ -684,9 +818,7 @@
         Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
              HdrNotSupportedVariant<PrimaryDisplayVariant>,
              Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using InvalidDisplayCase = Case<InvalidDisplayVariant, WideColorSupportNotConfiguredVariant,
-                                NonHwcDisplayHdrSupportVariant,
-                                NoPerFrameMetadataSupportVariant<InvalidDisplayVariant>>;
+
 /* ------------------------------------------------------------------------
  *
  * SurfaceFlinger::onHotplugReceived
@@ -694,8 +826,8 @@
 
 TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) {
     constexpr int currentSequenceId = 123;
-    constexpr hwc2_display_t displayId1 = 456;
-    constexpr hwc2_display_t displayId2 = 654;
+    constexpr hwc2_display_t hwcDisplayId1 = 456;
+    constexpr hwc2_display_t hwcDisplayId2 = 654;
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -718,8 +850,8 @@
     // Invocation
 
     // Simulate two hotplug events (a connect and a disconnect)
-    mFlinger.onHotplugReceived(currentSequenceId, displayId1, HWC2::Connection::Connected);
-    mFlinger.onHotplugReceived(currentSequenceId, displayId2, HWC2::Connection::Disconnected);
+    mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, HWC2::Connection::Connected);
+    mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, HWC2::Connection::Disconnected);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -730,9 +862,9 @@
     // All events should be in the pending event queue.
     const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
     ASSERT_EQ(2u, pendingEvents.size());
-    EXPECT_EQ(displayId1, pendingEvents[0].display);
+    EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
     EXPECT_EQ(HWC2::Connection::Connected, pendingEvents[0].connection);
-    EXPECT_EQ(displayId2, pendingEvents[1].display);
+    EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
     EXPECT_EQ(HWC2::Connection::Disconnected, pendingEvents[1].connection);
 }
 
@@ -846,8 +978,8 @@
     // The display should have been added to the current state
     ASSERT_TRUE(hasCurrentDisplayState(displayToken));
     const auto& display = getCurrentDisplayState(displayToken);
-    EXPECT_EQ(DisplayDevice::DISPLAY_VIRTUAL, display.type);
-    EXPECT_EQ(false, display.isSecure);
+    EXPECT_TRUE(display.isVirtual());
+    EXPECT_FALSE(display.isSecure);
     EXPECT_EQ(name.string(), display.displayName);
 
     // --------------------------------------------------------------------
@@ -877,8 +1009,8 @@
     // The display should have been added to the current state
     ASSERT_TRUE(hasCurrentDisplayState(displayToken));
     const auto& display = getCurrentDisplayState(displayToken);
-    EXPECT_EQ(DisplayDevice::DISPLAY_VIRTUAL, display.type);
-    EXPECT_EQ(true, display.isSecure);
+    EXPECT_TRUE(display.isVirtual());
+    EXPECT_TRUE(display.isSecure);
     EXPECT_EQ(name.string(), display.displayName);
 
     // --------------------------------------------------------------------
@@ -965,8 +1097,8 @@
     // The call disable vsyncs
     EXPECT_CALL(*mEventControlThread, setVsyncEnabled(false)).Times(1);
 
-    // The call clears the current render engine surface
-    EXPECT_CALL(*mRenderEngine, resetCurrentSurface());
+    // The call ends any display resyncs
+    EXPECT_CALL(*mPrimaryDispSync, endResync()).Times(1);
 
     // --------------------------------------------------------------------
     // Invocation
@@ -991,6 +1123,219 @@
 }
 
 /* ------------------------------------------------------------------------
+ * DisplayDevice::GetBestColorMode
+ */
+class GetBestColorModeTest : public DisplayTransactionTest {
+public:
+    static constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
+
+    GetBestColorModeTest()
+          : DisplayTransactionTest(),
+            mInjector(FakeDisplayDeviceInjector(mFlinger, DEFAULT_DISPLAY_ID, false /* isVirtual */,
+                                                true /* isPrimary */)) {}
+
+    void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
+
+    void addHwcColorModesMapping(ui::ColorMode colorMode,
+                                 std::vector<ui::RenderIntent> renderIntents) {
+        mHwcColorModes[colorMode] = renderIntents;
+    }
+
+    void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
+
+    void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
+
+    void getBestColorMode() {
+        mInjector.setHwcColorModes(mHwcColorModes);
+        mInjector.setHasWideColorGamut(mHasWideColorGamut);
+        mInjector.setNativeWindow(mNativeWindow);
+
+        // Creating a DisplayDevice requires getting default dimensions from the
+        // native window.
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(1080 /* arbitrary */), Return(0)));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(1920 /* arbitrary */), Return(0)));
+        EXPECT_CALL(*mNativeWindow, perform(9)).Times(1);
+        EXPECT_CALL(*mNativeWindow, perform(13)).Times(1);
+        EXPECT_CALL(*mNativeWindow, perform(30)).Times(1);
+        auto displayDevice = mInjector.inject();
+
+        displayDevice->getCompositionDisplay()
+                ->getDisplayColorProfile()
+                ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
+                                   &mOutColorMode, &mOutRenderIntent);
+    }
+
+    ui::Dataspace mOutDataspace;
+    ui::ColorMode mOutColorMode;
+    ui::RenderIntent mOutRenderIntent;
+
+private:
+    ui::Dataspace mInputDataspace;
+    ui::RenderIntent mInputRenderIntent;
+    bool mHasWideColorGamut = false;
+    std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
+    FakeDisplayDeviceInjector mInjector;
+};
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
+    addHwcColorModesMapping(ui::ColorMode::SRGB,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    setInputDataspace(ui::Dataspace::DISPLAY_P3);
+    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+    setHasWideColorGamut(true);
+
+    getBestColorMode();
+
+    ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace);
+    ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
+    addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    addHwcColorModesMapping(ui::ColorMode::SRGB,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    setInputDataspace(ui::Dataspace::DISPLAY_P3);
+    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+    setHasWideColorGamut(true);
+
+    getBestColorMode();
+
+    ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
+    ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
+    addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+                            std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+    setInputDataspace(ui::Dataspace::DISPLAY_P3);
+    setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+    setHasWideColorGamut(true);
+
+    getBestColorMode();
+
+    ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
+    ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+/* ------------------------------------------------------------------------
+ * SurfaceFlinger::getDisplayNativePrimaries
+ */
+
+class GetDisplayNativePrimaries : public DisplayTransactionTest {
+public:
+    GetDisplayNativePrimaries();
+    void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
+    void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
+
+private:
+    static constexpr float mStartingTestValue = 1.0f;
+};
+
+GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
+    SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
+    injectFakeNativeWindowSurfaceFactory();
+}
+
+void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
+        ui::DisplayPrimaries& primaries) {
+    float startingVal = mStartingTestValue;
+    primaries.red.X = startingVal++;
+    primaries.red.Y = startingVal++;
+    primaries.red.Z = startingVal++;
+    primaries.green.X = startingVal++;
+    primaries.green.Y = startingVal++;
+    primaries.green.Z = startingVal++;
+    primaries.blue.X = startingVal++;
+    primaries.blue.Y = startingVal++;
+    primaries.blue.Z = startingVal++;
+    primaries.white.X = startingVal++;
+    primaries.white.Y = startingVal++;
+    primaries.white.Z = startingVal++;
+}
+
+void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
+        const ui::DisplayPrimaries& primaries) {
+    float startingVal = mStartingTestValue;
+    EXPECT_EQ(primaries.red.X, startingVal++);
+    EXPECT_EQ(primaries.red.Y, startingVal++);
+    EXPECT_EQ(primaries.red.Z, startingVal++);
+    EXPECT_EQ(primaries.green.X, startingVal++);
+    EXPECT_EQ(primaries.green.Y, startingVal++);
+    EXPECT_EQ(primaries.green.Z, startingVal++);
+    EXPECT_EQ(primaries.blue.X, startingVal++);
+    EXPECT_EQ(primaries.blue.Y, startingVal++);
+    EXPECT_EQ(primaries.blue.Z, startingVal++);
+    EXPECT_EQ(primaries.white.X, startingVal++);
+    EXPECT_EQ(primaries.white.Y, startingVal++);
+    EXPECT_EQ(primaries.white.Z, startingVal++);
+}
+
+TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
+    ui::DisplayPrimaries primaries;
+    EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
+}
+
+TEST_F(GetDisplayNativePrimaries, internalDisplayWithDefaultPrimariesData) {
+    auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
+    injector.inject();
+    auto internalDisplayToken = injector.token();
+    // A nullptr would trigger a different execution path than what's being tested here
+    EXPECT_NE(nullptr, internalDisplayToken.get());
+
+    mFlinger.initDefaultDisplayNativePrimaries();
+
+    ui::DisplayPrimaries primaries;
+    // Expecting sRGB primaries
+    EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
+    EXPECT_EQ(primaries.red.X, 0.4123f);
+    EXPECT_EQ(primaries.red.Y, 0.2126f);
+    EXPECT_EQ(primaries.red.Z, 0.0193f);
+    EXPECT_EQ(primaries.green.X, 0.3576f);
+    EXPECT_EQ(primaries.green.Y, 0.7152f);
+    EXPECT_EQ(primaries.green.Z, 0.1192f);
+    EXPECT_EQ(primaries.blue.X, 0.1805f);
+    EXPECT_EQ(primaries.blue.Y, 0.0722f);
+    EXPECT_EQ(primaries.blue.Z, 0.9506f);
+    EXPECT_EQ(primaries.white.X, 0.9505f);
+    EXPECT_EQ(primaries.white.Y, 1.0000f);
+    EXPECT_EQ(primaries.white.Z, 1.0891f);
+}
+
+TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
+    auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
+    injector.inject();
+    auto internalDisplayToken = injector.token();
+
+    ui::DisplayPrimaries expectedPrimaries;
+    populateDummyDisplayNativePrimaries(expectedPrimaries);
+    mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
+
+    ui::DisplayPrimaries primaries;
+    EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
+
+    checkDummyDisplayNativePrimaries(primaries);
+}
+
+TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
+    sp<BBinder> notInternalDisplayToken = new BBinder();
+
+    ui::DisplayPrimaries primaries;
+    populateDummyDisplayNativePrimaries(primaries);
+    EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
+
+    // Check primaries argument wasn't modified in case of failure
+    checkDummyDisplayNativePrimaries(primaries);
+}
+
+/* ------------------------------------------------------------------------
  * SurfaceFlinger::setupNewDisplayDeviceInternal
  */
 
@@ -1003,7 +1348,8 @@
 template <typename Case>
 void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
     const sp<BBinder> displayToken = new BBinder();
-    const sp<mock::DisplaySurface> displaySurface = new mock::DisplaySurface();
+    const sp<compositionengine::mock::DisplaySurface> displaySurface =
+            new compositionengine::mock::DisplaySurface();
     const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
 
     // --------------------------------------------------------------------
@@ -1032,19 +1378,27 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    auto state = DisplayDeviceState(Case::Display::TYPE, static_cast<bool>(Case::Display::SECURE));
-    auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, Case::Display::TYPE, state,
-                                                         displaySurface, producer);
+    DisplayDeviceState state;
+    state.displayId = static_cast<bool>(Case::Display::VIRTUAL) ? std::nullopt
+                                                                : Case::Display::DISPLAY_ID::get();
+    state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+    auto device =
+            mFlinger.setupNewDisplayDeviceInternal(displayToken, Case::Display::DISPLAY_ID::get(),
+                                                   state, displaySurface, producer);
 
     // --------------------------------------------------------------------
     // Postconditions
 
     ASSERT_TRUE(device != nullptr);
-    EXPECT_EQ(Case::Display::TYPE, device->getDisplayType());
+    EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
+    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
+    EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
     EXPECT_EQ(Case::Display::WIDTH, device->getWidth());
     EXPECT_EQ(Case::Display::HEIGHT, device->getHeight());
     EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut());
+    EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport());
     EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
     EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
     EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
@@ -1069,17 +1423,24 @@
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
-    // We need to resize this so that the HWC thinks the virtual display
-    // is something it created.
-    mFlinger.mutableHwcDisplayData().resize(3);
+    using Case = HwcVirtualDisplayCase;
 
-    setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
+    // Insert display data so that the HWC thinks it created the virtual display.
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(displayId);
+    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
+
+    setupNewDisplayDeviceInternalTest<Case>();
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
     setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>();
 }
 
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) {
+    setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>();
+}
+
 TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) {
     setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>();
 }
@@ -1160,13 +1521,17 @@
     Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
 
     EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-    EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, true)).Times(1);
+
+    EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::displayType(), true)).Times(1);
+    EXPECT_CALL(*mSFEventThread, onHotplugReceived(Case::Display::displayType(), true)).Times(1);
 }
 
 template <typename Case>
 void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
     EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-    EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, false)).Times(1);
+
+    EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::displayType(), false)).Times(1);
+    EXPECT_CALL(*mSFEventThread, onHotplugReceived(Case::Display::displayType(), false)).Times(1);
 }
 
 template <typename Case>
@@ -1175,30 +1540,35 @@
     ASSERT_TRUE(hasDisplayDevice(displayToken));
     const auto& device = getDisplayDevice(displayToken);
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
-    EXPECT_EQ(Case::Display::TYPE == DisplayDevice::DISPLAY_PRIMARY, device->isPrimary());
+    EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
 
     // The display should have been set up in the current display state
     ASSERT_TRUE(hasCurrentDisplayState(displayToken));
     const auto& current = getCurrentDisplayState(displayToken);
-    EXPECT_EQ(Case::Display::TYPE, current.type);
+    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
+    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL) ? std::nullopt
+                                                        : Case::Display::DISPLAY_ID::get(),
+              current.displayId);
 
     // The display should have been set up in the drawing display state
     ASSERT_TRUE(hasDrawingDisplayState(displayToken));
     const auto& draw = getDrawingDisplayState(displayToken);
-    EXPECT_EQ(Case::Display::TYPE, draw.type);
+    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
+    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL) ? std::nullopt
+                                                        : Case::Display::DISPLAY_ID::get(),
+              draw.displayId);
 }
 
 template <typename Case>
 void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
     // HWComposer should have an entry for the display
-    EXPECT_TRUE(hasHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+    EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-    // The display should be set up as a built-in display.
-    static_assert(0 <= Case::Display::TYPE &&
-                          Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES,
-                  "Must use a valid physical display type index for the fixed-size array");
-    auto& displayToken = mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE];
-    ASSERT_TRUE(displayToken != nullptr);
+    // SF should have a display token.
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(displayId);
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
+    auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[*displayId];
 
     verifyDisplayIsConnected<Case>(displayToken);
 }
@@ -1264,7 +1634,7 @@
     // Postconditions
 
     // HWComposer should not have an entry for the display
-    EXPECT_FALSE(hasHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 }
 
 template <typename Case>
@@ -1286,6 +1656,8 @@
     // Call Expectations
 
     EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
+            .Times(0);
 
     setupCommonCallExpectationsForDisconnectProcessing<Case>();
 
@@ -1298,13 +1670,12 @@
     // Postconditions
 
     // HWComposer should not have an entry for the display
-    EXPECT_FALSE(hasHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-    // The display should not be set up as a built-in display.
-    ASSERT_TRUE(0 <= Case::Display::TYPE &&
-                Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES);
-    auto displayToken = mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE];
-    EXPECT_TRUE(displayToken == nullptr);
+    // SF should not have a display token.
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(displayId);
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
 
     // The existing token should have been removed
     verifyDisplayIsNotConnected(existing.token());
@@ -1391,13 +1762,12 @@
     // Postconditions
 
     // HWComposer should not have an entry for the display
-    EXPECT_FALSE(hasHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+    EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-    // The display should not be set up as a primary built-in display.
-    ASSERT_TRUE(0 <= Case::Display::TYPE &&
-                Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES);
-    auto displayToken = mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE];
-    EXPECT_TRUE(displayToken == nullptr);
+    // SF should not have a display token.
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(displayId);
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
 }
 
 TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
@@ -1436,10 +1806,10 @@
 
     // The existing token should have been removed
     verifyDisplayIsNotConnected(existing.token());
-    static_assert(0 <= Case::Display::TYPE &&
-                          Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES,
-                  "Display type must be a built-in display");
-    EXPECT_NE(existing.token(), mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE]);
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(displayId);
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
+    EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[*displayId]);
 
     // A new display should be connected in its place
 
@@ -1468,10 +1838,13 @@
     // A virtual display was added to the current state, and it has a
     // surface(producer)
     sp<BBinder> displayToken = new BBinder();
-    DisplayDeviceState info(Case::Display::TYPE, static_cast<bool>(Case::Display::SECURE));
+
+    DisplayDeviceState state;
+    state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
     sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()};
-    info.surface = surface;
-    mFlinger.mutableCurrentState().displays.add(displayToken, info);
+    state.surface = surface;
+    mFlinger.mutableCurrentState().displays.add(displayToken, state);
 
     // --------------------------------------------------------------------
     // Call Expectations
@@ -1491,7 +1864,6 @@
 
     EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
 
-    EXPECT_CALL(*mProducer, connect(_, _, _, _)).Times(1);
     EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
 
     Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
@@ -1532,8 +1904,11 @@
     // A virtual display was added to the current state, but it does not have a
     // surface.
     sp<BBinder> displayToken = new BBinder();
-    DisplayDeviceState info(Case::Display::TYPE, static_cast<bool>(Case::Display::SECURE));
-    mFlinger.mutableCurrentState().displays.add(displayToken, info);
+
+    DisplayDeviceState state;
+    state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+    mFlinger.mutableCurrentState().displays.add(displayToken, state);
 
     // --------------------------------------------------------------------
     // Call Expectations
@@ -1552,7 +1927,7 @@
     // The drawing display state will be set from the current display state.
     ASSERT_TRUE(hasDrawingDisplayState(displayToken));
     const auto& draw = getDrawingDisplayState(displayToken);
-    EXPECT_EQ(Case::Display::TYPE, draw.type);
+    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
 }
 
 TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
@@ -1562,7 +1937,9 @@
     // Preconditions
 
     // A virtual display is set up but is removed from the current state.
-    mFlinger.mutableHwcDisplayData().resize(3);
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(displayId);
+    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
     Case::Display::injectHwcDisplay(this);
     auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
     existing.inject();
@@ -1709,12 +2086,19 @@
 
     // A display is set up
     auto nativeWindow = new mock::NativeWindow();
-    auto displaySurface = new mock::DisplaySurface();
-    auto renderSurface = new RE::mock::Surface();
+    auto displaySurface = new compositionengine::mock::DisplaySurface();
+    sp<GraphicBuffer> buf = new GraphicBuffer();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
     display.setDisplaySurface(displaySurface);
-    display.setRenderSurface(std::unique_ptr<RE::Surface>(renderSurface));
+    // Setup injection expections
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+    EXPECT_CALL(*nativeWindow, perform(9)).Times(1);
+    EXPECT_CALL(*nativeWindow, perform(13)).Times(1);
+    EXPECT_CALL(*nativeWindow, perform(30)).Times(1);
     display.inject();
 
     // There is a change to the viewport state
@@ -1726,11 +2110,7 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    EXPECT_CALL(*renderSurface, setNativeWindow(nullptr)).Times(1);
     EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1);
-    EXPECT_CALL(*renderSurface, setNativeWindow(nativeWindow)).Times(1);
-    EXPECT_CALL(*renderSurface, queryWidth()).WillOnce(Return(newWidth));
-    EXPECT_CALL(*renderSurface, queryHeight()).WillOnce(Return(oldHeight));
 
     // --------------------------------------------------------------------
     // Invocation
@@ -1750,12 +2130,19 @@
 
     // A display is set up
     auto nativeWindow = new mock::NativeWindow();
-    auto displaySurface = new mock::DisplaySurface();
-    auto renderSurface = new RE::mock::Surface();
+    auto displaySurface = new compositionengine::mock::DisplaySurface();
+    sp<GraphicBuffer> buf = new GraphicBuffer();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
     display.setDisplaySurface(displaySurface);
-    display.setRenderSurface(std::unique_ptr<RE::Surface>(renderSurface));
+    // Setup injection expections
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+            .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+    EXPECT_CALL(*nativeWindow, perform(9)).Times(1);
+    EXPECT_CALL(*nativeWindow, perform(13)).Times(1);
+    EXPECT_CALL(*nativeWindow, perform(30)).Times(1);
     display.inject();
 
     // There is a change to the viewport state
@@ -1767,11 +2154,7 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    EXPECT_CALL(*renderSurface, setNativeWindow(nullptr)).Times(1);
     EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1);
-    EXPECT_CALL(*renderSurface, setNativeWindow(nativeWindow)).Times(1);
-    EXPECT_CALL(*renderSurface, queryWidth()).WillOnce(Return(oldWidth));
-    EXPECT_CALL(*renderSurface, queryHeight()).WillOnce(Return(newHeight));
 
     // --------------------------------------------------------------------
     // Invocation
@@ -1811,40 +2194,6 @@
     EXPECT_FALSE(hasCurrentDisplayState(displayToken));
 }
 
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWithInvalidDisplay) {
-    using Case = InvalidDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // An invalid display is set up
-    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
-    display.inject();
-
-    // The invalid display has some state
-    display.mutableCurrentDisplayState().layerStack = 654u;
-
-    // The requested display state tries to change the display state.
-    DisplayState state;
-    state.what = DisplayState::eLayerStackChanged;
-    state.token = display.token();
-    state.layerStack = 456;
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The returned flags are empty
-    EXPECT_EQ(0u, flags);
-
-    // The current display layer stack value is unchanged.
-    EXPECT_EQ(654u, getCurrentDisplayState(display.token()).layerStack);
-}
-
 TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWhenNoChanges) {
     using Case = SimplePrimaryDisplayCase;
 
@@ -2358,14 +2707,23 @@
  */
 
 // Used when we simulate a display that supports doze.
+template <typename Display>
 struct DozeIsSupportedVariant {
     static constexpr bool DOZE_SUPPORTED = true;
     static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
             IComposerClient::PowerMode::DOZE;
     static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
             IComposerClient::PowerMode::DOZE_SUSPEND;
+
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>(
+                                        {Hwc2::DisplayCapability::DOZE})),
+                                Return(Error::NONE)));
+    }
 };
 
+template <typename Display>
 // Used when we simulate a display that does not support doze.
 struct DozeNotSupportedVariant {
     static constexpr bool DOZE_SUPPORTED = false;
@@ -2373,6 +2731,12 @@
             IComposerClient::PowerMode::ON;
     static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
             IComposerClient::PowerMode::ON;
+
+    static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+                                Return(Error::NONE)));
+    }
 };
 
 struct EventThreadBaseSupportedVariant {
@@ -2420,6 +2784,24 @@
     }
 };
 
+struct DispSyncIsSupportedVariant {
+    static void setupBeginResyncCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mPrimaryDispSync, reset()).Times(1);
+        EXPECT_CALL(*test->mPrimaryDispSync, setPeriod(DEFAULT_REFRESH_RATE)).Times(1);
+        EXPECT_CALL(*test->mPrimaryDispSync, beginResync()).Times(1);
+    }
+
+    static void setupEndResyncCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mPrimaryDispSync, endResync()).Times(1);
+    }
+};
+
+struct DispSyncNotSupportedVariant {
+    static void setupBeginResyncCallExpectations(DisplayTransactionTest* /* test */) {}
+
+    static void setupEndResyncCallExpectations(DisplayTransactionTest* /* test */) {}
+};
+
 // --------------------------------------------------------------------
 // Note:
 //
@@ -2441,6 +2823,7 @@
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
         Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+        Case::DispSync::setupBeginResyncCallExpectations(test);
         Case::setupRepaintEverythingCallExpectations(test);
     }
 
@@ -2470,6 +2853,7 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+        Case::DispSync::setupEndResyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
     }
 
@@ -2505,6 +2889,7 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+        Case::DispSync::setupBeginResyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
     }
 };
@@ -2523,6 +2908,7 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+        Case::DispSync::setupBeginResyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
     }
 };
@@ -2532,6 +2918,7 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+        Case::DispSync::setupEndResyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
     }
 };
@@ -2554,11 +2941,12 @@
 // --------------------------------------------------------------------
 
 template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
-          typename TransitionVariant>
+          typename DispSyncVariant, typename TransitionVariant>
 struct DisplayPowerCase {
     using Display = DisplayVariant;
     using Doze = DozeVariant;
     using EventThread = EventThreadVariant;
+    using DispSync = DispSyncVariant;
     using Transition = TransitionVariant;
 
     static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, int mode) {
@@ -2605,16 +2993,19 @@
 // A sample configuration for the primary display.
 // In addition to having event thread support, we emulate doze support.
 template <typename TransitionVariant>
-using PrimaryDisplayPowerCase = DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant,
-                                                 EventThreadIsSupportedVariant, TransitionVariant>;
+using PrimaryDisplayPowerCase =
+        DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
+                         EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
+                         TransitionVariant>;
 
 // A sample configuration for the external display.
 // In addition to not having event thread support, we emulate not having doze
 // support.
 template <typename TransitionVariant>
 using ExternalDisplayPowerCase =
-        DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant,
-                         EventThreadNotSupportedVariant, TransitionVariant>;
+        DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
+                         EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
+                         TransitionVariant>;
 
 class SetPowerModeInternalTest : public DisplayTransactionTest {
 public:
@@ -2636,6 +3027,7 @@
     // --------------------------------------------------------------------
     // Preconditions
 
+    Case::Doze::setupComposerCallExpectations(this);
     auto display =
             Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
     Case::setInitialPrimaryHWVsyncEnabled(this,
@@ -2671,7 +3063,7 @@
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.inject();
 
-    // The diplay is already set to HWC_POWER_MODE_NORMAL
+    // The display is already set to HWC_POWER_MODE_NORMAL
     display.mutableDisplayDevice()->setPowerMode(HWC_POWER_MODE_NORMAL);
 
     // --------------------------------------------------------------------
@@ -2685,28 +3077,29 @@
     EXPECT_EQ(HWC_POWER_MODE_NORMAL, display.mutableDisplayDevice()->getPowerMode());
 }
 
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalJustSetsInternalStateIfVirtualDisplay) {
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
     using Case = HwcVirtualDisplayCase;
 
     // --------------------------------------------------------------------
     // Preconditions
 
-    // We need to resize this so that the HWC thinks the virtual display
-    // is something it created.
-    mFlinger.mutableHwcDisplayData().resize(3);
+    // Insert display data so that the HWC thinks it created the virtual display.
+    const auto displayId = Case::Display::DISPLAY_ID::get();
+    ASSERT_TRUE(displayId);
+    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
 
     // A virtual display device is set up
     Case::Display::injectHwcDisplay(this);
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.inject();
 
-    // The display is set to HWC_POWER_MODE_OFF
-    getDisplayDevice(display.token())->setPowerMode(HWC_POWER_MODE_OFF);
+    // The display is set to HWC_POWER_MODE_NORMAL
+    getDisplayDevice(display.token())->setPowerMode(HWC_POWER_MODE_NORMAL);
 
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_NORMAL);
+    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_OFF);
 
     // --------------------------------------------------------------------
     // Postconditions
diff --git a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
index b346454..9dc4193 100644
--- a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
@@ -23,7 +23,7 @@
 #include <log/log.h>
 
 #include "AsyncCallRecorder.h"
-#include "EventControlThread.h"
+#include "Scheduler/EventControlThread.h"
 
 namespace android {
 namespace {
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 80fdb80..dd90063 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -25,7 +25,7 @@
 #include <utils/Errors.h>
 
 #include "AsyncCallRecorder.h"
-#include "EventThread.h"
+#include "Scheduler/EventThread.h"
 
 using namespace std::chrono_literals;
 using namespace std::placeholders;
@@ -47,10 +47,11 @@
 
 class EventThreadTest : public testing::Test {
 protected:
-    class MockEventThreadConnection : public android::impl::EventThread::Connection {
+    class MockEventThreadConnection : public EventThreadConnection {
     public:
-        explicit MockEventThreadConnection(android::impl::EventThread* eventThread)
-              : android::impl::EventThread::Connection(eventThread) {}
+        MockEventThreadConnection(android::impl::EventThread* eventThread,
+                                  ResyncCallback&& resyncCallback)
+              : EventThreadConnection(eventThread, std::move(resyncCallback)) {}
         MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
     };
 
@@ -71,7 +72,8 @@
                                               ConnectionEventRecorder& connectionEventRecorder,
                                               nsecs_t expectedTimestamp, unsigned expectedCount);
     void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
-    void expectHotplugEventReceivedByConnection(int expectedDisplayType, bool expectedConnected);
+    void expectHotplugEventReceivedByConnection(EventThread::DisplayType expectedDisplayType,
+                                                bool expectedConnected);
 
     AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
     AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
@@ -81,6 +83,7 @@
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
 
     MockVSyncSource mVSyncSource;
+    VSyncSource::Callback* mCallback = nullptr;
     std::unique_ptr<android::impl::EventThread> mThread;
     sp<MockEventThreadConnection> mConnection;
 };
@@ -101,25 +104,36 @@
 
     createThread();
     mConnection = createConnection(mConnectionEventCallRecorder);
+
+    // A display must be connected for VSYNC events to be delivered.
+    mThread->onHotplugReceived(EventThread::DisplayType::Primary, true);
+    expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, true);
 }
 
 EventThreadTest::~EventThreadTest() {
     const ::testing::TestInfo* const test_info =
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+
+    // EventThread should unregister itself as VSyncSource callback.
+    EXPECT_FALSE(expectVSyncSetCallbackCallReceived());
 }
 
 void EventThreadTest::createThread() {
     mThread =
             std::make_unique<android::impl::EventThread>(&mVSyncSource,
-                                                         mResyncCallRecorder.getInvocable(),
                                                          mInterceptVSyncCallRecorder.getInvocable(),
                                                          "unit-test-event-thread");
+
+    // EventThread should register itself as VSyncSource callback.
+    mCallback = expectVSyncSetCallbackCallReceived();
+    ASSERT_TRUE(mCallback);
 }
 
 sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
         ConnectionEventRecorder& recorder) {
-    sp<MockEventThreadConnection> connection = new MockEventThreadConnection(mThread.get());
+    sp<MockEventThreadConnection> connection =
+            new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable());
     EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
     return connection;
 }
@@ -169,13 +183,16 @@
                                          expectedCount);
 }
 
-void EventThreadTest::expectHotplugEventReceivedByConnection(int expectedDisplayType,
-                                                             bool expectedConnected) {
+void EventThreadTest::expectHotplugEventReceivedByConnection(
+        EventThread::DisplayType expectedDisplayType, bool expectedConnected) {
+    const uint32_t expectedDisplayId =
+            expectedDisplayType == EventThread::DisplayType::Primary ? 0 : 1;
+
     auto args = mConnectionEventCallRecorder.waitForCall();
     ASSERT_TRUE(args.has_value());
     const auto& event = std::get<0>(args.value());
     EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, event.header.type);
-    EXPECT_EQ(static_cast<unsigned>(expectedDisplayType), event.header.id);
+    EXPECT_EQ(expectedDisplayId, event.header.id);
     EXPECT_EQ(expectedConnected, event.hotplug.connected);
 }
 
@@ -194,29 +211,37 @@
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
 }
 
+TEST_F(EventThreadTest, vsyncRequestIsIgnoredIfDisplayIsDisconnected) {
+    mThread->onHotplugReceived(EventThread::DisplayType::Primary, false);
+    expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, false);
+
+    // Signal that we want the next vsync event to be posted to the connection.
+    mThread->requestNextVsync(mConnection, false);
+
+    // EventThread should not enable vsync callbacks.
+    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
 TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
     // Signal that we want the next vsync event to be posted to the connection
-    mThread->requestNextVsync(mConnection);
+    mThread->requestNextVsync(mConnection, false);
 
     // EventThread should immediately request a resync.
     EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // Use the received callback to signal a first vsync event.
     // The interceptor should receive the event, as well as the connection.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // Use the received callback to signal a second vsync event.
     // The interceptor should receive the event, but the the connection should
     // not as it was only interested in the first.
-    callback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456);
     expectInterceptCallReceived(456);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
@@ -241,16 +266,13 @@
             createConnection(secondConnectionEventRecorder);
     mThread->setVsyncRate(1, secondConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // Send a vsync event. EventThread should then make a call to the
     // interceptor, and the second connection. The first connection should not
     // get the event.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
     expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
@@ -260,25 +282,22 @@
 TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) {
     mThread->setVsyncRate(1, mConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // Send a vsync event. EventThread should then make a call to the
     // interceptor, and the connection.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // A second event should go to the same places.
-    callback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // A third event should go to the same places.
-    callback->onVSyncEvent(789);
+    mCallback->onVSyncEvent(789);
     expectInterceptCallReceived(789);
     expectVsyncEventReceivedByConnection(789, 3u);
 }
@@ -286,29 +305,26 @@
 TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) {
     mThread->setVsyncRate(2, mConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // The first event will be seen by the interceptor, and not the connection.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // The second event will be seen by the interceptor and the connection.
-    callback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // The third event will be seen by the interceptor, and not the connection.
-    callback->onVSyncEvent(789);
+    mCallback->onVSyncEvent(789);
     expectInterceptCallReceived(789);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // The fourth event will be seen by the interceptor and the connection.
-    callback->onVSyncEvent(101112);
+    mCallback->onVSyncEvent(101112);
     expectInterceptCallReceived(101112);
     expectVsyncEventReceivedByConnection(101112, 4u);
 }
@@ -316,17 +332,14 @@
 TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) {
     mThread->setVsyncRate(1, mConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // Destroy the only (strong) reference to the connection.
     mConnection = nullptr;
 
     // The first event will be seen by the interceptor, and not the connection.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
@@ -339,21 +352,18 @@
     sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
     mThread->setVsyncRate(1, errorConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // The first event will be seen by the interceptor, and by the connection,
     // which then returns an error.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
     // A subsequent event will be seen by the interceptor and not by the
     // connection.
-    callback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456);
     expectInterceptCallReceived(456);
     EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
 
@@ -366,21 +376,18 @@
     sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
     mThread->setVsyncRate(1, errorConnection);
 
-    // EventThread should enable vsync callbacks, and set a callback interface
-    // pointer to use them with the VSync source.
+    // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
-    auto callback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(callback);
 
     // The first event will be seen by the interceptor, and by the connection,
     // which then returns an non-fatal error.
-    callback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
     // A subsequent event will be seen by the interceptor, and by the connection,
     // which still then returns an non-fatal error.
-    callback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
 
@@ -394,28 +401,23 @@
 }
 
 TEST_F(EventThreadTest, postHotplugPrimaryDisconnect) {
-    mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, false);
-    expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, false);
+    mThread->onHotplugReceived(EventThread::DisplayType::Primary, false);
+    expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, false);
 }
 
 TEST_F(EventThreadTest, postHotplugPrimaryConnect) {
-    mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true);
-    expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, true);
+    mThread->onHotplugReceived(EventThread::DisplayType::Primary, true);
+    expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, true);
 }
 
 TEST_F(EventThreadTest, postHotplugExternalDisconnect) {
-    mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, false);
-    expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, false);
+    mThread->onHotplugReceived(EventThread::DisplayType::External, false);
+    expectHotplugEventReceivedByConnection(EventThread::DisplayType::External, false);
 }
 
 TEST_F(EventThreadTest, postHotplugExternalConnect) {
-    mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, true);
-    expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, true);
-}
-
-TEST_F(EventThreadTest, postHotplugVirtualDisconnectIsFilteredOut) {
-    mThread->onHotplugReceived(DisplayDevice::DISPLAY_VIRTUAL, false);
-    EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+    mThread->onHotplugReceived(EventThread::DisplayType::External, true);
+    expectHotplugEventReceivedByConnection(EventThread::DisplayType::External, true);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
new file mode 100644
index 0000000..0739f15
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 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 <gmock/gmock.h>
+
+#include "Scheduler/PhaseOffsets.h"
+
+namespace android {
+namespace scheduler {
+
+class FakePhaseOffsets : public android::scheduler::PhaseOffsets {
+    nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+
+public:
+    FakePhaseOffsets() = default;
+    ~FakePhaseOffsets() = default;
+
+    nsecs_t getCurrentAppOffset() override { return FAKE_PHASE_OFFSET_NS; }
+    nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; }
+
+    // Returns early, early GL, and late offsets for Apps and SF.
+    PhaseOffsets::Offsets getCurrentOffsets() const override {
+        return Offsets{{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+                       {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+                       {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
+    }
+
+    // This function should be called when the device is switching between different
+    // refresh rates, to properly update the offsets.
+    void setRefreshRateType(RefreshRateConfigs::RefreshRateType /*refreshRateType*/) override {}
+
+    // Returns current offsets in human friendly format.
+    void dump(std::string& /*result*/) const override {}
+};
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
new file mode 100644
index 0000000..9fe9a18
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include "AsyncCallRecorder.h"
+#include "Scheduler/IdleTimer.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace scheduler {
+
+class IdleTimerTest : public testing::Test {
+protected:
+    IdleTimerTest() = default;
+    ~IdleTimerTest() override = default;
+
+    AsyncCallRecorder<void (*)()> mExpiredTimerCallback;
+
+    std::unique_ptr<IdleTimer> mIdleTimer;
+};
+
+namespace {
+TEST_F(IdleTimerTest, createAndDestroyTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, [] {});
+}
+
+TEST_F(IdleTimerTest, startStopTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer->start();
+    std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    // The timer expires after 30 ms, so the call to the callback should not happen.
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall().has_value());
+    mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, resetTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer->start();
+    std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    // The timer expires after 30 ms, so the call to the callback should not happen.
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    mIdleTimer->reset();
+    // The timer was reset, so the call to the callback should not happen.
+    std::this_thread::sleep_for(std::chrono::milliseconds(15));
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, startNotCalledTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+    std::this_thread::sleep_for(6ms);
+    // The start hasn't happened, so the callback does not happen.
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, idleTimerIdlesTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer->start();
+    std::this_thread::sleep_for(6ms);
+    // The timer expires after 3 ms, so the call to the callback happens.
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    std::this_thread::sleep_for(6ms);
+    // Timer can be idle.
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    // Timer can be reset.
+    mIdleTimer->reset();
+    std::this_thread::sleep_for(6ms);
+    // Timer fires again.
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, timeoutCallbackExecutionTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+
+    mIdleTimer->start();
+    std::this_thread::sleep_for(6ms);
+    // The timer expires after 3 ms, so the call to the callback should happen.
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer->start();
+    std::this_thread::sleep_for(6ms);
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+    mIdleTimer->stop();
+    mIdleTimer->reset();
+    std::this_thread::sleep_for(6ms);
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall().has_value());
+}
+
+TEST_F(IdleTimerTest, noCallbacksAfterStopTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer->start();
+    std::this_thread::sleep_for(1ms);
+    mIdleTimer->stop();
+    std::this_thread::sleep_for(3ms);
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
new file mode 100644
index 0000000..3fb1a6e
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -0,0 +1,174 @@
+#undef LOG_TAG
+#define LOG_TAG "LayerHistoryUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include <mutex>
+
+#include "Scheduler/LayerHistory.h"
+
+using testing::_;
+using testing::Return;
+
+namespace android {
+
+class LayerHistoryTest : public testing::Test {
+public:
+    LayerHistoryTest();
+    ~LayerHistoryTest() override;
+
+protected:
+    std::unique_ptr<LayerHistory> mLayerHistory;
+};
+
+LayerHistoryTest::LayerHistoryTest() {
+    mLayerHistory = std::make_unique<LayerHistory>();
+}
+LayerHistoryTest::~LayerHistoryTest() {}
+
+namespace {
+TEST_F(LayerHistoryTest, simpleInsertAndGet) {
+    mLayerHistory->insert("TestLayer", 0);
+
+    const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0);
+    EXPECT_EQ(1, testMap.size());
+    auto element = testMap.find("TestLayer");
+    EXPECT_EQ("TestLayer", element->first);
+    EXPECT_EQ(0, element->second);
+
+    // Testing accessing object at an empty container will return an empty map.
+    const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1);
+    EXPECT_EQ(0, emptyMap.size());
+}
+
+TEST_F(LayerHistoryTest, multipleInserts) {
+    mLayerHistory->insert("TestLayer0", 0);
+    mLayerHistory->insert("TestLayer1", 1);
+    mLayerHistory->insert("TestLayer2", 2);
+    mLayerHistory->insert("TestLayer3", 3);
+
+    const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0);
+    // Because the counter was not incremented, all elements were inserted into the first
+    // container.
+    EXPECT_EQ(4, testMap.size());
+    auto element = testMap.find("TestLayer0");
+    EXPECT_EQ("TestLayer0", element->first);
+    EXPECT_EQ(0, element->second);
+
+    element = testMap.find("TestLayer1");
+    EXPECT_EQ("TestLayer1", element->first);
+    EXPECT_EQ(1, element->second);
+
+    element = testMap.find("TestLayer2");
+    EXPECT_EQ("TestLayer2", element->first);
+    EXPECT_EQ(2, element->second);
+
+    element = testMap.find("TestLayer3");
+    EXPECT_EQ("TestLayer3", element->first);
+    EXPECT_EQ(3, element->second);
+
+    // Testing accessing object at an empty container will return an empty map.
+    const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1);
+    EXPECT_EQ(0, emptyMap.size());
+}
+
+TEST_F(LayerHistoryTest, incrementingCounter) {
+    mLayerHistory->insert("TestLayer0", 0);
+    mLayerHistory->incrementCounter();
+    mLayerHistory->insert("TestLayer1", 1);
+    mLayerHistory->insert("TestLayer2", 2);
+    mLayerHistory->incrementCounter();
+    mLayerHistory->insert("TestLayer3", 3);
+
+    // Because the counter was incremented, the elements were inserted into different
+    // containers.
+    // We expect the get method to access the slot at the current counter of the index
+    // is 0.
+    const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
+    EXPECT_EQ(1, testMap1.size());
+    auto element = testMap1.find("TestLayer3");
+    EXPECT_EQ("TestLayer3", element->first);
+    EXPECT_EQ(3, element->second);
+
+    const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(1);
+    EXPECT_EQ(2, testMap2.size());
+    element = testMap2.find("TestLayer1");
+    EXPECT_EQ("TestLayer1", element->first);
+    EXPECT_EQ(1, element->second);
+    element = testMap2.find("TestLayer2");
+    EXPECT_EQ("TestLayer2", element->first);
+    EXPECT_EQ(2, element->second);
+
+    const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(2);
+    EXPECT_EQ(1, testMap3.size());
+    element = testMap3.find("TestLayer0");
+    EXPECT_EQ("TestLayer0", element->first);
+    EXPECT_EQ(0, element->second);
+
+    // Testing accessing object at an empty container will return an empty map.
+    const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(3);
+    EXPECT_EQ(0, emptyMap.size());
+}
+
+TEST_F(LayerHistoryTest, clearTheMap) {
+    mLayerHistory->insert("TestLayer0", 0);
+
+    const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
+    EXPECT_EQ(1, testMap1.size());
+    auto element = testMap1.find("TestLayer0");
+    EXPECT_EQ("TestLayer0", element->first);
+    EXPECT_EQ(0, element->second);
+
+    mLayerHistory->incrementCounter();
+    // The array currently contains 30 elements.
+    for (int i = 1; i < 30; ++i) {
+        mLayerHistory->insert("TestLayer0", i);
+        mLayerHistory->incrementCounter();
+    }
+    // Expect the map to be cleared.
+    const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(0);
+    EXPECT_EQ(0, testMap2.size());
+
+    mLayerHistory->insert("TestLayer30", 30);
+    const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(0);
+    element = testMap3.find("TestLayer30");
+    EXPECT_EQ("TestLayer30", element->first);
+    EXPECT_EQ(30, element->second);
+    // The original element in this location does not exist anymore.
+    element = testMap3.find("TestLayer0");
+    EXPECT_EQ(testMap3.end(), element);
+}
+
+TEST_F(LayerHistoryTest, testingGet) {
+    // The array currently contains 30 elements.
+    for (int i = 0; i < 30; ++i) {
+        const auto name = "TestLayer" + std::to_string(i);
+        mLayerHistory->insert(name, i);
+        mLayerHistory->incrementCounter();
+    }
+
+    // The counter should be set to 0, and the map at 0 should be cleared.
+    const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
+    EXPECT_EQ(0, testMap1.size());
+
+    // This should return (ARRAY_SIZE + (counter - 3)) % ARRAY_SIZE
+    const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(3);
+    EXPECT_EQ(1, testMap2.size());
+    auto element = testMap2.find("TestLayer27");
+    EXPECT_EQ("TestLayer27", element->first);
+    EXPECT_EQ(27, element->second);
+
+    // If the user gives an out of bound index, we should mod it with ARRAY_SIZE first,
+    // so requesting element 40 would be the same as requesting element 10.
+    const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(40);
+    EXPECT_EQ(1, testMap3.size());
+    element = testMap3.find("TestLayer20");
+    EXPECT_EQ("TestLayer20", element->first);
+    EXPECT_EQ(20, element->second);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
new file mode 100644
index 0000000..92c9f92
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <binder/Parcel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+#include <log/log.h>
+
+namespace android {
+namespace {
+
+class LayerMetadataTest : public testing::Test {
+public:
+    LayerMetadataTest();
+    ~LayerMetadataTest() override;
+};
+
+LayerMetadataTest::LayerMetadataTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+LayerMetadataTest::~LayerMetadataTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+TEST_F(LayerMetadataTest, testLayerMetadata) {
+    LayerMetadata metadata;
+
+    ASSERT_EQ(0, metadata.mMap.size());
+
+    // Test non-set
+    ASSERT_EQ(3, metadata.getInt32(4, 3));
+
+    // Make sure it's still unset
+    ASSERT_EQ(5, metadata.getInt32(4, 5));
+
+    metadata.setInt32(4, 2);
+    ASSERT_EQ(2, metadata.getInt32(4, 0));
+
+    // data is too small
+    metadata.mMap[2] = std::vector<uint8_t>{'a', 'b'};
+    ASSERT_EQ(0, metadata.getInt32(2, 0));
+
+    LayerMetadata second;
+    std::vector<uint8_t> someData{'c', 'd', '\0'};
+    second.mMap[2] = someData;
+    second.setInt32(6, 5);
+    metadata.merge(second);
+
+    ASSERT_EQ(3, metadata.mMap.size());
+    ASSERT_EQ(someData, second.mMap[2]);
+    ASSERT_EQ(5, metadata.getInt32(6, 0));
+    ASSERT_EQ(2, metadata.getInt32(4, 0));
+
+    Parcel p;
+    metadata.writeToParcel(&p);
+    LayerMetadata reconstructed;
+    reconstructed.setInt32(3, 1); // to make sure it gets replaced
+    p.setDataPosition(0);
+    reconstructed.readFromParcel(&p);
+    ASSERT_EQ(metadata.mMap, reconstructed.mMap);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
new file mode 100644
index 0000000..0384d9d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/RefreshRateStats.h"
+#include "mock/DisplayHardware/MockDisplay.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace scheduler {
+
+class RefreshRateStatsTest : public testing::Test {
+protected:
+    static constexpr int CONFIG_ID_90 = 0;
+    static constexpr int CONFIG_ID_60 = 1;
+    static constexpr int64_t VSYNC_90 = 11111111;
+    static constexpr int64_t VSYNC_60 = 16666667;
+
+    RefreshRateStatsTest();
+    ~RefreshRateStatsTest();
+
+    void init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs);
+
+    std::unique_ptr<RefreshRateStats> mRefreshRateStats;
+};
+
+RefreshRateStatsTest::RefreshRateStatsTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+RefreshRateStatsTest::~RefreshRateStatsTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+void RefreshRateStatsTest::init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs) {
+    mRefreshRateStats = std::make_unique<RefreshRateStats>(configs);
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) {
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+    init(configs);
+
+    // There is one default config, so the refresh rates should have one item.
+    ASSERT_EQ(1, mRefreshRateStats->getTotalTimes().size());
+}
+
+TEST_F(RefreshRateStatsTest, oneConfigTest) {
+    auto display = new Hwc2::mock::Display();
+
+    auto config = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+    config.setVsyncPeriod(VSYNC_90);
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+    configs.push_back(config.build());
+
+    init(configs);
+
+    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(2, times.size());
+    ASSERT_EQ(0, times["ScreenOff"]);
+    ASSERT_EQ(0, times["90fps"]);
+    // Setting up tests on mobile harness can be flaky with time passing, so testing for
+    // exact time changes can result in flaxy numbers. To avoid that remember old
+    // numbers to make sure the correct values are increasing in the next test.
+    int screenOff = times["ScreenOff"];
+    int ninety = times["90fps"];
+
+    // Screen is off by default.
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(0, times["90fps"]);
+    screenOff = times["ScreenOff"];
+
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(screenOff, times["ScreenOff"]);
+    ASSERT_LT(ninety, times["90fps"]);
+    ninety = times["90fps"];
+
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    screenOff = times["ScreenOff"];
+
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
+    // does not update refresh rates that come from the config.
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+}
+
+TEST_F(RefreshRateStatsTest, twoConfigsTest) {
+    auto display = new Hwc2::mock::Display();
+
+    auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+    config90.setVsyncPeriod(VSYNC_90);
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+    configs.push_back(config90.build());
+
+    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+    config60.setVsyncPeriod(VSYNC_60);
+    configs.push_back(config60.build());
+
+    init(configs);
+
+    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(3, times.size());
+    ASSERT_EQ(0, times["ScreenOff"]);
+    ASSERT_EQ(0, times["60fps"]);
+    ASSERT_EQ(0, times["90fps"]);
+    // Setting up tests on mobile harness can be flaky with time passing, so testing for
+    // exact time changes can result in flaxy numbers. To avoid that remember old
+    // numbers to make sure the correct values are increasing in the next test.
+    int screenOff = times["ScreenOff"];
+    int sixty = times["60fps"];
+    int ninety = times["90fps"];
+
+    // Screen is off by default.
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(sixty, times["60fps"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    screenOff = times["ScreenOff"];
+
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(sixty, times["60fps"]);
+    ASSERT_LT(ninety, times["90fps"]);
+    ninety = times["90fps"];
+
+    // When power mode is normal, time for configs updates.
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    ASSERT_LT(sixty, times["60fps"]);
+    sixty = times["60fps"];
+
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(screenOff, times["ScreenOff"]);
+    ASSERT_LT(ninety, times["90fps"]);
+    ASSERT_EQ(sixty, times["60fps"]);
+    ninety = times["90fps"];
+
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    ASSERT_LT(sixty, times["60fps"]);
+    sixty = times["60fps"];
+
+    // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
+    // does not update refresh rates that come from the config.
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    ASSERT_EQ(sixty, times["60fps"]);
+    screenOff = times["ScreenOff"];
+
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    ASSERT_EQ(sixty, times["60fps"]);
+}
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
new file mode 100644
index 0000000..4d9aec6
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -0,0 +1,181 @@
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include <mutex>
+
+#include "Scheduler/EventControlThread.h"
+#include "Scheduler/EventThread.h"
+#include "Scheduler/Scheduler.h"
+#include "mock/MockEventThread.h"
+
+using testing::_;
+using testing::Return;
+
+namespace android {
+
+class SchedulerTest : public testing::Test {
+protected:
+    class MockEventThreadConnection : public android::EventThreadConnection {
+    public:
+        explicit MockEventThreadConnection(EventThread* eventThread)
+              : EventThreadConnection(eventThread, ResyncCallback()) {}
+        ~MockEventThreadConnection() = default;
+
+        MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
+        MOCK_METHOD1(setVsyncRate, status_t(uint32_t count));
+        MOCK_METHOD0(requestNextVsync, void());
+    };
+
+    /**
+     * This mock Scheduler class uses implementation of mock::EventThread but keeps everything else
+     * the same.
+     */
+    class MockScheduler : public android::Scheduler {
+    public:
+        MockScheduler(std::unique_ptr<EventThread> eventThread)
+              : Scheduler([](bool) {}), mEventThread(std::move(eventThread)) {}
+
+        std::unique_ptr<EventThread> makeEventThread(
+                const char* /* connectionName */, DispSync* /* dispSync */,
+                nsecs_t /* phaseOffsetNs */,
+                impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override {
+            return std::move(mEventThread);
+        }
+
+        MockScheduler() = default;
+        ~MockScheduler() override = default;
+
+        std::unique_ptr<EventThread> mEventThread;
+    };
+
+    SchedulerTest();
+    ~SchedulerTest() override;
+
+    sp<Scheduler::ConnectionHandle> mConnectionHandle;
+    mock::EventThread* mEventThread;
+    std::unique_ptr<MockScheduler> mScheduler;
+    sp<MockEventThreadConnection> mEventThreadConnection;
+};
+
+SchedulerTest::SchedulerTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+    std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>();
+    mEventThread = eventThread.get();
+    mScheduler = std::make_unique<MockScheduler>(std::move(eventThread));
+    EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
+
+    mEventThreadConnection = new MockEventThreadConnection(mEventThread);
+
+    // createConnection call to scheduler makes a createEventConnection call to EventThread. Make
+    // sure that call gets executed and returns an EventThread::Connection object.
+    EXPECT_CALL(*mEventThread, createEventConnection(_))
+            .WillRepeatedly(Return(mEventThreadConnection));
+
+    mConnectionHandle = mScheduler->createConnection("appConnection", 16, ResyncCallback(),
+                                                     impl::EventThread::InterceptVSyncsCallback());
+    EXPECT_TRUE(mConnectionHandle != nullptr);
+}
+
+SchedulerTest::~SchedulerTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+
+TEST_F(SchedulerTest, testNullPtr) {
+    // Passing a null pointer for ConnectionHandle is a valid argument. The code doesn't throw any
+    // exceptions, just gracefully continues.
+    sp<IDisplayEventConnection> returnedValue;
+    ASSERT_NO_FATAL_FAILURE(
+            returnedValue = mScheduler->createDisplayEventConnection(nullptr, ResyncCallback()));
+    EXPECT_TRUE(returnedValue == nullptr);
+    EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr);
+    EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr);
+    ASSERT_NO_FATAL_FAILURE(
+            mScheduler->hotplugReceived(nullptr, EventThread::DisplayType::Primary, false));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(nullptr));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(nullptr));
+    std::string testString;
+    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(nullptr, testString));
+    EXPECT_TRUE(testString == "");
+    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(nullptr, 10));
+}
+
+TEST_F(SchedulerTest, invalidConnectionHandle) {
+    // Passing an invalid ConnectionHandle is a valid argument. The code doesn't throw any
+    // exceptions, just gracefully continues.
+    sp<Scheduler::ConnectionHandle> connectionHandle = new Scheduler::ConnectionHandle(20);
+
+    sp<IDisplayEventConnection> returnedValue;
+    ASSERT_NO_FATAL_FAILURE(
+            returnedValue =
+                    mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback()));
+    EXPECT_TRUE(returnedValue == nullptr);
+    EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr);
+    EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr);
+
+    // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
+    EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(connectionHandle,
+                                                        EventThread::DisplayType::Primary, false));
+
+    EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(connectionHandle));
+
+    EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(connectionHandle));
+
+    std::string testString;
+    EXPECT_CALL(*mEventThread, dump(_)).Times(0);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(connectionHandle, testString));
+    EXPECT_TRUE(testString == "");
+
+    EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(connectionHandle, 10));
+}
+
+TEST_F(SchedulerTest, validConnectionHandle) {
+    sp<IDisplayEventConnection> returnedValue;
+    ASSERT_NO_FATAL_FAILURE(
+            returnedValue =
+                    mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback()));
+    EXPECT_TRUE(returnedValue != nullptr);
+    ASSERT_EQ(returnedValue, mEventThreadConnection);
+
+    EXPECT_TRUE(mScheduler->getEventThread(mConnectionHandle) != nullptr);
+    EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle) != nullptr);
+
+    EXPECT_CALL(*mEventThread, onHotplugReceived(EventThread::DisplayType::Primary, false))
+            .Times(1);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(mConnectionHandle,
+                                                        EventThread::DisplayType::Primary, false));
+
+    EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
+
+    EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle));
+
+    std::string testString("dump");
+    EXPECT_CALL(*mEventThread, dump(testString)).Times(1);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, testString));
+    EXPECT_TRUE(testString != "");
+
+    EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
+}
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
new file mode 100644
index 0000000..5865579
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <array>
+
+#include "Scheduler/SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+class SchedulerUtilsTest : public testing::Test {
+public:
+    SchedulerUtilsTest() = default;
+    ~SchedulerUtilsTest() override = default;
+};
+
+namespace {
+TEST_F(SchedulerUtilsTest, calculate_mean) {
+    std::array<int64_t, 30> testArray{};
+    // Calling the function on empty array returns 0.
+    EXPECT_EQ(0, calculate_mean(testArray));
+
+    testArray[0] = 33;
+    EXPECT_EQ(1, calculate_mean(testArray));
+    testArray[1] = 33;
+    testArray[2] = 33;
+    EXPECT_EQ(3, calculate_mean(testArray));
+    testArray[3] = 42;
+    EXPECT_EQ(4, calculate_mean(testArray));
+    testArray[4] = 33;
+    EXPECT_EQ(5, calculate_mean(testArray));
+    testArray[5] = 42;
+    EXPECT_EQ(7, calculate_mean(testArray));
+    for (int i = 6; i < 30; i++) {
+        testArray[i] = 33;
+    }
+    EXPECT_EQ(33, calculate_mean(testArray));
+}
+
+TEST_F(SchedulerUtilsTest, calculate_median) {
+    std::vector<int64_t> testVector;
+    // Calling the function on empty vector returns 0.
+    EXPECT_EQ(0, calculate_median(&testVector));
+
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(33);
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_median(&testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(42, calculate_median(&testVector));
+    testVector.push_back(60);
+    EXPECT_EQ(42, calculate_median(&testVector));
+    testVector.push_back(60);
+    EXPECT_EQ(42, calculate_median(&testVector));
+    testVector.push_back(33);
+    EXPECT_EQ(42, calculate_median(&testVector));
+    testVector.push_back(33);
+    EXPECT_EQ(42, calculate_median(&testVector));
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_median(&testVector));
+}
+
+TEST_F(SchedulerUtilsTest, calculate_mode) {
+    std::vector<int64_t> testVector;
+    // Calling the function on empty vector returns 0.
+    EXPECT_EQ(0, calculate_mode(testVector));
+
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(33);
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(33);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(60);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(60);
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(33);
+    // 5 occurences of 33.
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    // 5 occurences of 33, 5 occurences of 42. We choose the first one.
+    EXPECT_EQ(33, calculate_mode(testVector));
+    testVector.push_back(42);
+    // 5 occurences of 33, 6 occurences of 42.
+    EXPECT_EQ(42, calculate_mode(testVector));
+    testVector.push_back(42);
+    // 5 occurences of 33, 7 occurences of 42.
+    EXPECT_EQ(42, calculate_mode(testVector));
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 558845f..3a62c40 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -16,46 +16,200 @@
 
 #pragma once
 
+#include <compositionengine/impl/CompositionEngine.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "ColorLayer.h"
+#include "ContainerLayer.h"
 #include "DisplayDevice.h"
+#include "FakePhaseOffsets.h"
+#include "Layer.h"
+#include "NativeWindowSurface.h"
+#include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
+#include "SurfaceFlingerFactory.h"
+#include "SurfaceInterceptor.h"
+
+#include "TimeStats/TimeStats.h"
 
 namespace android {
 
 class EventThread;
 
-namespace RE {
+namespace renderengine {
+
 class RenderEngine;
-}
+
+} // namespace renderengine
 
 namespace Hwc2 {
+
 class Composer;
-}
+
+} // namespace Hwc2
+
+namespace surfaceflinger::test {
+
+class Factory final : public surfaceflinger::Factory {
+public:
+    ~Factory() = default;
+
+    std::unique_ptr<DispSync> createDispSync(const char*, bool, int64_t) override {
+        // TODO: Use test-fixture controlled factory
+        return nullptr;
+    }
+
+    std::unique_ptr<EventControlThread> createEventControlThread(
+            std::function<void(bool)>) override {
+        // TODO: Use test-fixture controlled factory
+        return nullptr;
+    }
+
+    std::unique_ptr<HWComposer> createHWComposer(const std::string&) override {
+        // TODO: Use test-fixture controlled factory
+        return nullptr;
+    }
+
+    std::unique_ptr<MessageQueue> createMessageQueue() override {
+        // TODO: Use test-fixture controlled factory
+        return std::make_unique<android::impl::MessageQueue>();
+    }
+
+    std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+        return std::make_unique<scheduler::FakePhaseOffsets>();
+    }
+
+    std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>) override {
+        // TODO: Use test-fixture controlled factory
+        return nullptr;
+    }
+
+    std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger* flinger) override {
+        // TODO: Use test-fixture controlled factory
+        return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+    }
+
+    sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
+        // TODO: Use test-fixture controlled factory
+        return new StartPropertySetThread(timestampPropertyValue);
+    }
+
+    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override {
+        // TODO: Use test-fixture controlled factory
+        return new DisplayDevice(std::move(creationArgs));
+    }
+
+    sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
+                                          uint32_t layerCount, uint64_t usage,
+                                          std::string requestorName) override {
+        // TODO: Use test-fixture controlled factory
+        return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
+    }
+
+    void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+                           sp<IGraphicBufferConsumer>* outConsumer,
+                           bool consumerIsSurfaceFlinger) override {
+        if (!mCreateBufferQueue) return;
+        mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
+    }
+
+    std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+            const sp<IGraphicBufferProducer>& producer) override {
+        if (!mCreateNativeWindowSurface) return nullptr;
+        return mCreateNativeWindowSurface(producer);
+    }
+
+    std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override {
+        return compositionengine::impl::createCompositionEngine();
+    }
+
+    sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs&) override {
+        // TODO: Use test-fixture controlled factory
+        return nullptr;
+    }
+
+    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs&) override {
+        // TODO: Use test-fixture controlled factory
+        return nullptr;
+    }
+
+    sp<ColorLayer> createColorLayer(const LayerCreationArgs&) override {
+        // TODO: Use test-fixture controlled factory
+        return nullptr;
+    }
+
+    sp<ContainerLayer> createContainerLayer(const LayerCreationArgs&) override {
+        // TODO: Use test-fixture controlled factory
+        return nullptr;
+    }
+
+    std::unique_ptr<TimeStats> createTimeStats() override {
+        // TODO: Use test-fixture controlled factory
+        return std::make_unique<TimeStats>();
+    }
+
+    using CreateBufferQueueFunction =
+            std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
+                               sp<IGraphicBufferConsumer>* /* outConsumer */,
+                               bool /* consumerIsSurfaceFlinger */)>;
+    CreateBufferQueueFunction mCreateBufferQueue;
+
+    using CreateNativeWindowSurfaceFunction =
+            std::function<std::unique_ptr<surfaceflinger::NativeWindowSurface>(
+                    const sp<IGraphicBufferProducer>&)>;
+    CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface;
+
+    using CreateCompositionEngineFunction =
+            std::function<std::unique_ptr<compositionengine::CompositionEngine>()>;
+    CreateCompositionEngineFunction mCreateCompositionEngine;
+};
+
+} // namespace surfaceflinger::test
 
 class TestableSurfaceFlinger {
 public:
     // Extend this as needed for accessing SurfaceFlinger private (and public)
     // functions.
 
-    void setupRenderEngine(std::unique_ptr<RE::RenderEngine> renderEngine) {
-        mFlinger->getBE().mRenderEngine = std::move(renderEngine);
+    void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
+        mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine));
     }
 
     void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
-        mFlinger->getBE().mHwc.reset(new HWComposer(std::move(composer)));
+        mFlinger->mCompositionEngine->setHwComposer(
+                std::make_unique<impl::HWComposer>(std::move(composer)));
     }
 
-    using CreateBufferQueueFunction = SurfaceFlinger::CreateBufferQueueFunction;
+    using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
     void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
-        mFlinger->mCreateBufferQueue = f;
+        mFactory.mCreateBufferQueue = f;
     }
 
-    using CreateNativeWindowSurfaceFunction = SurfaceFlinger::CreateNativeWindowSurfaceFunction;
+    using CreateNativeWindowSurfaceFunction =
+            surfaceflinger::test::Factory::CreateNativeWindowSurfaceFunction;
     void setCreateNativeWindowSurface(CreateNativeWindowSurfaceFunction f) {
-        mFlinger->mCreateNativeWindowSurface = f;
+        mFactory.mCreateNativeWindowSurface = f;
+    }
+
+    void setInternalDisplayPrimaries(const ui::DisplayPrimaries& primaries) {
+        memcpy(&mFlinger->mInternalDisplayPrimaries, &primaries, sizeof(ui::DisplayPrimaries));
     }
 
     using HotplugEvent = SurfaceFlinger::HotplugEvent;
 
+    auto& mutableLayerCurrentState(sp<Layer> layer) { return layer->mCurrentState; }
+    auto& mutableLayerDrawingState(sp<Layer> layer) { return layer->mDrawingState; }
+
+    void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) {
+        layer->mDrawingState.sidebandStream = sidebandStream;
+        layer->getBE().compositionInfo.hwc.sidebandStream = sidebandStream;
+    }
+
+    void setLayerPotentialCursor(sp<Layer> layer, bool potentialCursor) {
+        layer->mPotentialCursor = potentialCursor;
+    }
+
     /* ------------------------------------------------------------------------
      * Forwarding for functions being tested
      */
@@ -64,15 +218,18 @@
         return mFlinger->createDisplay(displayName, secure);
     }
 
-    auto destroyDisplay(const sp<IBinder>& display) { return mFlinger->destroyDisplay(display); }
+    auto destroyDisplay(const sp<IBinder>& displayToken) {
+        return mFlinger->destroyDisplay(displayToken);
+    }
 
     auto resetDisplayState() { return mFlinger->resetDisplayState(); }
 
-    auto setupNewDisplayDeviceInternal(const wp<IBinder>& display, int hwcId,
+    auto setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken,
+                                       const std::optional<DisplayId>& displayId,
                                        const DisplayDeviceState& state,
-                                       const sp<DisplaySurface>& dispSurface,
+                                       const sp<compositionengine::DisplaySurface>& dispSurface,
                                        const sp<IGraphicBufferProducer>& producer) {
-        return mFlinger->setupNewDisplayDeviceInternal(display, hwcId, state, dispSurface,
+        return mFlinger->setupNewDisplayDeviceInternal(displayToken, displayId, state, dispSurface,
                                                        producer);
     }
 
@@ -87,10 +244,38 @@
 
     auto setDisplayStateLocked(const DisplayState& s) { return mFlinger->setDisplayStateLocked(s); }
 
-    auto onInitializeDisplays() { return mFlinger->onInitializeDisplays(); }
+    // Allow reading display state without locking, as if called on the SF main thread.
+    auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
+        return mFlinger->onInitializeDisplays();
+    }
 
-    auto setPowerModeInternal(const sp<DisplayDevice>& hw, int mode, bool stateLockHeld = false) {
-        return mFlinger->setPowerModeInternal(hw, mode, stateLockHeld);
+    // Allow reading display state without locking, as if called on the SF main thread.
+    auto setPowerModeInternal(const sp<DisplayDevice>& display,
+                              int mode) NO_THREAD_SAFETY_ANALYSIS {
+        return mFlinger->setPowerModeInternal(display, mode);
+    }
+
+    auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what); }
+
+    auto captureScreenImplLocked(const RenderArea& renderArea,
+                                 TraverseLayersFunction traverseLayers, ANativeWindowBuffer* buffer,
+                                 bool useIdentityTransform, bool forSystem, int* outSyncFd) {
+        return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer,
+                                                 useIdentityTransform, forSystem, outSyncFd);
+    }
+
+    auto traverseLayersInDisplay(const sp<const DisplayDevice>& display,
+                                 const LayerVector::Visitor& visitor) {
+        return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor);
+    }
+
+    auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+                                   ui::DisplayPrimaries &primaries) {
+        return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
+    }
+
+    void initDefaultDisplayNativePrimaries() {
+        mFlinger->SurfaceFlinger::initDefaultDisplayNativePrimaries();
     }
 
     /* ------------------------------------------------------------------------
@@ -101,6 +286,9 @@
     const auto& getHasPoweredOff() const { return mFlinger->mHasPoweredOff; }
     const auto& getHWVsyncAvailable() const { return mFlinger->mHWVsyncAvailable; }
     const auto& getVisibleRegionsDirty() const { return mFlinger->mVisibleRegionsDirty; }
+    auto& getHwComposer() const {
+        return static_cast<impl::HWComposer&>(mFlinger->getHwComposer());
+    }
 
     const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
 
@@ -110,26 +298,34 @@
      */
 
     auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; }
+    auto& mutablePrimaryDisplayOrientation() { return SurfaceFlinger::primaryDisplayOrientation; }
+    auto& mutableUseColorManagement() { return SurfaceFlinger::useColorManagement; }
 
-    auto& mutableBuiltinDisplays() { return mFlinger->mBuiltinDisplays; }
     auto& mutableCurrentState() { return mFlinger->mCurrentState; }
-    auto& mutableDisplays() { return mFlinger->mDisplays; }
     auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
+    auto& mutableDisplays() { return mFlinger->mDisplays; }
     auto& mutableDrawingState() { return mFlinger->mDrawingState; }
     auto& mutableEventControlThread() { return mFlinger->mEventControlThread; }
     auto& mutableEventQueue() { return mFlinger->mEventQueue; }
     auto& mutableEventThread() { return mFlinger->mEventThread; }
+    auto& mutableSFEventThread() { return mFlinger->mSFEventThread; }
+    auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; }
     auto& mutableHWVsyncAvailable() { return mFlinger->mHWVsyncAvailable; }
     auto& mutableInterceptor() { return mFlinger->mInterceptor; }
     auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
     auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
+    auto& mutablePhysicalDisplayTokens() { return mFlinger->mPhysicalDisplayTokens; }
+    auto& mutablePrimaryDispSync() { return mFlinger->mPrimaryDispSync; }
     auto& mutablePrimaryHWVsyncEnabled() { return mFlinger->mPrimaryHWVsyncEnabled; }
+    auto& mutableTexturePool() { return mFlinger->mTexturePool; }
     auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
     auto& mutableUseHwcVirtualDisplays() { return mFlinger->mUseHwcVirtualDisplays; }
 
     auto& mutableComposerSequenceId() { return mFlinger->getBE().mComposerSequenceId; }
-    auto& mutableHwcDisplayData() { return mFlinger->getBE().mHwc->mDisplayData; }
-    auto& mutableHwcDisplaySlots() { return mFlinger->getBE().mHwc->mHwcDisplaySlots; }
+    auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
+    auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
+    auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
+    auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
 
     ~TestableSurfaceFlinger() {
         // All these pointer and container clears help ensure that GMock does
@@ -140,9 +336,12 @@
         mutableEventControlThread().reset();
         mutableEventQueue().reset();
         mutableEventThread().reset();
+        mutableSFEventThread().reset();
         mutableInterceptor().reset();
-        mFlinger->getBE().mHwc.reset();
-        mFlinger->getBE().mRenderEngine.reset();
+        mutablePrimaryDispSync().reset();
+        mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
+        mFlinger->mCompositionEngine->setRenderEngine(
+                std::unique_ptr<renderengine::RenderEngine>());
     }
 
     /* ------------------------------------------------------------------------
@@ -153,14 +352,14 @@
     public:
         FakePowerAdvisor() = default;
         ~FakePowerAdvisor() override = default;
-        void setExpensiveRenderingExpected(hwc2_display_t, bool) override { }
+        void setExpensiveRenderingExpected(hwc2_display_t, bool) override {}
     };
 
-    struct HWC2Display : public HWC2::Display {
+    struct HWC2Display : public HWC2::impl::Display {
         HWC2Display(Hwc2::Composer& composer, Hwc2::PowerAdvisor& advisor,
                     const std::unordered_set<HWC2::Capability>& capabilities, hwc2_display_t id,
                     HWC2::DisplayType type)
-              : HWC2::Display(composer, advisor, capabilities, id, type) {}
+              : HWC2::impl::Display(composer, advisor, capabilities, id, type) {}
         ~HWC2Display() {
             // Prevents a call to disable vsyncs.
             mType = HWC2::DisplayType::Invalid;
@@ -168,6 +367,7 @@
 
         auto& mutableIsConnected() { return this->mIsConnected; }
         auto& mutableConfigs() { return this->mConfigs; }
+        auto& mutableLayers() { return this->mLayers; }
     };
 
     class FakeHwcDisplayInjector {
@@ -179,8 +379,9 @@
         static constexpr int32_t DEFAULT_DPI = 320;
         static constexpr int32_t DEFAULT_ACTIVE_CONFIG = 0;
 
-        FakeHwcDisplayInjector(DisplayDevice::DisplayType type, HWC2::DisplayType hwcDisplayType)
-              : mType(type), mHwcDisplayType(hwcDisplayType) {}
+        FakeHwcDisplayInjector(DisplayId displayId, HWC2::DisplayType hwcDisplayType,
+                               bool isPrimary)
+              : mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {}
 
         auto& setHwcDisplayId(hwc2_display_t displayId) {
             mHwcDisplayId = displayId;
@@ -249,17 +450,22 @@
             display->mutableConfigs().emplace(mActiveConfig, config.build());
             display->mutableIsConnected() = true;
 
-            ASSERT_TRUE(flinger->mutableHwcDisplayData().size() > static_cast<size_t>(mType));
-            flinger->mutableHwcDisplayData()[mType].reset();
-            flinger->mutableHwcDisplayData()[mType].hwcDisplay = display.get();
-            flinger->mutableHwcDisplaySlots().emplace(mHwcDisplayId, mType);
+            flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = display.get();
+
+            if (mHwcDisplayType == HWC2::DisplayType::Physical) {
+                flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId);
+                (mIsPrimary ? flinger->mutableInternalHwcDisplayId()
+                            : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
+            }
 
             flinger->mFakeHwcDisplays.push_back(std::move(display));
         }
 
     private:
-        DisplayDevice::DisplayType mType;
-        HWC2::DisplayType mHwcDisplayType;
+        const DisplayId mDisplayId;
+        const HWC2::DisplayType mHwcDisplayType;
+        const bool mIsPrimary;
+
         hwc2_display_t mHwcDisplayId = DEFAULT_HWC_DISPLAY_ID;
         int32_t mWidth = DEFAULT_WIDTH;
         int32_t mHeight = DEFAULT_HEIGHT;
@@ -273,9 +479,13 @@
 
     class FakeDisplayDeviceInjector {
     public:
-        FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger, DisplayDevice::DisplayType type,
-                                  int hwcId)
-              : mFlinger(flinger), mType(type), mHwcId(hwcId) {}
+        FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
+                                  const std::optional<DisplayId>& displayId, bool isVirtual,
+                                  bool isPrimary)
+              : mFlinger(flinger), mCreationArgs(flinger.mFlinger.get(), mDisplayToken, displayId) {
+            mCreationArgs.isVirtual = isVirtual;
+            mCreationArgs.isPrimary = isPrimary;
+        }
 
         sp<IBinder> token() const { return mDisplayToken; }
 
@@ -295,43 +505,53 @@
             return mFlinger.mutableCurrentState().displays.valueFor(mDisplayToken);
         }
 
-        auto& mutableDisplayDevice() { return mFlinger.mutableDisplays().valueFor(mDisplayToken); }
+        auto& mutableDisplayDevice() { return mFlinger.mutableDisplays()[mDisplayToken]; }
 
         auto& setNativeWindow(const sp<ANativeWindow>& nativeWindow) {
-            mNativeWindow = nativeWindow;
+            mCreationArgs.nativeWindow = nativeWindow;
             return *this;
         }
 
-        auto& setDisplaySurface(const sp<DisplaySurface>& displaySurface) {
-            mDisplaySurface = displaySurface;
-            return *this;
-        }
-
-        auto& setRenderSurface(std::unique_ptr<RE::Surface> renderSurface) {
-            mRenderSurface = std::move(renderSurface);
+        auto& setDisplaySurface(const sp<compositionengine::DisplaySurface>& displaySurface) {
+            mCreationArgs.displaySurface = displaySurface;
             return *this;
         }
 
         auto& setSecure(bool secure) {
-            mSecure = secure;
+            mCreationArgs.isSecure = secure;
+            return *this;
+        }
+
+        auto& setPowerMode(int mode) {
+            mCreationArgs.initialPowerMode = mode;
+            return *this;
+        }
+
+        auto& setHwcColorModes(
+                const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>
+                        hwcColorModes) {
+            mCreationArgs.hwcColorModes = hwcColorModes;
+            return *this;
+        }
+
+        auto& setHasWideColorGamut(bool hasWideColorGamut) {
+            mCreationArgs.hasWideColorGamut = hasWideColorGamut;
             return *this;
         }
 
         sp<DisplayDevice> inject() {
-            std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hdrAndRenderIntents;
-            sp<DisplayDevice> device =
-                    new DisplayDevice(mFlinger.mFlinger.get(), mType, mHwcId, mSecure, mDisplayToken,
-                                      mNativeWindow, mDisplaySurface, std::move(mRenderSurface), 0,
-                                      0, false, HdrCapabilities(), 0, hdrAndRenderIntents,
-                                      HWC_POWER_MODE_NORMAL);
-            mFlinger.mutableDisplays().add(mDisplayToken, device);
+            DisplayDeviceState state;
+            state.displayId = mCreationArgs.isVirtual ? std::nullopt : mCreationArgs.displayId;
+            state.isSecure = mCreationArgs.isSecure;
 
-            DisplayDeviceState state(mType, mSecure);
+            sp<DisplayDevice> device = new DisplayDevice(std::move(mCreationArgs));
+            mFlinger.mutableDisplays().emplace(mDisplayToken, device);
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
             mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
 
-            if (mType >= DisplayDevice::DISPLAY_PRIMARY && mType < DisplayDevice::DISPLAY_VIRTUAL) {
-                mFlinger.mutableBuiltinDisplays()[mType] = mDisplayToken;
+            if (!mCreationArgs.isVirtual) {
+                LOG_ALWAYS_FATAL_IF(!state.displayId);
+                mFlinger.mutablePhysicalDisplayTokens()[*state.displayId] = mDisplayToken;
             }
 
             return device;
@@ -340,15 +560,11 @@
     private:
         TestableSurfaceFlinger& mFlinger;
         sp<BBinder> mDisplayToken = new BBinder();
-        DisplayDevice::DisplayType mType;
-        int mHwcId;
-        sp<ANativeWindow> mNativeWindow;
-        sp<DisplaySurface> mDisplaySurface;
-        std::unique_ptr<RE::Surface> mRenderSurface;
-        bool mSecure = false;
+        DisplayDeviceCreationArgs mCreationArgs;
     };
 
-    sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(SurfaceFlinger::SkipInitialization);
+    surfaceflinger::test::Factory mFactory;
+    sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
 
     // We need to keep a reference to these so they are properly destroyed.
     std::vector<std::unique_ptr<HWC2Display>> mFakeHwcDisplays;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
new file mode 100644
index 0000000..0f95cf9
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -0,0 +1,517 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include <random>
+
+#include "TimeStats/TimeStats.h"
+
+#include "libsurfaceflinger_unittest_main.h"
+
+using namespace android::surfaceflinger;
+using namespace google::protobuf;
+
+namespace android {
+namespace {
+
+// clang-format off
+#define FMT_PROTO          true
+#define FMT_STRING         false
+#define LAYER_ID_0         0
+#define LAYER_ID_1         1
+#define LAYER_ID_INVALID   -1
+#define NUM_LAYERS         1
+#define NUM_LAYERS_INVALID "INVALID"
+
+enum InputCommand : int32_t {
+    ENABLE                 = 0,
+    DISABLE                = 1,
+    CLEAR                  = 2,
+    DUMP_ALL               = 3,
+    DUMP_MAXLAYERS_1       = 4,
+    DUMP_MAXLAYERS_INVALID = 5,
+    INPUT_COMMAND_BEGIN    = ENABLE,
+    INPUT_COMMAND_END      = DUMP_MAXLAYERS_INVALID,
+    INPUT_COMMAND_RANGE    = INPUT_COMMAND_END - INPUT_COMMAND_BEGIN + 1,
+};
+
+enum TimeStamp : int32_t {
+    POST                   = 0,
+    ACQUIRE                = 1,
+    ACQUIRE_FENCE          = 2,
+    LATCH                  = 3,
+    DESIRED                = 4,
+    PRESENT                = 5,
+    PRESENT_FENCE          = 6,
+    TIME_STAMP_BEGIN       = POST,
+    TIME_STAMP_END         = PRESENT,
+    TIME_STAMP_RANGE       = TIME_STAMP_END - TIME_STAMP_BEGIN + 1,
+};
+
+static const TimeStamp NORMAL_SEQUENCE[] = {
+        TimeStamp::POST,
+        TimeStamp::ACQUIRE,
+        TimeStamp::LATCH,
+        TimeStamp::DESIRED,
+        TimeStamp::PRESENT,
+};
+
+static const TimeStamp NORMAL_SEQUENCE_2[] = {
+        TimeStamp::POST,
+        TimeStamp::ACQUIRE_FENCE,
+        TimeStamp::LATCH,
+        TimeStamp::DESIRED,
+        TimeStamp::PRESENT_FENCE,
+};
+
+static const TimeStamp UNORDERED_SEQUENCE[] = {
+        TimeStamp::ACQUIRE,
+        TimeStamp::LATCH,
+        TimeStamp::POST,
+        TimeStamp::DESIRED,
+        TimeStamp::PRESENT,
+};
+
+static const TimeStamp INCOMPLETE_SEQUENCE[] = {
+        TimeStamp::POST,
+};
+// clang-format on
+
+class TimeStatsTest : public testing::Test {
+public:
+    TimeStatsTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    ~TimeStatsTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    std::string inputCommand(InputCommand cmd, bool useProto);
+
+    void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts);
+
+    int32_t genRandomInt32(int32_t begin, int32_t end);
+
+    template <size_t N>
+    void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
+                          nsecs_t ts) {
+        for (size_t i = 0; i < N; i++, ts += 1000000) {
+            setTimeStamp(sequence[i], id, frameNumber, ts);
+        }
+    }
+
+    std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
+    std::unique_ptr<TimeStats> mTimeStats = std::make_unique<TimeStats>();
+};
+
+std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
+    std::string result;
+    Vector<String16> args;
+
+    switch (cmd) {
+        case InputCommand::ENABLE:
+            args.push_back(String16("-enable"));
+            break;
+        case InputCommand::DISABLE:
+            args.push_back(String16("-disable"));
+            break;
+        case InputCommand::CLEAR:
+            args.push_back(String16("-clear"));
+            break;
+        case InputCommand::DUMP_ALL:
+            args.push_back(String16("-dump"));
+            break;
+        case InputCommand::DUMP_MAXLAYERS_1:
+            args.push_back(String16("-dump"));
+            args.push_back(String16("-maxlayers"));
+            args.push_back(String16(std::to_string(NUM_LAYERS).c_str()));
+            break;
+        case InputCommand::DUMP_MAXLAYERS_INVALID:
+            args.push_back(String16("-dump"));
+            args.push_back(String16("-maxlayers"));
+            args.push_back(String16(NUM_LAYERS_INVALID));
+            break;
+        default:
+            ALOGD("Invalid control command");
+    }
+
+    EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, result));
+    return result;
+}
+
+static std::string genLayerName(int32_t layerID) {
+    return (layerID < 0 ? "invalid.dummy" : "com.dummy#") + std::to_string(layerID);
+}
+
+void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
+    switch (type) {
+        case TimeStamp::POST:
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts));
+            break;
+        case TimeStamp::ACQUIRE:
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts));
+            break;
+        case TimeStamp::ACQUIRE_FENCE:
+            ASSERT_NO_FATAL_FAILURE(
+                    mTimeStats->setAcquireFence(id, frameNumber, std::make_shared<FenceTime>(ts)));
+            break;
+        case TimeStamp::LATCH:
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setLatchTime(id, frameNumber, ts));
+            break;
+        case TimeStamp::DESIRED:
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts));
+            break;
+        case TimeStamp::PRESENT:
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts));
+            break;
+        case TimeStamp::PRESENT_FENCE:
+            ASSERT_NO_FATAL_FAILURE(
+                    mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts)));
+            break;
+        default:
+            ALOGD("Invalid timestamp type");
+    }
+}
+
+int32_t TimeStatsTest::genRandomInt32(int32_t begin, int32_t end) {
+    std::uniform_int_distribution<int32_t> distr(begin, end);
+    return distr(mRandomEngine);
+}
+
+TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    ASSERT_TRUE(mTimeStats->isEnabled());
+
+    EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
+    ASSERT_FALSE(mTimeStats->isEnabled());
+}
+
+TEST_F(TimeStatsTest, canIncreaseGlobalStats) {
+    constexpr size_t TOTAL_FRAMES = 5;
+    constexpr size_t MISSED_FRAMES = 4;
+    constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    for (size_t i = 0; i < TOTAL_FRAMES; i++) {
+        ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames());
+    }
+    for (size_t i = 0; i < MISSED_FRAMES; i++) {
+        ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
+    }
+    for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) {
+        ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
+    }
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_TRUE(globalProto.has_total_frames());
+    EXPECT_EQ(TOTAL_FRAMES, globalProto.total_frames());
+    ASSERT_TRUE(globalProto.has_missed_frames());
+    EXPECT_EQ(MISSED_FRAMES, globalProto.missed_frames());
+    ASSERT_TRUE(globalProto.has_client_composition_frames());
+    EXPECT_EQ(CLIENT_COMPOSITION_FRAMES, globalProto.client_composition_frames());
+}
+
+TEST_F(TimeStatsTest, canInsertGlobalPresentToPresent) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(2000000)));
+
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000)));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000)));
+
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_OFF));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(6000000)));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(8000000)));
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.present_to_present_size());
+    const SFTimeStatsHistogramBucketProto& histogramProto = globalProto.present_to_present().Get(0);
+    EXPECT_EQ(1, histogramProto.frame_count());
+    EXPECT_EQ(2, histogramProto.time_millis());
+}
+
+TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_layer_name());
+    EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(1, layerProto.total_frames());
+    ASSERT_EQ(6, layerProto.deltas_size());
+    for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
+        ASSERT_EQ(1, deltaProto.histograms_size());
+        const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+        EXPECT_EQ(1, histogramProto.frame_count());
+        if ("post2acquire" == deltaProto.delta_name()) {
+            EXPECT_EQ(1, histogramProto.time_millis());
+        } else if ("post2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(4, histogramProto.time_millis());
+        } else if ("acquire2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(3, histogramProto.time_millis());
+        } else if ("latch2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(2, histogramProto.time_millis());
+        } else if ("desired2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(1, histogramProto.time_millis());
+        } else if ("present2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(1, histogramProto.time_millis());
+        } else {
+            FAIL() << "Unknown delta_name: " << deltaProto.delta_name();
+        }
+    }
+}
+
+TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_INVALID, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_INVALID, 2, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canInsertMultipleLayersTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    EXPECT_EQ(2, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canInsertUnorderedLayerTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(UNORDERED_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_layer_name());
+    EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(1, layerProto.total_frames());
+    ASSERT_EQ(6, layerProto.deltas_size());
+    for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
+        ASSERT_EQ(1, deltaProto.histograms_size());
+        const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+        EXPECT_EQ(1, histogramProto.frame_count());
+        if ("post2acquire" == deltaProto.delta_name()) {
+            EXPECT_EQ(0, histogramProto.time_millis());
+        } else if ("post2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(2, histogramProto.time_millis());
+        } else if ("acquire2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(2, histogramProto.time_millis());
+        } else if ("latch2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(2, histogramProto.time_millis());
+        } else if ("desired2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(1, histogramProto.time_millis());
+        } else if ("present2present" == deltaProto.delta_name()) {
+            EXPECT_EQ(1, histogramProto.time_millis());
+        } else {
+            FAIL() << "Unknown delta_name: " << deltaProto.delta_name();
+        }
+    }
+}
+
+TEST_F(TimeStatsTest, canRemoveTimeRecord) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(0, 2));
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canRecoverFromIncompleteTimeRecordError) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    uint64_t frameNumber = 1;
+    nsecs_t ts = 1000000;
+    insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    for (size_t i = 0; i < TimeStats::MAX_NUM_TIME_RECORDS + 2; i++) {
+        frameNumber++;
+        ts += 1000000;
+        insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, frameNumber, ts);
+    }
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, layerTimeStatsOnDestroy) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(0));
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canClearTimeStats) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames());
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
+    ASSERT_NO_FATAL_FAILURE(
+            mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(2000000)));
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+    EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    EXPECT_EQ(0, globalProto.total_frames());
+    EXPECT_EQ(0, globalProto.missed_frames());
+    EXPECT_EQ(0, globalProto.client_composition_frames());
+    EXPECT_EQ(0, globalProto.present_to_present_size());
+    EXPECT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 3, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(
+            globalProto.ParseFromString(inputCommand(InputCommand::DUMP_MAXLAYERS_1, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.stats_size());
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    ASSERT_TRUE(layerProto.has_layer_name());
+    EXPECT_EQ(genLayerName(LAYER_ID_1), layerProto.layer_name());
+    ASSERT_TRUE(layerProto.has_total_frames());
+    EXPECT_EQ(2, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canDumpWithInvalidMaxLayers) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(
+            inputCommand(InputCommand::DUMP_MAXLAYERS_INVALID, FMT_PROTO)));
+
+    ASSERT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canSurviveMonkey) {
+    if (g_noSlowTests) {
+        GTEST_SKIP();
+    }
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    for (size_t i = 0; i < 10000000; ++i) {
+        const int32_t layerID = genRandomInt32(-1, 10);
+        const int32_t frameNumber = genRandomInt32(1, 10);
+        switch (genRandomInt32(0, 100)) {
+            case 0:
+                ALOGV("removeTimeRecord");
+                ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerID, frameNumber));
+                continue;
+            case 1:
+                ALOGV("onDestroy");
+                ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerID));
+                continue;
+        }
+        TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
+        const int32_t ts = genRandomInt32(1, 1000000000);
+        ALOGV("type[%d], layerID[%d], frameNumber[%d], ts[%d]", type, layerID, frameNumber, ts);
+        setTimeStamp(type, layerID, frameNumber, ts);
+    }
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp
new file mode 100644
index 0000000..bc1f00d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "libsurfaceflinger_unittest_main.h"
+
+// ------------------------------------------------------------------------
+// To pass extra command line arguments to the Google Test executable from
+// atest, you have to use this somewhat verbose syntax:
+//
+// clang-format off
+//
+//     atest libsurfaceflinger_unittest -- --module-arg libsurfaceflinger_unittest:native-test-flag:<--flag>[:<value>]
+//
+// For example:
+//
+//     atest libsurfaceflinger_unittest -- --module-arg libsurfaceflinger_unittest:native-test-flag:--no-slow
+//
+// clang-format on
+//  ------------------------------------------------------------------------
+
+// Set to true if "--no-slow" is passed to the test.
+bool g_noSlowTests = false;
+
+int main(int argc, char **argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    for (int i = 1; i < argc; i++) {
+        if (strcmp(argv[i], "--no-slow") == 0) {
+            g_noSlowTests = true;
+        }
+    }
+
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.h
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.h
index e6ac6bf..e742c50 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,7 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
 
-namespace android {
-namespace mock {
-
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
-
-} // namespace mock
-} // namespace android
+// Set to true if "--no-slow" is passed to the test.
+extern bool g_noSlowTests;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 267670a..e6f1a06 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -28,12 +28,12 @@
 namespace mock {
 
 using android::hardware::graphics::common::V1_0::ColorTransform;
-using android::hardware::graphics::common::V1_0::Hdr;
 using android::hardware::graphics::common::V1_0::Transform;
-using android::hardware::graphics::common::V1_1::ColorMode;
-using android::hardware::graphics::common::V1_1::Dataspace;
-using android::hardware::graphics::common::V1_1::PixelFormat;
 using android::hardware::graphics::common::V1_1::RenderIntent;
+using android::hardware::graphics::common::V1_2::ColorMode;
+using android::hardware::graphics::common::V1_2::Dataspace;
+using android::hardware::graphics::common::V1_2::Hdr;
+using android::hardware::graphics::common::V1_2::PixelFormat;
 
 using android::hardware::graphics::composer::V2_1::Config;
 using android::hardware::graphics::composer::V2_1::Display;
@@ -41,7 +41,7 @@
 using android::hardware::graphics::composer::V2_1::IComposer;
 using android::hardware::graphics::composer::V2_1::IComposerCallback;
 using android::hardware::graphics::composer::V2_1::Layer;
-using android::hardware::graphics::composer::V2_2::IComposerClient;
+using android::hardware::graphics::composer::V2_3::IComposerClient;
 
 class Composer : public Hwc2::Composer {
 public:
@@ -74,9 +74,10 @@
     MOCK_METHOD2(getDisplayType, Error(Display, IComposerClient::DisplayType*));
     MOCK_METHOD2(getDozeSupport, Error(Display, bool*));
     MOCK_METHOD5(getHdrCapabilities, Error(Display, std::vector<Hdr>*, float*, float*, float*));
-    MOCK_METHOD2(getPerFrameMetadataKeys,
-                 Error(Display, std::vector<IComposerClient::PerFrameMetadataKey>*));
+    MOCK_METHOD1(getPerFrameMetadataKeys,
+                 std::vector<IComposerClient::PerFrameMetadataKey>(Display));
     MOCK_METHOD2(getDataspaceSaturationMatrix, Error(Dataspace, mat4*));
+    MOCK_METHOD3(getDisplayIdentificationData, Error(Display, uint8_t*, std::vector<uint8_t>*));
     MOCK_METHOD3(getReleaseFences, Error(Display, std::vector<Layer>*, std::vector<int>*));
     MOCK_METHOD2(presentDisplay, Error(Display, int*));
     MOCK_METHOD2(setActiveConfig, Error(Display, Config));
@@ -111,6 +112,15 @@
     MOCK_METHOD3(setLayerZOrder, Error(Display, Layer, uint32_t));
     MOCK_METHOD4(setLayerInfo, Error(Display, Layer, uint32_t, uint32_t));
     MOCK_METHOD3(getRenderIntents, Error(Display, ColorMode, std::vector<RenderIntent>*));
+    MOCK_METHOD3(setLayerColorTransform, Error(Display, Layer, const float*));
+    MOCK_METHOD4(getDisplayedContentSamplingAttributes,
+                 Error(Display, PixelFormat*, Dataspace*, uint8_t*));
+    MOCK_METHOD4(setDisplayContentSamplingEnabled, Error(Display, bool, uint8_t, uint64_t));
+    MOCK_METHOD4(getDisplayedContentSample,
+                 Error(Display, uint64_t, uint64_t, DisplayedFrameStats*));
+    MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
+    MOCK_METHOD3(setLayerPerFrameMetadataBlobs,
+                 Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
similarity index 77%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
index e6ac6bf..2ec37c1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "mock/DisplayHardware/MockDisplay.h"
 
 namespace android {
+namespace Hwc2 {
 namespace mock {
 
 // Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+Display::Display() = default;
+Display::~Display() = default;
 
 } // namespace mock
-} // namespace android
+} // namespace Hwc2
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
new file mode 100644
index 0000000..d7e20c4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2019 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 <gmock/gmock.h>
+
+#include "DisplayHardware/HWC2.h"
+
+using HWC2::Error;
+using HWC2::Layer;
+
+namespace android {
+namespace Hwc2 {
+namespace mock {
+
+class Display : public HWC2::Display {
+public:
+    Display();
+    ~Display();
+
+    MOCK_CONST_METHOD0(getId, hwc2_layer_t());
+    MOCK_CONST_METHOD0(isConnected, bool());
+    MOCK_METHOD1(setConnected, void(bool));
+    MOCK_CONST_METHOD0(getCapabilities, const std::unordered_set<HWC2::DisplayCapability>&());
+
+    MOCK_METHOD0(acceptChanges, Error());
+    MOCK_METHOD1(createLayer, Error(Layer**));
+    MOCK_METHOD1(destroyLayer, Error(Layer*));
+    MOCK_CONST_METHOD1(getActiveConfig, Error(std::shared_ptr<const Config>*));
+    MOCK_CONST_METHOD1(getActiveConfigIndex, Error(int* outIndex));
+    MOCK_METHOD1(getChangedCompositionTypes, Error(std::unordered_map<Layer*, HWC2::Composition>*));
+    MOCK_CONST_METHOD1(getColorModes, Error(std::vector<android::ui::ColorMode>*));
+
+    MOCK_CONST_METHOD0(getSupportedPerFrameMetadata, int32_t());
+    MOCK_CONST_METHOD2(getRenderIntents,
+                       Error(android::ui::ColorMode, std::vector<android::ui::RenderIntent>*));
+    MOCK_METHOD2(getDataspaceSaturationMatrix, Error(android::ui::Dataspace, android::mat4*));
+    MOCK_CONST_METHOD0(getConfigs, std::vector<std::shared_ptr<const Config>>());
+
+    MOCK_CONST_METHOD1(getName, Error(std::string*));
+    MOCK_METHOD2(getRequests,
+                 Error(HWC2::DisplayRequest*, std::unordered_map<Layer*, HWC2::LayerRequest>*));
+    MOCK_CONST_METHOD1(getType, Error(HWC2::DisplayType*));
+    MOCK_CONST_METHOD1(supportsDoze, Error(bool*));
+    MOCK_CONST_METHOD1(getHdrCapabilities, Error(android::HdrCapabilities*));
+    MOCK_CONST_METHOD3(getDisplayedContentSamplingAttributes,
+                       Error(android::ui::PixelFormat*, android::ui::Dataspace*, uint8_t*));
+    MOCK_CONST_METHOD3(setDisplayContentSamplingEnabled, Error(bool, uint8_t, uint64_t));
+    MOCK_CONST_METHOD3(getDisplayedContentSample,
+                       Error(uint64_t, uint64_t, android::DisplayedFrameStats*));
+    MOCK_CONST_METHOD1(getReleaseFences,
+                       Error(std::unordered_map<Layer*, android::sp<android::Fence>>* outFences));
+    MOCK_METHOD1(present, Error(android::sp<android::Fence>*));
+    MOCK_METHOD1(setActiveConfig, Error(const std::shared_ptr<const HWC2::Display::Config>&));
+    MOCK_METHOD4(setClientTarget,
+                 Error(uint32_t, const android::sp<android::GraphicBuffer>&,
+                       const android::sp<android::Fence>&, android::ui::Dataspace));
+    MOCK_METHOD2(setColorMode, Error(android::ui::ColorMode, android::ui::RenderIntent));
+    MOCK_METHOD2(setColorTransform, Error(const android::mat4&, android_color_transform_t));
+    MOCK_METHOD2(setOutputBuffer,
+                 Error(const android::sp<android::GraphicBuffer>&,
+                       const android::sp<android::Fence>&));
+    MOCK_METHOD1(setPowerMode, Error(HWC2::PowerMode));
+    MOCK_METHOD1(setVsyncEnabled, Error(HWC2::Vsync));
+    MOCK_METHOD2(validate, Error(uint32_t*, uint32_t*));
+    MOCK_METHOD4(presentOrValidate,
+                 Error(uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*));
+};
+
+} // namespace mock
+} // namespace Hwc2
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
similarity index 83%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
rename to services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
index e6ac6bf..2f7e5ea 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "mock/MockDispSync.h"
 
 namespace android {
 namespace mock {
 
 // Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+DispSync::DispSync() = default;
+DispSync::~DispSync() = default;
 
 } // namespace mock
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
new file mode 100644
index 0000000..9213ae5
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/DispSync.h"
+
+namespace android {
+namespace mock {
+
+class DispSync : public android::DispSync {
+public:
+    DispSync();
+    ~DispSync() override;
+
+    MOCK_METHOD0(reset, void());
+    MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
+    MOCK_METHOD0(beginResync, void());
+    MOCK_METHOD1(addResyncSample, bool(nsecs_t));
+    MOCK_METHOD0(endResync, void());
+    MOCK_METHOD1(setPeriod, void(nsecs_t));
+    MOCK_METHOD0(getPeriod, nsecs_t());
+    MOCK_METHOD1(setRefreshSkipCount, void(int));
+    MOCK_METHOD3(addEventListener, status_t(const char*, nsecs_t, Callback*));
+    MOCK_METHOD1(removeEventListener, status_t(Callback*));
+    MOCK_METHOD2(changePhaseOffset, status_t(Callback*, nsecs_t));
+    MOCK_CONST_METHOD1(computeNextRefresh, nsecs_t(int));
+    MOCK_METHOD1(setIgnorePresentFences, void(bool));
+    MOCK_METHOD0(expectedPresentTime, nsecs_t());
+
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
index 8ac09a9..6ef352a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
@@ -18,7 +18,7 @@
 
 #include <gmock/gmock.h>
 
-#include "EventControlThread.h"
+#include "Scheduler/EventControlThread.h"
 
 namespace android {
 namespace mock {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index e6ea663..3242ef1 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -18,7 +18,7 @@
 
 #include <gmock/gmock.h>
 
-#include "EventThread.h"
+#include "Scheduler/EventThread.h"
 
 namespace android {
 namespace mock {
@@ -28,12 +28,16 @@
     EventThread();
     ~EventThread() override;
 
-    MOCK_CONST_METHOD0(createEventConnection, sp<BnDisplayEventConnection>());
+    MOCK_CONST_METHOD1(createEventConnection, sp<EventThreadConnection>(ResyncCallback));
     MOCK_METHOD0(onScreenReleased, void());
     MOCK_METHOD0(onScreenAcquired, void());
-    MOCK_METHOD2(onHotplugReceived, void(int, bool));
-    MOCK_CONST_METHOD1(dump, void(String8&));
+    MOCK_METHOD2(onHotplugReceived, void(DisplayType, bool));
+    MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset));
+    MOCK_METHOD1(registerDisplayEventConnection,
+                 status_t(const sp<android::EventThreadConnection> &));
+    MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
+    MOCK_METHOD2(requestNextVsync, void(const sp<android::EventThreadConnection> &, bool));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index cf07cf7..e22d3e8 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -18,7 +18,8 @@
 
 #include <gmock/gmock.h>
 
-#include "MessageQueue.h"
+#include "Scheduler/EventThread.h"
+#include "Scheduler/MessageQueue.h"
 
 namespace android {
 namespace mock {
@@ -29,10 +30,12 @@
     ~MessageQueue() override;
 
     MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
-    MOCK_METHOD1(setEventThread, void(android::EventThread*));
+    MOCK_METHOD2(setEventThread, void(android::EventThread*, ResyncCallback));
+    MOCK_METHOD1(setEventConnection, void(const sp<EventThreadConnection>& connection));
     MOCK_METHOD0(waitMessage, void());
     MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t));
     MOCK_METHOD0(invalidate, void());
+    MOCK_METHOD0(invalidateForHWC, void());
     MOCK_METHOD0(refresh, void());
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp
index 25ff39b..9cc6d38 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * Copyright 2018 The Android Open Source Project
+
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -16,12 +16,10 @@
 
 #include "mock/MockNativeWindowSurface.h"
 
-namespace android {
-namespace mock {
+namespace android::surfaceflinger::mock {
 
 // Explicit default instantiation is recommended.
 NativeWindowSurface::NativeWindowSurface() = default;
 NativeWindowSurface::~NativeWindowSurface() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::surfaceflinger::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h
index 88d1a9f..7bc1151 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h
@@ -20,19 +20,17 @@
 
 #include <system/window.h> // for ANativeWindow
 
-#include "SurfaceFlinger.h" // for base NativeWindowSurface
+#include "NativeWindowSurface.h"
 
-namespace android {
-namespace mock {
+namespace android::surfaceflinger::mock {
 
-class NativeWindowSurface : public android::NativeWindowSurface {
+class NativeWindowSurface : public surfaceflinger::NativeWindowSurface {
 public:
     NativeWindowSurface();
-    ~NativeWindowSurface();
+    ~NativeWindowSurface() override;
 
     MOCK_CONST_METHOD0(getNativeWindow, sp<ANativeWindow>());
     MOCK_METHOD0(preallocateBuffers, void());
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::surfaceflinger::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
deleted file mode 100644
index ac08293..0000000
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "RenderEngine/Image.h"
-#include "RenderEngine/Mesh.h"
-#include "RenderEngine/RenderEngine.h"
-#include "RenderEngine/Surface.h"
-#include "RenderEngine/Texture.h"
-
-namespace android {
-namespace RE {
-namespace mock {
-
-class RenderEngine : public RE::RenderEngine {
-public:
-    RenderEngine();
-    ~RenderEngine() override;
-
-    MOCK_METHOD0(createSurface, std::unique_ptr<RE::Surface>());
-    MOCK_METHOD0(createImage, std::unique_ptr<RE::Image>());
-    MOCK_CONST_METHOD0(primeCache, void());
-    MOCK_METHOD1(dump, void(String8&));
-    MOCK_CONST_METHOD0(supportsImageCrop, bool());
-    MOCK_CONST_METHOD0(isCurrent, bool());
-    MOCK_METHOD1(setCurrentSurface, bool(const RE::Surface&));
-    MOCK_METHOD0(resetCurrentSurface, void());
-    MOCK_METHOD0(flush, base::unique_fd());
-    MOCK_METHOD0(finish, bool());
-    MOCK_METHOD1(waitFence, bool(base::unique_fd*));
-    bool waitFence(base::unique_fd fd) override { return waitFence(&fd); };
-    MOCK_METHOD4(clearWithColor, void(float, float, float, float));
-    MOCK_METHOD6(fillRegionWithColor, void(const Region&, uint32_t, float, float, float, float));
-    MOCK_METHOD4(setScissor, void(uint32_t, uint32_t, uint32_t, uint32_t));
-    MOCK_METHOD0(disableScissor, void());
-    MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
-    MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
-    MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const RE::Image&));
-    MOCK_METHOD5(readPixels, void(size_t, size_t, size_t, size_t, uint32_t*));
-    MOCK_CONST_METHOD0(checkErrors, void());
-    MOCK_METHOD6(setViewportAndProjection,
-                 void(size_t, size_t, Rect, size_t, bool, Transform::orientation_flags));
-    MOCK_METHOD4(setupLayerBlending, void(bool, bool, bool, const half4&));
-    MOCK_METHOD1(setupLayerTexturing, void(const Texture&));
-    MOCK_METHOD0(setupLayerBlackedOut, void());
-    MOCK_METHOD4(setupFillWithColor, void(float, float, float, float));
-    MOCK_METHOD1(setupColorTransform, void(const mat4&));
-    MOCK_METHOD1(setSaturationMatrix, void(const mat4&));
-    MOCK_METHOD0(disableTexturing, void());
-    MOCK_METHOD0(disableBlending, void());
-    MOCK_METHOD1(setSourceY410BT2020, void(bool));
-    MOCK_METHOD1(setSourceDataSpace, void(ui::Dataspace));
-    MOCK_METHOD1(setOutputDataSpace, void(ui::Dataspace));
-    MOCK_METHOD1(setDisplayMaxLuminance, void(const float));
-    MOCK_METHOD2(bindNativeBufferAsFrameBuffer,
-                 void(ANativeWindowBuffer*, RE::BindNativeBufferAsFramebuffer*));
-    MOCK_METHOD1(unbindNativeBufferAsFrameBuffer, void(RE::BindNativeBufferAsFramebuffer*));
-    MOCK_METHOD1(drawMesh, void(const Mesh&));
-    MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
-    MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
-};
-
-class Surface : public RE::Surface {
-public:
-    Surface();
-    ~Surface() override;
-
-    MOCK_METHOD1(setCritical, void(bool));
-    MOCK_METHOD1(setAsync, void(bool));
-    MOCK_METHOD1(setNativeWindow, void(ANativeWindow*));
-    MOCK_CONST_METHOD0(swapBuffers, void());
-    MOCK_CONST_METHOD0(queryRedSize, int32_t());
-    MOCK_CONST_METHOD0(queryGreenSize, int32_t());
-    MOCK_CONST_METHOD0(queryBlueSize, int32_t());
-    MOCK_CONST_METHOD0(queryAlphaSize, int32_t());
-    MOCK_CONST_METHOD0(queryWidth, int32_t());
-    MOCK_CONST_METHOD0(queryHeight, int32_t());
-};
-
-class Image : public RE::Image {
-public:
-    Image();
-    ~Image() override;
-
-    MOCK_METHOD4(setNativeWindowBuffer,
-                 bool(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth,
-                      int32_t cropHeight));
-};
-
-} // namespace mock
-} // namespace RE
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h b/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h
index 561fd58..4950a4b 100644
--- a/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h
+++ b/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h
@@ -36,6 +36,7 @@
     MOCK_METHOD1(queueBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
     MOCK_CONST_METHOD2(query, int(int, int*));
     MOCK_METHOD1(perform, int(int));
+    MOCK_METHOD2(perform, int(int, int));
     MOCK_METHOD1(cancelBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
     MOCK_METHOD2(dequeueBuffer, int(struct ANativeWindowBuffer**, int*));
     MOCK_METHOD2(queueBuffer, int(struct ANativeWindowBuffer*, int));
diff --git a/services/thermalservice/Android.bp b/services/thermalservice/Android.bp
deleted file mode 100644
index d754560..0000000
--- a/services/thermalservice/Android.bp
+++ /dev/null
@@ -1,61 +0,0 @@
-subdirs = [
-    "libthermalcallback"
-]
-
-cc_library {
-    name: "libthermalservice",
-
-    srcs: [
-        "aidl/android/os/IThermalEventListener.aidl",
-        "aidl/android/os/IThermalService.aidl",
-        "aidl/android/os/Temperature.cpp",
-    ],
-    aidl: {
-      include_dirs: ["frameworks/native/services/thermalservice/aidl"],
-      export_aidl_headers: true,
-    },
-    export_include_dirs: ["aidl"],
-
-    shared_libs: [
-        "libbinder",
-        "libutils",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wunused",
-        "-Wunreachable-code",
-    ],
-}
-
-cc_binary {
-    name: "thermalserviced",
-
-    srcs: [
-        "ThermalService.cpp",
-        "thermalserviced.cpp",
-    ],
-
-    include_dirs: ["frameworks/native"],
-
-    shared_libs: [
-        "libthermalservice",
-        "libbinder",
-        "libutils",
-        "libthermalcallback",
-        "android.hardware.thermal@1.1",
-        "libhidlbase",
-        "libhidltransport",
-        "liblog",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wunused",
-        "-Wunreachable-code",
-    ],
-
-    init_rc: ["thermalservice.rc"],
-}
diff --git a/services/thermalservice/ThermalService.cpp b/services/thermalservice/ThermalService.cpp
deleted file mode 100644
index 6e09a83..0000000
--- a/services/thermalservice/ThermalService.cpp
+++ /dev/null
@@ -1,126 +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.
- */
-
-#include "ThermalService.h"
-#include <android/os/IThermalService.h>
-#include <android/os/IThermalEventListener.h>
-#include <android/os/Temperature.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <utils/Errors.h>
-#include <utils/Mutex.h>
-#include <utils/String16.h>
-
-namespace android {
-namespace os {
-
-/**
- * Notify registered listeners of a thermal throttling start/stop event.
- * @param temperature the temperature at which the event was generated
- */
-binder::Status ThermalService::notifyThrottling(
-    const bool isThrottling, const Temperature& temperature) {
-    Mutex::Autolock _l(mListenersLock);
-
-    mThrottled = isThrottling;
-    mThrottleTemperature = temperature;
-
-    for (size_t i = 0; i < mListeners.size(); i++) {
-      mListeners[i]->notifyThrottling(isThrottling, temperature);
-    }
-    return binder::Status::ok();
-}
-
-/**
- * Query whether the system is currently thermal throttling.
- * @return true if currently thermal throttling, else false
- */
-binder::Status ThermalService::isThrottling(bool* _aidl_return) {
-    Mutex::Autolock _l(mListenersLock);
-    *_aidl_return = mThrottled;
-    return binder::Status::ok();
-}
-
-/**
- * Register a new thermal event listener.
- * @param listener the client's IThermalEventListener instance to which
- *                 notifications are to be sent
- */
-binder::Status ThermalService::registerThermalEventListener(
-    const sp<IThermalEventListener>& listener) {
-    {
-        if (listener == NULL)
-            return binder::Status::ok();
-        Mutex::Autolock _l(mListenersLock);
-        // check whether this is a duplicate
-        for (size_t i = 0; i < mListeners.size(); i++) {
-            if (IInterface::asBinder(mListeners[i]) ==
-                IInterface::asBinder(listener)) {
-                return binder::Status::ok();
-            }
-        }
-
-        mListeners.add(listener);
-        IInterface::asBinder(listener)->linkToDeath(this);
-    }
-
-    return binder::Status::ok();
-}
-
-/**
- * Unregister a previously-registered thermal event listener.
- * @param listener the client's IThermalEventListener instance to which
- *                 notifications are to no longer be sent
- */
-binder::Status ThermalService::unregisterThermalEventListener(
-    const sp<IThermalEventListener>& listener) {
-    if (listener == NULL)
-        return binder::Status::ok();
-    Mutex::Autolock _l(mListenersLock);
-    for (size_t i = 0; i < mListeners.size(); i++) {
-        if (IInterface::asBinder(mListeners[i]) ==
-            IInterface::asBinder(listener)) {
-            IInterface::asBinder(mListeners[i])->unlinkToDeath(this);
-            mListeners.removeAt(i);
-            break;
-        }
-    }
-
-    return binder::Status::ok();
-}
-
-void ThermalService::binderDied(const wp<IBinder>& who) {
-    Mutex::Autolock _l(mListenersLock);
-
-    for (size_t i = 0; i < mListeners.size(); i++) {
-        if (IInterface::asBinder(mListeners[i]) == who) {
-            mListeners.removeAt(i);
-            break;
-        }
-    }
-}
-
-/**
- * Publish the supplied ThermalService to servicemanager.
- */
-void ThermalService::publish(
-    const sp<ThermalService>& service) {
-    defaultServiceManager()->addService(String16("thermalservice"),
-                                        service);
-}
-
-}  // namespace os
-}  // namespace android
diff --git a/services/thermalservice/ThermalService.h b/services/thermalservice/ThermalService.h
deleted file mode 100644
index 17dfcbc..0000000
--- a/services/thermalservice/ThermalService.h
+++ /dev/null
@@ -1,55 +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_THERMALSERVICE_THERMALSERVICE_H
-#define ANDROID_THERMALSERVICE_THERMALSERVICE_H
-
-#include <android/os/BnThermalService.h>
-#include <android/os/IThermalEventListener.h>
-#include <android/os/Temperature.h>
-#include <utils/Mutex.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-
-namespace android {
-namespace os {
-
-class ThermalService : public BnThermalService,
-                       public IBinder::DeathRecipient {
-public:
-  ThermalService() : mThrottled(false) {};
-    void publish(const sp<ThermalService>& service);
-    binder::Status notifyThrottling(
-        const bool isThrottling, const Temperature& temperature);
-
-private:
-    Mutex mListenersLock;
-    Vector<sp<IThermalEventListener> > mListeners;
-    bool mThrottled;
-    Temperature mThrottleTemperature;
-
-    binder::Status registerThermalEventListener(
-        const sp<IThermalEventListener>& listener);
-    binder::Status unregisterThermalEventListener(
-        const sp<IThermalEventListener>& listener);
-    binder::Status isThrottling(bool* _aidl_return);
-    void binderDied(const wp<IBinder>& who);
-};
-
-};  // namespace os
-};  // namespace android
-
-#endif // ANDROID_THERMALSERVICE_THERMALSERVICE_H
diff --git a/services/thermalservice/aidl/android/os/IThermalEventListener.aidl b/services/thermalservice/aidl/android/os/IThermalEventListener.aidl
deleted file mode 100644
index 050325e..0000000
--- a/services/thermalservice/aidl/android/os/IThermalEventListener.aidl
+++ /dev/null
@@ -1,32 +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.
- */
-
-package android.os;
-
-import android.os.Temperature;
-
-/**
-  * Listener for thermal events.
-  * {@hide}
-  */
-oneway interface IThermalEventListener {
-    /**
-     * Called when a thermal throttling start/stop event is received.
-     * @param temperature the temperature at which the event was generated.
-     */
-    void notifyThrottling(
-        in boolean isThrottling, in Temperature temperature);
-}
diff --git a/services/thermalservice/aidl/android/os/IThermalService.aidl b/services/thermalservice/aidl/android/os/IThermalService.aidl
deleted file mode 100644
index e699202..0000000
--- a/services/thermalservice/aidl/android/os/IThermalService.aidl
+++ /dev/null
@@ -1,48 +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.
- */
-
-package android.os;
-
-import android.os.IThermalEventListener;
-import android.os.Temperature;
-
-/** {@hide} */
-interface IThermalService {
-    /**
-      * Register a listener for thermal events.
-      * @param listener the IThermalEventListener to be notified.
-      * {@hide}
-      */
-    void registerThermalEventListener(in IThermalEventListener listener);
-    /**
-      * Unregister a previously-registered listener for thermal events.
-      * @param listener the IThermalEventListener to no longer be notified.
-      * {@hide}
-      */
-    void unregisterThermalEventListener(in IThermalEventListener listener);
-    /**
-      * Send a thermal throttling start/stop notification to all listeners.
-      * @param temperature the temperature at which the event was generated.
-      * {@hide}
-      */
-    oneway void notifyThrottling(
-        in boolean isThrottling, in Temperature temperature);
-    /**
-      * Return whether system performance is currently thermal throttling.
-      * {@hide}
-      */
-    boolean isThrottling();
-}
diff --git a/services/thermalservice/aidl/android/os/Temperature.aidl b/services/thermalservice/aidl/android/os/Temperature.aidl
deleted file mode 100644
index 0293c39..0000000
--- a/services/thermalservice/aidl/android/os/Temperature.aidl
+++ /dev/null
@@ -1,5 +0,0 @@
-package android.os;
-
-/* Encodes a temperature used by ThermalService. */
-
-parcelable Temperature cpp_header "android/os/Temperature.h";
diff --git a/services/thermalservice/aidl/android/os/Temperature.cpp b/services/thermalservice/aidl/android/os/Temperature.cpp
deleted file mode 100644
index df207b7..0000000
--- a/services/thermalservice/aidl/android/os/Temperature.cpp
+++ /dev/null
@@ -1,54 +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.
- */
-
-#include "android/os/Temperature.h"
-
-#include <math.h>
-#include <stdint.h>
-#include <binder/Parcel.h>
-#include <hardware/thermal.h>
-#include <sys/types.h>
-#include <utils/Errors.h>
-
-namespace android {
-namespace os {
-
-Temperature::Temperature() : value_(NAN), type_(DEVICE_TEMPERATURE_UNKNOWN) {}
-
-Temperature::Temperature(const float value, const int type) :
-    value_(value), type_(type)  {}
-
-Temperature::~Temperature() {}
-
-/*
- * Parcel read/write code must be kept in sync with
- * frameworks/base/core/java/android/os/Temperature.java
- */
-
-status_t Temperature::readFromParcel(const Parcel* p) {
-    value_ = p->readFloat();
-    type_ = p->readInt32();
-    return OK;
-}
-
-status_t Temperature::writeToParcel(Parcel* p) const {
-    p->writeFloat(value_);
-    p->writeInt32(type_);
-    return OK;
-}
-
-}  // namespace os
-}  // namespace android
diff --git a/services/thermalservice/aidl/android/os/Temperature.h b/services/thermalservice/aidl/android/os/Temperature.h
deleted file mode 100644
index bbc5607..0000000
--- a/services/thermalservice/aidl/android/os/Temperature.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef ANDROID_THERMALSERVICE_AIDL_ANDROID_OS_TEMPERATURE_H
-#define ANDROID_THERMALSERVICE_AIDL_ANDROID_OS_TEMPERATURE_H
-
-#include <binder/Parcelable.h>
-
-namespace android {
-namespace os {
-
-class Temperature : public Parcelable {
- public:
-
-  Temperature();
-  Temperature(const float value, const int type);
-  ~Temperature() override;
-
-  float getValue() const {return value_;};
-  float getType() const {return type_;};
-
-  status_t writeToParcel(Parcel* parcel) const override;
-  status_t readFromParcel(const Parcel* parcel) override;
-
- private:
-  // The value of the temperature as a float, or NAN if unknown.
-  float value_;
-  // The type of the temperature, an enum temperature_type from
-  // hardware/thermal.h
-  int type_;
-};
-
-}  // namespace os
-}  // namespace android
-
-#endif   // ANDROID_THERMALSERVICE_AIDL_ANDROID_OS_TEMPERATURE_H
diff --git a/services/thermalservice/libthermalcallback/Android.bp b/services/thermalservice/libthermalcallback/Android.bp
deleted file mode 100644
index e98506e..0000000
--- a/services/thermalservice/libthermalcallback/Android.bp
+++ /dev/null
@@ -1,19 +0,0 @@
-cc_library_shared {
-    name: "libthermalcallback",
-    srcs: [
-        "ThermalCallback.cpp",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    include_dirs: ["frameworks/native"],
-    shared_libs: [
-        "android.hardware.thermal@1.1",
-        "libhidlbase",
-        "libhidltransport",
-        "liblog",
-        "libthermalservice",
-        "libutils",
-    ],
-}
diff --git a/services/thermalservice/libthermalcallback/ThermalCallback.cpp b/services/thermalservice/libthermalcallback/ThermalCallback.cpp
deleted file mode 100644
index 5e094fa..0000000
--- a/services/thermalservice/libthermalcallback/ThermalCallback.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-#define LOG_TAG "android.hardware.thermal.thermalcallback@1.1-impl"
-#include <log/log.h>
-
-#include "ThermalCallback.h"
-#include "services/thermalservice/ThermalService.h"
-#include <math.h>
-#include <android/os/Temperature.h>
-#include <hardware/thermal.h>
-
-namespace android {
-namespace hardware {
-namespace thermal {
-namespace V1_1 {
-namespace implementation {
-
-using ::android::os::ThermalService;
-using ::android::hardware::thermal::V1_0::TemperatureType;
-
-// Register a binder ThermalService object for sending events
-void ThermalCallback::registerThermalService(sp<ThermalService> thermalService)
-{
-    mThermalService = thermalService;
-}
-
-// Methods from IThermalCallback::V1_1 follow.
-Return<void> ThermalCallback::notifyThrottling(
-      bool isThrottling,
-      const android::hardware::thermal::V1_0::Temperature& temperature) {
-
-    // Convert HIDL IThermal Temperature to binder IThermalService Temperature.
-    if (mThermalService != nullptr) {
-        float value = NAN;
-        int type = DEVICE_TEMPERATURE_UNKNOWN;
-
-        switch(temperature.type) {
-          case TemperatureType::CPU:
-            type = DEVICE_TEMPERATURE_CPU;
-            break;
-          case TemperatureType::GPU:
-            type = DEVICE_TEMPERATURE_GPU;
-            break;
-          case TemperatureType::BATTERY:
-            type = DEVICE_TEMPERATURE_BATTERY;
-            break;
-          case TemperatureType::SKIN:
-            type = DEVICE_TEMPERATURE_SKIN;
-            break;
-          case TemperatureType::UNKNOWN:
-          default:
-            type = DEVICE_TEMPERATURE_UNKNOWN;
-            break;
-        }
-
-        value = temperature.currentValue == UNKNOWN_TEMPERATURE ? NAN :
-            temperature.currentValue;
-
-        android::os::Temperature thermal_svc_temp(value, type);
-        mThermalService->notifyThrottling(isThrottling, thermal_svc_temp);
-    } else {
-        ALOGE("IThermalService binder service not created, drop throttling event");
-    }
-    return Void();
-}
-
-}  // namespace implementation
-}  // namespace V1_1
-}  // namespace thermal
-}  // namespace hardware
-}  // namespace android
diff --git a/services/thermalservice/libthermalcallback/ThermalCallback.h b/services/thermalservice/libthermalcallback/ThermalCallback.h
deleted file mode 100644
index 3d72c68..0000000
--- a/services/thermalservice/libthermalcallback/ThermalCallback.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef ANDROID_HARDWARE_THERMAL_V1_1_THERMALCALLBACK_H
-#define ANDROID_HARDWARE_THERMAL_V1_1_THERMALCALLBACK_H
-
-#include <android/hardware/thermal/1.1/IThermalCallback.h>
-#include <android/hardware/thermal/1.0/types.h>
-#include <android/os/Temperature.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-#include "services/thermalservice/ThermalService.h"
-
-namespace android {
-namespace hardware {
-namespace thermal {
-namespace V1_1 {
-namespace implementation {
-
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::os::ThermalService;
-
-class ThermalCallback : public IThermalCallback {
- public:
-    // Register a binder ThermalService object for sending events
-    void registerThermalService(sp<ThermalService> thermalService);
-
-    // Methods from IThermalCallback::V1_1 follow.
-    Return<void> notifyThrottling(
-        bool isThrottling,
-        const android::hardware::thermal::V1_0::Temperature& temperature)
-        override;
-
- private:
-    // Our registered binder ThermalService object to use for sending events
-    sp<android::os::ThermalService> mThermalService;
-};
-
-}  // namespace implementation
-}  // namespace V1_1
-}  // namespace thermal
-}  // namespace hardware
-}  // namespace android
-
-#endif  // ANDROID_HARDWARE_THERMAL_V1_1_THERMALCALLBACK_H
diff --git a/services/thermalservice/thermalservice.rc b/services/thermalservice/thermalservice.rc
deleted file mode 100644
index 94c2c78..0000000
--- a/services/thermalservice/thermalservice.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service thermalservice /system/bin/thermalserviced
-    class core
-    user system
-    group system
diff --git a/services/thermalservice/thermalserviced.cpp b/services/thermalservice/thermalserviced.cpp
deleted file mode 100644
index 8e27266..0000000
--- a/services/thermalservice/thermalserviced.cpp
+++ /dev/null
@@ -1,115 +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.
- */
-
-#define LOG_TAG "thermalserviced"
-#include <log/log.h>
-
-#include "thermalserviced.h"
-#include "ThermalService.h"
-#include "libthermalcallback/ThermalCallback.h"
-
-#include <android/hardware/thermal/1.1/IThermal.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <hidl/HidlTransportSupport.h>
-
-using namespace android;
-using ::android::hardware::thermal::V1_1::IThermal;
-using ::android::hardware::thermal::V1_0::Temperature;
-using ::android::hardware::thermal::V1_1::IThermalCallback;
-using ::android::hardware::thermal::V1_1::implementation::ThermalCallback;
-using ::android::hardware::configureRpcThreadpool;
-using ::android::hardware::hidl_death_recipient;
-using ::android::hidl::base::V1_0::IBase;
-using ::android::os::ThermalService;
-
-template<typename T>
-using Return = hardware::Return<T>;
-
-namespace {
-
-// Our thermalserviced main object
-ThermalServiceDaemon* gThermalServiceDaemon;
-
-// Thermal HAL client
-sp<IThermal> gThermalHal = nullptr;
-
-// Binder death notifier informing of Thermal HAL death.
-struct ThermalServiceDeathRecipient : hidl_death_recipient {
-    virtual void serviceDied(
-        uint64_t cookie __unused, const wp<IBase>& who __unused) {
-        gThermalHal = nullptr;
-        ALOGE("IThermal HAL died");
-        gThermalServiceDaemon->getThermalHal();
-    }
-};
-
-sp<ThermalServiceDeathRecipient> gThermalHalDied = nullptr;
-
-}  // anonymous namespace
-
-void ThermalServiceDaemon::thermalServiceStartup() {
-    // Binder IThermalService startup
-    mThermalService = new android::os::ThermalService;
-    mThermalService->publish(mThermalService);
-    // Register IThermalService object with IThermalCallback
-    if (mThermalCallback != nullptr)
-        mThermalCallback->registerThermalService(mThermalService);
-    IPCThreadState::self()->joinThreadPool();
-}
-
-// Lookup Thermal HAL, register death notifier, register our
-// ThermalCallback with the Thermal HAL.
-void ThermalServiceDaemon::getThermalHal() {
-    gThermalHal = IThermal::getService();
-    if (gThermalHal == nullptr) {
-        ALOGW("Unable to get Thermal HAL V1.1, vendor thermal event notification not available");
-        return;
-    }
-
-    // Binder death notifier for Thermal HAL
-    if (gThermalHalDied == nullptr)
-        gThermalHalDied = new ThermalServiceDeathRecipient();
-
-    if (gThermalHalDied != nullptr)
-        gThermalHal->linkToDeath(gThermalHalDied, 0x451F /* cookie */);
-
-    if (mThermalCallback != nullptr) {
-        Return<void> ret = gThermalHal->registerThermalCallback(
-            mThermalCallback);
-        if (!ret.isOk())
-            ALOGE("registerThermalCallback failed, status: %s",
-                  ret.description().c_str());
-    }
-}
-
-void ThermalServiceDaemon::thermalCallbackStartup() {
-    // HIDL IThermalCallback startup
-    // Need at least 2 threads in thread pool since we wait for dead HAL
-    // to come back on the binder death notification thread and we need
-    // another thread for the incoming service now available call.
-    configureRpcThreadpool(2, false /* callerWillJoin */);
-    mThermalCallback = new ThermalCallback();
-    // Lookup Thermal HAL and register our ThermalCallback.
-    getThermalHal();
-}
-
-int main(int /*argc*/, char** /*argv*/) {
-    gThermalServiceDaemon = new ThermalServiceDaemon();
-    gThermalServiceDaemon->thermalCallbackStartup();
-    gThermalServiceDaemon->thermalServiceStartup();
-    /* NOTREACHED */
-}
diff --git a/services/thermalservice/thermalserviced.h b/services/thermalservice/thermalserviced.h
deleted file mode 100644
index 309e2fe..0000000
--- a/services/thermalservice/thermalserviced.h
+++ /dev/null
@@ -1,40 +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_THERMALSERVICE_THERMALSERVICED_H
-#define ANDROID_THERMALSERVICE_THERMALSERVICED_H
-
-#include "ThermalService.h"
-#include "libthermalcallback/ThermalCallback.h"
-
-using namespace android;
-using ::android::hardware::thermal::V1_0::Temperature;
-using ::android::hardware::thermal::V1_1::implementation::ThermalCallback;
-using ::android::os::ThermalService;
-
-class ThermalServiceDaemon {
- public:
-    void thermalServiceStartup();
-    void thermalCallbackStartup();
-    void getThermalHal();
-    ThermalServiceDaemon() {};
-
- private:
-    sp<ThermalService> mThermalService;
-    sp<ThermalCallback> mThermalCallback;
-};
-
-#endif  // ANDROID_THERMALSERVICE_THERMALSERVICED_H
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index 6122846..4e24a64 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -12,45 +12,57 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-sourceFiles = [
-    "buffer_hub.cpp",
-    "bufferhubd.cpp",
-    "consumer_channel.cpp",
-    "producer_channel.cpp",
-    "detached_buffer_channel.cpp",
-    "consumer_queue_channel.cpp",
-    "producer_queue_channel.cpp",
-]
-
-headerLibraries = ["libdvr_headers"]
-
-staticLibraries = [
-    "libperformance",
-    "libbufferhub",
-]
-
 sharedLibraries = [
     "libbase",
-    "libbinder",
     "libcutils",
-    "liblog",
-    "libsync",
-    "libutils",
+    "libgtest_prod",
     "libgui",
-    "libui",
+    "liblog",
     "libpdx_default_transport",
+    "libsync",
+    "libui",
+    "libutils",
 ]
 
+cc_library_static {
+    name: "libbufferhubd",
+    srcs: [
+        "buffer_hub.cpp",
+        "consumer_channel.cpp",
+        "consumer_queue_channel.cpp",
+        "producer_channel.cpp",
+        "producer_queue_channel.cpp",
+    ],
+    cflags: [
+        "-DLOG_TAG=\"libbufferhubd\"",
+        "-DTRACE=0",
+        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+    ],
+    export_include_dirs: ["include"],
+    header_libs: ["libdvr_headers"],
+    shared_libs: sharedLibraries,
+    static_libs: [
+        "libbufferhub",
+    ],
+
+    // TODO(b/117568153): Temporarily opt out using libcrt.
+    no_libcrt: true,
+}
+
 cc_binary {
-    srcs: sourceFiles,
+    srcs: ["bufferhubd.cpp"],
     cflags: [
         "-DLOG_TAG=\"bufferhubd\"",
         "-DTRACE=0",
         "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
     ],
-    header_libs: headerLibraries,
-    static_libs: staticLibraries,
+    header_libs: ["libdvr_headers"],
     shared_libs: sharedLibraries,
+    static_libs: [
+        "libbufferhub",
+        "libbufferhubd",
+        "libperformance",
+    ],
     name: "bufferhubd",
     init_rc: ["bufferhubd.rc"],
 }
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index e57c8ed..6409265 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -1,9 +1,5 @@
-#include "buffer_hub.h"
-
 #include <inttypes.h>
-#include <log/log.h>
 #include <poll.h>
-#include <utils/Trace.h>
 
 #include <iomanip>
 #include <memory>
@@ -11,12 +7,14 @@
 #include <string>
 #include <thread>
 
+#include <log/log.h>
 #include <pdx/default_transport/service_endpoint.h>
 #include <private/dvr/bufferhub_rpc.h>
-#include "consumer_channel.h"
-#include "detached_buffer_channel.h"
-#include "producer_channel.h"
-#include "producer_queue_channel.h"
+#include <private/dvr/buffer_hub.h>
+#include <private/dvr/consumer_channel.h>
+#include <private/dvr/producer_channel.h>
+#include <private/dvr/producer_queue_channel.h>
+#include <utils/Trace.h>
 
 using android::pdx::Channel;
 using android::pdx::ErrorStatus;
@@ -57,8 +55,6 @@
   stream << " ";
   stream << std::setw(10) << "Usage";
   stream << " ";
-  stream << std::setw(9) << "Pending";
-  stream << " ";
   stream << std::setw(18) << "State";
   stream << " ";
   stream << std::setw(18) << "Signaled";
@@ -91,8 +87,6 @@
       stream << std::setw(8) << info.usage;
       stream << std::dec << std::setfill(' ');
       stream << " ";
-      stream << std::setw(9) << info.pending_count;
-      stream << " ";
       stream << "0x" << std::hex << std::setfill('0');
       stream << std::setw(16) << info.state;
       stream << " ";
@@ -247,33 +241,11 @@
           *this, &BufferHubService::OnCreateBuffer, message);
       return {};
 
-    case DetachedBufferRPC::Create::Opcode:
-      DispatchRemoteMethod<DetachedBufferRPC::Create>(
-          *this, &BufferHubService::OnCreateDetachedBuffer, message);
-      return {};
-
     case BufferHubRPC::CreateProducerQueue::Opcode:
       DispatchRemoteMethod<BufferHubRPC::CreateProducerQueue>(
           *this, &BufferHubService::OnCreateProducerQueue, message);
       return {};
 
-    case BufferHubRPC::ProducerBufferDetach::Opcode:
-      // In addition to the message handler in the ProducerChannel's
-      // HandleMessage method, we also need to invalid the producer channel (and
-      // all associated consumer channels). Note that this has to be done after
-      // HandleMessage returns to make sure the IPC request has went back to the
-      // client first.
-      SetChannel(channel->channel_id(), nullptr);
-      return {};
-
-    case DetachedBufferRPC::Promote::Opcode:
-      // In addition to the message handler in the DetachedBufferChannel's
-      // HandleMessage method, we also need to invalid the channel. Note that
-      // this has to be done after HandleMessage returns to make sure the IPC
-      // request has went back to the client first.
-      SetChannel(channel->channel_id(), nullptr);
-      return {};
-
     default:
       return DefaultHandleMessage(message);
   }
@@ -310,43 +282,6 @@
   }
 }
 
-pdx::Status<void> BufferHubService::OnCreateDetachedBuffer(
-    pdx::Message& message, uint32_t width, uint32_t height,
-    uint32_t layer_count, uint32_t format, uint64_t usage,
-    size_t user_metadata_size) {
-  // Use the producer channel id as the global buffer id.
-  const int buffer_id = message.GetChannelId();
-  ALOGD_IF(TRACE,
-           "BufferHubService::OnCreateDetachedBuffer: buffer_id=%d width=%u "
-           "height=%u layer_count=%u format=%u usage=%" PRIx64
-           " user_metadata_size=%zu",
-           buffer_id, width, height, layer_count, format, usage,
-           user_metadata_size);
-
-  // See if this channel is already attached to a buffer.
-  if (const auto channel = message.GetChannel<BufferHubChannel>()) {
-    ALOGE(
-        "BufferHubService::OnCreateDetachedBuffer: Buffer already created: "
-        "buffer=%d",
-        buffer_id);
-    return ErrorStatus(EALREADY);
-  }
-
-  std::unique_ptr<DetachedBufferChannel> channel =
-      DetachedBufferChannel::Create(this, buffer_id, width, height, layer_count,
-                                    format, usage, user_metadata_size);
-  if (!channel) {
-    ALOGE(
-        "BufferHubService::OnCreateDetachedBuffer: Failed to allocate buffer, "
-        "buffer=%d.",
-        buffer_id);
-    return ErrorStatus(ENOMEM);
-  }
-
-  message.SetChannel(std::move(channel));
-  return {};
-}
-
 Status<QueueInfo> BufferHubService::OnCreateProducerQueue(
     pdx::Message& message, const ProducerQueueConfig& producer_config,
     const UsagePolicy& usage_policy) {
diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp
index b27f218..50cb3b7 100644
--- a/services/vr/bufferhubd/bufferhubd.cpp
+++ b/services/vr/bufferhubd/bufferhubd.cpp
@@ -1,17 +1,15 @@
 #include <sched.h>
+#include <sys/resource.h>
 #include <unistd.h>
 
-#include <log/log.h>
-#include <sys/resource.h>
-
 #include <dvr/performance_client_api.h>
+#include <log/log.h>
 #include <pdx/service_dispatcher.h>
-
-#include "buffer_hub.h"
+#include <private/dvr/buffer_hub.h>
 
 int main(int, char**) {
   int ret = -1;
-  std::shared_ptr<android::pdx::Service> service;
+  std::shared_ptr<android::dvr::BufferHubService> pdx_service;
   std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher;
 
   // We need to be able to create endpoints with full perms.
@@ -37,9 +35,9 @@
   dispatcher = android::pdx::ServiceDispatcher::Create();
   CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher\n");
 
-  service = android::dvr::BufferHubService::Create();
-  CHECK_ERROR(!service, error, "Failed to create buffer hub service\n");
-  dispatcher->AddService(service);
+  pdx_service = android::dvr::BufferHubService::Create();
+  CHECK_ERROR(!pdx_service, error, "Failed to create bufferhub pdx service\n");
+  dispatcher->AddService(pdx_service);
 
   ret = dvrSetSchedulerClass(0, "graphics");
   CHECK_ERROR(ret < 0, error, "Failed to set thread priority");
diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
index a6d2dbb..c7695bc 100644
--- a/services/vr/bufferhubd/consumer_channel.cpp
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -1,12 +1,10 @@
-#include "consumer_channel.h"
-
-#include <log/log.h>
-#include <utils/Trace.h>
-
 #include <thread>
 
+#include <log/log.h>
 #include <private/dvr/bufferhub_rpc.h>
-#include "producer_channel.h"
+#include <private/dvr/consumer_channel.h>
+#include <private/dvr/producer_channel.h>
+#include <utils/Trace.h>
 
 using android::pdx::BorrowedHandle;
 using android::pdx::Channel;
@@ -19,10 +17,10 @@
 namespace dvr {
 
 ConsumerChannel::ConsumerChannel(BufferHubService* service, int buffer_id,
-                                 int channel_id, uint64_t consumer_state_bit,
+                                 int channel_id, uint32_t client_state_mask,
                                  const std::shared_ptr<Channel> producer)
     : BufferHubChannel(service, buffer_id, channel_id, kConsumerType),
-      consumer_state_bit_(consumer_state_bit),
+      client_state_mask_(client_state_mask),
       producer_(producer) {
   GetProducer()->AddConsumer(this);
 }
@@ -43,7 +41,7 @@
     // If producer has not hung up, copy most buffer info from the producer.
     info = producer->GetBufferInfo();
   } else {
-    info.signaled_mask = consumer_state_bit();
+    info.signaled_mask = client_state_mask();
   }
   info.id = buffer_id();
   return info;
@@ -92,11 +90,6 @@
           *this, &ConsumerChannel::OnConsumerRelease, message);
       return true;
 
-    case BufferHubRPC::ConsumerSetIgnore::Opcode:
-      DispatchRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(
-          *this, &ConsumerChannel::OnConsumerSetIgnore, message);
-      return true;
-
     default:
       return false;
   }
@@ -107,7 +100,7 @@
   ATRACE_NAME("ConsumerChannel::OnGetBuffer");
   ALOGD_IF(TRACE, "ConsumerChannel::OnGetBuffer: buffer=%d", buffer_id());
   if (auto producer = GetProducer()) {
-    return {producer->GetBuffer(consumer_state_bit_)};
+    return {producer->GetBuffer(client_state_mask_)};
   } else {
     return ErrorStatus(EPIPE);
   }
@@ -122,9 +115,8 @@
   if (acquired_ || released_) {
     ALOGE(
         "ConsumerChannel::OnConsumerAcquire: Acquire when not posted: "
-        "ignored=%d acquired=%d released=%d channel_id=%d buffer_id=%d",
-        ignored_, acquired_, released_, message.GetChannelId(),
-        producer->buffer_id());
+        "acquired=%d released=%d channel_id=%d buffer_id=%d",
+        acquired_, released_, message.GetChannelId(), producer->buffer_id());
     return ErrorStatus(EBUSY);
   } else {
     auto status = producer->OnConsumerAcquire(message);
@@ -146,9 +138,8 @@
   if (!acquired_ || released_) {
     ALOGE(
         "ConsumerChannel::OnConsumerRelease: Release when not acquired: "
-        "ignored=%d acquired=%d released=%d channel_id=%d buffer_id=%d",
-        ignored_, acquired_, released_, message.GetChannelId(),
-        producer->buffer_id());
+        "acquired=%d released=%d channel_id=%d buffer_id=%d",
+        acquired_, released_, message.GetChannelId(), producer->buffer_id());
     return ErrorStatus(EBUSY);
   } else {
     auto status =
@@ -162,36 +153,21 @@
   }
 }
 
-Status<void> ConsumerChannel::OnConsumerSetIgnore(Message&, bool ignored) {
-  ATRACE_NAME("ConsumerChannel::OnConsumerSetIgnore");
-  auto producer = GetProducer();
-  if (!producer)
-    return ErrorStatus(EPIPE);
-
-  ignored_ = ignored;
-  if (ignored_ && acquired_) {
-    // Update the producer if ignore is set after the consumer acquires the
-    // buffer.
-    ClearAvailable();
-    producer->OnConsumerIgnored();
-    acquired_ = false;
-    released_ = true;
-  }
-
-  return {};
+void ConsumerChannel::OnProducerGained() {
+  // Clear the signal if exist. There is a possiblity that the signal still
+  // exist in consumer client when producer gains the buffer, e.g. newly added
+  // consumer fail to acquire the previous posted buffer in time. Then, when
+  // producer gains back the buffer, posts the buffer again and signal the
+  // consumer later, there won't be an signal change in eventfd, and thus,
+  // consumer will miss the posted buffer later. Thus, we need to clear the
+  // signal in consumer clients if the signal exist.
+  ClearAvailable();
 }
 
-bool ConsumerChannel::OnProducerPosted() {
-  if (ignored_) {
-    acquired_ = false;
-    released_ = true;
-    return false;
-  } else {
-    acquired_ = false;
-    released_ = false;
-    SignalAvailable();
-    return true;
-  }
+void ConsumerChannel::OnProducerPosted() {
+  acquired_ = false;
+  released_ = false;
+  SignalAvailable();
 }
 
 void ConsumerChannel::OnProducerClosed() {
diff --git a/services/vr/bufferhubd/consumer_queue_channel.cpp b/services/vr/bufferhubd/consumer_queue_channel.cpp
index 4d43001..5d7d4e9 100644
--- a/services/vr/bufferhubd/consumer_queue_channel.cpp
+++ b/services/vr/bufferhubd/consumer_queue_channel.cpp
@@ -1,8 +1,6 @@
-#include "consumer_queue_channel.h"
-
 #include <pdx/channel_handle.h>
-
-#include "producer_channel.h"
+#include <private/dvr/consumer_queue_channel.h>
+#include <private/dvr/producer_channel.h>
 
 using android::pdx::ErrorStatus;
 using android::pdx::RemoteChannelHandle;
@@ -82,27 +80,35 @@
 }
 
 void ConsumerQueueChannel::RegisterNewBuffer(
-    const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot) {
-  ALOGD_IF(TRACE,
-           "ConsumerQueueChannel::RegisterNewBuffer: queue_id=%d buffer_id=%d "
-           "slot=%zu silent=%d",
-           buffer_id(), producer_channel->buffer_id(), slot, silent_);
+    const std::shared_ptr<ProducerChannel>& producer_channel,
+    size_t producer_slot) {
+  ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu silent=%d",
+           __FUNCTION__, buffer_id(), producer_channel->buffer_id(),
+           producer_slot, silent_);
   // Only register buffers if the queue is not silent.
-  if (!silent_) {
-    pending_buffer_slots_.emplace(producer_channel, slot);
-
-    // Signal the client that there is new buffer available.
-    SignalAvailable();
+  if (silent_) {
+    return;
   }
+
+  auto status = producer_channel->CreateConsumerStateMask();
+  if (!status.ok()) {
+    ALOGE("%s: Failed to create consumer state mask: %s", __FUNCTION__,
+          status.GetErrorMessage().c_str());
+    return;
+  }
+  uint64_t consumer_state_mask = status.get();
+
+  pending_buffer_slots_.emplace(producer_channel, producer_slot,
+                                consumer_state_mask);
+  // Signal the client that there is new buffer available.
+  SignalAvailable();
 }
 
 Status<std::vector<std::pair<RemoteChannelHandle, size_t>>>
 ConsumerQueueChannel::OnConsumerQueueImportBuffers(Message& message) {
   std::vector<std::pair<RemoteChannelHandle, size_t>> buffer_handles;
-  ATRACE_NAME("ConsumerQueueChannel::OnConsumerQueueImportBuffers");
-  ALOGD_IF(TRACE,
-           "ConsumerQueueChannel::OnConsumerQueueImportBuffers: "
-           "pending_buffer_slots=%zu",
+  ATRACE_NAME(__FUNCTION__);
+  ALOGD_IF(TRACE, "%s: pending_buffer_slots=%zu", __FUNCTION__,
            pending_buffer_slots_.size());
 
   // Indicate this is a silent queue that will not import buffers.
@@ -110,30 +116,30 @@
     return ErrorStatus(EBADR);
 
   while (!pending_buffer_slots_.empty()) {
-    auto producer_channel = pending_buffer_slots_.front().first.lock();
-    size_t producer_slot = pending_buffer_slots_.front().second;
+    auto producer_channel =
+        pending_buffer_slots_.front().producer_channel.lock();
+    size_t producer_slot = pending_buffer_slots_.front().producer_slot;
+    uint64_t consumer_state_mask =
+        pending_buffer_slots_.front().consumer_state_mask;
     pending_buffer_slots_.pop();
 
     // It's possible that the producer channel has expired. When this occurs,
     // ignore the producer channel.
     if (producer_channel == nullptr) {
-      ALOGW(
-          "ConsumerQueueChannel::OnConsumerQueueImportBuffers: producer "
-          "channel has already been expired.");
+      ALOGW("%s: producer channel has already been expired.", __FUNCTION__);
       continue;
     }
 
-    auto status = producer_channel->CreateConsumer(message);
+    auto status =
+        producer_channel->CreateConsumer(message, consumer_state_mask);
 
     // 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 (!status) {
-      ALOGE(
-          "ConsumerQueueChannel::OnConsumerQueueImportBuffers: Failed create "
-          "consumer: %s",
-          status.GetErrorMessage().c_str());
+      ALOGE("%s: Failed create consumer: %s", __FUNCTION__,
+            status.GetErrorMessage().c_str());
       if (buffer_handles.empty()) {
         ClearAvailable();
         return status.error_status();
diff --git a/services/vr/bufferhubd/detached_buffer_channel.cpp b/services/vr/bufferhubd/detached_buffer_channel.cpp
deleted file mode 100644
index a5cf68d..0000000
--- a/services/vr/bufferhubd/detached_buffer_channel.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-#include "detached_buffer_channel.h"
-#include "producer_channel.h"
-
-using android::pdx::BorrowedHandle;
-using android::pdx::ErrorStatus;
-using android::pdx::Message;
-using android::pdx::RemoteChannelHandle;
-using android::pdx::Status;
-using android::pdx::rpc::DispatchRemoteMethod;
-
-namespace android {
-namespace dvr {
-
-DetachedBufferChannel::DetachedBufferChannel(BufferHubService* service,
-                                             int buffer_id, int channel_id,
-                                             IonBuffer buffer,
-                                             IonBuffer metadata_buffer,
-                                             size_t user_metadata_size)
-    : BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType),
-      buffer_(std::move(buffer)),
-      metadata_buffer_(std::move(metadata_buffer)),
-      user_metadata_size_(user_metadata_size) {
-}
-
-DetachedBufferChannel::DetachedBufferChannel(BufferHubService* service,
-                                             int buffer_id, uint32_t width,
-                                             uint32_t height,
-                                             uint32_t layer_count,
-                                             uint32_t format, uint64_t usage,
-                                             size_t user_metadata_size)
-    : BufferHubChannel(service, buffer_id, buffer_id, kDetachedBufferType),
-      user_metadata_size_(user_metadata_size) {
-  // The size the of metadata buffer is used as the "width" parameter during
-  // allocation. Thus it cannot overflow uint32_t.
-  if (user_metadata_size_ >= (std::numeric_limits<uint32_t>::max() -
-                              BufferHubDefs::kMetadataHeaderSize)) {
-    ALOGE(
-        "DetachedBufferChannel::DetachedBufferChannel: metadata size too big.");
-    return;
-  }
-
-  if (int ret = buffer_.Alloc(width, height, layer_count, format, usage)) {
-    ALOGE(
-        "DetachedBufferChannel::DetachedBufferChannel: Failed to allocate "
-        "buffer: %s",
-        strerror(-ret));
-    return;
-  }
-
-  // Buffer metadata has two parts: 1) a fixed sized metadata header; and 2)
-  // user requested metadata.
-  const size_t size = BufferHubDefs::kMetadataHeaderSize + user_metadata_size_;
-  if (int ret = metadata_buffer_.Alloc(size,
-                                       /*height=*/1,
-                                       /*layer_count=*/1,
-                                       BufferHubDefs::kMetadataFormat,
-                                       BufferHubDefs::kMetadataUsage)) {
-    ALOGE(
-        "DetachedBufferChannel::DetachedBufferChannel: Failed to allocate "
-        "metadata: %s",
-        strerror(-ret));
-    return;
-  }
-}
-
-DetachedBufferChannel::~DetachedBufferChannel() {
-  ALOGD_IF(TRACE,
-           "DetachedBufferChannel::~DetachedBufferChannel: channel_id=%d "
-           "buffer_id=%d.",
-           channel_id(), buffer_id());
-  Hangup();
-}
-
-BufferHubChannel::BufferInfo DetachedBufferChannel::GetBufferInfo() const {
-  return BufferInfo(buffer_id(), /*consumer_count=*/0, buffer_.width(),
-                    buffer_.height(), buffer_.layer_count(), buffer_.format(),
-                    buffer_.usage(), /*pending_count=*/0, /*state=*/0,
-                    /*signaled_mask=*/0, /*index=*/0);
-}
-
-void DetachedBufferChannel::HandleImpulse(Message& /*message*/) {
-  ATRACE_NAME("DetachedBufferChannel::HandleImpulse");
-}
-
-bool DetachedBufferChannel::HandleMessage(Message& message) {
-  ATRACE_NAME("DetachedBufferChannel::HandleMessage");
-  switch (message.GetOp()) {
-    case DetachedBufferRPC::Import::Opcode:
-      DispatchRemoteMethod<DetachedBufferRPC::Import>(
-          *this, &DetachedBufferChannel::OnImport, message);
-      return true;
-
-    case DetachedBufferRPC::Promote::Opcode:
-      DispatchRemoteMethod<DetachedBufferRPC::Promote>(
-          *this, &DetachedBufferChannel::OnPromote, message);
-      return true;
-
-    default:
-      return false;
-  }
-}
-
-Status<BufferDescription<BorrowedHandle>> DetachedBufferChannel::OnImport(
-    Message& /*message*/) {
-  ATRACE_NAME("DetachedBufferChannel::OnGetBuffer");
-  ALOGD_IF(TRACE, "DetachedBufferChannel::OnGetBuffer: buffer=%d.",
-           buffer_id());
-
-  return BufferDescription<BorrowedHandle>{buffer_,
-                                           metadata_buffer_,
-                                           buffer_id(),
-                                           /*buffer_state_bit=*/0,
-                                           BorrowedHandle{},
-                                           BorrowedHandle{}};
-}
-
-Status<RemoteChannelHandle> DetachedBufferChannel::OnPromote(
-    Message& message) {
-  ATRACE_NAME("DetachedBufferChannel::OnPromote");
-  ALOGD_IF(TRACE, "DetachedBufferChannel::OnPromote: buffer_id=%d",
-           buffer_id());
-
-  // Note that the new ProducerChannel will have different channel_id, but
-  // inherits the buffer_id from the DetachedBuffer.
-  int channel_id;
-  auto status = message.PushChannel(0, nullptr, &channel_id);
-  if (!status) {
-    ALOGE(
-        "DetachedBufferChannel::OnPromote: Failed to push ProducerChannel: %s.",
-        status.GetErrorMessage().c_str());
-    return ErrorStatus(ENOMEM);
-  }
-
-  std::unique_ptr<ProducerChannel> channel = ProducerChannel::Create(
-      service(), buffer_id(), channel_id, std::move(buffer_),
-      std::move(metadata_buffer_), user_metadata_size_);
-  if (!channel) {
-    ALOGE(
-        "DetachedBufferChannel::OnPromote: Failed to create ProducerChannel "
-        "from a DetachedBufferChannel, buffer_id=%d.",
-        buffer_id());
-  }
-
-  const auto channel_status =
-      service()->SetChannel(channel_id, std::move(channel));
-  if (!channel_status) {
-    // Technically, this should never fail, as we just pushed the channel. Note
-    // that LOG_FATAL will be stripped out in non-debug build.
-    LOG_FATAL(
-        "DetachedBufferChannel::OnPromote: Failed to set new producer buffer "
-        "channel: %s.",
-        channel_status.GetErrorMessage().c_str());
-  }
-
-  return status;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/bufferhubd/detached_buffer_channel.h b/services/vr/bufferhubd/detached_buffer_channel.h
deleted file mode 100644
index 8b6dab8..0000000
--- a/services/vr/bufferhubd/detached_buffer_channel.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
-#define ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
-
-#include "buffer_hub.h"
-
-#include <pdx/channel_handle.h>
-#include <pdx/file_handle.h>
-
-namespace android {
-namespace dvr {
-
-class DetachedBufferChannel : public BufferHubChannel {
- public:
-  ~DetachedBufferChannel() override;
-
-  template <typename... Args>
-  static std::unique_ptr<DetachedBufferChannel> Create(Args&&... args) {
-    auto buffer = std::unique_ptr<DetachedBufferChannel>(
-        new DetachedBufferChannel(std::forward<Args>(args)...));
-    return buffer->IsValid() ? std::move(buffer) : nullptr;
-  }
-
-  // Returns whether the object holds a valid graphic buffer.
-  bool IsValid() const {
-    return buffer_.IsValid() && metadata_buffer_.IsValid();
-  }
-
-  size_t user_metadata_size() const { return user_metadata_size_; }
-
-  // Captures buffer info for use by BufferHubService::DumpState().
-  BufferInfo GetBufferInfo() const override;
-
-  bool HandleMessage(pdx::Message& message) override;
-  void HandleImpulse(pdx::Message& message) override;
-
- private:
-  // Creates a detached buffer from existing IonBuffers.
-  DetachedBufferChannel(BufferHubService* service, int buffer_id,
-                        int channel_id, IonBuffer buffer,
-                        IonBuffer metadata_buffer, size_t user_metadata_size);
-
-  // Allocates a new detached buffer.
-  DetachedBufferChannel(BufferHubService* service, int buffer_id,
-                        uint32_t width, uint32_t height, uint32_t layer_count,
-                        uint32_t format, uint64_t usage,
-                        size_t user_metadata_size);
-
-  pdx::Status<BufferDescription<pdx::BorrowedHandle>> OnImport(
-      pdx::Message& message);
-  pdx::Status<pdx::RemoteChannelHandle> OnPromote(pdx::Message& message);
-
-  // Gralloc buffer handles.
-  IonBuffer buffer_;
-  IonBuffer metadata_buffer_;
-
-  // Size of user requested metadata.
-  const size_t user_metadata_size_;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
diff --git a/services/vr/bufferhubd/buffer_hub.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
similarity index 87%
rename from services/vr/bufferhubd/buffer_hub.h
rename to services/vr/bufferhubd/include/private/dvr/buffer_hub.h
index e47ffa3..909d69b 100644
--- a/services/vr/bufferhubd/buffer_hub.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
@@ -41,18 +41,17 @@
 
   // Captures buffer info for use by BufferHubService::DumpState().
   struct BufferInfo {
-    // Common data field shared by BufferProducer and ProducerQueue.
+    // Common data field shared by ProducerBuffer and ProducerQueue.
     int id = -1;
     int type = -1;
     size_t consumer_count = 0;
 
-    // Data field for buffer producer.
+    // Data field for producer buffer.
     uint32_t width = 0;
     uint32_t height = 0;
     uint32_t layer_count = 0;
     uint32_t format = 0;
     uint64_t usage = 0;
-    size_t pending_count = 0;
     uint64_t state = 0;
     uint64_t signaled_mask = 0;
     uint64_t index = 0;
@@ -63,8 +62,7 @@
 
     BufferInfo(int id, size_t consumer_count, uint32_t width, uint32_t height,
                uint32_t layer_count, uint32_t format, uint64_t usage,
-               size_t pending_count, uint64_t state, uint64_t signaled_mask,
-               uint64_t index)
+               uint64_t state, uint64_t signaled_mask, uint64_t index)
         : id(id),
           type(kProducerType),
           consumer_count(consumer_count),
@@ -73,7 +71,6 @@
           layer_count(layer_count),
           format(format),
           usage(usage),
-          pending_count(pending_count),
           state(state),
           signaled_mask(signaled_mask),
           index(index) {}
@@ -147,11 +144,6 @@
   pdx::Status<void> OnCreateBuffer(pdx::Message& message, uint32_t width,
                                    uint32_t height, uint32_t format,
                                    uint64_t usage, size_t meta_size_bytes);
-  pdx::Status<void> OnCreateDetachedBuffer(pdx::Message& message,
-                                           uint32_t width, uint32_t height,
-                                           uint32_t layer_count,
-                                           uint32_t format, uint64_t usage,
-                                           size_t user_metadata_size);
   pdx::Status<QueueInfo> OnCreateProducerQueue(
       pdx::Message& message, const ProducerQueueConfig& producer_config,
       const UsagePolicy& usage_policy);
diff --git a/services/vr/bufferhubd/consumer_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
similarity index 80%
rename from services/vr/bufferhubd/consumer_channel.h
rename to services/vr/bufferhubd/include/private/dvr/consumer_channel.h
index 55cf969..5ee551f 100644
--- a/services/vr/bufferhubd/consumer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
@@ -1,10 +1,9 @@
 #ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
 #define ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
 
-#include "buffer_hub.h"
-
 #include <pdx/rpc/buffer_wrapper.h>
 #include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/buffer_hub.h>
 
 namespace android {
 namespace dvr {
@@ -17,17 +16,18 @@
   using Message = pdx::Message;
 
   ConsumerChannel(BufferHubService* service, int buffer_id, int channel_id,
-                  uint64_t consumer_state_bit,
+                  uint32_t client_state_mask,
                   const std::shared_ptr<Channel> producer);
   ~ConsumerChannel() override;
 
   bool HandleMessage(Message& message) override;
   void HandleImpulse(Message& message) override;
 
-  uint64_t consumer_state_bit() const { return consumer_state_bit_; }
+  uint32_t client_state_mask() const { return client_state_mask_; }
   BufferInfo GetBufferInfo() const override;
 
-  bool OnProducerPosted();
+  void OnProducerGained();
+  void OnProducerPosted();
   void OnProducerClosed();
 
  private:
@@ -38,12 +38,10 @@
   pdx::Status<LocalFence> OnConsumerAcquire(Message& message);
   pdx::Status<void> OnConsumerRelease(Message& message,
                                       LocalFence release_fence);
-  pdx::Status<void> OnConsumerSetIgnore(Message& message, bool ignore);
 
-  uint64_t consumer_state_bit_{0};
+  uint32_t client_state_mask_{0U};
   bool acquired_{false};
   bool released_{true};
-  bool ignored_{false};  // True if we are ignoring events.
   std::weak_ptr<Channel> producer_;
 
   ConsumerChannel(const ConsumerChannel&) = delete;
diff --git a/services/vr/bufferhubd/consumer_queue_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h
similarity index 69%
rename from services/vr/bufferhubd/consumer_queue_channel.h
rename to services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h
index 8437c4c..3a81b03 100644
--- a/services/vr/bufferhubd/consumer_queue_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h
@@ -1,14 +1,12 @@
 #ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
 #define ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
 
-#include "buffer_hub.h"
-
-#include <private/dvr/bufferhub_rpc.h>
-
 #include <queue>
 
-#include "consumer_channel.h"
-#include "producer_queue_channel.h"
+#include <private/dvr/buffer_hub.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/consumer_channel.h>
+#include <private/dvr/producer_queue_channel.h>
 
 namespace android {
 namespace dvr {
@@ -30,7 +28,8 @@
   // Called by ProdcuerQueueChannel to notify consumer queue that a new
   // buffer has been allocated.
   void RegisterNewBuffer(
-      const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot);
+      const std::shared_ptr<ProducerChannel>& producer_channel,
+      size_t producer_slot);
 
   // Called after clients been signaled by service that new buffer has been
   // allocated. Clients uses kOpConsumerQueueImportBuffers to import new
@@ -42,14 +41,29 @@
   void OnProducerClosed();
 
  private:
+  // Data structure to store relavant info of a newly allocated producer buffer
+  // so that consumer channel and buffer can be created later.
+  struct PendingBuffer {
+    PendingBuffer(std::shared_ptr<ProducerChannel> channel, size_t slot,
+                  uint64_t mask) {
+      producer_channel = channel;
+      producer_slot = slot;
+      consumer_state_mask = mask;
+    }
+    PendingBuffer() = delete;
+
+    std::weak_ptr<ProducerChannel> producer_channel;
+    size_t producer_slot;
+    uint64_t consumer_state_mask;
+  };
+
   std::shared_ptr<ProducerQueueChannel> GetProducer() const;
 
-  // Pointer to the prodcuer channel
+  // Pointer to the producer channel.
   std::weak_ptr<Channel> producer_;
 
   // Tracks newly allocated buffer producers along with it's slot number.
-  std::queue<std::pair<std::weak_ptr<ProducerChannel>, size_t>>
-      pending_buffer_slots_;
+  std::queue<PendingBuffer> pending_buffer_slots_;
 
   // Tracks how many buffers have this queue imported.
   size_t capacity_;
diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
similarity index 77%
rename from services/vr/bufferhubd/producer_channel.h
rename to services/vr/bufferhubd/include/private/dvr/producer_channel.h
index 67fdf15..45593ad 100644
--- a/services/vr/bufferhubd/producer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
@@ -1,8 +1,6 @@
 #ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
 #define ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
 
-#include "buffer_hub.h"
-
 #include <functional>
 #include <memory>
 #include <vector>
@@ -10,6 +8,7 @@
 #include <pdx/channel_handle.h>
 #include <pdx/file_handle.h>
 #include <pdx/rpc/buffer_wrapper.h>
+#include <private/dvr/buffer_hub.h>
 #include <private/dvr/bufferhub_rpc.h>
 #include <private/dvr/ion_buffer.h>
 
@@ -43,56 +42,55 @@
 
   ~ProducerChannel() override;
 
+  uint32_t buffer_state() const {
+    return buffer_state_->load(std::memory_order_acquire);
+  }
+
   bool HandleMessage(Message& message) override;
   void HandleImpulse(Message& message) override;
 
   BufferInfo GetBufferInfo() const override;
 
-  BufferDescription<BorrowedHandle> GetBuffer(uint64_t buffer_state_bit);
+  BufferDescription<BorrowedHandle> GetBuffer(uint32_t client_state_mask);
 
-  pdx::Status<RemoteChannelHandle> CreateConsumer(Message& message);
+  pdx::Status<RemoteChannelHandle> CreateConsumer(Message& message,
+                                                  uint32_t consumer_state_mask);
+  pdx::Status<uint32_t> CreateConsumerStateMask();
   pdx::Status<RemoteChannelHandle> OnNewConsumer(Message& message);
 
   pdx::Status<LocalFence> OnConsumerAcquire(Message& message);
   pdx::Status<void> OnConsumerRelease(Message& message,
                                       LocalFence release_fence);
 
-  void OnConsumerIgnored();
-  void OnConsumerOrphaned(ConsumerChannel* channel);
+  void OnConsumerOrphaned(const uint32_t& consumer_state_mask);
 
   void AddConsumer(ConsumerChannel* channel);
   void RemoveConsumer(ConsumerChannel* channel);
 
   bool CheckParameters(uint32_t width, uint32_t height, uint32_t layer_count,
                        uint32_t format, uint64_t usage,
-                       size_t user_metadata_size);
+                       size_t user_metadata_size) const;
 
  private:
   std::vector<ConsumerChannel*> consumer_channels_;
-  // This counts the number of consumers left to process this buffer. If this is
-  // zero then the producer can re-acquire ownership.
-  int pending_consumers_;
 
   IonBuffer buffer_;
 
   // IonBuffer that is shared between bufferhubd, producer, and consumers.
   IonBuffer metadata_buffer_;
   BufferHubDefs::MetadataHeader* metadata_header_ = nullptr;
-  std::atomic<uint64_t>* buffer_state_ = nullptr;
-  std::atomic<uint64_t>* fence_state_ = nullptr;
+  std::atomic<uint32_t>* buffer_state_ = nullptr;
+  std::atomic<uint32_t>* fence_state_ = nullptr;
+  std::atomic<uint32_t>* active_clients_bit_mask_ = nullptr;
 
-  // All active consumer bits. Valid bits are the lower 63 bits, while the
-  // highest bit is reserved for the producer and should not be set.
-  uint64_t active_consumer_bit_mask_{0ULL};
   // All orphaned consumer bits. Valid bits are the lower 63 bits, while the
   // highest bit is reserved for the producer and should not be set.
-  uint64_t orphaned_consumer_bit_mask_{0ULL};
+  uint32_t orphaned_consumer_bit_mask_{0U};
 
-  bool producer_owns_;
   LocalFence post_fence_;
   LocalFence returned_fence_;
   size_t user_metadata_size_;  // size of user requested buffer buffer size.
-  size_t metadata_buf_size_;  // size of the ion buffer that holds metadata.
+  size_t metadata_buf_size_;   // size of the ion buffer that holds metadata.
 
   pdx::LocalHandle acquire_fence_fd_;
   pdx::LocalHandle release_fence_fd_;
@@ -109,7 +107,14 @@
   pdx::Status<BufferDescription<BorrowedHandle>> OnGetBuffer(Message& message);
   pdx::Status<void> OnProducerPost(Message& message, LocalFence acquire_fence);
   pdx::Status<LocalFence> OnProducerGain(Message& message);
-  pdx::Status<RemoteChannelHandle> OnProducerDetach(Message& message);
+
+  // Remove consumer from atomics in shared memory based on consumer_state_mask.
+  // This function is used for clean up for failures in CreateConsumer method.
+  void RemoveConsumerClientMask(uint32_t consumer_state_mask);
+
+  // Checks whether the buffer is released by all active clients, excluding
+  // orphaned consumers.
+  bool IsBufferReleasedByAllActiveClientsExceptForOrphans() const;
 
   ProducerChannel(const ProducerChannel&) = delete;
   void operator=(const ProducerChannel&) = delete;
diff --git a/services/vr/bufferhubd/producer_queue_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_queue_channel.h
similarity index 89%
rename from services/vr/bufferhubd/producer_queue_channel.h
rename to services/vr/bufferhubd/include/private/dvr/producer_queue_channel.h
index e825f47..0456ad8 100644
--- a/services/vr/bufferhubd/producer_queue_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/producer_queue_channel.h
@@ -1,10 +1,9 @@
 #ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
 #define ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
 
-#include "buffer_hub.h"
-
 #include <pdx/status.h>
 #include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/buffer_hub.h>
 
 namespace android {
 namespace dvr {
@@ -38,8 +37,12 @@
                                  uint32_t format, uint64_t usage,
                                  size_t buffer_count);
 
-  // Detach a BufferHubProducer indicated by |slot|. Note that the buffer must
-  // be in Gain'ed state for the producer queue to detach.
+  // Inserts a ProducerBuffer into the queue. Note that the buffer must be in
+  // Gain'ed state for the operation to succeed.
+  pdx::Status<size_t> OnProducerQueueInsertBuffer(pdx::Message& message, int buffer_cid);
+
+  // Removes a ProducerBuffer indicated by |slot|. Note that the buffer must be
+  // in Gain'ed state for the operation to succeed.
   pdx::Status<void> OnProducerQueueRemoveBuffer(pdx::Message& message,
                                                 size_t slot);
 
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index b6977aa..164d9e6 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -1,19 +1,17 @@
-#include "producer_channel.h"
-
-#include <log/log.h>
-#include <sync/sync.h>
 #include <sys/epoll.h>
 #include <sys/eventfd.h>
 #include <sys/poll.h>
-#include <utils/Trace.h>
 
 #include <algorithm>
 #include <atomic>
 #include <thread>
 
+#include <log/log.h>
 #include <private/dvr/bufferhub_rpc.h>
-#include "consumer_channel.h"
-#include "detached_buffer_channel.h"
+#include <private/dvr/consumer_channel.h>
+#include <private/dvr/producer_channel.h>
+#include <sync/sync.h>
+#include <utils/Trace.h>
 
 using android::pdx::BorrowedHandle;
 using android::pdx::ErrorStatus;
@@ -27,14 +25,6 @@
 namespace android {
 namespace dvr {
 
-namespace {
-
-static inline uint64_t FindNextClearedBit(uint64_t bits) {
-  return ~bits - (~bits & (~bits - 1));
-}
-
-}  // namespace
-
 ProducerChannel::ProducerChannel(BufferHubService* service, int buffer_id,
                                  int channel_id, IonBuffer buffer,
                                  IonBuffer metadata_buffer,
@@ -65,8 +55,6 @@
                                  uint64_t usage, size_t user_metadata_size,
                                  int* error)
     : BufferHubChannel(service, channel_id, channel_id, kProducerType),
-      pending_consumers_(0),
-      producer_owns_(true),
       user_metadata_size_(user_metadata_size),
       metadata_buf_size_(BufferHubDefs::kMetadataHeaderSize +
                          user_metadata_size) {
@@ -104,9 +92,16 @@
   // Using placement new here to reuse shared memory instead of new allocation
   // and also initialize the value to zero.
   buffer_state_ =
-      new (&metadata_header_->buffer_state) std::atomic<uint64_t>(0);
-  fence_state_ =
-      new (&metadata_header_->fence_state) std::atomic<uint64_t>(0);
+      new (&metadata_header_->buffer_state) std::atomic<uint32_t>(0);
+  fence_state_ = new (&metadata_header_->fence_state) std::atomic<uint32_t>(0);
+  active_clients_bit_mask_ =
+      new (&metadata_header_->active_clients_bit_mask) std::atomic<uint32_t>(0);
+
+  // Producer channel is never created after consumer channel, and one buffer
+  // only have one fixed producer for now. Thus, it is correct to assume
+  // producer state bit is kFirstClientBitMask for now.
+  active_clients_bit_mask_->store(BufferHubDefs::kFirstClientBitMask,
+                                  std::memory_order_release);
 
   acquire_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
   release_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
@@ -123,7 +118,7 @@
 
   epoll_event event;
   event.events = 0;
-  event.data.u64 = 0ULL;
+  event.data.u32 = 0U;
   if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_ADD, dummy_fence_fd_.Get(),
                 &event) < 0) {
     ALOGE(
@@ -168,8 +163,9 @@
 ProducerChannel::~ProducerChannel() {
   ALOGD_IF(TRACE,
            "ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d "
-           "state=%" PRIx64 ".",
-           channel_id(), buffer_id(), buffer_state_->load());
+           "state=%" PRIx32 ".",
+           channel_id(), buffer_id(),
+           buffer_state_->load(std::memory_order_acquire));
   for (auto consumer : consumer_channels_) {
     consumer->OnProducerClosed();
   }
@@ -178,14 +174,15 @@
 
 BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const {
   // Derive the mask of signaled buffers in this producer / consumer set.
-  uint64_t signaled_mask = signaled() ? BufferHubDefs::kProducerStateBit : 0;
+  uint32_t signaled_mask = signaled() ? BufferHubDefs::kFirstClientBitMask : 0;
   for (const ConsumerChannel* consumer : consumer_channels_) {
-    signaled_mask |= consumer->signaled() ? consumer->consumer_state_bit() : 0;
+    signaled_mask |= consumer->signaled() ? consumer->client_state_mask() : 0;
   }
 
   return BufferInfo(buffer_id(), consumer_channels_.size(), buffer_.width(),
                     buffer_.height(), buffer_.layer_count(), buffer_.format(),
-                    buffer_.usage(), pending_consumers_, buffer_state_->load(),
+                    buffer_.usage(),
+                    buffer_state_->load(std::memory_order_acquire),
                     signaled_mask, metadata_header_->queue_index);
 }
 
@@ -224,107 +221,192 @@
           *this, &ProducerChannel::OnProducerGain, message);
       return true;
 
-    case BufferHubRPC::ProducerBufferDetach::Opcode:
-      DispatchRemoteMethod<BufferHubRPC::ProducerBufferDetach>(
-          *this, &ProducerChannel::OnProducerDetach, message);
-      return true;
-
     default:
       return false;
   }
 }
 
 BufferDescription<BorrowedHandle> ProducerChannel::GetBuffer(
-    uint64_t buffer_state_bit) {
-  return {
-      buffer_,          metadata_buffer_,           buffer_id(),
-      buffer_state_bit, acquire_fence_fd_.Borrow(), release_fence_fd_.Borrow()};
+    uint32_t client_state_mask) {
+  return {buffer_,
+          metadata_buffer_,
+          buffer_id(),
+          channel_id(),
+          client_state_mask,
+          acquire_fence_fd_.Borrow(),
+          release_fence_fd_.Borrow()};
 }
 
 Status<BufferDescription<BorrowedHandle>> ProducerChannel::OnGetBuffer(
     Message& /*message*/) {
   ATRACE_NAME("ProducerChannel::OnGetBuffer");
-  ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d, state=%" PRIx64 ".",
-           buffer_id(), buffer_state_->load());
-  return {GetBuffer(BufferHubDefs::kProducerStateBit)};
+  ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d, state=%" PRIx32 ".",
+           buffer_id(), buffer_state_->load(std::memory_order_acquire));
+  return {GetBuffer(BufferHubDefs::kFirstClientBitMask)};
 }
 
-Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) {
-  ATRACE_NAME("ProducerChannel::CreateConsumer");
-  ALOGD_IF(TRACE,
-           "ProducerChannel::CreateConsumer: buffer_id=%d, producer_owns=%d",
-           buffer_id(), producer_owns_);
+Status<uint32_t> ProducerChannel::CreateConsumerStateMask() {
+  // Try find the next consumer state bit which has not been claimed by any
+  // consumer yet.
+  // memory_order_acquire is chosen here because all writes in other threads
+  // that release active_clients_bit_mask_ need to be visible here.
+  uint32_t current_active_clients_bit_mask =
+      active_clients_bit_mask_->load(std::memory_order_acquire);
+  uint32_t consumer_state_mask =
+      BufferHubDefs::FindNextAvailableClientStateMask(
+          current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
+  if (consumer_state_mask == 0U) {
+    ALOGE("%s: reached the maximum mumber of consumers per producer: 63.",
+          __FUNCTION__);
+    return ErrorStatus(E2BIG);
+  }
+  uint32_t updated_active_clients_bit_mask =
+      current_active_clients_bit_mask | consumer_state_mask;
+  // Set the updated value only if the current value stays the same as what was
+  // read before. If the comparison succeeds, update the value without
+  // reordering anything before or after this read-modify-write in the current
+  // thread, and the modification will be visible in other threads that acquire
+  // active_clients_bit_mask_. If the comparison fails, load the result of
+  // all writes from all threads to updated_active_clients_bit_mask.
+  // Keep on finding the next available slient state mask until succeed or out
+  // of memory.
+  while (!active_clients_bit_mask_->compare_exchange_weak(
+      current_active_clients_bit_mask, updated_active_clients_bit_mask,
+      std::memory_order_acq_rel, std::memory_order_acquire)) {
+    ALOGE("%s: Current active clients bit mask is changed to %" PRIx32
+          ", which was expected to be %" PRIx32
+          ". Trying to generate a new client state mask to resolve race "
+          "condition.",
+          __FUNCTION__, updated_active_clients_bit_mask,
+          current_active_clients_bit_mask);
+    consumer_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
+        current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
+    if (consumer_state_mask == 0U) {
+      ALOGE("%s: reached the maximum mumber of consumers per producer: %d.",
+            __FUNCTION__, (BufferHubDefs::kMaxNumberOfClients - 1));
+      return ErrorStatus(E2BIG);
+    }
+    updated_active_clients_bit_mask =
+        current_active_clients_bit_mask | consumer_state_mask;
+  }
+
+  return {consumer_state_mask};
+}
+
+void ProducerChannel::RemoveConsumerClientMask(uint32_t consumer_state_mask) {
+  // Clear up the buffer state and fence state in case there is already
+  // something there due to possible race condition between producer post and
+  // consumer failed to create channel.
+  buffer_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
+  fence_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
+
+  // Restore the consumer state bit and make it visible in other threads that
+  // acquire the active_clients_bit_mask_.
+  active_clients_bit_mask_->fetch_and(~consumer_state_mask,
+                                      std::memory_order_release);
+}
+
+Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(
+    Message& message, uint32_t consumer_state_mask) {
+  ATRACE_NAME(__FUNCTION__);
+  ALOGD("%s: buffer_id=%d", __FUNCTION__, buffer_id());
 
   int channel_id;
   auto status = message.PushChannel(0, nullptr, &channel_id);
   if (!status) {
-    ALOGE(
-        "ProducerChannel::CreateConsumer: Failed to push consumer channel: %s",
-        status.GetErrorMessage().c_str());
+    ALOGE("%s: Failed to push consumer channel: %s", __FUNCTION__,
+          status.GetErrorMessage().c_str());
+    RemoveConsumerClientMask(consumer_state_mask);
     return ErrorStatus(ENOMEM);
   }
 
-  // Try find the next consumer state bit which has not been claimed by any
-  // consumer yet.
-  uint64_t consumer_state_bit = FindNextClearedBit(
-      active_consumer_bit_mask_ | orphaned_consumer_bit_mask_ |
-      BufferHubDefs::kProducerStateBit);
-  if (consumer_state_bit == 0ULL) {
-    ALOGE(
-        "ProducerChannel::CreateConsumer: reached the maximum mumber of "
-        "consumers per producer: 63.");
-    return ErrorStatus(E2BIG);
-  }
-
-  auto consumer =
-      std::make_shared<ConsumerChannel>(service(), buffer_id(), channel_id,
-                                        consumer_state_bit, shared_from_this());
+  auto consumer = std::make_shared<ConsumerChannel>(
+      service(), buffer_id(), channel_id, consumer_state_mask,
+      shared_from_this());
   const auto channel_status = service()->SetChannel(channel_id, consumer);
   if (!channel_status) {
-    ALOGE(
-        "ProducerChannel::CreateConsumer: failed to set new consumer channel: "
-        "%s",
-        channel_status.GetErrorMessage().c_str());
+    ALOGE("%s: failed to set new consumer channel: %s.", __FUNCTION__,
+          channel_status.GetErrorMessage().c_str());
+    RemoveConsumerClientMask(consumer_state_mask);
     return ErrorStatus(ENOMEM);
   }
 
-  if (!producer_owns_ &&
-      !BufferHubDefs::IsBufferReleased(buffer_state_->load())) {
-    // Signal the new consumer when adding it to a posted producer.
-    if (consumer->OnProducerPosted())
-      pending_consumers_++;
+  uint32_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  // Return the consumer channel handle without signal when adding the new
+  // consumer to a buffer that is available to producer (a.k.a a fully-released
+  // buffer) or a gained buffer.
+  if (current_buffer_state == 0U ||
+      BufferHubDefs::AnyClientGained(current_buffer_state)) {
+    return {status.take()};
   }
 
-  active_consumer_bit_mask_ |= consumer_state_bit;
+  // Signal the new consumer when adding it to a posted producer.
+  bool update_buffer_state = true;
+  if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+                                     consumer_state_mask)) {
+    uint32_t updated_buffer_state =
+        current_buffer_state ^
+        (consumer_state_mask & BufferHubDefs::kHighBitsMask);
+    while (!buffer_state_->compare_exchange_weak(
+        current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+        std::memory_order_acquire)) {
+      ALOGI(
+          "%s: Failed to post to the new consumer. "
+          "Current buffer state was changed to %" PRIx32
+          " when trying to acquire the buffer and modify the buffer state to "
+          "%" PRIx32
+          ". About to try again if the buffer is still not gained nor fully "
+          "released.",
+          __FUNCTION__, current_buffer_state, updated_buffer_state);
+      if (current_buffer_state == 0U ||
+          BufferHubDefs::AnyClientGained(current_buffer_state)) {
+        ALOGI("%s: buffer is gained or fully released, state=%" PRIx32 ".",
+              __FUNCTION__, current_buffer_state);
+        update_buffer_state = false;
+        break;
+      }
+      updated_buffer_state =
+          current_buffer_state ^
+          (consumer_state_mask & BufferHubDefs::kHighBitsMask);
+    }
+  }
+  if (update_buffer_state || BufferHubDefs::IsClientPosted(
+                                 buffer_state_->load(std::memory_order_acquire),
+                                 consumer_state_mask)) {
+    consumer->OnProducerPosted();
+  }
+
   return {status.take()};
 }
 
 Status<RemoteChannelHandle> ProducerChannel::OnNewConsumer(Message& message) {
   ATRACE_NAME("ProducerChannel::OnNewConsumer");
   ALOGD_IF(TRACE, "ProducerChannel::OnNewConsumer: buffer_id=%d", buffer_id());
-  return CreateConsumer(message);
+  auto status = CreateConsumerStateMask();
+  if (!status.ok()) {
+    return status.error_status();
+  }
+  return CreateConsumer(message, /*consumer_state_mask=*/status.get());
 }
 
-Status<void> ProducerChannel::OnProducerPost(
-    Message&, LocalFence acquire_fence) {
+Status<void> ProducerChannel::OnProducerPost(Message&,
+                                             LocalFence acquire_fence) {
   ATRACE_NAME("ProducerChannel::OnProducerPost");
-  ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id());
-  if (!producer_owns_) {
-    ALOGE("ProducerChannel::OnProducerPost: Not in gained state!");
-    return ErrorStatus(EBUSY);
-  }
+  ALOGD("ProducerChannel::OnProducerPost: buffer_id=%d, state=0x%x",
+        buffer_id(), buffer_state_->load(std::memory_order_acquire));
 
   epoll_event event;
   event.events = 0;
-  event.data.u64 = 0ULL;
+  event.data.u32 = 0U;
   int ret = epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD,
                       dummy_fence_fd_.Get(), &event);
   ALOGE_IF(ret < 0,
-      "ProducerChannel::OnProducerPost: Failed to modify the shared "
-      "release fence to include the dummy fence: %s",
-      strerror(errno));
+           "ProducerChannel::OnProducerPost: Failed to modify the shared "
+           "release fence to include the dummy fence: %s",
+           strerror(errno));
 
-  eventfd_t dummy_fence_count = 0ULL;
+  eventfd_t dummy_fence_count = 0U;
   if (eventfd_read(dummy_fence_fd_.Get(), &dummy_fence_count) < 0) {
     const int error = errno;
     if (error != EAGAIN) {
@@ -343,57 +425,44 @@
            dummy_fence_count, buffer_id());
 
   post_fence_ = std::move(acquire_fence);
-  producer_owns_ = false;
 
   // Signal any interested consumers. If there are none, the buffer will stay
   // in posted state until a consumer comes online. This behavior guarantees
   // that no frame is silently dropped.
-  pending_consumers_ = 0;
-  for (auto consumer : consumer_channels_) {
-    if (consumer->OnProducerPosted())
-      pending_consumers_++;
+  for (auto& consumer : consumer_channels_) {
+    consumer->OnProducerPosted();
   }
-  ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers",
-           pending_consumers_);
 
   return {};
 }
 
 Status<LocalFence> ProducerChannel::OnProducerGain(Message& /*message*/) {
   ATRACE_NAME("ProducerChannel::OnGain");
-  ALOGD_IF(TRACE, "ProducerChannel::OnGain: buffer_id=%d", buffer_id());
-  if (producer_owns_) {
-    ALOGE("ProducerChanneL::OnGain: Already in gained state: channel=%d",
-          channel_id());
-    return ErrorStatus(EALREADY);
-  }
-
-  // There are still pending consumers, return busy.
-  if (pending_consumers_ > 0) {
-    ALOGE(
-        "ProducerChannel::OnGain: Producer (id=%d) is gaining a buffer that "
-        "still has %d pending consumer(s).",
-        buffer_id(), pending_consumers_);
-    return ErrorStatus(EBUSY);
-  }
+  ALOGW("ProducerChannel::OnGain: buffer_id=%d", buffer_id());
 
   ClearAvailable();
-  producer_owns_ = true;
   post_fence_.close();
+  for (auto& consumer : consumer_channels_) {
+    consumer->OnProducerGained();
+  }
   return {std::move(returned_fence_)};
 }
 
-Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach(
+// TODO(b/112338294) Keep here for reference. Remove it after new logic is
+// written.
+/* Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach(
     Message& message) {
   ATRACE_NAME("ProducerChannel::OnProducerDetach");
   ALOGD_IF(TRACE, "ProducerChannel::OnProducerDetach: buffer_id=%d",
            buffer_id());
 
-  uint64_t buffer_state = buffer_state_->load();
-  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
-    // Can only detach a BufferProducer when it's in gained state.
+  uint32_t buffer_state = buffer_state_->load(std::memory_order_acquire);
+  if (!BufferHubDefs::IsClientGained(
+      buffer_state, BufferHubDefs::kFirstClientStateMask)) {
+    // Can only detach a ProducerBuffer when it's in gained state.
     ALOGW(
-        "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=0x%" PRIx64
+        "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=%"
+        PRIx32
         ") is not in gained state.",
         buffer_id(), buffer_state);
     return {};
@@ -415,10 +484,9 @@
     return ErrorStatus(-ret);
   };
 
-  std::unique_ptr<DetachedBufferChannel> channel =
-      DetachedBufferChannel::Create(
-          service(), buffer_id(), channel_id, std::move(buffer_),
-          std::move(metadata_buffer_), user_metadata_size_);
+  std::unique_ptr<BufferChannel> channel =
+      BufferChannel::Create(service(), buffer_id(), channel_id,
+                            std::move(buffer_), user_metadata_size_);
   if (!channel) {
     ALOGE("ProducerChannel::OnProducerDetach: Invalid buffer.");
     return ErrorStatus(EINVAL);
@@ -427,28 +495,23 @@
   const auto channel_status =
       service()->SetChannel(channel_id, std::move(channel));
   if (!channel_status) {
-    // Technically, this should never fail, as we just pushed the channel. Note
-    // that LOG_FATAL will be stripped out in non-debug build.
+    // Technically, this should never fail, as we just pushed the channel.
+    // Note that LOG_FATAL will be stripped out in non-debug build.
     LOG_FATAL(
-        "ProducerChannel::OnProducerDetach: Failed to set new detached buffer "
-        "channel: %s.",
-        channel_status.GetErrorMessage().c_str());
+        "ProducerChannel::OnProducerDetach: Failed to set new detached "
+        "buffer channel: %s.", channel_status.GetErrorMessage().c_str());
   }
 
   return status;
-}
+} */
 
 Status<LocalFence> ProducerChannel::OnConsumerAcquire(Message& /*message*/) {
   ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
   ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",
            buffer_id());
-  if (producer_owns_) {
-    ALOGE("ProducerChannel::OnConsumerAcquire: Not in posted state!");
-    return ErrorStatus(EBUSY);
-  }
 
-  // Return a borrowed fd to avoid unnecessary duplication of the underlying fd.
-  // Serialization just needs to read the handle.
+  // Return a borrowed fd to avoid unnecessary duplication of the underlying
+  // fd. Serialization just needs to read the handle.
   return {std::move(post_fence_)};
 }
 
@@ -457,10 +520,6 @@
   ATRACE_NAME("ProducerChannel::OnConsumerRelease");
   ALOGD_IF(TRACE, "ProducerChannel::OnConsumerRelease: buffer_id=%d",
            buffer_id());
-  if (producer_owns_) {
-    ALOGE("ProducerChannel::OnConsumerRelease: Not in acquired state!");
-    return ErrorStatus(EBUSY);
-  }
 
   // Attempt to merge the fences if necessary.
   if (release_fence) {
@@ -480,74 +539,51 @@
     }
   }
 
-  OnConsumerIgnored();
-  if (pending_consumers_ == 0) {
-    // Clear the producer bit atomically to transit into released state. This
-    // has to done by BufferHub as it requries synchronization among all
-    // consumers.
-    BufferHubDefs::ModifyBufferState(buffer_state_,
-                                     BufferHubDefs::kProducerStateBit, 0ULL);
-    ALOGD_IF(TRACE,
-             "ProducerChannel::OnConsumerRelease: releasing last consumer: "
-             "buffer_id=%d state=%" PRIx64 ".",
-             buffer_id(), buffer_state_->load());
-
+  if (IsBufferReleasedByAllActiveClientsExceptForOrphans()) {
+    buffer_state_->store(0U);
+    SignalAvailable();
     if (orphaned_consumer_bit_mask_) {
       ALOGW(
-          "ProducerChannel::OnConsumerRelease: orphaned buffer detected "
-          "during the this acquire/release cycle: id=%d orphaned=0x%" PRIx64
-          " queue_index=%" PRIu64 ".",
-          buffer_id(), orphaned_consumer_bit_mask_,
+          "%s: orphaned buffer detected during the this acquire/release cycle: "
+          "id=%d orphaned=0x%" PRIx32 " queue_index=%" PRId64 ".",
+          __FUNCTION__, buffer_id(), orphaned_consumer_bit_mask_,
           metadata_header_->queue_index);
       orphaned_consumer_bit_mask_ = 0;
     }
-
-    SignalAvailable();
   }
 
-  ALOGE_IF(pending_consumers_ &&
-               BufferHubDefs::IsBufferReleased(buffer_state_->load()),
-           "ProducerChannel::OnConsumerRelease: buffer state inconsistent: "
-           "pending_consumers=%d, buffer buffer is in releaed state.",
-           pending_consumers_);
   return {};
 }
 
-void ProducerChannel::OnConsumerIgnored() {
-  if (pending_consumers_ == 0) {
-    ALOGE("ProducerChannel::OnConsumerIgnored: no pending consumer.");
-    return;
+void ProducerChannel::OnConsumerOrphaned(const uint32_t& consumer_state_mask) {
+  // Remember the ignored consumer so that newly added consumer won't be
+  // taking the same state mask as this orphaned consumer.
+  ALOGE_IF(orphaned_consumer_bit_mask_ & consumer_state_mask,
+           "%s: Consumer (consumer_state_mask=%" PRIx32
+           ") is already orphaned.",
+           __FUNCTION__, consumer_state_mask);
+  orphaned_consumer_bit_mask_ |= consumer_state_mask;
+
+  if (IsBufferReleasedByAllActiveClientsExceptForOrphans()) {
+    buffer_state_->store(0U);
+    SignalAvailable();
   }
 
-  --pending_consumers_;
-  ALOGD_IF(TRACE,
-           "ProducerChannel::OnConsumerIgnored: buffer_id=%d %d consumers left",
-           buffer_id(), pending_consumers_);
-}
-
-void ProducerChannel::OnConsumerOrphaned(ConsumerChannel* channel) {
-  // Ignore the orphaned consumer.
-  OnConsumerIgnored();
-
-  const uint64_t consumer_state_bit = channel->consumer_state_bit();
-  ALOGE_IF(orphaned_consumer_bit_mask_ & consumer_state_bit,
-           "ProducerChannel::OnConsumerOrphaned: Consumer "
-           "(consumer_state_bit=%" PRIx64 ") is already orphaned.",
-           consumer_state_bit);
-  orphaned_consumer_bit_mask_ |= consumer_state_bit;
-
   // Atomically clear the fence state bit as an orphaned consumer will never
-  // signal a release fence. Also clear the buffer state as it won't be released
-  // as well.
-  fence_state_->fetch_and(~consumer_state_bit);
-  BufferHubDefs::ModifyBufferState(buffer_state_, consumer_state_bit, 0ULL);
+  // signal a release fence.
+  fence_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
+
+  // Atomically set the buffer state of this consumer to released state.
+  buffer_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
 
   ALOGW(
-      "ProducerChannel::OnConsumerOrphaned: detected new orphaned consumer "
-      "buffer_id=%d consumer_state_bit=%" PRIx64 " queue_index=%" PRIu64
-      " buffer_state=%" PRIx64 " fence_state=%" PRIx64 ".",
-      buffer_id(), consumer_state_bit, metadata_header_->queue_index,
-      buffer_state_->load(), fence_state_->load());
+      "%s: detected new orphaned consumer buffer_id=%d "
+      "consumer_state_mask=%" PRIx32 " queue_index=%" PRId64
+      " buffer_state=%" PRIx32 " fence_state=%" PRIx32 ".",
+      __FUNCTION__, buffer_id(), consumer_state_mask,
+      metadata_header_->queue_index,
+      buffer_state_->load(std::memory_order_acquire),
+      fence_state_->load(std::memory_order_acquire));
 }
 
 void ProducerChannel::AddConsumer(ConsumerChannel* channel) {
@@ -557,52 +593,82 @@
 void ProducerChannel::RemoveConsumer(ConsumerChannel* channel) {
   consumer_channels_.erase(
       std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
-  active_consumer_bit_mask_ &= ~channel->consumer_state_bit();
+  // Restore the consumer state bit and make it visible in other threads that
+  // acquire the active_clients_bit_mask_.
+  uint32_t consumer_state_mask = channel->client_state_mask();
+  uint32_t current_active_clients_bit_mask =
+      active_clients_bit_mask_->load(std::memory_order_acquire);
+  uint32_t updated_active_clients_bit_mask =
+      current_active_clients_bit_mask & (~consumer_state_mask);
+  while (!active_clients_bit_mask_->compare_exchange_weak(
+      current_active_clients_bit_mask, updated_active_clients_bit_mask,
+      std::memory_order_acq_rel, std::memory_order_acquire)) {
+    ALOGI(
+        "%s: Failed to remove consumer state mask. Current active clients bit "
+        "mask is changed to %" PRIx32
+        " when trying to acquire and modify it to %" PRIx32
+        ". About to try again.",
+        __FUNCTION__, current_active_clients_bit_mask,
+        updated_active_clients_bit_mask);
+    updated_active_clients_bit_mask =
+        current_active_clients_bit_mask & (~consumer_state_mask);
+  }
 
-  const uint64_t buffer_state = buffer_state_->load();
-  if (BufferHubDefs::IsBufferPosted(buffer_state) ||
-      BufferHubDefs::IsBufferAcquired(buffer_state)) {
+  const uint32_t current_buffer_state =
+      buffer_state_->load(std::memory_order_acquire);
+  if (BufferHubDefs::IsClientPosted(current_buffer_state,
+                                    consumer_state_mask) ||
+      BufferHubDefs::IsClientAcquired(current_buffer_state,
+                                      consumer_state_mask)) {
     // The consumer client is being destoryed without releasing. This could
     // happen in corner cases when the consumer crashes. Here we mark it
     // orphaned before remove it from producer.
-    OnConsumerOrphaned(channel);
+    OnConsumerOrphaned(consumer_state_mask);
+    return;
   }
 
-  if (BufferHubDefs::IsBufferReleased(buffer_state) ||
-      BufferHubDefs::IsBufferGained(buffer_state)) {
+  if (BufferHubDefs::IsClientReleased(current_buffer_state,
+                                      consumer_state_mask) ||
+      BufferHubDefs::AnyClientGained(current_buffer_state)) {
     // The consumer is being close while it is suppose to signal a release
     // fence. Signal the dummy fence here.
-    if (fence_state_->load() & channel->consumer_state_bit()) {
+    if (fence_state_->load(std::memory_order_acquire) & consumer_state_mask) {
       epoll_event event;
       event.events = EPOLLIN;
-      event.data.u64 = channel->consumer_state_bit();
+      event.data.u32 = consumer_state_mask;
       if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD,
                     dummy_fence_fd_.Get(), &event) < 0) {
         ALOGE(
-            "ProducerChannel::RemoveConsumer: Failed to modify the shared "
-            "release fence to include the dummy fence: %s",
-            strerror(errno));
+            "%s: Failed to modify the shared release fence to include the "
+            "dummy fence: %s",
+            __FUNCTION__, strerror(errno));
         return;
       }
-      ALOGW(
-          "ProducerChannel::RemoveConsumer: signal dummy release fence "
-          "buffer_id=%d",
-          buffer_id());
+      ALOGW("%s: signal dummy release fence buffer_id=%d", __FUNCTION__,
+            buffer_id());
       eventfd_write(dummy_fence_fd_.Get(), 1);
     }
   }
 }
 
-// Returns true if the given parameters match the underlying buffer parameters.
+// Returns true if the given parameters match the underlying buffer
+// parameters.
 bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height,
                                       uint32_t layer_count, uint32_t format,
                                       uint64_t usage,
-                                      size_t user_metadata_size) {
+                                      size_t user_metadata_size) const {
   return user_metadata_size == user_metadata_size_ &&
          buffer_.width() == width && buffer_.height() == height &&
          buffer_.layer_count() == layer_count && buffer_.format() == format &&
          buffer_.usage() == usage;
 }
 
+bool ProducerChannel::IsBufferReleasedByAllActiveClientsExceptForOrphans()
+    const {
+  return (buffer_state_->load(std::memory_order_acquire) &
+          ~orphaned_consumer_bit_mask_ &
+          active_clients_bit_mask_->load(std::memory_order_acquire)) == 0U;
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp
index c0c48c2..6b33f50 100644
--- a/services/vr/bufferhubd/producer_queue_channel.cpp
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -1,9 +1,8 @@
-#include "producer_queue_channel.h"
-
 #include <inttypes.h>
 
-#include "consumer_queue_channel.h"
-#include "producer_channel.h"
+#include <private/dvr/consumer_queue_channel.h>
+#include <private/dvr/producer_channel.h>
+#include <private/dvr/producer_queue_channel.h>
 
 using android::pdx::ErrorStatus;
 using android::pdx::Message;
@@ -76,6 +75,11 @@
           message);
       return true;
 
+    case BufferHubRPC::ProducerQueueInsertBuffer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>(
+          *this, &ProducerQueueChannel::OnProducerQueueInsertBuffer, message);
+      return true;
+
     case BufferHubRPC::ProducerQueueRemoveBuffer::Opcode:
       DispatchRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(
           *this, &ProducerQueueChannel::OnProducerQueueRemoveBuffer, message);
@@ -278,6 +282,86 @@
   return {{std::move(buffer_handle), slot}};
 }
 
+Status<size_t> ProducerQueueChannel::OnProducerQueueInsertBuffer(
+    pdx::Message& message, int buffer_cid) {
+  ATRACE_NAME("ProducerQueueChannel::InsertBuffer");
+  ALOGD_IF(TRACE,
+           "ProducerQueueChannel::InsertBuffer: channel_id=%d, buffer_cid=%d",
+           channel_id(), buffer_cid);
+
+  if (capacity_ >= BufferHubRPC::kMaxQueueCapacity) {
+    ALOGE("ProducerQueueChannel::InsertBuffer: reaches kMaxQueueCapacity.");
+    return ErrorStatus(E2BIG);
+  }
+  auto producer_channel = std::static_pointer_cast<ProducerChannel>(
+      service()->GetChannel(buffer_cid));
+  if (producer_channel == nullptr ||
+      producer_channel->channel_type() != BufferHubChannel::kProducerType) {
+    // Rejects the request if the requested buffer channel is invalid and/or
+    // it's not a ProducerChannel.
+    ALOGE(
+        "ProducerQueueChannel::InsertBuffer: Invalid buffer_cid=%d, "
+        "producer_buffer=0x%p, channel_type=%d.",
+        buffer_cid, producer_channel.get(),
+        producer_channel == nullptr ? -1 : producer_channel->channel_type());
+    return ErrorStatus(EINVAL);
+  }
+  if (producer_channel->GetActiveProcessId() != message.GetProcessId()) {
+    // Rejects the request if the requested buffer channel is not currently
+    // connected to the caller this is IPC request. This effectively prevents
+    // fake buffer_cid from being injected.
+    ALOGE(
+        "ProducerQueueChannel::InsertBuffer: Requested buffer channel "
+        "(buffer_cid=%d) is not connected to the calling process (pid=%d). "
+        "It's connected to a different process (pid=%d).",
+        buffer_cid, message.GetProcessId(),
+        producer_channel->GetActiveProcessId());
+    return ErrorStatus(EINVAL);
+  }
+  uint64_t buffer_state = producer_channel->buffer_state();
+  // TODO(b/112007999) add an atomic variable in metadata header in shared
+  // memory to indicate which client is the last producer of the buffer.
+  // Currently, the first client is the only producer to the buffer.
+  // Thus, it checks whether the first client gains the buffer below.
+  if (!BufferHubDefs::IsClientGained(buffer_state,
+                                     BufferHubDefs::kFirstClientBitMask)) {
+    // Rejects the request if the requested buffer is not in Gained state.
+    ALOGE(
+        "ProducerQueueChannel::InsertBuffer: The buffer (cid=%d, "
+        "state=0x%" PRIx64 ") is not in gained state.",
+        buffer_cid, buffer_state);
+    return ErrorStatus(EINVAL);
+  }
+
+  // Register the to-be-inserted buffer's channel_id into the first empty
+  // buffer slot.
+  size_t slot = 0;
+  for (; slot < BufferHubRPC::kMaxQueueCapacity; slot++) {
+    if (buffers_[slot].expired())
+      break;
+  }
+  if (slot == BufferHubRPC::kMaxQueueCapacity) {
+    ALOGE(
+        "ProducerQueueChannel::AllocateBuffer: Cannot find empty slot for new "
+        "buffer allocation.");
+    return ErrorStatus(E2BIG);
+  }
+
+  buffers_[slot] = producer_channel;
+  capacity_++;
+
+  // Notify each consumer channel about the new buffer.
+  for (auto* consumer_channel : consumer_channels_) {
+    ALOGD(
+        "ProducerQueueChannel::AllocateBuffer: Notified consumer with new "
+        "buffer, buffer_cid=%d",
+        buffer_cid);
+    consumer_channel->RegisterNewBuffer(producer_channel, slot);
+  }
+
+  return {slot};
+}
+
 Status<void> ProducerQueueChannel::OnProducerQueueRemoveBuffer(
     Message& /*message*/, size_t slot) {
   if (buffers_[slot].expired()) {
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 90edf69..d9df204 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -15,6 +15,7 @@
     "android.frameworks.vr.composer@1.0",
     "android.hardware.graphics.composer@2.1",
     "android.hardware.graphics.mapper@2.0",
+    "android.hardware.graphics.mapper@3.0",
     "libbase",
     "libbufferhubqueue",
     "libbinder",
@@ -39,6 +40,10 @@
     "android.hardware.graphics.composer@2.1-hal",
   ],
 
+  export_static_lib_headers: [
+    "libdisplay",
+  ],
+
   export_shared_lib_headers: [
     "android.frameworks.vr.composer@1.0",
     "android.hardware.graphics.composer@2.1",
@@ -48,9 +53,9 @@
 
   cflags: [
     "-DLOG_TAG=\"vr_hwc\"",
+    "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
     "-Wall",
     "-Werror",
-    // mVrClient unused in vr_composer_client.cpp
     "-Wno-error=unused-private-field",
     // Warnings in vr_hwc.cpp to be fixed after sync of goog/master.
     "-Wno-sign-compare",
@@ -115,6 +120,7 @@
 
 cc_binary {
   name: "vr_hwc",
+  vintf_fragments: ["manifest_vr_hwc.xml"],
   srcs: [
     "vr_hardware_composer_service.cpp"
   ],
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp
index d775711..786d5fa 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.cpp
+++ b/services/vr/hardware_composer/impl/vr_composer_client.cpp
@@ -45,7 +45,7 @@
 }
 
 VrComposerClient::VrCommandEngine::VrCommandEngine(VrComposerClient& client)
-    : ComposerCommandEngine(client.mHal, client.mResources.get()), mVrClient(client),
+    : ComposerCommandEngine(client.mHal, client.mResources.get()),
       mVrHal(client.mVrHal) {}
 
 VrComposerClient::VrCommandEngine::~VrCommandEngine() {}
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h
index 2ad95fc..0b7ce5e 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.h
+++ b/services/vr/hardware_composer/impl/vr_composer_client.h
@@ -54,7 +54,6 @@
 
     IVrComposerClient::BufferMetadata readBufferMetadata();
 
-    VrComposerClient& mVrClient;
     android::dvr::VrHwc& mVrHal;
 
     VrCommandEngine(const VrCommandEngine&) = delete;
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 4af47d2..fb7932d 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -16,9 +16,11 @@
 #include "impl/vr_hwc.h"
 
 #include "android-base/stringprintf.h"
+#include <binder/IServiceManager.h>
 #include <cutils/properties.h>
 #include <private/dvr/display_client.h>
 #include <ui/Fence.h>
+#include <utils/Trace.h>
 
 #include <mutex>
 
@@ -244,29 +246,38 @@
 ////////////////////////////////////////////////////////////////////////////////
 // VrHwcClient
 
-VrHwc::VrHwc() {}
+VrHwc::VrHwc() {
+  vsync_callback_ = new VsyncCallback;
+}
 
-VrHwc::~VrHwc() {}
+VrHwc::~VrHwc() {
+  vsync_callback_->SetEventCallback(nullptr);
+}
 
 bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; }
 
 void VrHwc::registerEventCallback(EventCallback* callback) {
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-    event_callback_ = callback;
-    int32_t width, height;
-    GetPrimaryDisplaySize(&width, &height);
-    // Create the primary display late to avoid initialization issues between
-    // VR HWC and SurfaceFlinger.
-    displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
-  }
+  std::unique_lock<std::mutex> lock(mutex_);
+  event_callback_ = callback;
+  int32_t width, height;
+  GetPrimaryDisplaySize(&width, &height);
+  // Create the primary display late to avoid initialization issues between
+  // VR HWC and SurfaceFlinger.
+  displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
+
+  // Surface flinger will make calls back into vr_hwc when it receives the
+  // onHotplug() call, so it's important to release mutex_ here.
+  lock.unlock();
   event_callback_->onHotplug(kDefaultDisplayId,
                              IComposerCallback::Connection::CONNECTED);
+  lock.lock();
+  UpdateVsyncCallbackEnabledLocked();
 }
 
 void VrHwc::unregisterEventCallback() {
   std::lock_guard<std::mutex> guard(mutex_);
   event_callback_ = nullptr;
+  UpdateVsyncCallbackEnabledLocked();
 }
 
 uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; }
@@ -321,10 +332,14 @@
   return Error::NONE;
 }
 
-Error VrHwc::getClientTargetSupport(Display /* display */, uint32_t /* width */,
+Error VrHwc::getClientTargetSupport(Display display, uint32_t /* width */,
                                     uint32_t /* height */,
                                     PixelFormat /* format */,
                                     Dataspace /* dataspace */) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!FindDisplay(display))
+    return Error::BAD_DISPLAY;
+
   return Error::NONE;
 }
 
@@ -455,16 +470,37 @@
   if (!display_ptr)
     return Error::BAD_DISPLAY;
 
+  if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3)
+    return Error::BAD_PARAMETER;
+
   display_ptr->set_color_mode(mode);
   return Error::NONE;
 }
 
 Error VrHwc::setPowerMode(Display display, IComposerClient::PowerMode mode) {
+  bool dozeSupported = false;
+
+  Error dozeSupportError = getDozeSupport(display, &dozeSupported);
+
+  if (dozeSupportError != Error::NONE)
+    return dozeSupportError;
+
   std::lock_guard<std::mutex> guard(mutex_);
   auto display_ptr = FindDisplay(display);
   if (!display_ptr)
     return Error::BAD_DISPLAY;
 
+  if (mode < IComposerClient::PowerMode::OFF ||
+      mode > IComposerClient::PowerMode::DOZE_SUSPEND) {
+    return Error::BAD_PARAMETER;
+  }
+
+  if (!dozeSupported &&
+      (mode == IComposerClient::PowerMode::DOZE ||
+       mode == IComposerClient::PowerMode::DOZE_SUSPEND)) {
+    return Error::UNSUPPORTED;
+  }
+
   display_ptr->set_power_mode(mode);
   return Error::NONE;
 }
@@ -475,8 +511,45 @@
   if (!display_ptr)
     return Error::BAD_DISPLAY;
 
-  display_ptr->set_vsync_enabled(enabled);
-  return Error::NONE;
+  if (enabled != IComposerClient::Vsync::ENABLE &&
+      enabled != IComposerClient::Vsync::DISABLE) {
+    return Error::BAD_PARAMETER;
+  }
+
+  Error set_vsync_result = Error::NONE;
+  if (display == kDefaultDisplayId) {
+    sp<IVsyncService> vsync_service = interface_cast<IVsyncService>(
+        defaultServiceManager()->getService(
+            String16(IVsyncService::GetServiceName())));
+    if (vsync_service == nullptr) {
+      ALOGE("Failed to get vsync service");
+      return Error::NO_RESOURCES;
+    }
+
+    if (enabled == IComposerClient::Vsync::ENABLE) {
+      ALOGI("Enable vsync");
+      display_ptr->set_vsync_enabled(true);
+      status_t result = vsync_service->registerCallback(vsync_callback_);
+      if (result != OK) {
+        ALOGE("%s service registerCallback() failed: %s (%d)",
+            IVsyncService::GetServiceName(), strerror(-result), result);
+        set_vsync_result = Error::NO_RESOURCES;
+      }
+    } else if (enabled == IComposerClient::Vsync::DISABLE) {
+      ALOGI("Disable vsync");
+      display_ptr->set_vsync_enabled(false);
+      status_t result = vsync_service->unregisterCallback(vsync_callback_);
+      if (result != OK) {
+        ALOGE("%s service unregisterCallback() failed: %s (%d)",
+            IVsyncService::GetServiceName(), strerror(-result), result);
+        set_vsync_result = Error::NO_RESOURCES;
+      }
+    }
+
+    UpdateVsyncCallbackEnabledLocked();
+  }
+
+  return set_vsync_result;
 }
 
 Error VrHwc::setColorTransform(Display display, const float* matrix,
@@ -559,7 +632,8 @@
   frame.display_height = display_ptr->height();
   frame.active_config = display_ptr->active_config();
   frame.power_mode = display_ptr->power_mode();
-  frame.vsync_enabled = display_ptr->vsync_enabled();
+  frame.vsync_enabled = display_ptr->vsync_enabled() ?
+      IComposerClient::Vsync::ENABLE : IComposerClient::Vsync::DISABLE;
   frame.color_transform_hint = display_ptr->color_transform_hint();
   frame.color_mode = display_ptr->color_mode();
   memcpy(frame.color_transform, display_ptr->color_transform(),
@@ -911,6 +985,15 @@
   return iter == displays_.end() ? nullptr : iter->second.get();
 }
 
+void VrHwc::UpdateVsyncCallbackEnabledLocked() {
+  auto primary_display = FindDisplay(kDefaultDisplayId);
+  LOG_ALWAYS_FATAL_IF(event_callback_ != nullptr && primary_display == nullptr,
+      "Should have created the primary display by now");
+  bool send_vsync =
+      event_callback_ != nullptr && primary_display->vsync_enabled();
+  vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr);
+}
+
 void HwcLayer::dumpDebugInfo(std::string* result) const {
   if (!result) {
     return;
@@ -928,5 +1011,18 @@
       buffer_metadata.layerCount, buffer_metadata.format);
 }
 
+status_t VrHwc::VsyncCallback::onVsync(int64_t vsync_timestamp) {
+  ATRACE_NAME("vr_hwc onVsync");
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (callback_ != nullptr)
+    callback_->onVsync(kDefaultDisplayId, vsync_timestamp);
+  return OK;
+}
+
+void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  callback_ = callback;
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
index 74c1699..e8c0212 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ b/services/vr/hardware_composer/impl/vr_hwc.h
@@ -20,6 +20,7 @@
 #include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
 #include <android/hardware/graphics/composer/2.1/IComposer.h>
 #include <composer-hal/2.1/ComposerHal.h>
+#include <private/dvr/vsync_service.h>
 #include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/StrongPointer.h>
@@ -154,10 +155,8 @@
   IComposerClient::PowerMode power_mode() const { return power_mode_; }
   void set_power_mode(IComposerClient::PowerMode mode) { power_mode_ = mode; }
 
-  IComposerClient::Vsync vsync_enabled() const { return vsync_enabled_; }
-  void set_vsync_enabled(IComposerClient::Vsync vsync) {
-    vsync_enabled_ = vsync;
-  }
+  bool vsync_enabled() const { return vsync_enabled_; }
+  void set_vsync_enabled(bool vsync) {vsync_enabled_ = vsync;}
 
   const float* color_transform() const { return color_transform_; }
   int32_t color_transform_hint() const { return color_transform_hint_; }
@@ -185,7 +184,7 @@
   Config active_config_;
   ColorMode color_mode_;
   IComposerClient::PowerMode power_mode_;
-  IComposerClient::Vsync vsync_enabled_;
+  bool vsync_enabled_ = false;
   float color_transform_[16];
   int32_t color_transform_hint_;
 
@@ -297,8 +296,23 @@
   void UnregisterObserver(Observer* observer) override;
 
  private:
+  class VsyncCallback : public BnVsyncCallback {
+   public:
+    status_t onVsync(int64_t vsync_timestamp) override;
+    void SetEventCallback(EventCallback* callback);
+   private:
+    std::mutex mutex_;
+    EventCallback* callback_;
+  };
+
   HwcDisplay* FindDisplay(Display display);
 
+  // Re-evaluate whether or not we should start making onVsync() callbacks to
+  // the client. We need enableCallback(true) to have been called, and
+  // setVsyncEnabled() to have been called for the primary display. The caller
+  // must have mutex_ locked already.
+  void UpdateVsyncCallbackEnabledLocked();
+
   wp<VrComposerClient> client_;
 
   // Guard access to internal state from binder threads.
@@ -310,6 +324,8 @@
   EventCallback* event_callback_ = nullptr;
   Observer* observer_ = nullptr;
 
+  sp<VsyncCallback> vsync_callback_;
+
   VrHwc(const VrHwc&) = delete;
   void operator=(const VrHwc&) = delete;
 };
diff --git a/services/vr/hardware_composer/manifest_vr_hwc.xml b/services/vr/hardware_composer/manifest_vr_hwc.xml
new file mode 100644
index 0000000..1068cac
--- /dev/null
+++ b/services/vr/hardware_composer/manifest_vr_hwc.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+    <hal>
+      <name>android.hardware.graphics.composer</name>
+      <transport>hwbinder</transport>
+      <version>2.1</version>
+      <interface>
+          <name>IComposer</name>
+          <instance>vr</instance>
+      </interface>
+    </hal>
+</manifest>
diff --git a/services/vr/performanced/cpu_set.cpp b/services/vr/performanced/cpu_set.cpp
index 1a7264c..d940b79 100644
--- a/services/vr/performanced/cpu_set.cpp
+++ b/services/vr/performanced/cpu_set.cpp
@@ -106,7 +106,7 @@
   return sets;
 }
 
-std::string CpuSetManager::DumpState() const {
+void CpuSetManager::DumpState(std::ostringstream& stream) const {
   size_t max_path = 0;
   std::vector<CpuSet*> sets;
 
@@ -119,8 +119,6 @@
     return a->path() < b->path();
   });
 
-  std::ostringstream stream;
-
   stream << std::left;
   stream << std::setw(max_path) << "Path";
   stream << " ";
@@ -146,8 +144,6 @@
     stream << std::setw(6) << set->GetTasks().size();
     stream << std::endl;
   }
-
-  return stream.str();
 }
 
 void CpuSetManager::MoveUnboundTasks(const std::string& target_set) {
diff --git a/services/vr/performanced/cpu_set.h b/services/vr/performanced/cpu_set.h
index 6879272..4c25e9e 100644
--- a/services/vr/performanced/cpu_set.h
+++ b/services/vr/performanced/cpu_set.h
@@ -5,6 +5,7 @@
 
 #include <memory>
 #include <mutex>
+#include <sstream>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -83,7 +84,7 @@
   // to shield the system from interference from unbound kernel threads.
   void MoveUnboundTasks(const std::string& target_set);
 
-  std::string DumpState() const;
+  void DumpState(std::ostringstream& stream) const;
 
   operator bool() const { return root_set_ != nullptr; }
 
diff --git a/services/vr/performanced/performance_service.cpp b/services/vr/performanced/performance_service.cpp
index 4c26671..73dcf76 100644
--- a/services/vr/performanced/performance_service.cpp
+++ b/services/vr/performanced/performance_service.cpp
@@ -1,5 +1,7 @@
 #include "performance_service.h"
 
+#include <sstream>
+
 #include <sched.h>
 #include <sys/prctl.h>
 #include <unistd.h>
@@ -31,6 +33,10 @@
 
 const char kRootCpuSet[] = "/";
 
+const char kVrAppRenderPolicy[] = "vr:app:render";
+
+const bool kAllowAppsToRequestVrAppRenderPolicy = false;
+
 constexpr unsigned long kTimerSlackForegroundNs = 50000;
 constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
 
@@ -124,9 +130,6 @@
   // TODO(eieio): Replace this witha device-specific config file. This is just a
   // hack for now to put some form of permission logic in place while a longer
   // term solution is developed.
-  using AllowRootSystem =
-      CheckAnd<SameProcess,
-               CheckOr<UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>>;
   using AllowRootSystemGraphics =
       CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_GRAPHICS>,
                                     GroupId<AID_SYSTEM, AID_GRAPHICS>>>;
@@ -136,6 +139,17 @@
   using AllowRootSystemTrusted =
       CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>;
 
+  auto vr_app_render_permission_check = [](
+      const pdx::Message& sender, const Task& task) {
+          // For vr:app:render, in addition to system/root apps and VrCore, we
+          // also allow apps to request vr:app:render if
+          // kAllowAppsToRequestVrAppRenderPolicy == true, but not for other
+          // apps, only for themselves.
+          return (task && task.thread_group_id() == sender.GetProcessId() &&
+                  kAllowAppsToRequestVrAppRenderPolicy)
+              || AllowRootSystemTrusted::Check(sender, task);
+      };
+
   partition_permission_check_ = AllowRootSystemTrusted::Check;
 
   // Setup the scheduler classes.
@@ -170,28 +184,28 @@
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
         .priority = fifo_low,
-        .permission_check = AllowRootSystem::Check}},
+        .permission_check = AllowRootSystemTrusted::Check}},
       {"sensors:low",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
         .priority = fifo_low,
-        .permission_check = AllowRootSystem::Check}},
+        .permission_check = AllowRootSystemTrusted::Check}},
       {"sensors:high",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
         .priority = fifo_low + 1,
-        .permission_check = AllowRootSystem::Check}},
+        .permission_check = AllowRootSystemTrusted::Check}},
       {"vr:system:arp",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
         .priority = fifo_medium + 2,
         .permission_check = AllowRootSystemTrusted::Check,
         "/system/performance"}},
-      {"vr:app:render",
+      {kVrAppRenderPolicy,
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
         .priority = fifo_medium + 1,
-        .permission_check = AllowRootSystemTrusted::Check,
+        .permission_check = vr_app_render_permission_check,
         "/application/performance"}},
       {"normal",
        {.timer_slack = kTimerSlackForegroundNs,
@@ -218,7 +232,10 @@
 }
 
 std::string PerformanceService::DumpState(size_t /*max_length*/) {
-  return cpuset_.DumpState();
+  std::ostringstream stream;
+  stream << "vr_app_render_thread: " << vr_app_render_thread_ << std::endl;
+  cpuset_.DumpState(stream);
+  return stream.str();
 }
 
 Status<void> PerformanceService::OnSetSchedulerPolicy(
@@ -244,7 +261,12 @@
     // Make sure the sending process is allowed to make the requested change to
     // this task.
     if (!config.IsAllowed(message, task))
-      return ErrorStatus(EINVAL);
+      return ErrorStatus(EPERM);
+
+    if (scheduler_policy == kVrAppRenderPolicy) {
+      // We only allow one vr:app:render thread at a time
+      SetVrAppRenderThread(task_id);
+    }
 
     // Get the thread group's cpu set. Policies that do not specify a cpuset
     // should default to this cpuset.
@@ -302,14 +324,16 @@
 Status<void> PerformanceService::OnSetCpuPartition(
     Message& message, pid_t task_id, const std::string& partition) {
   Task task(task_id);
-  if (!task || task.thread_group_id() != message.GetProcessId())
+  if (!task)
     return ErrorStatus(EINVAL);
+  if (task.thread_group_id() != message.GetProcessId())
+    return ErrorStatus(EPERM);
 
   // Temporary permission check.
   // TODO(eieio): Replace this with a configuration file.
   if (partition_permission_check_ &&
       !partition_permission_check_(message, task)) {
-    return ErrorStatus(EINVAL);
+    return ErrorStatus(EPERM);
   }
 
   auto target_set = cpuset_.Lookup(partition);
@@ -336,7 +360,12 @@
     // Make sure the sending process is allowed to make the requested change to
     // this task.
     if (!config.IsAllowed(message, task))
-      return ErrorStatus(EINVAL);
+      return ErrorStatus(EPERM);
+
+    if (scheduler_class == kVrAppRenderPolicy) {
+      // We only allow one vr:app:render thread at a time
+      SetVrAppRenderThread(task_id);
+    }
 
     struct sched_param param;
     param.sched_priority = config.priority;
@@ -359,8 +388,10 @@
                                                           pid_t task_id) {
   // Make sure the task id is valid and belongs to the sending process.
   Task task(task_id);
-  if (!task || task.thread_group_id() != message.GetProcessId())
+  if (!task)
     return ErrorStatus(EINVAL);
+  if (task.thread_group_id() != message.GetProcessId())
+    return ErrorStatus(EPERM);
 
   return task.GetCpuSetPath();
 }
@@ -393,5 +424,38 @@
   }
 }
 
+void PerformanceService::SetVrAppRenderThread(pid_t new_vr_app_render_thread) {
+  ALOGI("SetVrAppRenderThread old=%d new=%d",
+      vr_app_render_thread_, new_vr_app_render_thread);
+
+  if (vr_app_render_thread_ >= 0 &&
+      vr_app_render_thread_ != new_vr_app_render_thread) {
+    // Restore the default scheduler policy and priority on the previous
+    // vr:app:render thread.
+    struct sched_param param;
+    param.sched_priority = 0;
+    if (sched_setscheduler(vr_app_render_thread_, SCHED_NORMAL, &param) < 0) {
+      if (errno == ESRCH) {
+        ALOGI("Failed to revert %s scheduler policy. Couldn't find thread %d."
+            " Was the app killed?", kVrAppRenderPolicy, vr_app_render_thread_);
+      } else {
+        ALOGE("Failed to revert %s scheduler policy: %s",
+            kVrAppRenderPolicy, strerror(errno));
+      }
+    }
+
+    // Restore the default timer slack on the previous vr:app:render thread.
+    prctl(PR_SET_TIMERSLACK_PID, kTimerSlackForegroundNs,
+        vr_app_render_thread_);
+  }
+
+  // We could also reset the thread's cpuset here, but the cpuset is already
+  // managed by Android. Better to let Android adjust the cpuset as the app
+  // moves to the background, rather than adjust it ourselves here, and risk
+  // stomping on the value set by Android.
+
+  vr_app_render_thread_ = new_vr_app_render_thread;
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/performanced/performance_service.h b/services/vr/performanced/performance_service.h
index 6b519ab..fe63756 100644
--- a/services/vr/performanced/performance_service.h
+++ b/services/vr/performanced/performance_service.h
@@ -39,6 +39,14 @@
   pdx::Status<std::string> OnGetCpuPartition(pdx::Message& message,
                                              pid_t task_id);
 
+  // Set which thread gets the vr:app:render policy. Only one thread at a time
+  // is allowed to have vr:app:render. If multiple threads are allowed
+  // vr:app:render, and those threads busy loop, the system can freeze. When
+  // SetVrAppRenderThread() is called, the thread which we had previously
+  // assigned vr:app:render will have its scheduling policy reset to default
+  // values.
+  void SetVrAppRenderThread(pid_t new_vr_app_render_thread);
+
   CpuSetManager cpuset_;
 
   int sched_fifo_min_priority_;
@@ -70,6 +78,8 @@
   std::function<bool(const pdx::Message& message, const Task& task)>
       partition_permission_check_;
 
+  pid_t vr_app_render_thread_ = -1;
+
   PerformanceService(const PerformanceService&) = delete;
   void operator=(const PerformanceService&) = delete;
 };
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index fdbd969..a607a5d 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -984,7 +984,7 @@
     uint32_t icd_api_version;
     PFN_vkEnumerateInstanceVersion pfn_enumerate_instance_version =
         reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
-            Hal::Device().GetInstanceProcAddr(NULL,
+            Hal::Device().GetInstanceProcAddr(nullptr,
                                               "vkEnumerateInstanceVersion"));
     if (!pfn_enumerate_instance_version) {
         icd_api_version = VK_API_VERSION_1_0;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 3db8a39..8601373 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -335,15 +335,15 @@
             swapchain.surface.window.get(), ti.native_frame_id_,
             &desired_present_time, &render_complete_time,
             &composition_latch_time,
-            NULL,  //&first_composition_start_time,
-            NULL,  //&last_composition_start_time,
-            NULL,  //&composition_finish_time,
+            nullptr,  //&first_composition_start_time,
+            nullptr,  //&last_composition_start_time,
+            nullptr,  //&composition_finish_time,
             // TODO(ianelliott): Maybe ask if this one is
             // supported, at startup time (since it may not be
             // supported):
             &actual_present_time,
-            NULL,  //&dequeue_ready_time,
-            NULL /*&reads_done_time*/);
+            nullptr,  //&dequeue_ready_time,
+            nullptr /*&reads_done_time*/);
 
         if (ret != android::NO_ERROR) {
             continue;
@@ -569,21 +569,17 @@
     switch (native_format) {
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_RGB_565:
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
             format_supported = true;
             break;
         default:
             break;
     }
 
-    // USAGE_CPU_READ_MASK 0xFUL
-    // USAGE_CPU_WRITE_MASK (0xFUL << 4)
-    // The currently used bits are as below:
-    // USAGE_CPU_READ_RARELY = 2UL
-    // USAGE_CPU_READ_OFTEN = 3UL
-    // USAGE_CPU_WRITE_RARELY = (2UL << 4)
-    // USAGE_CPU_WRITE_OFTEN = (3UL << 4)
-    *supported = static_cast<VkBool32>(format_supported ||
-                                       (surface->consumer_usage & 0xFFUL) == 0);
+    *supported = static_cast<VkBool32>(
+        format_supported || (surface->consumer_usage &
+                             (AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
+                              AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) == 0);
 
     return VK_SUCCESS;
 }
@@ -700,6 +696,10 @@
          VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
         {VK_FORMAT_R8G8B8A8_SRGB,
          VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
+        {VK_FORMAT_R16G16B16A16_SFLOAT,
+         VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT},
+        {VK_FORMAT_R16G16B16A16_SFLOAT,
+         VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT},
     };
     const uint32_t kNumWideColorFormats =
         sizeof(kWideColorFormats) / sizeof(kWideColorFormats[0]);
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index 93620f4..78d6694 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -10,7 +10,6 @@
         "-Wimplicit-fallthrough",
     ],
     cppflags: [
-        "-std=c++11",
         "-Wno-sign-compare",
     ],
     export_include_dirs: [
@@ -37,7 +36,6 @@
         "-Wimplicit-fallthrough",
     ],
     cppflags: [
-        "-std=c++11",
         "-Wno-sign-compare",
     ],
     export_include_dirs: [